1

I am experimenting with the json_value. The goal is to create dynamic model in the database which can contain any type of data using json.

So this is the model:

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;
}

So for example if the Content contains:

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

The problem if I put the cars into the same row, the data will be too big and not efficient for accessing the data.

So I figure it will be better if I separate the nested list of the car into different row:

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,
    ...
}

So here is how I modify the entity framework to be able to access json_value:

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

then

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

then

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();
    }
}

Finally this is how I access the value:

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

I wonder if there is a convenient way to access the cars with the Linq query?

Right now I have to get the x value first, then look for the cars which has the Container value as the x.ID, get the list, then append to the cars:[] which really takes time and memory to process it.

So what do you think guys? Is there a convenient way to achieve this in a simpler way?

1 Answer 1

1

You could conceivably register another DB function to use the JSON_QUERY() function which will can be used to select arrays and objects from within the JSON data.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.