web-dev-qa-db-ja.com

Entity Framework Core-一般的に値コンバーターを設定する

Entity Framework Core 2.1は現在、私が業務用の会社で使用している会社で使用することを目的として試用しています。私はテストプロジェクトにValue Converterを実装する方法のほとんどを持っていますが、私の既存の知識ベースは最後のハードルで私を失望させました!

私がやろうとしていること

私の理解では、列挙値の場合、組み込みの型コンバーターは列挙値から同等の文字列(EnumToStringConverter)に、または列挙値からその数値表現(EnumToNumberConverter)に変換できます。ただし、データベース内の列挙型を表すためにカスタム文字列値を使用しているため、この変換を行うカスタムEnumToDbStringEquivalentConvertorを記述し、モデル内の各列挙値の属性としてデータベース文字列値を指定しています。

コードは次のとおりです。

モデル

public class User
{
    [Key] public int ID { get; set; }
    public EmployeeType EmployeeType { get; set; }
}

public enum EmployeeType
{
    [EnumDbStringValue("D")]
    Director,
    [EnumDbStringValue("W")]
    Weekly,
    [EnumDbStringValue("S")]
    Salaried
}

DataContext

public class MyDataContext : DbContext
{
    public DbSet<User> Users { get; set; }

    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType.IsEnum)
                {
                    property.SetValueConverter(new EnumToDbStringEquivalentConvertor<EmployeeType>());
                }
             }
         }
    }
}

値コンバーター

public class EnumToDbStringEquivalentConvertor<T> : ValueConverter<T, string>
{
    public EnumToDbStringEquivalentConvertor(ConverterMappingHints mappingHints = null) : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
    { }

    private static Expression<Func<T, string>> convertToProviderExpression = x => ToDbString(x);
    private static Expression<Func<string, T>> convertFromProviderExpression = x => ToEnum<T>(x);

    public static string ToDbString<TEnum>(TEnum tEnum)
    {
        var enumType = tEnum.GetType();
        var enumTypeMemberInfo = enumType.GetMember(tEnum.ToString());
        EnumDbStringValueAttribute enumDbStringValueAttribute = (EnumDbStringValueAttribute)enumTypeMemberInfo[0]
            .GetCustomAttributes(typeof(EnumDbStringValueAttribute), false)
            .FirstOrDefault();

        return enumDbStringValueAttribute.StringValue;
    }

    public static TEnum ToEnum<TEnum>(string stringValue)
    {
        // Code not included for brevity
    }
}

このコード(言ってよかった)は問題なく動作しているようです。

私の問題

値コンバーターに関するドキュメントでは、OnModelCreatingメソッドで値コンバーターを割り当てる方法が、モデルの個々のプロパティに個々の型コンバーターを物理的に割り当てることを示唆しているようです。これを実行する必要はありません-モデルをドライバーにしてください。これは後で実装しますが、今のところ、現在のバージョンのコードでは、モデルのエンティティタイプをループ処理して、 'IsEnum'プロパティ値を確認し、その時点で値コンバーターを割り当てています。

私の問題は、使用しているSetValueConverter拡張メソッドに、EnumToDbStringEquivalentConvertorの新しいインスタンスを渡す必要があることです。この例では、EnumToDbStringEquivalentConvertorが機能するようにハードコードされています。ただし、これをハードコードしたくないので、エンティティタイプのClrTypeを渡します。

以前にリフレクションを使用してジェネリック型とジェネリックメソッドを作成しましたが、これを機能させるための適切なコードを見つけることができません。

この:

public class MyDataContext : DbContext
{
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType.IsEnum)
                {
                    var converterType = typeof(EnumToDbStringEquivalentConvertor<>);
                    var genericConverterType = converterType.MakeGenericType(property.ClrType);

                    MethodInfo setValueConverterMethodInfo = typeof(MutablePropertyExtensions).GetMethod("SetValueConverter");
                    setValueConverterMethodInfo.Invoke(property,
                            new object[] { property, Activator.CreateInstance(genericConverterType) });
                }
             }
         }
    }
}

microsoft.EntityFrameworkCore.InfrastructureのGetModelメソッドで、「System.MissingMethodException: 'このオブジェクトにはパラメーターのないコンストラクターが定義されていません。」というエラーが表示されます

だから私の質問は、値コンバーターをEF Coreの「SetValueConveter」メソッドに一般的に渡す方法を誰かに教えてもらえますか?

よろしくお願いします。

6
Steven Day

あと少しです。問題はこのコードです

Activator.CreateInstance(genericConverterType)

これは、コンバータークラスのパラメーターのないコンストラクターを見つけて起動しようとします。しかし、クラスコンストラクターはオプションですがパラメーターを持っています。オプションのパラメーターは単なるコンパイラーシュガーであり、リフレクションを使用する場合は明示的に渡す必要があります。

したがって、CreateInstanceoverload を受け入れてparams object[] argsを受け入れ、nullmappingHintsに渡す必要があります。

また、リフレクションを介して SetValueConverter を呼び出す必要はありません。これはパブリックAPIの一部です。

作業コードは次のようになります。

if (property.ClrType.IsEnum)
{
    var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
        .MakeGenericType(property.ClrType);    
    var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
    property.SetValueConverter(converter);
}
1
Ivan Stoev