C# 如何在EF Core中使用DbFunction转换?
我正在寻找类似于C# 如何在EF Core中使用DbFunction转换?,c#,mysql,linq-to-sql,entity-framework-core,full-text-search,C#,Mysql,Linq To Sql,Entity Framework Core,Full Text Search,我正在寻找类似于EF.Functions.FreeText的东西,它是在SQL Server中实现的,但是使用了MySQL的MATCH…语法 这是我当前的工作流程: AspNetCore 2.1.1 EntityFrameworkCore 2.1.4 Pomelo.EntityFrameworkCore.MySql 2.1.4 问题是MySQL使用了两个函数,我不知道如何用DbFunction解释这一点,并将每个函数的参数分开。有人知道如何实现这一点吗 这应该是Linq语法: query.Whe
EF.Functions.FreeText的东西,它是在SQL Server中实现的,但是使用了MySQL的MATCH…语法
这是我当前的工作流程:
AspNetCore 2.1.1
EntityFrameworkCore 2.1.4
Pomelo.EntityFrameworkCore.MySql 2.1.4
问题是MySQL使用了两个函数,我不知道如何用DbFunction
解释这一点,并将每个函数的参数分开。有人知道如何实现这一点吗
这应该是Linq语法:
query.Where(x=>DbContext.FullText(新[]{x.Col1,x.Col2,x.Col3},“关键字”);
这应该是在SQL中生成的结果:
SELECT*fromt,其中将(`Col1`、`Col2`、`Col3`)与('keywords')匹配;
我尝试使用HasTranslation
函数来遵循以下示例:
注意:我知道它可以用SQL中的解决,但这不是我想要的。当我需要EF Core中的支持时,您的用例与我的非常相似
例如:
// gets translated to
// ROW_NUMBER() OVER(PARTITION BY ProductId ORDER BY OrderId, Count)
DbContext.OrderItems.Select(o => new {
RowNumber = EF.Functions.RowNumber(o.ProductId, new {
o.OrderId,
o.Count
})
})
使用匿名类而不是数组
您必须做的第一件事是从使用数组切换到匿名类,即从
DbContext.FullText(新[]{x.Col1,x.Col2,x.Col3},“关键字”)
到
DbContext.FullText(新的{x.Col1,x.Col2,x.Col3},“关键字”)
参数的排序顺序将与查询中的定义相同,
i、 enew{x.Col1,x.Col2}
将被翻译成Col1,Col2
和new{x.Col2,x.Col1}
到Col2,Col1
您甚至可以访问以下内容:new{x.Col1,=x.Col1,Foo=“bar”}
,它将被转换为Col1,Col1,'bar'
实现自定义IMethodCallTranslator
如果您需要一些提示,那么您可以在上查看我的代码,或者如果您可以等待几天,那么我将提供一篇关于自定义函数实现的博客文章
更新(2019年7月31日)
博客帖子:
更新(2019年7月27日)
由于下面的评论,我认为需要一些澄清
1)正如下面的评论所指出的,还有另一种方法。使用HasDbFunction
我可以保存一些输入,比如用于向EF注册翻译器的代码,但我仍然需要行数表达式
,因为该函数有两组参数(用于按
划分和按
排序)而现有的SqlFunctionExpression
不支持这一点。(或者我错过了什么?)我之所以选择使用IMethodCallTranslator
的方法,是因为我希望在设置DbContextOptionsBuilder
时完成此功能的配置,而不是在OnModelCreating
中。也就是说,这是我个人的偏好
最后,线程创建者还可以使用HasDbFunction
实现所需的功能。在我的例子中,代码如下所示:
// OnModelCreating
var methodInfo = typeof(DemoDbContext).GetMethod(nameof(DemoRowNumber));
modelBuilder.HasDbFunction(methodInfo)
.HasTranslation(expressions => {
var partitionBy = (Expression[])((ConstantExpression)expressions.First()).Value;
var orderBy = (Expression[])((ConstantExpression)expressions.Skip(1).First()).Value;
return new RowNumberExpression(partitionBy, orderBy);
});
// the usage with this approach is identical to my current approach
.Select(c => new {
RowNumber = DemoDbContext.DemoRowNumber(
new { c.Id },
new { c.RowVersion })
})
2)匿名类型无法强制其成员的类型,因此如果使用integer
而不是string
调用函数,则可以获得运行时异常。不过,这可能是一个有效的解决方案。根据您为之工作的客户,解决方案可能或多或少可行,最终决定权在于客户。不提供任何替代方案也是一个可能的解决方案,但不是一个令人满意的解决方案。
特别是,如果不希望使用SQL(因为编译器提供的支持更少),那么运行时异常可能是一个很好的折衷方案
但是,如果折衷方案仍然不可接受,那么我们可以研究如何添加对阵列的支持。
第一种方法是实现一个定制的IExpressionFragmentTranslator
,将数组的处理“重定向”给我们
请注意,这只是一个原型,需要更多的调查/测试:-)
并调整了方法的使用
// Translates to ROW_NUMBER() OVER(ORDER BY Id)
.Select(c => new {
RowNumber = EF.Functions.RowNumber(new Guid[] { c.Id })
})
有问题的方法的签名是什么?我们可以遵循它们在中使用的相同设计模式,但使用数组来接受多个属性,类似于下面的公共静态bool全文(string[]propertyReferences,string FullText)
问题是不幸的是,当前new[]{…}表达式内部不支持
,因此根本不会调用我们的翻译。不幸的是,您无法在查询外部创建数组,因为您需要从x=>
访问x
。创建一个带有多个重载的函数,虽然相对容易,但要创建一个函数要有多个重载(string,string)
,(string,string,string)
等等。如果对你有效,请告诉我。因为处理new[]{…}
参数需要深入EF核心基础设施。这是一个很好的尝试,但是。。。(1) 过于复杂。添加简单方法的管道代码太多。如果该方法不需要自定义表达式,HasDbFunction
+HasTranslation
就足够了。(2) new{x.Col1,x.Col2,x.Col3}
与new string[]{x.Col1,x.Col2,x.Col3}
-后者是编译时安全的,而前者不是(不强制所有成员都是string
类型)。我理解你为什么这么建议,但大多数阅读的人不会这样做——这是一种针对当前EF核心方法参数翻译的类型不安全的解决方法,缺乏新的t[]{…}
支持。@IvanStoev感谢你的评论,他们给我带来了一些想法!我在我的原始答案中添加了几个部分。添加了博客帖子的链接
public static long RowNumber(this DbFunctions _, Guid[] orderBy)
// Translates to ROW_NUMBER() OVER(ORDER BY Id)
.Select(c => new {
RowNumber = EF.Functions.RowNumber(new Guid[] { c.Id })
})