我们有一列,其中JSON数据存储为字符串。读取此JSON数据,并通过实例化将其转换为IDictionary<string, object>
。一切正常,直到我要对其进行过滤。仅在从数据库中获取数据之后才应用过滤。我们将有数百万条记录,因此这是不可接受的。显然,我的过滤器被EF Core完全忽略为WHERE子句,因为它可能不知道如何解析MethodCallExpressions。
我正在寻找一种使与我下面具有表达式树的SQL查询尽可能接近的方法。
我需要进行以下转换:
.Call System.Linq.Queryable.Where(
.Constant<QueryTranslator`1[Setting]>(QueryTranslator`1[Setting]),
'(.Lambda #Lambda1<System.Func`2[Setting,System.Boolean]>))
.Lambda #Lambda1<System.Func`2[Setting,System.Boolean]>(Setting $$it)
{
((System.Nullable`1[System.Int32]).If (
$$it.Value != null && .Call ($$it.Value).ContainsKey("Name")
) {
($$it.Value).Item["Name"]
} .Else {
null
} > (System.Nullable`1[System.Int32]).Constant<Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.Int32]>(Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.Int32]).TypedProperty)
== .Constant<System.Nullable`1[System.Boolean]>(True)
}
对此:
SELECT *
FROM [Setting]
WHERE JSON_VALUE([Value], 'lax $.Name') > 1; -- [Value_Name] > 1 is also fine
通过ExpressionVisitor
,我成功地接近了
WHERE [Value] ='Something',但这仅适用于字符串,并且缺少键名。
答案 0 :(得分:6)
在获得“官方”支持之前,您可以使用EF Core 2.0引入的Database scalar function mapping来映射JSON_VALUE
。
例如,在上下文派生类中或以下单独的静态类中添加以下静态方法:
public static class MyDbFunctions
{
[DbFunction("JSON_VALUE", "")]
public static string JsonValue(string source, string path) => throw new NotSupportedException();
}
,如果它在单独的类中,则将以下内容添加到您的上下文OnModelCreating
覆盖中(如果该方法在上下文中则不需要):
modelBuilder.HasDbFunction(() => MyDbFunctions.JsonValue(default(string), default(string)));
现在,您可以在类似于EF.Functions
的LINQ to Entities查询中使用它。请注意,该函数返回string
,因此,为了诱使编译器将其“强制转换”为数字,可以使用下面显示的double cast技术(已在EF Core 2.1.2中进行了测试和工作): / p>
var query = db.Set<Setting>()
.Where(s => (int)(object)MyDbFunctions.JsonValue(s.Value, "lax $.Name") > 1);
翻译成所需的
WHERE JSON_VALUE([Value], 'lax $.Name') > 1
另一种(可能类型更安全)执行转换的方法是使用Convert
类方法(令人惊讶的是SqlServer EF Core提供程序支持):
var query = db.Set<Setting>()
.Where(s => Convert.ToInt32(MyDbFunctions.JsonValue(s.Value, "lax $.Name")) > 1);
翻译为
WHERE CONVERT(int, JSON_VALUE([Value], 'lax $.Name')) > 1
答案 1 :(得分:0)
Entity Framework Core 3.X中发生了重大变化。 DbFunction.Schema being null or empty string configures it to be in model's default schema
只有在链接中有该示例,我才能够将DBFunction添加到我们的项目中。
MyDbContext内部的功能:
[DbFunction("JSON_VALUE", "dbo")]
public static string JsonValue(string source, string path) => throw new NotSupportedException();
[DbFunction("JSON_QUERY", "dbo")]
public static string JsonQuery(string source, string path) => throw new NotSupportedException();
设置:
modelBuilder
.HasDbFunction(typeof(MyDbContext).GetMethod(nameof(MyDbContext.JsonQuery)))
.HasTranslation(args => SqlFunctionExpression.Create("JSON_QUERY", args, typeof(string), null));
modelBuilder
.HasDbFunction(typeof(MyDbContext).GetMethod(nameof(MyDbContext.JsonValue)))
.HasTranslation(args => SqlFunctionExpression.Create("JSON_VALUE", args, typeof(string), null));
使用(简体):
var query = from sometable in _context.SomeEntity
where MyDbContext.JsonValue(sometable.Data, "$.PrimaryKey.Id") == somevalue
orderby sometable.Date descending
select new SomeModel
{
SomeJsonArray = MyDbContext.JsonQuery(sometable.Data, "$.Changes")
};