问题描述
我认为在实现 LinqToHql 生成器类时我缺少一些基本的东西.
I think I'm missing something fundamental when implementing a LinqToHql generator class.
我已经使用自定义方言通过此注册成功注册了 SQL Server 2008 contains
查询:
I've successfully registered the SQL Server 2008 contains
query using a custom dialect with this registration:
RegisterFunction("contains", new StandardSQLFunction("contains", null));
我只有一个类需要查询全文索引:
I have only one class with a full text index to be queried:
public class SearchName
{
public virtual Guid Id {get; set;}
public virtual string Name {get; set;} // this is the search field
}
contains 函数在 HQL 中正常工作:
The contains function works properly in HQL:
var names = Session.CreateQuery("from SearchName where contains(Name,:keywords)")
.SetString("keywords", "john")
.List();
生成的 SQL 是完美的:
and the generated SQL is perfect:
select searchname0_.Id as Id4_,
searchname0_.Name as Name4_
from Search_Name searchname0_
where contains(searchname0_.Name, 'john' /* @p0 */)
下一个挑战是实现 Linq to HQL 生成器:
The next challenge was to implement the Linq to HQL generator:
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是错误的:
Unfortunately, this doesn't seem to override the built-in Contains
method, and the generated SQL is wrong:
select searchname0_.Id as Id4_,
searchname0_.Name as Name4_
from Search_Name searchname0_
where searchname0_.Name like ('%' + 'john' /* @p0 */ + '%')
是否无法覆盖默认的 Contains
方法,或者我只是犯了一个愚蠢的错误?
Is it not possible to override the default Contains
method, or have I just made a silly mistake?
PS - 我使用的是 NHibernate 3.3.1.4000
PS - I'm using NHibernate 3.3.1.4000
推荐答案
好吧,我终于想通了!
首先,我设法从我的配置中删除了注册码:
First, I managed to delete the registration code from my configuration:
...
.ExposeConfiguration(cfg =>
{
cfg.LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry>();
...
}
第二,不要试图覆盖现有的 Linq 行为.我将我的 Contains 扩展方法移动到全文类.
Second, don't try to override the existing Linq behaviors. I moved my Contains extension method to the full-text class.
第三,正确构建Hql树.
Third, build the Hql tree correctly.
对于尝试实现 SQL 2008 自由文本包含搜索的其他人,这里是完整的实现:
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)
);
}
}
然后您可以这样使用新的 contains 搜索:
Then you can use your new contains search this way:
var namesLinq = Session.Query<SearchName>().Where(x => x.Contains("john")).ToList();
... 生成的 SQL 是完美的!(至少如果你只有一张表,你正在执行全文搜索)
... and the resulting SQL is perfect! (at least if you only have one table you're performing full-text searches on)
更新的实现支持多个全文包含"每个查询的搜索.
修改后的版本:
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();
这篇关于NHibernate LinqToHqlGenerator for SQL Server 2008 全文索引 'Containing' 关键字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!