Create an extension method that you'll use in LINQ queries. This method does not need an implementation--we'll provide that later:
public static class LinqExtensions
{
public static bool IsLikeWithEscapeChar(
this string input,
string like,
char? escapeChar)
{
throw new NotImplementedException();
}
}
Create an HqlEscape
tree node that we will use to represent the escape
portion of the like
operator:
public class HqlEscape : HqlExpression
{
public HqlEscape(IASTFactory factory, params HqlTreeNode[] children)
: base(HqlSqlWalker.ESCAPE, "escape", factory, children)
{
}
}
Create an HqlLikeWithEscape
tree node. The default HqlLike
node cannot handle the escape
part, so we need to create a new node that can handle three children:
public class HqlLikeWithEscape : HqlBooleanExpression
{
public HqlLikeWithEscape(IASTFactory factory, HqlExpression lhs, HqlExpression rhs, HqlEscape escape)
: base(HqlSqlWalker.LIKE, "like", factory, lhs, rhs, escape)
{
}
}
Create a generator for the IsLikeWithEscapeChar
extension method we defined earlier. This class' responsibility is to take the information the method is invoked with and return an HQL tree structure that will ultimately be turned into SQL:
public class CustomLikeGenerator : BaseHqlGeneratorForMethod
{
public CustomLikeGenerator()
{
this.SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition(
() => LinqExtensions.IsLikeWithEscapeChar(null, null, null))
};
}
public override HqlTreeNode BuildHql(
MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
var factory = new ASTFactory(new ASTTreeAdaptor());
HqlTreeNode escapeCharNode = visitor.Visit(arguments[2]).AsExpression();
var escapeNode = new HqlEscape(factory, escapeCharNode);
HqlLikeWithEscape likeClauseNode =
new HqlLikeWithEscape(
factory,
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression(),
escapeNode);
return likeClauseNode;
}
}
As you can see, we've utilized the new HQL tree nodes we defined earlier. The major downside to this approach is that it required me to manually create an ASTFactory
and ASTTreeAdaptor
. The use of these classes is usually encapsulated inside of HqlTreeBuilder
, but HqlTreeBuilder
doesn't lend itself to being subclassed. Would appreciate some input on this if someone has some advice.
Create a new LINQ to HQL generators registry. This class just just associates our extension method with the HQL implementation we provided in step 4:
public class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public LinqToHqlGeneratorsRegistry() : base()
{
RegisterGenerator(
ReflectionHelper.GetMethodDefinition(() => LinqExtensions.IsLikeWithEscapeChar(null, null, null)),
new CustomLikeGenerator());
}
}
Update your configuration to use the new LinqToHqlGeneratorsRegistry
:
cfg.LinqToHqlGeneratorsRegistry<LinqToHqlGeneratorsRegistry>()
(Finally) use your new extension method in a query:
session.Query<Person>().Where(p => p.FirstName.IsLikeWithEscapeChar("%Foo", '\\'))
Note that you need to specify the wildcard character. This could be smoothed out, but that wouldn't be too hard to do.
这是我第一次以这种方式扩展HQL,所以可能会存在问题。我只能在SQL Server上测试过,但是我很有信心,因为它创建了与HQL查询相同的树形结构,所以应该能够正常工作。
var factory = treeBuilder.Constant(null).Factory
来获取内部工厂。我不确定这是否完全正确,但它可以工作。 - Matthias