ごった煮

色々な事を書いてます

ASP.NET Core 2.2 から ASP.NET Core 3.1 へ移行する際に ExpressionMetadataProvider が読み出せなくなった件と解決方法について

手元のプロジェクトを移行していた際に、引っかかったのでメモとして残します。

何が起きたか

2.x系で動いていたコードがバージョンアップとともに実行時エラーが発生した ExpressionMetadataProvider の読み出し時になぜか読み出せなくなった

エラーになるコードは、以下のような感じ

        public static IHtmlContent Sample<T, TProperty>(this IHtmlHelper<T> htmlHelper,
            Expression<Func<T, TProperty>> selector)
        {
            var metadata =
                ExpressionMetadataProvider.FromLambdaExpression(selector, htmlHelper.ViewData,
                    htmlHelper.MetadataProvider);
            var defaultModelMetadata = metadata.Metadata as DefaultModelMetadata;

            return new HtmlString("");
        }

具体的なエラーは、以下のような感じ

TypeLoadException: Could not load type 'Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ExpressionMetadataProvider' from assembly 'Microsoft.AspNetCore.Mvc.ViewFeatures, Version=3.1.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

原因

ASP.NET Core 3.0 になったタイミングで、Microsoft.AspNet.*.Internal といった API群が本当の internal に変更された 今までは、public internal (pubternal) な API だったのでアクセスできたが、本当に Internal になったのでアクセスできなくなった。

対処法

HttpContext にアクセスできる場合、HttpContext.RequestServices を使って ModelExpressionProvider にアクセスできるので、以下のようにして対応

        public static IHtmlContent Sample<T, TProperty>(this IHtmlHelper<T> htmlHelper,
            Expression<Func<T, TProperty>> selector)
        {
            var modelExpressionProvider = (ModelExpressionProvider) htmlHelper.ViewContext.HttpContext.RequestServices.GetService(typeof(IModelExpressionProvider));
            var metadata = modelExpressionProvider.CreateModelExpression(htmlHelper.ViewData, selector);
            var defaultModelMetadata = metadata.Metadata as DefaultModelMetadata;

            return new HtmlString("");
        }

まとめ

.NET Core のバージョンアップは、とても辛い