首页 > 解决方案 > 实体框架和 MSSQL json_value 嵌套数组在不同的行中

问题描述

我正在试验 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();

我想知道是否有一种方便的方法可以使用carsLinq 查询访问?

现在我必须先获取x值,然后查找值为 的cars,获取列表,然后附加到确实需要时间和内存来处理它的。Containerx.IDcars:[]

那么小伙伴们怎么看呢?有没有一种方便的方法可以以更简单的方式实现这一目标?

标签: c#sql-serverentity-frameworkentity-framework-6

解决方案


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


推荐阅读