如何解决自定义ModelMetadataProvider添加的属性未在模型绑定中考虑 背景解决方案问题
免责声明:这是一个漫长的声明。几乎肯定需要对ASP.NET Core MVC内部知识不了解。这是龙。
背景
我正在尝试实现一种在ASP.NET MVC Core中扩展类型元数据的方法。
我要这样做的原因是我的数据模型被多个项目使用,因此我将它们放在NuGet包中进行了共享:
// defined in NuGet package
public class MyModel
{
public int MyProperty { get; set; }
public MynestedModel nestedModel { get; set; }
}
public class MynestedModel
{
public bool nestedProperty { get; set; }
}
^清单1
但是,某些项目将需要对模型类型应用其他元数据-例如,在ASP.NET Core项目中,这些模型将用作输入并因此参与模型绑定,因此它们需要FromQuery
,FromHeader
等属性。我显然不能在程序包中执行此操作,因为不同的消费者项目将需要根据其用例应用不同的属性。
最简单且最保证工作的方法是将所有模型属性修改为virtual
,然后让子类在添加属性时根据需要覆盖这些属性:
// defined in NuGet package
public class MyModel
{
public virtual int MyProperty { get; set; }
public virtual MynestedModel nestedModel { get; set; }
}
public class MynestedModel
{
public virtual bool nestedProperty { get; set; }
}
// defined in ASP.NET MVC project consuming above package
public class Myviewmodel : MyModel
{
[FromQuery(Name = "foo")]
public override int MyProperty { get; set; }
public new // can't use override,as type is different
Mynestedviewmodel nestedModel { get; set; }
}
[Bind(Prefix = "")]
public class Mynestedviewmodel : MynestedModel
{
[FromQuery(Name = "bar")]
public override bool nestedProperty { get; set; }
}
^清单2
我不想这样做,因为在每个具有子模型属性的模型中,都必须对该子模型进行子类化,然后才能将其用作替代,但必须用new
重新声明-new
的语义在这里对我不起作用。另外,我并不打算将模型类型进行子类化。
我知道了ModelMetadataTypeAttribute
,但是该属性的预期用途是在要增强的模型类型上,而不是在进行增强的类型上。因为在我的情况下,无法提前知道实际的元数据类型是什么(因为它们在使用项目中已定义),所以我无法使用ModelMetadataTypeAttribute
。子类和子类也将不起作用-ModelMetadataTypeAttribute
仅适用于指定的类,partial
不能跨程序集。
解决方案
实质上,ModelMetadataTypeAttribute
的“反向”版本应用于类型进行增强,并指向要增强的模型类型 :
// defined in ASP.NET MVC project consuming package from Listing 1
[ReverseModelMetadataType(typeof(MyModel))]
public class MyModelMetadata
{
[FromQuery(Name = "foo")]
public int MyProperty { get; set; }
public MynestedModel nestedModel { get; set; }
}
[ReverseModelMetadataType(typeof(nestedModel))]
[Bind(Prefix = "")]
public class nestedModelMetadata
{
[FromQuery(Name = "bar")]
public bool nestedProperty { get; set; }
}
^清单3
目的是在运行时,模型绑定基础结构将使用MyModel.MyProperty
来绑定[FromQuery(Name = "foo")]
,即从查询字符串中获取名为foo
的变量。同样,MyModel.nestedModel.nestedProperty
应该简单地与查询字符串绑定为bar
。
为了完成这项工作,我实现了一个自定义ModelMetadataProvider
,它继承了DefaultModelMetadataProvider
并覆盖了CreatePropertyDetails
方法:
// defined in same ASP.NET Core MVC project as Listing 3
public class MyModelMetadataProvider : DefaultModelMetadataProvider
{
protected override DefaultMetadataDetails[] CreatePropertyDetails(ModelMetadataIdentity key)
{
var defaultPropertyDetails = base.CreatePropertyDetails(key);
// check if the key.ModelType should be augmented
// if so,mutate the relevant element(s) of the defaultPropertyDetails array to do the augmentation
return defaultPropertyDetails;
}
}
变异部分有效地更改了相关的DefaultMetadataDetails.modelattributes
成员,以将其他属性应用于用ReverseModelMetadataTypeAttribute
装饰的类型的属性。然后,通过DefaultModelMetadataProvider
中的以下内容,使ASP.NET MVC Core使用我的提供程序而不是其Startup.cs#ConfigureServices
:
services.AddSingleton<IModelMetadataProvider,MyModelMetadataProvider>();
我已经验证了提供者已注册并且突变代码可以正常工作:击中MyModelMetadataProvider.CreatePropertyDetails
并且返回的defaultPropertyDetails
确实包含清单3中我的*ModelMetadata
类应用的额外属性。
问题
模型绑定不起作用::ASP.NET Core MVC本质上表现为好像清单3中添加的属性不存在,我也不知道为什么。据我所知,对各种IModelMetadataProvider
方法的调用结果将被缓存,然后用于随后所需的所有元数据查找,包括用于绑定模型。
我希望有人在我自己逐步经历ASP.NET Core源代码的痛苦之前,可以就如何使其工作提供一些建议。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。