【问题标题】:How would I alter the SQL that Linq-to-Nhibernate generates for specific columns?我将如何更改 Linq-to-Nhibernate 为特定列生成的 SQL?
【发布时间】:2015-01-10 23:33:30
【问题描述】:

要利用 MariaDB 10 上的全文索引,我需要在 sql 字符串中使用这种新的“MATCH AGAINST”语法。

http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html#function_match

我认为,如果仅针对某些列,我可以覆盖 linq-to-nhibernate 以更改它在使用时生成的 sql,那将是非常酷的

.Where(x => FullTextIndexedStringProperty.Contains("Some word")).ToList().

谁能给我一些关于如何开始的一般指导?

【问题讨论】:

    标签: nhibernate linq-to-nhibernate nhibernate-4


    【解决方案1】:

    这将为您提供一个非常简单的MATCH ... AGAINST 子句。如果您想变得更复杂(更多参数,指定搜索修饰符),则必须进行一些更大的更改。希望这能让你开始:

    1. 新建一个方言,注册一个简单的MATCH (...) AGAINST (...)函数:

      public class CustomMySQLDialect : MySQLDialect
      {
          public CustomMySQLDialect()
          {
              this.RegisterFunction(
                  "matchagainst",
                  new SQLFunctionTemplate(
                      NHibernateUtil.Boolean,
                      "match (?1) against (?2)"));
          }
      }
      
    2. string 上创建一个静态扩展方法,您将在 LINQ 语句中使用它:

      public static class LinqExtensions
      {
          public static bool MatchAgainst(this string source, string against)
          {
              throw new NotImplementedException();
          }
      }
      
    3. 创建一个新的 LINQ to HQL 生成器类,将方法与我们在自定义方言中注册的 SQL 函数相关联:

      public class MatchAgainstGenerator : BaseHqlGeneratorForMethod
      {
          public MatchAgainstGenerator()
          {
              this.SupportedMethods = new[]
              {
                  ReflectionHelper.GetMethod(() => LinqExtensions.MatchAgainst(null, null))
              };
          }
      
          public override HqlTreeNode BuildHql(
              MethodInfo method,
              System.Linq.Expressions.Expression targetObject,
              ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
              HqlTreeBuilder treeBuilder,
              IHqlExpressionVisitor visitor)
          {
      
              return treeBuilder.BooleanMethodCall(
                  "matchagainst",
                  arguments.Select(visitor.Visit).Cast<HqlExpression>());
          }
      }
      
    4. 创建自定义 LinqToHqlGeneratorsRegistry:

      public class MyLinqToHqlRegistry : DefaultLinqToHqlGeneratorsRegistry
      {
          public MyLinqToHqlRegistry()
          {
             var generator = new MatchAgainstGenerator();
             RegisterGenerator(typeof(LinqExtensions).GetMethod("MatchAgainst"), generator);
          }
      }
      
    5. 在您的 cfg.xml 文件或代码中使用您的自定义方言和 Linq to HQL 注册表:

      var cfg = new Configuration()
          .DataBaseIntegration(db =>
      {
          db.Dialect<CustomMySQLDialect>();
      })
      .LinqToHqlGeneratorsRegistry<MyLinqToHqlRegistry>();
      
    6. 最后,在 LINQ-to-NHibernate 查询中使用您的扩展方法:

      session.Query<Article>()
          .Where(a => a.Body.MatchAgainst("configured"))
          .ToList()
          .Dump();
      

      这将生成如下所示的 SQL:

      select 
          userquery_0_.Id as Id51_, 
          userquery_0_.Title as Title51_, 
          userquery_0_.Body as Body51_ 
      from 
          articles userquery_0_ 
      where 
          match (userquery_0_.Body) against ('configured');
      

    同样,如果您有更复杂的要求,这将无济于事。但希望这至少是一个好的起点。

    如果有人对如何使这种支持更复杂的场景感到好奇,我认为您会遇到以下问题:

    • MATCH 的参数与AGAINST 的参数分开。
    • 向 NHibernate 注册一个自定义 SQL 函数,该函数可以在不同位置接受任意数量的参数
    • 即使在解决了上述两个问题之后,仍然可以创建正确的 HQL。

    【讨论】:

    • 非常感谢。稍后我会查看您的教程。
    • 另外,最好在 linq 中创建一个新函数 MatchAgainst() 而不是覆盖 contains。
    • 是的,我希望我们有足够多的人在接下来的一段时间里保持活力
    • 您能否将缺少的第 4 步编辑到我在下面发布的答案中,我无法通过编辑。
    • @IsaacBolinger:很抱歉,我不知道我是怎么漏掉的。看起来它已经过去了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-12
    相关资源
    最近更新 更多