代码之家  ›  专栏  ›  技术社区  ›  Desolator

C#——使用Entity Framework Core 3 HasConversion将字段转换为JSON。净核心3.1

  •  0
  • Desolator  · 技术社区  · 5 年前

    我试图在我的项目中的所有模型之间动态地完成转换:

    DbContext。反恐精英

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
        foreach (var entityType in entityTypes)
        {
            foreach (var property in entityType.ClrType.GetProperties().Where(x => x != null && x.GetCustomAttribute<HasJsonConversionAttribute>() != null))
            {
                modelBuilder.Entity(entityType.ClrType)
                    .Property(property.PropertyType, property.Name)
                    .HasJsonConversion();
            }
        }
    
        base.OnModelCreating(modelBuilder);
    }
    

    然后创建了一个数据注释属性,将模型中的字段标记为“Json”

    public class HasJsonConversionAttribute : Attribute {}
    

    这是我的扩展方法,用于将Json转换为对象并返回Json

    public static class SqlExtensions
    {
        public static PropertyBuilder HasJsonConversion(this PropertyBuilder propertyBuilder)
        {
            ParameterExpression parameter1 = Expression.Parameter(propertyBuilder.Metadata.ClrType, "v");
    
            MethodInfo methodInfo1 = typeof(Newtonsoft.Json.JsonConvert).GetMethod("SerializeObject", types: new Type[] { typeof(object) });
            MethodCallExpression expression1 = Expression.Call(methodInfo1 ?? throw new Exception("Method not found"), parameter1);
    
            ParameterExpression parameter2 = Expression.Parameter(typeof(string), "v");
            MethodInfo methodInfo2 = typeof(Newtonsoft.Json.JsonConvert).GetMethod("DeserializeObject", 1, BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, CallingConventions.Any, types: new Type[] { typeof(string) }, null)?.MakeGenericMethod(propertyBuilder.Metadata.ClrType) ?? throw new Exception("Method not found");
            MethodCallExpression expression2 = Expression.Call(methodInfo2, parameter2);
            
            var converter = Activator.CreateInstance(typeof(ValueConverter<,>)
                                     .MakeGenericType(propertyBuilder.Metadata.ClrType, typeof(string)), new object[]
            {
                Expression.Lambda( expression1,parameter1),
                Expression.Lambda( expression2,parameter2),
                (ConverterMappingHints) null
            });
    
            propertyBuilder.HasConversion(converter as ValueConverter);
            return propertyBuilder;
        }
    }
    

    为了简单起见,我使用以下用户模型:

    public class User : IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder)
        {
            // Apply some settings defined in custom annotations in the model properties
            //builder.ApplyCustomAnnotationsAndConfigs(this);
        }
        
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string Username { get; set; }
    
        [HasJsonConversion]
        public List<LocalizedName> Values { get; set; }
    }
    

    这是我想转换成JSON的类:

    public class LocalizedName
    {
        public string Value { get; set; }
        public string Code { get; set; }
    }
    

    现在我面临的问题是,它一直在检测 LocalizedName 以牙还牙 model 那没什么大不了的 Key 然后抛出一个错误告诉我添加 Key/PK 即使这没有被标记为模型。

    现在如果我从 User -> Configure() 它会起作用,但它会向我展示其他问题,比如模型失去了自身的关系和与其他模型的关联,现在它又给我带来了一系列我一开始甚至没有遇到的错误。

    我也注意到了 EF 移除 本地化名称 从属性列表中选择,并显示在 Navigation 属性列表。最后,我检查了 OnModelCreating -> modelBuilder.Model.GetEntityTypes() 并注意到EF将其视为一种新的 Model 这有点奇怪。

    有什么办法可以告诉你吗 EF 将此标记为 string/json 字段而不是EF auto假设它是一个模型?

    任何帮助都将不胜感激。

    0 回复  |  直到 5 年前
        1
  •  2
  •   Desolator    5 年前

    所以我用了这个 NuGet package 从…起 https://github.com/Innofactor/EfCoreJsonValueConverter

    然后在我的 DbContext.cs :

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Ignore<LocalizedName>(); //<--- Ignore this model from being added by convention
        modelBuilder.AddJsonFields(); //<--- Auto add all json fields in all models
    }
    

    然后在我的 User model :

    public class User : IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder) {}
        
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string Username { get; set; }
    
        [JsonField] //<------ Add this attribute
        public List<LocalizedName> Values { get; set; }
    }
    

    现在它如预期般工作。

        2
  •  1
  •   sa-es-ir    5 年前

    你可以忽略你的实体 OnModelCreating 这样地:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       modelBuilder.Ignore<LocalizedName>();
       //rest of your code
    }
    

    对于所有的模型 HasJsonConversion 属性你可以这样做(我没有测试):

           var entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
            foreach (var entityType in entityTypes)
            {
                foreach (var property in entityType.ClrType.GetProperties().Where(x => x != null && x.GetCustomAttribute<HasJsonConversionAttribute>() != null))
                {
                    modelBuilder.Entity(entityType.ClrType)
                        .Property(property.PropertyType, property.Name)
                        .HasJsonConversion();
    
                    modelBuilder.Ignore(property.PropertyType);
                }
            }