我认为在实现LinqToHql生成器类时我缺少一些基本的东西.
我已使用此注册使用自定义方言成功注册了sql Server 2008包含的查询:
RegisterFunction("contains",new StandardsqlFunction("contains",null));
我只有一个类要查询全文索引:
public class SearchName { public virtual Guid Id {get; set;} public virtual string Name {get; set;} // this is the search field }
contains函数在HQL中正常工作:
var names = Session.CreateQuery("from SearchName where contains(Name,:keywords)") .SetString("keywords","john") .List();
select searchname0_.Id as Id4_,searchname0_.Name as Name4_ from Search_Name searchname0_ where contains(searchname0_.Name,'john' /* @p0 */)
下一个挑战是实现Linq到HQL生成器:
public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { public MyLinqtoHqlGeneratorsRegistry() { this.Merge(new ContainsGenerator()); } } public class ContainsGenerator : BaseHqlGeneratorForMethod { public ContainsGenerator() { SupportedMethods = new[] { ReflectionHelper.GetMethodDefinition<SearchName>(d => d.Name.Contains(String.Empty)) }; } public override HqlTreeNode BuildHql(MethodInfo method,System.Linq.Expressions.Expression targetObject,ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,HqlTreeBuilder treeBuilder,IHqlExpressionVisitor visitor) { return treeBuilder.MethodCall("contains",visitor.Visit(targetObject).AsExpression(),visitor.Visit(arguments[0]).AsExpression() ); } } }
var namesLinq = Session.Query<SearchName>().Where(x=> x.Name.Contains("john")).ToList();
不幸的是,这似乎没有覆盖内置的Contains方法,并且生成的sql是错误的:
select searchname0_.Id as Id4_,searchname0_.Name as Name4_ from Search_Name searchname0_ where searchname0_.Name like ('%' + 'john' /* @p0 */ + '%')
是否无法覆盖默认的Contains方法,或者我只是犯了一个愚蠢的错误?
PS – 我正在使用NHibernate 3.3.1.4000
解决方法
好的,我终于搞清楚了!
... .ExposeConfiguration(cfg => { cfg.LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry>(); ... }
其次,不要试图覆盖现有的Linq行为.我将Contains扩展方法移动到全文类.
第三,正确构建Hql树.
对于其他尝试实现sql 2008自由文本包含搜索的人来说,这是完整的实现:
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,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); } }
要使上述工作正常,您必须声明并使用自定义方言:
public class CustomMssql2008Dialect : NHibernate.Dialect.Mssql2008Dialect { public CustomMssql2008Dialect() { RegisterFunction( "contains",null) ); } }
然后您可以这样使用新的包含搜索:
var namesLinq = Session.Query<SearchName>().Where(x => x.Contains("john")).ToList();
…而得到的sql是完美的! (至少如果你只有一个表,你正在进行全文搜索)
编辑:更新实现支持超过一个FULLTEXT’包含’搜索每个查询.
这是修订版:
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,new FullTextContainsGenerator()); } } public class FullTextContainsGenerator : BaseHqlGeneratorForMethod { public FullTextContainsGenerator() { SupportedMethods = new[] { ReflectionHelper.GetMethod(() => DialectExtensions.FullTextContains(null,null)) }; } public override HqlTreeNode BuildHql(MethodInfo method,IHqlExpressionVisitor visitor) { HqlExpression[] args = new HqlExpression[2] { visitor.Visit(arguments[0]).AsExpression(),visitor.Visit(arguments[1]).AsExpression() }; return treeBuilder.BooleanMethodCall("contains",args); } }
要使用修订版,语法略有不同:
var namesLinq = Session.Query<SearchName>().Where(x => x.Name.FullTextContains("john")).ToList();