How would I alter the SQL that Linq-to-Nhibernate generates for specific columns?

末鹿安然 提交于 2019-11-29 02:45:05

This will get you a very simple MATCH ... AGAINST clause. If you want to get more complex (more arguments, specifying the search modifier), you'll have to make some bigger changes. Hopefully this will get you started though:

  1. Create a new dialect and register a simple MATCH (...) AGAINST (...) function:

    public class CustomMySQLDialect : MySQLDialect
    {
        public CustomMySQLDialect()
        {
            this.RegisterFunction(
                "matchagainst",
                new SQLFunctionTemplate(
                    NHibernateUtil.Boolean,
                    "match (?1) against (?2)"));
        }
    }
    
  2. Create a static extension method on string that you'll use in LINQ statements:

    public static class LinqExtensions
    {
        public static bool MatchAgainst(this string source, string against)
        {
            throw new NotImplementedException();
        }
    }
    
  3. Create a new LINQ to HQL generator class that associates the method with the SQL function we registered in the custom dialect:

    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. Create a custom LinqToHqlGeneratorsRegistry:

    public class MyLinqToHqlRegistry : DefaultLinqToHqlGeneratorsRegistry
    {
        public MyLinqToHqlRegistry()
        {
           var generator = new MatchAgainstGenerator();
           RegisterGenerator(typeof(LinqExtensions).GetMethod("MatchAgainst"), generator);
        }
    }
    
  5. Use your custom dialect, and Linq to HQL registry either in your cfg.xml file or in code:

    var cfg = new Configuration()
        .DataBaseIntegration(db =>
    {
        db.Dialect<CustomMySQLDialect>();
    })
    .LinqToHqlGeneratorsRegistry<MyLinqToHqlRegistry>();
    
  6. Finally, use your extension method in a LINQ-to-NHibernate query:

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

    This will generate SQL that looks like this:

    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');
    

Again, this won't help if you have more complicated requirements. But hopefully this is at least a good starting point.

In case anyone is curious about how to make this support more complex scenarios, here are the problems I think you'd run into:

  • Separating the arguments to MATCH from those to AGAINST.
  • Registering a custom SQL function with NHibernate that can take an arbitrary number of arguments in different places
  • Creating the correct HQL even after solving the two issues above.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!