2017-11-14 16 views
0

これはASP.NET Coreフレームワークの深い知識を必要とする難しい質問です。まず、MVC 3実装のアプリケーションで何が起こっているのかを説明します。ASP.NETコアでIMetadataDetailsProvidersを2回以上実行する方法

特定のビューでViewModelのModelMetaDataを処理する必要がある複雑な要件がありました。これは高度に構成可能なアプリケーションです。したがって、1つの「ジャーナルタイプ」では、プロパティが必須であるのに対し、別のプロパティでは必須ではないプロパティもあります。さらに、1つの「ジャーナルタイプ」のラジオボタンと別のジャーナルの選択リストでもよい。膨大な数の組み合わせ、これらすべての構成オプションのミキシングとマッチングがあったため、可能なすべての順列に対して別々のViewModelタイプを作成することは現実的ではありませんでした。そのため、ViewModel型が1つあり、その型のプロパティにModelMetaDataが動的に設定されていました。

これは、カスタムModelMetadataProviderを継承することによって(DataAnnotationsModelMetadataProviderを継承して)行われました。

今すぐスマートにカットして、アプリケーションをアップグレードし、ASP.NETコアにサーバーの内容を書き込んでいます。私はIDisplayMetadataProviderの実装がASP.NET Coreのモデルメタデータを変更するのと同じ方法であることを確認しました。

問題は、フレームワークにキャッシングが組み込まれていて、IDisplayMetadataProviderを実装するクラスが1回だけ実行されることです。 ASP.NET Coreフレームワークのデバッグ中にこれを発見し、this commentが私の発見を確認しました。最初のViewModelタイプにアクセスしたときに、MetadataDetailsProviderが実行され、結果がキャッシュされるため、私たちの要件はもはやそのようなキャッシングでは満たされません。しかし、上記のように、非常に動的な設定のため、私はそれがすべてModelBindingの前に実行する必要があります。それ以外の場合は、ModelStateを利用することはできません。エンドポイントが最初にヒットしたとき、将来のすべての要求に対してメタデータが設定されます。

私たちは、メタデータを設定するためにリフレクションを使用してすべてのプロパティを処理する再帰的プロセスを利用する必要があります。私たちは自分自身で行う必要はありません(私のペイスケールを超える大規模な努力) 。

誰かが私が見逃した新しいコアフレームワークに何かがあると思うなら、是非教えてください。 ModelBindersIDisplayMetadataProvidersのキャッシング機能を削除するだけで簡単であっても(これは、今後数日間、ASP.NETソースを調べることで調べる予定です)

答えて

3

モデルメタデータは、パフォーマンス上の理由からキャッシュされます。クラスDefaultModelMetadataProvider(デフォルトの実装はIModelMetadataProvider)がこのキャッシュを担当します。アプリケーションロジックが、すべての要求でメタデータを再構築する必要がある場合は、この実装を独自のものに置き換える必要があります。

あなたの実装を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); 
    } 
} 

あなたCustomModelMetadataProviderConfigureServices()に以下を追加してDefaultModelMetadataProviderを置き換えるには:

services.AddSingleton<IModelMetadataProvider, CustomModelMetadataProvider>(); 
+0

途方もない!どのように私はそれを逃したのか分からない!それは私が精査しなかったいくつかのクラスの一つです。おそらく私はいくつかのスマートを構築して、実行時ロジックを必要とするModelTypes以外のすべてのModelTypeでキャッシュを維持するようにします。どうもありがとう。 – onefootswill

+0

彼らは理解するのがずっと難しくなってきました。古いバージョンでは、IsRequiredをfalseに設定して完了しました。これは今やゲッターであり、すべての属性リストはReadonlyコレクションです。プロパティが必要かどうかを動的に設定する方法を解明することは、大規模なタイムシンクであることが証明されています。 – onefootswill

+0

私はその部分を分かったと考えてください。 IValidationMetadataProviderを実装する必要があり、その中でIsRequiredを設定できます。うまくいけばそれは今私のすべての方法を取得します。 – onefootswill

関連する問題