我们正在开发一个最终用户模式是动态的应用程序(为此,我们有一个很好的业务案例-静态模型无法轻松处理)。
我已经使用.NET DynamicObject类来允许从代码轻松地解决这些动态模式对象,并且期望它可以与MVC模型元数据一起使用。但是,MVC元数据支持似乎受阻,因为它仅处理按类型定义的元数据,而不是按对象定义的元数据,在这种情况下就是这种情况。
即使当我挖掘并尝试实现自己的ModelMetadataProvider时,似乎也没有传入必要的信息-GetMetadataForProperty方法尤其成问题。实际上,我需要访问该属性的父对象或容器对象,但所传递的只是类型。
上面的内容主要是通过ModelMetadata类中的FromStringExpression方法调用的。实际上,此方法确实具有容器(至少在这种情况下),但是没有通过容器。当找到有关存储在ViewData中的表达式的 View 数据时,将执行此分支。如果失败了,它会退回到通过ModelMetadata对象进行查找-具有讽刺意味的是,它可能对我有用。尤其令人烦恼的是FromStringExpression方法是静态的,因此我不能轻易覆盖其行为。
无奈之下,我已经考虑过尝试遍历modelAccessor表达式,但这似乎充其量也是极其脆弱的。
我已经广泛搜索了解决方案。很多人都提到了布拉德·威尔逊(Brad Wilson)关于非类模型的演讲(http://channel9.msdn.com/Series/mvcConf/mvcConf-2011-Brad-Wilson-Advanced-MVC-3),但是,如果查看所提供的实际代码,您会发现它TOO绑定(bind)到TYPE而不是对象-换句话说,不是很有用。其他人指出了http://fluentvalidation.codeplex.com/,但这似乎仅适用于验证方,我怀疑它遭受与上述相同的问题(绑定(bind)到类型而不是对象)。
例如,我可能有一个包含一系列字段对象的字典对象。看起来像(非常简化/简化的示例):
public class Entity : DynamicObject, ICustomTypeDescriptor
{
public Guid ID { get; set; }
public Dictionary<string, EntityProp> Props { get; set; }
... DynamicObject and ICustomTypeDescriptor implementation to expose Props as dynamic properties against this Entity ...
}
public class EntityProp
{
public string Name { get; set; }
public object Value { get; set; }
public Type Type { get; set; }
public bool IsRequired { get; set; }
}
这可能作为其 View 模式(或其一部分)传递给 View ,在我看来,我想使用:
@Html.EditorForModel()
有没有人找到解决方法?
我已经确定了两种可能的替代方法,但是它们都有明显的缺点:
最佳答案
也许创建一个自定义的ModelMetadataProvider:
public class CustomViewModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
{
if (containerType == null)
{
throw new ArgumentNullException("containerType");
}
return GetMetadataForPropertiesImpl(container, containerType);
}
private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType)
{
var propertiesMetadata = new List<ModelMetadata>();
foreach (EntityProp eprop in ((Entity)container).Props.Values)
{
Func<object> modelAccessor = () => eprop;
propertiesMetadata.add(GetMetadataForProperty(modelAccessor, containerType, eprop.Name));
}
return propertiesMetadata; // List returned instead of yielding, hoping not be needed to re-call this method more than once
}
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) {
if (containerType == null) {
throw new ArgumentNullException("containerType");
}
if (String.IsNullOrEmpty(propertyName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
}
return CreateMetadata(null, containerType, modelAccessor, modelAccessor().Type, propertyName);
}
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
EntityProp eprop = modelAccessor();
DataAnnotationsModelMetadata result;
if (propertyName == null)
{
// You have the main object
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
else
{
// You have here the property object
result = new DataAnnotationsModelMetadata(this, containerType, () => eprop.Value, modelType, propertyName, null);
result.IsRequired = eprop.IsRequired;
}
return result;
}
}
最后,在
Global.asax.cs
中设置您的自定义提供程序:protected void Application_Start()
{
//...
ModelMetadataProviders.Current = new CustomViewModelMetadataProvider();
}
关于c# - MVC中的动态模型(非类)元数据提供程序,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20602000/