我真的需要对这个问题进行第二次观察,所以我希望你们中的一些人能给我一些反馈,我想我已经盯着它太久了。
我正在尝试使用 ASP.NET MVC3 设置一个网站,在这个网站中我需要创建动态对象的灵活性。但我的意思是在我的数据库中设置了一系列表来存储有关这些动态对象中包含的结构和数据的信息。我正在使用一个预先存在的数据库,所以我(在一定程度上)我可以修改的内容是有限的。当我在数据库中查询一个动态对象(不是 .NET 4.0 的动态对象)时,我传入了我的 Id,我得到的是一个简单的对象,它可能有一些属性,这些属性仅供内部使用,还有一个属性是一个集合,其中包含我的动态对象的所有属性。因此,如果我的动态对象是针对具有 Name、DoB 和 Sex 的人,那么我的集合将包含三个对象,每个属性一个。这允许站点管理员在运行时添加新字段,并且网站将自动呈现它们,允许更新等。现在我的模型绑定当前可以用于此数据结构的显示和回发,对于集合中的每个对象,我渲染两条数据,属性的唯一 ID(目前是隐藏字段,Id 是 Guid)和属性的值。我的问题是安全方面。
如果我正在处理强类型对象,我可以创建自定义 ViewModel 并完成它,或者将 Bind() 属性添加到操作的签名中,但由于这些对象的属性是灵活的集合,我不知道如何处理它。操作级别的安全性很简单,我可以创建一个自定义的 Authorize 属性并查询数据库的权限,但我需要能够限制集合行为以根据用户权限显示和接受信息。例如,如果我要为 person 对象添加一个社会安全号码属性,我不希望它为某些人呈现到屏幕上。但是因为属性是可以在运行时改变的,所以权限也可以。
就我的想法而言,这就是我所处的位置......
因此,我需要一种方法来确定属性集合中的哪些对象可以呈现到屏幕或绑定到回发,具体取决于用户权限。对于显示对象,我认为我别无选择,只能以某种方式将权限包含在 ViewModel 对象中,并在用于属性集合中的对象类型的 DisplayTemplate 中查询这些权限。或者我可以编写某种自定义 ModelBinder,因为它用于调用 Html.Display() 和 Html.Editor() 并考虑过滤 ModelBinder 内的列表。
不过,我对回发也有类似的问题。当它被发回时,我有一组数据被传回,只有一个 Guid 和一个值。但是我需要确保用户没有将自己的字段注入到表单中,并且我还需要确保对于传递回操作的属性,用户具有足够的权限。理想情况下,我想将此检查集成到模型绑定中,并在可能的情况下重用从 MetaData 填充的一些信息,例如,这样它就可以简单地忽略传入的用户无权更改的数据,或者如果不这样做,请检查用户是否有权访问他们尝试在处理回发的操作开始时完成的 IsValid 检查中设置的所有属性。
然后是 MetaData 的动态构建,用于根据数据库中的信息为每个属性调用 Html.Display() 和 Html.Editor(),因为我没有物理属性是我可以装饰的类数据注释。
问题是,当涉及到覆盖 ModelBinders、ModelMetaDataProviders 或 ModelValidationProviders 之类的默认实现时,我不熟悉 MVC 的内部结构。
您能否就您能想到的实现我所描述的最佳方式提供一些建议,或者如果您知道涵盖此示例的其他文章,我非常希望看到它们,但我对 Google 的运气并不好到目前为止,关于这个特定主题。
编辑:有关我所做的全部详细信息,请参阅下面的答案
编辑:我让元数据提供程序工作。只需要实现我自己的类并从 ModelMetadataProvider 继承。
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
ModelMetadata metadata;
if (containerType == typeof(PseudoObjectAttributeViewModel))
{
switch (propertyName)
{
case "StringValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(string), propertyName);
break;
case "DateValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(DateTime?), propertyName);
break;
case "DoubleValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(double?), propertyName);
break;
case "LongValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(long?), propertyName);
break;
case "BooleanValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(bool?), propertyName);
break;
case "GuidValue":
metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(Guid?), propertyName);
break;
default:
return defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
break;
}
DataAnnotationsModelMetadata daMetadata = (DataAnnotationsModelMetadata)metadata;
System.Reflection.FieldInfo container = modelAccessor.Target.GetType().GetField("vdi");
AddSupplimentalMetadata(daMetadata, (PseudoObjectAttributeViewModel)((System.Web.Mvc.ViewDataInfo)container.GetValue(modelAccessor.Target)).Container);
}
else
metadata = defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
return metadata;
}
第一部分非常自我解释,首先使用 GetMetadataForType() 填充元数据,方法是传入与从中提取数据的列名称最匹配的 .NET 类型。(我的编辑器模板通过动态选择定义该数据的数据结构中定义的数据所在的列来帮助解决此问题)
Html.Editor(Model.PseudoObjectStructure.PseudoObjectControl.DataType)
使用起来有点奇怪,但就像我说的,它是一个预先存在的数据结构。
在 switch 语句之后是奇怪的地方。据我了解,在 MVC2 中,该GetMetadataForProperty()
方法不再将模型本身作为参数并使用 查找属性propertyName
,而是传入一个类型的表达式,该表达式Func<object>
指向 MVC 需要元数据的属性。这带来了一个问题,因为我需要根模型使用不同的属性来确定结构细节。我在这里找到了另一个解决方案,说您可以使用反射来获取模型,但它需要反射。不是我所希望的,但它有效。在我拥有模型之后,我将到目前为止的元数据和模型传递给一个创建的方法,然后我将从那里设置MVC 使用的对象AddSupplimentalMetadata()
的其余属性。DataAnnotationsModelMetadata
现在我只需要找出一种方法来根据用户权限动态选择渲染或不渲染某些属性。我想我可能需要做的是在将模型传递给视图之前过滤属性列表,使用 LINQ 或类似的东西。我不喜欢将业务逻辑放在 Display/EditorTemplate 中的想法。为了保存更改,我仍然需要查看验证系统,看看我是否可以使用该系统来验证用户尝试传递信息的属性。