NHibernate LinqToHqlGenerator for SQL Server 2008 full text index 'Containing' keyword

前端 未结 1 789
小鲜肉
小鲜肉 2021-01-03 05:30

I think I\'m missing something fundamental when implementing a LinqToHql generator class.

I\'ve successfully registered the SQL Server 2008 contains que

相关标签:
1条回答
  • 2021-01-03 06:24

    OK, I've finally figured it out!

    First, I managed to delete the registration code from my configuration:

    ...
    .ExposeConfiguration(cfg =>
         {
            cfg.LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry>();
            ...
         }
    

    Second, don't try to override the existing Linq behaviors. I moved my Contains extension method to the full-text class.

    Third, build the Hql tree correctly.

    For others trying to implement a SQL 2008 Free-text contains search, here's the complete implementation:

    public static class DialectExtensions
        {
            public static bool Contains(this SearchName sn, string searchString)
            {
                // this is just a placeholder for the method info.  
                // It does not otherwise matter.
                return false;
            }
        }
    
        public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
        {
            public MyLinqtoHqlGeneratorsRegistry()
                : base()
            {
                RegisterGenerator(ReflectionHelper.GetMethod(() =>
                     DialectExtensions.Contains(null, null)),
                     new ContainsGenerator());
            }
        }
    
        public class ContainsGenerator : BaseHqlGeneratorForMethod
        {
            string fullTextFieldName = "Name";
    
            public ContainsGenerator()
                : base()
            {
                SupportedMethods = new[] {
                    ReflectionHelper.GetMethodDefinition(() =>
                    DialectExtensions.Contains(null, null))
              };
            }
    
            public override HqlTreeNode BuildHql(MethodInfo method,
              System.Linq.Expressions.Expression targetObject,
              ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
              HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
            {
                // cannot figure out how to interrogate the model class to get an 
                // arbitrary field name...
                // perhaps the RegisterGenerator() call above could be used to pass a
                // property name to the ContainsGenerator constructor?
                // in our case, we only have one full text searchable class, and its
                // full-text searchable field is "Name"
                HqlExpression[] args = new HqlExpression[2] {
                     treeBuilder.Ident(fullTextFieldName).AsExpression(),
                     visitor.Visit(arguments[1]).AsExpression() 
                     };
                return treeBuilder.BooleanMethodCall("contains", args);
            }
        }
    

    For the above to work, you must have declared and used your custom dialect:

    public class CustomMsSql2008Dialect : NHibernate.Dialect.MsSql2008Dialect
    {
        public CustomMsSql2008Dialect()
        {
            RegisterFunction(
                "contains",
                new StandardSQLFunction("contains", null)
                );
        }
    }
    

    Then you can use your new contains search this way:

    var namesLinq = Session.Query<SearchName>().Where(x => x.Contains("john")).ToList();
    

    ... and the resulting SQL is perfect! (at least if you only have one table you're performing full-text searches on)

    EDIT: UPDATED IMPLEMENTATION TO SUPPORT MORE THAN ONE FULLTEXT 'Contains' SEARCH PER QUERY.

    Here's the revised version:

    public static class DialectExtensions
        {
            public static bool FullTextContains(this string source, string pattern)
            {
                return false;
            }
        }
    
        public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
        {
            public MyLinqtoHqlGeneratorsRegistry()
                : base()
            {
                RegisterGenerator(ReflectionHelper.GetMethod(() => DialectExtensions.FullTextContains(null, null)),
                              new FullTextContainsGenerator());
            }
        }
    
        public class FullTextContainsGenerator : BaseHqlGeneratorForMethod
        {
            public FullTextContainsGenerator()
            {
                SupportedMethods = new[] { ReflectionHelper.GetMethod(() => DialectExtensions.FullTextContains(null, null)) };
            }
    
            public override HqlTreeNode BuildHql(MethodInfo method,
              System.Linq.Expressions.Expression targetObject,
              ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
              HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
            {
                HqlExpression[] args = new HqlExpression[2] { 
                    visitor.Visit(arguments[0]).AsExpression(),
                    visitor.Visit(arguments[1]).AsExpression() 
                };
                return treeBuilder.BooleanMethodCall("contains", args);
            }
        }
    

    To use the revised version, the syntax is slightly different:

    var namesLinq = Session.Query<SearchName>().Where(x => x.Name.FullTextContains("john")).ToList();
    
    0 讨论(0)
提交回复
热议问题