实体框架核心-一般设置值转换器

Ste*_*Day 5 c# entity-framework-core ef-core-2.1

我目前正在试用Entity Framework Core 2.1,以期在我工作的公司的业务应用程序中使用它。我已经在测试项目中实现了价值转换器的大部分方法,但是我现有的知识库使我无奈了!

我想做什么

我的理解是,对于枚举值,内置的类型转换器可以将枚举值转换为等效的字符串(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
}
Run Code Online (Sandbox Code Playgroud)

数据上下文

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

价值转换器

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

这段代码(我很高兴地说)似乎可以正常工作。

我的问题

有关值转换器的文档似乎建议我们在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) });
                }
             }
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

在Microsoft.EntityFrameworkCore.Infrastructure的GetModel方法上给我一个错误“ System.MissingMethodException:'没有为此对象定义无参数的构造函数。”

所以我的问题是,有人可以建议我如何将我的值转换器通用地传递给EF Core的“ SetValueConveter”方法吗?

预先感谢您的协助。

Iva*_*oev 5

你快到了。问题是这段代码

Activator.CreateInstance(genericConverterType)
Run Code Online (Sandbox Code Playgroud)

它试图查找并调用转换器类的无参数构造函数。但是您的类构造函数确实有一个参数,尽管是可选的。可选参数只是编译器的糖;使用反射时,您应该明确地传递它们。

所以你需要使用CreateInstance 重载接受params object[] args和传递nullfor mappingHints

此外,无需通过反射调用SetValueConverter - 它是公共 API 的一部分。

工作代码可能是这样的:

if (property.ClrType.IsEnum)
{
    var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
        .MakeGenericType(property.ClrType);    
    var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
    property.SetValueConverter(converter);
}
Run Code Online (Sandbox Code Playgroud)