实体框架和MSSQL json_value嵌套数组在不同的行中

时间:2019-02-20 07:21:36

标签: c# sql-server entity-framework entity-framework-6

我正在尝试json_value。目标是在数据库中创建动态模型,该模型可以使用json包含任何类型的数据。

这是模型:

public class DynamicModel
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid ID { get; set; }
    public string TableName { get; set; } = "MyTable";
    public Guid? Container { get; set; } = null; //Parent who hold this data for many-one relation

    public string Content { get; set; } = ""; //JSON content

    public bool DeleteOnCascade { get; set; } = false;

    public DateTime DateCreated { get; set; } = DateTime.UtcNow;
    public DateTime LastEdited { get; set; } = DateTime.UtcNow;
}

例如,如果Content包含:

"{ \"name\":\"John\", \"age\":30, \"cars\":[] }"

如果我将汽车放在同一行中,则会出现问题,因为数据太大,访问数据效率不高。

因此,我认为将汽车的嵌套列表分为不同的行会更好:

var car = new DynamicModel() {
    Id = ...,
    TableName = "Car",
    Container = //the parent ID of the above example (one - many relation),
    DeleteOnCascade = true, //Gets deleted if the parent gets deleted,
    ...
}

这就是我修改实体框架以能够访问json_value的方法:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Conventions.Add(new RegisterJsonValueFunctionConvention());
}

然后

public static class JH
{
    // Than define your function
    [DbFunction("CodeFirstDatabaseSchema", "JSON_VALUE")]
    public static string JsonValue(string expression, string path)
    {
        throw new NotSupportedException();
    }
}

然后

public class RegisterJsonValueFunctionConvention : IStoreModelConvention<EdmModel>
{
    public void Apply(EdmModel item, DbModel model)
    {
        var expressionParameter = FunctionParameter.Create("expression", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.In);
        var pathParameter = FunctionParameter.Create("path", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.In);
        var returnValue = FunctionParameter.Create("result", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.ReturnValue);
        CreateAndAddFunction(item, "JSON_VALUE", new[] { expressionParameter, pathParameter }, new[] { returnValue });
    }

    protected EdmFunction CreateAndAddFunction(EdmModel item, string name, IList<FunctionParameter> parameters, IList<FunctionParameter> returnValues)
    {
        var payload = new EdmFunctionPayload { StoreFunctionName = name, Parameters = parameters, ReturnParameters = returnValues, Schema = GetDefaultSchema(item), IsBuiltIn = true };
        var function = EdmFunction.Create(name, GetDefaultNamespace(item), item.DataSpace, payload, null);
        item.AddItem(function);
        return function;
    }

    protected EdmType GetStorePrimitiveType(DbModel model, PrimitiveTypeKind typeKind)
    {
        return model.ProviderManifest.GetStoreType(TypeUsage.CreateDefaultTypeUsage(PrimitiveType.GetEdmPrimitiveType(typeKind))).EdmType;
    }

    protected string GetDefaultNamespace(EdmModel layerModel)
    {
        return layerModel.GlobalItems.OfType<EdmType>().Select(t => t.NamespaceName).Distinct().Single();
    }

    protected string GetDefaultSchema(EdmModel layerModel)
    {
        return layerModel.Container.EntitySets.Select(s => s.Schema).Distinct().SingleOrDefault();
    }
}

最后,这就是我访问值的方式:

var x = db.DynamicData
        .Where(m => JH.JsonValue(m.Content, "$.name").Equals("John"))
        .Select(m => JH.JsonValue(m.Content, "$.age"))
        .FirstOrDefault();

我想知道是否有使用Linq查询访问cars的便捷方法?

现在我必须先获取x值,然后查找具有cars值的Container作为x.ID,获取列表,然后追加到cars:[]的位置,这确实需要时间和内存来处理它。

那你觉得家伙如何?有没有简便的方法可以更简单地实现这一目标?

1 个答案:

答案 0 :(得分:1)

可以想象,您可以注册另一个数据库函数来使用JSON_QUERY()函数,该函数可用于从JSON数据中选择数组和对象。