C# 如何在razor视图引擎中获取集合中项目的元数据?
我有一个项目是用ASP.NET MVC 5框架顶部的C# 如何在razor视图引擎中获取集合中项目的元数据?,c#,asp.net-mvc,razor,asp.net-mvc-5,modelmetadata,C#,Asp.net Mvc,Razor,Asp.net Mvc 5,Modelmetadata,我有一个项目是用ASP.NET MVC 5框架顶部的C#编写的。我正在尝试将视图与视图模型分离,以便使视图可重用。通过大量使用EditorTemplates,我可以通过评估ModelMetadata和模型上每个属性的数据注释属性,然后呈现页面来创建所有标准视图(即创建、编辑和详细信息)。我唯一困惑的是如何呈现索引视图 我的索引视图通常接受IEnumerable或IPagedList集合。在我看来,我希望能够评估集合中每个对象/记录的ModelMetadata,以确定是否应显示对象上的属性 换句话
C#
编写的。我正在尝试将视图与视图模型分离,以便使视图可重用。通过大量使用EditorTemplates
,我可以通过评估ModelMetadata
和模型上每个属性的数据注释属性,然后呈现页面来创建所有标准视图(即创建、编辑和详细信息)。我唯一困惑的是如何呈现索引
视图
我的索引视图通常接受IEnumerable
或IPagedList
集合。在我看来,我希望能够评估集合中每个对象
/记录的ModelMetadata,以确定是否应显示对象
上的属性
换句话说,我的视图模型看起来像这样
public class DisplayPersonViewModel
{
public int Id{ get; set; }
[ShowOnIndexView]
public string FirstName { get; set; }
[ShowOnIndexView]
public string LastName { get; set; }
[ShowOnIndexView]
public int? Age { get; set; }
public string Gender { get; set; }
}
@model IPagedList<object>
@{
var elements = ViewData.ModelMetadata.Properties.Where(metadata => !metadata.IsComplexType && !ViewData.TemplateInfo.Visited(metadata))
.OrderBy(x => x.Order)
.ToList();
}
<tr>
@foreach(var element in elements)
{
var onIndex = element.ContainerType.GetProperty(element.PropertyName)
.GetCustomAttributes(typeof(ShowOnIndexView), true)
.Select(x => x as ShowOnIndexView)
.FirstOrDefault(x => x != null);
if(onIndex == null)
{
continue;
}
@Html.Editor(element.PropertyName, "ReadOnly")
}
</tr>
public class PersonController : Controller
{
public ActionResult Index()
{
// This would be a call to a service to give me a collection with items. but I am but showing the I would pass a collection to my view
var viewModel = new List<DisplayPersonViewModel>();
return View(viewModel);
}
}
然后,我的Index.cshtml
视图将接受IPagedList
对于集合中的每个记录,我想显示用ShowOnIndexView
属性修饰的属性的值
通常情况下,我可以使用类似这样的方法在视图中评估ModelMetadata
public class DisplayPersonViewModel
{
public int Id{ get; set; }
[ShowOnIndexView]
public string FirstName { get; set; }
[ShowOnIndexView]
public string LastName { get; set; }
[ShowOnIndexView]
public int? Age { get; set; }
public string Gender { get; set; }
}
@model IPagedList<object>
@{
var elements = ViewData.ModelMetadata.Properties.Where(metadata => !metadata.IsComplexType && !ViewData.TemplateInfo.Visited(metadata))
.OrderBy(x => x.Order)
.ToList();
}
<tr>
@foreach(var element in elements)
{
var onIndex = element.ContainerType.GetProperty(element.PropertyName)
.GetCustomAttributes(typeof(ShowOnIndexView), true)
.Select(x => x as ShowOnIndexView)
.FirstOrDefault(x => x != null);
if(onIndex == null)
{
continue;
}
@Html.Editor(element.PropertyName, "ReadOnly")
}
</tr>
public class PersonController : Controller
{
public ActionResult Index()
{
// This would be a call to a service to give me a collection with items. but I am but showing the I would pass a collection to my view
var viewModel = new List<DisplayPersonViewModel>();
return View(viewModel);
}
}
@model IPagedList
@{
var elements=ViewData.ModelMetadata.Properties.Where(元数据=>!metadata.IsComplexType&&!ViewData.TemplateInfo.visted(元数据))
.OrderBy(x=>x.Order)
.ToList();
}
@foreach(元素中的var元素)
{
var onIndex=element.ContainerType.GetProperty(element.PropertyName)
.GetCustomAttributes(typeof(ShowOnIndexView),true)
.选择(x=>x作为ShowOnIndexView)
.FirstOrDefault(x=>x!=null);
如果(onIndex==null)
{
继续;
}
@编辑器(element.PropertyName,“只读”)
}
然后我的控制器会像这样
public class DisplayPersonViewModel
{
public int Id{ get; set; }
[ShowOnIndexView]
public string FirstName { get; set; }
[ShowOnIndexView]
public string LastName { get; set; }
[ShowOnIndexView]
public int? Age { get; set; }
public string Gender { get; set; }
}
@model IPagedList<object>
@{
var elements = ViewData.ModelMetadata.Properties.Where(metadata => !metadata.IsComplexType && !ViewData.TemplateInfo.Visited(metadata))
.OrderBy(x => x.Order)
.ToList();
}
<tr>
@foreach(var element in elements)
{
var onIndex = element.ContainerType.GetProperty(element.PropertyName)
.GetCustomAttributes(typeof(ShowOnIndexView), true)
.Select(x => x as ShowOnIndexView)
.FirstOrDefault(x => x != null);
if(onIndex == null)
{
continue;
}
@Html.Editor(element.PropertyName, "ReadOnly")
}
</tr>
public class PersonController : Controller
{
public ActionResult Index()
{
// This would be a call to a service to give me a collection with items. but I am but showing the I would pass a collection to my view
var viewModel = new List<DisplayPersonViewModel>();
return View(viewModel);
}
}
公共类PersonController:Controller
{
公共行动结果索引()
{
//这将是对服务的调用,以向我提供一个包含项目的集合。但我只是在显示“我会将集合传递给我的视图”
var viewModel=新列表();
返回视图(viewModel);
}
}
但是,为IPagedList
评估ModelMetadata
的问题在于,它为我提供了有关集合本身的信息,而不是集合中的泛型类型或单个模型的信息。换句话说,我得到的信息有,总页数,每页项目数,总记录数
问题
如何访问集合中每一行的
ModelMetadata
信息,才能知道要显示哪些属性,不显示哪些属性?在回答这个问题之前,我建议您不要用这种类型的代码污染视图。一个更好的解决方案是创建一个定制的HtmlHelper
扩展方法来生成html,它为您提供了更大的灵活性,可以进行单元测试,并且是可重用的
您需要更改的第一件事是视图中的模型声明,它需要
@model object
否则您将抛出(List
不是IEnumerable
或IPagedList
,而是对象
)
请注意,不清楚您是要为集合中的类型
还是为集合中的每个项目使用ModelMetadata
,因此我将两者都包括在内,再加上获取类型
@model object
@{
var elements = ViewData.ModelMetadata.Properties.Where(metadata => !metadata.IsComplexType && !ViewData.TemplateInfo.Visited(metadata)).OrderBy(x => x.Order).ToList();
// Get the metadata for the model
var collectionMetaData = ViewData.ModelMetadata;
// Get the collection type
Type type = collectionMetaData.Model.GetType();
// Validate its a collection and get the type in the collection
if (type.IsGenericType)
{
type = type.GetInterfaces().Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Single().GetGenericArguments()[0];
}
else if (type.IsArray)
{
type = type.GetElementType();
}
else
{
// its not a valid model
}
// Get the metadata for the type in the collection
ModelMetadata typeMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type);
}
....
@foreach (var element in collectionMetaData.Model as IEnumerable))
{
// Get the metadata for the element
ModelMetadata elementMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => element, type);
....
@model对象
@{
var elements=ViewData.ModelMetadata.Properties.Where(metadata=>!metadata.IsComplexType&&!ViewData.TemplateInfo.visted(metadata)).OrderBy(x=>x.Order.ToList();
//获取模型的元数据
var collectionMetaData=ViewData.ModelMetadata;
//获取集合类型
Type Type=collectionMetaData.Model.GetType();
//验证其是否为集合并获取集合中的类型
if(type.IsGenericType)
{
type=type.GetInterfaces()。其中(t=>t.IsGenericType)
.Where(t=>t.GetGenericTypeDefinition()==typeof(IEnumerable))
.Single().GetGenericArguments()[0];
}
else if(键入.IsArray)
{
type=type.GetElementType();
}
其他的
{
//这不是一个有效的模型
}
//获取集合中类型的元数据
ModelMetadata typeMetadata=ModelMetadataProviders.Current.GetMetadataForType(null,type);
}
....
@foreach(collectionMetaData.Model中的var元素为IEnumerable))
{
//获取元素的元数据
ModelMetadata elementMetadata=ModelMetadataProviders.Current.GetMetadataForType(()=>元素,类型);
....
请注意,使用反射来确定循环的每个迭代中是否存在该属性是低效的,我建议您这样做一次(基于
typeMetadata
)生成一个bool
值数组,然后在循环中使用索引器检查值)。更好的替代方法是让ShowOnIndexView
属性实现IMetadataAware
并向ModelMetadata
的附加值
添加一个值,以便var-onIndex
代码完全不是必需的。有关实现IMetadataAware
的示例,请参阅由于许多原因,您所做的是一个坏主意,包括使代码难以进行单元测试。该代码属于自定义HtmlHelper
方法,而不是视图。并且您注意到,您希望显示该值,在这种情况下,您为什么要是一个EditorTemplate
(相对于DisplayTemplate
)@StephenMuecke助手。但是如何编写助手并访问我的模型行呢?这取决于