让IMetadataDetailsProviders在ASP.NET Core中运行多次

one*_*ill 3 asp.net asp.net-core asp.net-core-2.0

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

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

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

Smash-cut到现在,我们正在升级应用程序并在ASP.NET Core中编写服务器内容.我已经确定实现IDisplayMetadataProvider是在ASP.NET Core中修改模型元数据的等效方法.

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

我们有点需要利用反射来通过所有属性的递归过程来设置元数据,因为我们不希望自己必须这样做(超出我的薪酬规模的巨大努力).

所以,如果有人认为我错过了新Core框架中的某些内容,请务必告诉我.即使是为消除那缓存功能简单ModelBindersIDisplayMetadataProviders(这就是我将通过ASP.NET源会寻找到在未来几天内).

Cod*_*ler 5

由于性能考虑,模型元数据被缓存.类DefaultModelMetadataProviderIModelMetadataProvider接口的默认实现,负责此缓存.如果您的应用程序逻辑要求在每个请求上重建元数据,则应将此实现替换为您自己的实现.

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要替换DefaultModelMetadataProviderCustomModelMetadataProvider以下内容ConfigureServices():

services.AddSingleton<IModelMetadataProvider, CustomModelMetadataProvider>();
Run Code Online (Sandbox Code Playgroud)