ale*_*291 6 c# mysql full-text-search linq-to-sql entity-framework-core
我正在寻找类似EF.Functions.FreeText在 SQL Server 中实现但使用MATCH...AGAINSTMySQL 语法的东西。
这是我当前的工作流程:
AspNetCore 2.1.1
EntityFrameworkCore 2.1.4
Pomelo.EntityFrameworkCore.MySql 2.1.4
问题是 MySQL 使用两个函数,我不知道如何解释它并DbFunction分隔每个函数的参数。有谁知道如何实现这个?
这应该是 Linq 语法:
query.Where(x => DbContext.FullText(new[] { x.Col1, x.Col2, x.Col3 }, "keywords"));
Run Code Online (Sandbox Code Playgroud)
这应该是 SQL 中生成的结果:
SELECT * FROM t WHERE MATCH(`Col1`, `Col2`, `Col3`) AGAINST('keywords');
Run Code Online (Sandbox Code Playgroud)
我尝试使用该HasTranslation函数遵循以下示例:
https://github.com/aspnet/EntityFrameworkCore/issues/11295#issuecomment-511440395
https://github.com/aspnet/EntityFrameworkCore/issues/10241#issuecomment- 342989770
注意:我知道可以用 来解决FromSql,但这不是我想要的。
ROW_NUMBER当我需要EF Core 的支持时,您的用例与我的非常相似。
例子:
\n\n// gets translated to\n// ROW_NUMBER() OVER(PARTITION BY ProductId ORDER BY OrderId, Count)\nDbContext.OrderItems.Select(o => new {\n RowNumber = EF.Functions.RowNumber(o.ProductId, new {\n o.OrderId,\n o.Count\n })\n})\nRun Code Online (Sandbox Code Playgroud)\n\n使用匿名类而不是数组
\n\n您要做的第一件事就是从使用数组切换到匿名类,即将调用从
\n\nDbContext.FullText(new[] { x.Col1, x.Col2, x.Col3 }, "keywords")
到
\n\nDbContext.FullText(new { x.Col1, x.Col2, x.Col3 }, "keywords")
参数的排序顺序将保持在查询中定义的那样, \ni.enew { x.Col1, x.Col2 }将被转换为Col1, Col2\nnew { x.Col2, x.Col1 }和Col2, Col1。
您甚至可以执行以下操作:new { x.Col1, _ = x.Col1, Foo = "bar" }that is will be translated to Col1, Col1, \'bar\'.
实施定制IMethodCallTranslator
如果您需要一些提示,那么您可以查看我在Azure DevOps 上的代码:RowNumber 支持,或者如果您可以等待几天,那么我将提供有关自定义函数实现的博客文章。
\n\n更新(2019 年 7 月 31 日)
\n\n博客文章:
\n\n更新(2019 年 7 月 27 日)
\n\n感谢下面的评论,我发现需要进行一些澄清。
\n\n1)正如下面的评论所指出的,还有另一种方法。使用 EF HasDbFunction,我可以节省一些输入,例如使用 EF 注册翻译器的代码,但我仍然需要 ,RowNumberExpression因为该函数有 2 组参数( forPARTITION BY和ORDER BY),而现有的SqlFunctionExpression不支持这一点。(或者我错过了什么?)我选择这种方法的原因IMethodCallTranslator是因为我希望在设置过程中完成此功能的配置,DbContextOptionsBuilder而不是在OnModelCreating. 也就是说,它\xe2\x80\x99是我的个人喜好。
最后,线程创建者HasDbFunction也可以用来实现所需的功能。就我而言,代码如下所示:
// OnModelCreating\n var methodInfo = typeof(DemoDbContext).GetMethod(nameof(DemoRowNumber));\n\n modelBuilder.HasDbFunction(methodInfo)\n .HasTranslation(expressions => {\n var partitionBy = (Expression[])((ConstantExpression)expressions.First()).Value;\n var orderBy = (Expression[])((ConstantExpression)expressions.Skip(1).First()).Value;\n\n return new RowNumberExpression(partitionBy, orderBy);\n});\n\n// the usage with this approach is identical to my current approach\n.Select(c => new {\n RowNumber = DemoDbContext.DemoRowNumber(\n new { c.Id },\n new { c.RowVersion })\n })\nRun Code Online (Sandbox Code Playgroud)\n\n2)匿名类型可以\xe2\x80\x99t强制其成员的类型,因此如果使用例如而不是调用该函数,您可能会得到运行时integer异常string。尽管如此,它仍然是有效的解决方案。根据您所服务的客户,解决方案可能或多或少可行,最终决定权在于客户。不提供任何替代方案也是一种可能的解决方案,但不是令人满意的解决方案。\n特别是,如果不希望使用 SQL(因为编译器对您的支持更少),那么运行时异常可能是一个很好的折衷方案。
但是,如果妥协仍然不可接受,那么我们可以研究如何添加对阵列的支持。\n第一种方法可能是执行自定义IExpressionFragmentTranslator\xe2\x80\x9credirect\xe2\x80\x9d 来对我们进行数组处理。
\n\n\n请注意,它只是一个原型,需要更多的调查/测试:-)
\n
// to get into EF pipeline\npublic class DemoArrayTranslator : IExpressionFragmentTranslator\n{\n public Expression Translate(Expression expression)\n {\n if (expression?.NodeType == ExpressionType.NewArrayInit)\n {\n var arrayInit = (NewArrayExpression)expression;\n return new DemoArrayInitExpression(arrayInit.Type, arrayInit.Expressions);\n }\n\n return null;\n }\n}\n\n// lets visitors visit the array-elements\npublic class DemoArrayInitExpression : Expression\n{\n private readonly ReadOnlyCollection<Expression> _expressions;\n\n public override Type Type { get; }\n public override ExpressionType NodeType => ExpressionType.Extension;\n\n public DemoArrayInitExpression(Type type, \n ReadOnlyCollection<Expression> expressions)\n {\n Type = type ?? throw new ArgumentNullException(nameof(type));\n _expressions = expressions ?? throw new ArgumentNullException(nameof(expressions));\n }\n\n protected override Expression Accept(ExpressionVisitor visitor)\n {\n var visitedExpression = visitor.Visit(_expressions);\n return NewArrayInit(Type.GetElementType(), visitedExpression);\n }\n}\n\n// adds our DemoArrayTranslator to the others\npublic class DemoRelationalCompositeExpressionFragmentTranslator \n : RelationalCompositeExpressionFragmentTranslator\n{\n public DemoRelationalCompositeExpressionFragmentTranslator(\n RelationalCompositeExpressionFragmentTranslatorDependencies dependencies)\n : base(dependencies)\n {\n AddTranslators(new[] { new DemoArrayTranslator() });\n }\n }\n\n// Register the translator\nservices\n .AddDbContext<DemoDbContext>(builder => builder\n .ReplaceService<IExpressionFragmentTranslator,\n DemoRelationalCompositeExpressionFragmentTranslator>());\n\nRun Code Online (Sandbox Code Playgroud)\n\n为了测试,我引入了另一个包含Guid[]参数的重载。\n尽管如此,此方法在我的用例中根本没有意义:)
public static long RowNumber(this DbFunctions _, Guid[] orderBy) \nRun Code Online (Sandbox Code Playgroud)\n\n并调整了方法的使用
\n\n// Translates to ROW_NUMBER() OVER(ORDER BY Id)\n.Select(c => new { \n RowNumber = EF.Functions.RowNumber(new Guid[] { c.Id })\n}) \nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
14093 次 |
| 最近记录: |