如何在razor视图引擎中获取集合中项目的元数据? [英] How can I get the metadata of the items in a collection in razor view-engine?

查看:117
本文介绍了如何在razor视图引擎中获取集合中项目的元数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在ASP.NET MVC 5框架的顶部,我有一个用C#编写的项目.我试图将视图与视图模型分离,以便使视图可重用.通过大量使用EditorTemplates,我可以通过评估模型上每个属性的ModelMetadata和数据注释属性来创建我的所有标准视图(即,创建,编辑和详细信息),然后呈现页面.我唯一不解的是如何渲染Index视图.

I have a project written in C# on the top on ASP.NET MVC 5 framework. I am trying to decouple my views from my view model so I can make my views reusable. With the heavy use of EditorTemplates I am able to create all of my standard views (i.e create, edit and details) by evaluating the ModelMetadata and the data-annotation-attributes for each property on the model, then render the page. The only thing that I am puzzled with is how to render the Index view.

我的索引视图通常接受IEnumerable<object>IPagedList<object>集合.在我看来,我希望能够评估集合中每个object/记录的ModelMetadata,以确定是否应该显示object上的属性.

My index view typically accepts an IEnumerable<object> or IPagedList<object> collection. In my view, I want to be able to evaluate the ModelMetadata of a each object/record in the collection to determine if a property on the object should be displayed or not.

换句话说,我的视图模型看起来像这样

In another words my view-model will look something like this

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; }
}

然后,我的Index.cshtml视图将为集合中的每个记录接受IPagedList<DisplayPersonViewModel>,我想显示用ShowOnIndexView属性修饰的属性的值.

Then my Index.cshtml view will accepts IPagedList<DisplayPersonViewModel> for each record in the collection, I want to display the value of the property that is decorated with ShowOnIndexView attribute.

通常,我可以使用这样的方法评估视图中的ModelMetadata

Typically I would be able to do that my evaluating the ModelMetadata in my view with something like this

@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>

然后我的控制器将如下所示

Then my controller will look something like this

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);
        }
}

但是,为IPagedList<DisplayPersonViewModel>评估ModelMetadata的问题在于,它为我提供了有关集合本身的信息,而不是有关集合中的泛型类型或单个模型的信息.换句话说,我得到的信息包括总页数,每页项数,总记录数…….

However the problem with evaluating ModelMetadata for the IPagedList<DisplayPersonViewModel> is that it gives me information about the collection itself not about the generic type or the single model in the collection. In another words, I get info like, total-pages, items-per-page, total-records....

问题

我如何访问集合中每一行的ModelMetadata信息,以便能够知道要显示哪个属性,而不要显示哪个属性?

How can I access the ModelMetadata info for each row in the collection to be able to know which property to display and which not to?

推荐答案

我将通过建议您不要使用这种类型的代码来污染您的视图作为开头.更好的解决方案是创建一个自定义的HtmlHelper扩展方法来生成html,从而为您提供更大的灵活性,可以进行单元测试并且可重复使用.

I will preface this answer by recommending you do not pollute your view with this type of code. A far better solution would be to create a custom HtmlHelper extension method to generate the html, which gives you far more flexibility, can be unit tested, and is reusable.

您需要更改的第一件事是视图中的模型声明,

The first thing you will need to change is the model declaration in the view which needs to be

@model object

否则,您将抛出

otherwise you will throw this exception (List<DisplayPersonViewModel> is not IEnumerable<object> or IPagedList<object>, but it is object)

请注意,不清楚是要集合中的type还是集合中的每个项目的ModelMetadata,因此我同时包括了这两个内容以及获得type

Note that it is not clear if you want the ModelMetadata for the type in the collection or for each item in the collection, so I have included both, plus code that gets the type

@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);
    ....

请注意,使用反射来确定循环的每次迭代中是否存在该属性效率不高,我建议您这样做一次(基于typeMetadata).然后,您可以(例如)生成一个bool值的数组,然后在循环中使用索引器检查该值).更好的替代方法是让您的ShowOnIndexView属性实现IMetadataAware并将值添加到ModelMetadataAdditionalValues中,从而完全不需要var onIndex代码.有关实现IMetadataAware的示例,请参考 CustomAttribute反映html属性MVC5​​

Note that using reflection to determine if the attribute exists in each iteration of your loop is inefficient, and I suggest you do that once (based on the typeMetadata). You could then (for example) generate an array of bool values, and then use an indexer in the loop to check the value). A better alternative would be to have your ShowOnIndexView attribute implement IMetadataAware and add a value to the AdditionalValues of ModelMetadata so that var onIndex code is not required at all. For an example of implementing IMetadataAware, refer CustomAttribute reflects html attribute MVC5

这篇关于如何在razor视图引擎中获取集合中项目的元数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆