LinqToHql 생성기 클래스를 구현할 때 근본적인 것을 놓치고 있다고 생각합니다.
contains
이 등록과 함께 사용자 지정 언어를 사용하여 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();
생성 된 SQL은 완벽합니다.
select searchname0_.Id as Id4_,
searchname0_.Name as Name4_
from Search_Name searchname0_
where contains(searchname0_.Name, 'john' /* @p0 */)
다음 과제는 Linq to 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
방법 을 재정의 할 수 없거나 어리석은 실수를 한 적이 있습니까?
추신-저는 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,
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);
}
}
위의 기능이 작동하려면 사용자 지정 방언을 선언하고 사용해야합니다.
public class CustomMsSql2008Dialect : NHibernate.Dialect.MsSql2008Dialect
{
public CustomMsSql2008Dialect()
{
RegisterFunction(
"contains",
new StandardSQLFunction("contains", null)
);
}
}
그런 다음 다음과 같은 방법으로 새 포함 검색을 사용할 수 있습니다.
var namesLinq = Session.Query<SearchName>().Where(x => x.Contains("john")).ToList();
... 그리고 결과 SQL은 완벽합니다! (적어도 전체 텍스트 검색을 수행하는 테이블이 하나만있는 경우)
수정 : 쿼리 당 하나 이상의 전체 텍스트 '포함'검색을 지원하도록 구현이 업데이트되었습니다.
수정 된 버전은 다음과 같습니다.
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);
}
}
수정 된 버전을 사용하려면 구문이 약간 다릅니다.
var namesLinq = Session.Query<SearchName>().Where(x => x.Name.FullTextContains("john")).ToList();
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다