C# 实体框架核心-通用设置值转换器
我目前正在试用EntityFrameworkCore2.1,以期在我工作的公司的业务应用程序中使用它。在我的测试项目中,我已经掌握了实现值转换器的大部分方法,但我现有的知识库让我在最后一个障碍上失望了 我想做什么 我的理解是,对于枚举值,内置类型转换器可以从枚举值转换为等效字符串(EnumToStringConverter),或从枚举值转换为其数值表示形式(EnumToNumberConverter)。但是,我们使用自定义字符串值来表示数据库中的枚举,因此我编写了一个自定义EnumToDbStringEquivalentConvertor来执行此转换,并且数据库字符串值被指定为模型中每个枚举值的属性 代码如下: 型号C# 实体框架核心-通用设置值转换器,c#,entity-framework-core,ef-core-2.1,C#,Entity Framework Core,Ef Core 2.1,我目前正在试用EntityFrameworkCore2.1,以期在我工作的公司的业务应用程序中使用它。在我的测试项目中,我已经掌握了实现值转换器的大部分方法,但我现有的知识库让我在最后一个障碍上失望了 我想做什么 我的理解是,对于枚举值,内置类型转换器可以从枚举值转换为等效字符串(EnumToStringConverter),或从枚举值转换为其数值表示形式(EnumToNumberConverter)。但是,我们使用自定义字符串值来表示数据库中的枚举,因此我编写了一个自定义EnumToDbStr
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>());
}
}
}
}
}
公共类MyDataContext:DbContext
{
公共数据库集用户{get;set;}
foreach(modelBuilder.Model.GetEntityTypes()中的var entityType)
{
foreach(entityType.GetProperties()中的var属性)
{
if(property.ClrType.IsEnum)
{
SetValueConverter(新的EnumToDbStringEquivalentConvertor());
}
}
}
}
}
值转换器
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
}
}
公共类EnumToDbstringEquivalentConverter:ValueConverter
{
public enumtobstringequivalentconvertor(ConverterMappingHints-mappinghits=null):base(converttoprovidedexpression、convertfromprovidedexpression、mappinghits)
{ }
私有静态表达式converttoprovidedexpression=x=>ToDbString(x);
私有静态表达式convertFromProviderExpression=x=>ToEnum(x);
公共静态字符串ToDbString(TEnum TEnum)
{
var enumType=tEnum.GetType();
var enumtypemberinfo=enumType.GetMember(tEnum.ToString());
EnumDbStringValueAttribute EnumDbStringValueAttribute=(EnumDbStringValueAttribute)enumTypeMemberInfo[0]
.GetCustomAttributes(typeof(EnumDbStringValueAttribute),false)
.FirstOrDefault();
返回enumDbStringValueAttribute.StringValue;
}
公共静态TEnum ToEnum(字符串字符串值)
{
//为简洁起见,不包括代码
}
}
这段代码(我很高兴地说)似乎没有任何问题
我的问题
关于值转换器的文档似乎建议我们在OnModelCreating方法中分配它们的方式是将每个单独的类型转换器物理地分配给模型中的每个单独属性。我不想这样做——我想让我的模特做司机。稍后我将实现这一点,但现在,在当前版本的代码中,我将循环使用模型中的实体类型,检查'IsEnum'属性值,然后在该点分配值转换器
我的问题是,我正在使用的SetValueConverter扩展方法要求我向它传递EnumToDbStringEquivalentConvertor的新实例,在我的示例中,该实例被硬编码为EnumToDbStringEquivalentConvertor,它可以工作。但是,我不希望它是硬编码的-我希望传递实体类型的ClrType
我以前使用过反射来创建泛型类型和泛型方法,但我似乎找不到正确的代码来实现这一点
这:
公共类MyDataContext:DbContext
{
公共数据库集用户{get;set;}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
foreach(modelBuilder.Model.GetEntityTypes()中的var entityType)
{
foreach(entityType.GetProperties()中的var属性)
{
if(property.ClrType.IsEnum)
{
var converterType=typeof(EnumToDbStringEquivalentConvertor);
var genericConverterType=converterType.MakeGenericType(property.ClrType);
MethodInfo setValueConverterMethodInfo=typeof(可变属性扩展).GetMethod(“SetValueConverter”);
setValueConverterMethodInfo.Invoke(属性,
新对象[]{property,Activator.CreateInstance(genericConverterType)};
}
}
}
}
}
在Microsoft.EntityFrameworkCore.Infrastructure中的GetModel方法上出现错误“System.MissingMethodException:“未为此对象定义无参数构造函数”。”
所以我的问题是,有人能告诉我如何将我的值转换器一般地传递给EF Core的“SetValueConverter”方法吗
提前感谢您的帮助。您就快到了。问题是这个代码
Activator.CreateInstance(genericConverterType)
它尝试查找并调用转换器类的无参数构造函数。但是您的类构造函数确实有参数,尽管是可选的。ppational参数只是编译器参数,在使用反射时,应该显式地传递它们
因此,您需要使用CreateInstance
接受params对象[]args
并将null
传递给映射提示
另外,不需要通过反射调用——它是公共API的一部分
工作代码可以如下所示:
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
.MakeGenericType(property.ClrType);
var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
property.SetValueConverter(converter);
}
if(property.ClrType.IsEnum)
{
var converterType=typeof(EnumToDbStringEquivalentConvertor)
.MakeGenericType(属性.ClrType);
var converter=(ValueConverter)Activator.CreateInstance(converterType,(object)null);
属性。SetValueConverter(转换器);
}
我想这表明,当你试图学习一些新东西时,你可能会错过一些显而易见的东西——在我的转换器中,所有的“东西”都在进行,我完全错过了构造函数中的映射提示。我试过那个代码,它工作得很好。非常感谢你的帮助@史蒂文代没问题,我们都是这样
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
.MakeGenericType(property.ClrType);
var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
property.SetValueConverter(converter);
}