将EF核心约定和DBFunction混合用于JObject属性

时间:2019-05-22 16:25:38

标签: entity-framework .net-core mapping dbfunctions

我有一个属性为JObject的实体,我需要能够针对这些属性使用DbFunctions。

当我执行该项目时,该项目将引发一个异常,说明DbFunction不允许使用JObject类型的参数。

实体就像...

public class OrchestrationRun
{
   public long Id { get; set; }
    public JObject MetaData { get; set; }
    public JObject SystemMetaData { get; set; }
}

DbContext 看起来像...

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public virtual DbSet<OrchestrationRun> OrchestrationRun { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new OrchestrationRunConfiguration());

       // DbFunction mapping for JSON_VALUE
       modelBuilder.HasDbFunction( typeof(MyDbContext).GetMethod(nameof(JsonValue)))
                      .HasName("JSON_VALUE")
                      .HasSchema("");
    }

    // DbFunction
    public static string JsonValue(JObject column, [NotParameterized] string path) => throw new NotSupportedException();
}

OrchestrationRunConfiguration 是...

public class OrchestrationRunConfiguration : IEntityTypeConfiguration<OrchestrationRun>
{
    public void Configure(EntityTypeBuilder<OrchestrationRun> builder)
    {
        builder.Property(e => e.MetaData).HasConversion(
            jObject => jObject != null ? jObject.ToString(Formatting.None) : null,
            json => string.IsNullOrWhiteSpace(json) ? null : JObject.Parse(json)
        );

        builder.Property(e => e.SystemMetaData).HasConversion(
             jObject => jObject != null ? jObject.ToString(Formatting.None): null,
             json => string.IsNullOrWhiteSpace(json) ? null : JObject.Parse(json)
         );
    }
}

我要执行的查询是...

var dbResponse = (from or in this.dbContext.OrchestrationRun
where MyDbContext.JsonValue(or.MetaData,"$.Product.ProductCategoryName") == "EXAMPLE"
select new
       {
          Id = or.Id,
          CategoryId = "EXAMPLE"
       }

    ).ToList();

注意: 在DbContext实例化时发生异常。因此,永远不会调用该查询。

抛出的异常是...

  

System.InvalidOperationException:DbFunction'MyDbContext.JsonValue'的参数'column'具有无效的类型'JObject'。确保参数类型可以被当前提供者映射。   在Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateDbFunctions(IModel模型)   在Microsoft.EntityFrameworkCore.Internal.SqlServerModelValidator.Validate(IModel模型)   在Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ValidatingConvention.Apply(InternalModelBuilder modelBuilder)

2 个答案:

答案 0 :(得分:1)

以下代码适用于完全相同的情况。

        var jsonValueMethod = typeof(MyDbContext).GetMethod(nameof(MyDbContext.JsonValue));

        builder.HasDbFunction(jsonValueMethod)
            .HasTranslation(args => {
                return SqlFunctionExpression.Create("JSON_VALUE", args, jsonValueMethod.ReturnType, null);
            })
            .HasParameter("column").Metadata.TypeMapping = new StringTypeMapping("NVARCHAR(MAX)");

下面的一行神奇地将JObject列转换为NVARCHAR(MAX)或任何您的字符串数据类型。

答案 1 :(得分:0)

感谢 Raghu,您的回答对我帮助很大。对于可能来到这里并希望将转换与 json 函数混合并使用最新的 EF Core 5.0 更新 Raghu 的答案的用户:

jsonvalue 函数:

public static class JsonExtensions
{
    public static string JsonValue(object expression, string path) => throw new InvalidOperationException($"{nameof(JsonValue)} cannot be called client side");
}

在 DbContext OnModelCreating 中:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
            var jsonValueMethodInfo = typeof(JsonExtensions).GetRuntimeMethod(nameof(JsonExtensions.JsonValue), new[] { typeof(string), typeof(string) });

            modelBuilder
                .HasDbFunction(jsonValueMethodInfo)
                .HasTranslation(args => new SqlFunctionExpression("JSON_VALUE", args, nullable: true, argumentsPropagateNullability: new[] { false, false }, typeof(string), null))
                .HasParameter("expression").Metadata.TypeMapping = new StringTypeMapping("NVARCHAR(MAX)"); // conversion

[...]

// example of conversion of a json property
                entity.Property(e => e.AdditionalProperties)
                    .HasColumnName("AdditionalJson")
                    .HasConversion(
                        v => Newtonsoft.Json.JsonConvert.SerializeObject(v, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore }),
                        v => Newtonsoft.Json.JsonConvert.DeserializeObject<AdditionalUserProperties>(v)
                     );
}

然后您就可以使用具有转换属性的扩展方法...例如:

    var testId = (from u in this.Users
                     join e in this.Employees.IgnoreQueryFilters() on JsonExtensions.JsonValue(u.AdditionalProperties, "$." + nameof(AdditionalUserProperties.PersonalId)) equals e.PersonalId
                     where u.Id == userId
                     select e.Id).FirstOrDefault();

生成的sql:

[...][e] ON JSON_VALUE([u].[AdditionalJson], N'$.PersonalId') = [e].[PersonalId][...]