我如何更改Linq-to-Nhibernate为特定列生成的SQL?

时间:2014-11-13 17:58:28

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

要利用MariaDB 10上的全文索引,我需要使用这个新的" MATCH AGAINST" sql string中的语法。

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().

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

1 个答案:

答案 0 :(得分:4)

这将为您提供一个非常简单的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');
    
  7. 同样,如果您有更复杂的要求,这将无济于事。但希望这至少是一个很好的起点。

    如果有人对如何使这种支持更复杂的情况感到好奇,那么我认为你遇到的问题是:

    • MATCH的参数与AGAINST的参数分开。
    • 使用NHibernate注册自定义SQL函数,可以在不同的地方使用任意数量的参数
    • 即使解决了上述两个问题,也要创建正确的HQL。