【问题标题】:C# - Using Entity Framework Core 3 HasConversion to convert a field to JSON in .Net Core 3.1C# - 在 .Net Core 3.1 中使用 Entity Framework Core 3 HasConversion 将字段转换为 JSON
【发布时间】:2021-01-16 02:04:29
【问题描述】:

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

DbContext.cs

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 对象检测为另一个没有Keymodel,并抛出一个错误,告诉我添加Key/PK,即使这是未标记为模型。

现在,如果我从 User -&gt; Configure() 执行此操作,它会起作用,但它会向我显示其他问题,例如模型失去与其他模型的关系和关联,现在它会抛出我没有的其他一组错误甚至一开始就有。

我还注意到EF 从属性列表中删除了LocalizedName 并将其显示在Navigation 属性列表下。最后,我检查了OnModelCreating -&gt; modelBuilder.Model.GetEntityTypes(),发现 EF 将其视为新的Model,这有点奇怪。

有没有办法让 EF 将此标记为 string/json 字段,而不是 EF 自动假设它是一个模型?

任何帮助将不胜感激。

【问题讨论】:

  • 你标记了这个 EF 而不是 EF 核心
  • @SoiidSnake 你看过这篇文章了吗? stackoverflow.com/questions/44829824/…
  • @GetFuzzy - 我做到了.. 他们没有提到任何关于这个问题的内容。

标签: c# json .net asp.net-core entity-framework-core


【解决方案1】:

所以我最终使用了来自https://github.com/Innofactor/EfCoreJsonValueConverterNuGet package

然后在我的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】:

    您可以像这样忽略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);
                }
            }
    

    【讨论】:

    • 如果我忽略实体,它将不会映射到数据库字段。我已经这样做了。它也失去了关联
    • @SolidSnake 您想从属性创建 json 字符串,因此不需要将实体作为表,或者可能应该使用具有 json 属性并且可以忽略它的不同模型(不是实体)
    • 这意味着我必须创建另一个字符串类型的字段才能工作并将这个 LocalizedName 字段映射到它。这有点为代码添加了更多逻辑,违背了使用 HasConversion 的目的
    • @SolidSnake 不,我最后说你在表中有一个 nvarchar(string) 类型的列,因为它是一个 json。对于您的模型,您可以对具有 json 转换属性的属性使用非实体模型(没有表的模型)并告诉 ef 忽略这些模型。
    猜你喜欢
    • 2021-11-08
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多