使IMetadataDetailsProviders在ASP.NET Core中多次运行 [英] Getting IMetadataDetailsProviders to Run More than Once in ASP.NET Core

查看:464
本文介绍了使IMetadataDetailsProviders在ASP.NET Core中多次运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个棘手的问题,需要对ASP.NET Core框架有深入的了解.我将首先解释在MVC 3实现中我们的应用程序中发生了什么.

This is a tricky question which will require some deep knowledge of the ASP.NET Core framework. I'll first explain what is happening in our application in the MVC 3 implementation.

有一个复杂的需求需要解决,涉及特定视图上的ViewModels的ModelMetaData.这是一个高度可配置的应用程序.因此,对于一种日记帐类型",属性可能是强制性的,而对于另一种,完全相同的属性可能是非强制性的.此外,它可能是一个日记帐类型"的单选按钮,而另一个则是选择列表.由于所有这些配置选项都有大量的组合,混合和匹配,因此无法为每个可能的排列创建单独的ViewModel类型.因此,只有一个ViewModel类型,并且在该类型的属性上动态设置了ModelMetaData.

There was a complex requirement which needed to be solved involving the ModelMetaData for our ViewModels on a particular view. This is a highly configurable application. So, for one "Journal Type", a property may be mandatory, whereas for another, the exact same property may be non-mandatory. Moreover, it may be a radio-button for one "Journal Type" and a select list for another. As there was a huge number of combinations, mixing and matching for all these configuration options, it was not practical to create a separate ViewModel type for each and every possible permutation. So, there was one ViewModel type and the ModelMetaData was set on the properties of that type dynamically.

这是通过创建自定义ModelMetadataProvider(通过继承DataAnnotationsModelMetadataProvider)来完成的.

This was done by creating a custom ModelMetadataProvider (by inheriting DataAnnotationsModelMetadataProvider).

粉碎到现在,我们将在其中升级应用程序并在ASP.NET Core中编写服务器内容.我发现实现IDisplayMetadataProvider是在ASP.NET Core中修改模型元数据的等效方法.

Smash-cut to now, where we are upgrading the application and writing the server stuff in ASP.NET Core. I have identified that implementing IDisplayMetadataProvider is the equivalent way of modifying Model Metadata in ASP.NET Core.

问题是,框架内置了缓存,而实现IDisplayMetadataProvider的任何类只能运行一次.我在调试ASP.NET Core框架和此评论时发现了这一点证实了我的发现.这种缓存将不再满足我们的要求,因为第一次访问ViewModel类型时,MetadataDetailsProvider将运行并且结果将被缓存.但是,如上所述,由于高度动态的配置,我需要它在每个ModelBinding之前运行.否则,我们将无法利用ModelState.首次命中该端点时,将为将来的所有请求设置元数据.

The problem is, the framework has caching built into it and any class which implements IDisplayMetadataProvider only runs once. I discovered this while debugging the ASP.NET Core framework and this comment confirms my finding. Our requirement will no longer be met with such caching, as the first time the ViewModel type is accessed, the MetadataDetailsProvider will run and the result will be cached. But, as mentioned above, owing to the highly dynamic configuration, I need it to run prior to every ModelBinding. Otherwise, we will not be able to take advantage of ModelState. The first time that endpoint is hit, the meta-data is set in stone for all future requests.

我们有点需要利用反射过程来遍历所有属性的递归过程来设置元数据,因为我们不想自己做(这超出了我的薪资范围).

And we kinda need to leverage that recursive process of going through all the properties using reflection to set the meta-data, as we don't want to have to do that ourselves (a massive endeavour beyond my pay-scale).

因此,如果有人认为我错过了新的Core框架中的某些内容,请务必让我知道.即使只是简单地删除ModelBindersIDisplayMetadataProviders的缓存功能(这就是我接下来几天将通过ASP.NET源代码研究的内容)一样简单.

So, if anyone thinks there's something in the new Core framework which I have missed, by all means let me know. Even if it is as simple as removing that caching feature of ModelBinders and IDisplayMetadataProviders (that is what I'll be looking into over the next couple of days by going through the ASP.NET source).

推荐答案

模型出于性能方面的考虑,已缓存元数据.类 DefaultModelMetadataProvider ,这是IModelMetadataProvider接口的默认实现,负责此缓存.如果您的应用程序逻辑要求在每个请求上都重建元数据,则应使用自己的实现替换该实现.

Model Metadata is cached due to performance considerations. Class DefaultModelMetadataProvider, which is default implementation of IModelMetadataProvider interface, is responsible for this caching. If your application logic requires that metadata is rebuilt on every request, you should substitute this implementation with your own.

如果您从DefaultModelMetadataProvider继承实现,并覆盖最低要求以实现目标,则将使您的生活更加轻松.似乎GetMetadataForType(Type modelType)应该足够了:

You will make your life easier if you inherit your implementation from DefaultModelMetadataProvider and override bare minimum for achieving your goal. Seems like GetMetadataForType(Type modelType) should be enough:

public class CustomModelMetadataProvider : DefaultModelMetadataProvider
{
    public CustomModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider)
        : base(detailsProvider)
    {
    }

    public CustomModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IOptions<MvcOptions> optionsAccessor)
        : base(detailsProvider, optionsAccessor)
    {
    }

    public override ModelMetadata GetMetadataForType(Type modelType)
    {
        //  Optimization for intensively used System.Object
        if (modelType == typeof(object))
        {
            return base.GetMetadataForType(modelType);
        }

        var identity = ModelMetadataIdentity.ForType(modelType);
        DefaultMetadataDetails details = CreateTypeDetails(identity);

        //  This part contains the same logic as DefaultModelMetadata.DisplayMetadata property
        //  See https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs

        var context = new DisplayMetadataProviderContext(identity, details.ModelAttributes);
        //  Here your implementation of IDisplayMetadataProvider will be called
        DetailsProvider.CreateDisplayMetadata(context);
        details.DisplayMetadata = context.DisplayMetadata;

        return CreateModelMetadata(details);
    }
}

要用CustomModelMetadataProvider替换DefaultModelMetadataProvider,请在ConfigureServices()中添加以下内容:

To replace DefaultModelMetadataProvider with your CustomModelMetadataProvider add following in ConfigureServices():

services.AddSingleton<IModelMetadataProvider, CustomModelMetadataProvider>();

这篇关于使IMetadataDetailsProviders在ASP.NET Core中多次运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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