Extending linq-to-nhibernate with custom hql generator - c#

The goal is to do something like this using NHibernate and Linq:
Session.Query<MyClass>().Where(x => x.DateGreaterThen(DateTime.Now))
This example is a little bit simplified, but the idea is that in Where predicate I would like to call MyClass method. So MyClass looks like:
public class MyClass
{
public static readonly Expression<Func<MyClass, DateTime, bool>> GreaterThenExpression =
(x, dt) => x.MyDateTimeProperty > dt.Date;
private static readonly Func<MyClass, DateTime, bool> GreaterThenFunc = GreaterThenExpression.Compile();
public Guid Id { get; set; }
public DateTime MyDateTimeProperty { get; set; }
public bool DateGreaterThen(DateTime dt)
{
return GreaterThenFunc(this, dt);
}
}
Custom generator:
public class EntityMethodGenerator<T1, T2, TResult> : BaseHqlGeneratorForMethod
{
private Expression<Func<T1, T2, TResult>> _returnValueExpression;
public EntityMethodGenerator()
{
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<MyClass>(myClass => myClass.DateGreaterThen(DateTime.Now))
};
}
public static void Register(ILinqToHqlGeneratorsRegistry registry, Expression<Action<T1>> method, Expression<Func<T1, T2, TResult>> returnValueExpression)
{
var generator = new EntityMethodGenerator<T1, T2, TResult> { _returnValueExpression = returnValueExpression };
registry.RegisterGenerator(ReflectionHelper.GetMethodDefinition(method), generator);
}
public override HqlTreeNode BuildHql(
MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor)
{
return visitor.Visit(_returnValueExpression);
}
}
And finally custom custom generators registry:
public class OwnLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public OwnLinqToHqlGeneratorsRegistry()
{
EntityMethodGenerator<MyClass, DateTime, bool>.Register(this, (myClass) => myClass.DateGreaterThen(DateTime.Now), MyClass.GreaterThenExpression);
}
}
But this is not working, I am getting System.Data.SqlClient.SqlException : Invalid column name 'dt'. I am suspecting that my BuildHql method is not implemented correctly, any help with fixing this?
By the way, I would like to use expressions in generator, like my GreaterThenExpression rather than building HqlTree manually in BuildHql method

GreaterThenExpression is a lamba. In BuildHql() you need to also take notice of the targetObject and the arguments, otherwise you are just inserting some HQL conversion of the unapplied lambda in the HQL tree.
You need to extract the body of the lambda and replace its parameters with the targetObject and arguments. Then you can generate HQL from that. Try using Remotion.Linq.Parsing.ExpressionTreeVisitors.MultiReplacingExpressionTreeVisitor.
(On a side note, it should be GreaterThan, not GreaterThen.)

Related

Register Linq extension method with NHibernate HQLGenerator

I have a extension method for IQueryable for performing OrderByDescending which I took from Marc gravell's answer in stackoverflow.
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
I want NHibernate to use this method for performing queries on the database. For this I know we must do ILinqToHqlGenerator for the extension method. I have wrote the following, but I'm sure this is not correct. Can anyone tell me the correct solution for registering the extension method ?
public class OrderByDescendingRuntimeHqlGenerator : IRuntimeMethodHqlGenerator
{
private readonly IHqlGeneratorForMethod _orderByDescendingGenerator = new OrderByDescendingExtension();
#region Impementation of IRuntimeMethodHqlGenerator
public bool SupportsMethod(MethodInfo method)
{
return method != null && method.Name == "OrderByDescending";
}
public IHqlGeneratorForMethod GetMethodGenerator(MethodInfo method)
{
return _orderByDescendingGenerator;
}
#endregion Impementation of IRuntimeMethodHqlGenerator
}
public class OrderByDescendingExtension : BaseHqlGeneratorForMethod
{
public OrderByDescendingExtension()
{
SupportedMethods = new[] {ReflectionHelper.GetMethodDefinition(() => QueryableExtensionsEx.OrderByDescending<object>(null, null))};
}
public override HqlTreeNode BuildHql(MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor)
{
return treeBuilder.MethodCall("OrderByDescending", visitor.Visit(arguments[0]).AsExpression());
}
}
I get the following error : Could not parse expression 'value(NHibernate.Linq.NhQueryable``1[Measurement.Model.Test.TestPieceDto]).Where(t => ((t.ComponentId == 8f136b42-8838-42ba-aee1-a33800ad2896) AndAlso (t.ProductId == 22cb6d1f-1934-489a-b7d7-a33800ad285c))).OrderByDescending("CreationTime")': This overload of the method 'Common.Linq.QueryableExtensionsEx.OrderByDescending' is currently not supported.

Query over interface properties using EntityFramework and LinqKit

I'm using EntityFramework and LinqKit to build expressions trees which get translated into SQL. Also we use the specification pattern to organize our queries.
There is a need in almost all of our domain objects to execute a query by description, but in some of those classes the property is called "Name", in others "Description", etc.
So initially was defined a interface as follow:
public interface IDescritible
{
string Description { get; }
}
The problem appeared when I tried to use it implemented explicitly in a class and made a generic query upon that:
public class Foo : IDescritible
{
public string Name { get; set; }
string IDescritible.Description
{
get { return this.Name; }
}
}
public class DescriptionMatch<T> : AbstractSpecification<T>
where T : IDescritible
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
return x => x.Description.Contains(Query);
}
}
When run, the EntityFramework is unable to translate the property get (a method call) into a SQL expression.
Then I tried to define a Expression in the class as follows:
public interface IDescritible<T> where T: IDescritible<T>
{
Expression<Func<T, string>> DescriptionExpression { get; }
}
public class Foo : IDescritible<Foo>
{
public string Name { get; set; }
public Expression<Func<Foo, string>> DescriptionExpression
{
get { return x => x.Name; }
}
}
public class DescriptionMatch<T> : AbstractSpecification<T> where T : IDescritible<T>
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
Expression<Func<T, bool>> combinedExpression = x => x.DescriptionExpression.Invoke(x).Contains(this.Query);
return combinedExpression.Expand();
}
}
But when it executes the .Expand() a exception is thrown: Unable to cast object of type 'System.Linq.Expressions.PropertyExpression' to type 'System.Linq.Expressions.LambdaExpression'.
Then I found out that LinqKit only support expanding local defined expressions (as stated on this question) and that there are some unoficial code write to solve those issues (answers on this question).
But when I modified the LinqKit library to try either one of the proposed solutions in second question it started to throw: variable 'x' of type 'Foo' referenced from scope '', but it is not defined.
Maybe that was caused by the fact that I don't have a instance of Foo yet? Only got the generic type parameter, and that's why I can't define my expression on a local variable. Maybe there is a way to define as a static expression somehow "attached" into class? But then I would not be able to use it in a generic query, there will be no interface...
There is a way to modify LinqKit so I can build expressions defined on a explicit implementation of a interface? Or there is another way to make a generic query upon properties explicitly implemented? Or any other solution or things to look into?
There is no need to use neither LinqKit or specification pattern for that, but EntityFramework is mandatory, what is important is to have a interface/any other way that support "pointing out" which property is the description property and make a generic Expression upon that.
Thank you in advance!
The easiest way to do it is to make the following change
public class DescriptionMatch<T> : AbstractSpecification<T> where T : IDescritible<T>, new()
Then you can get an instance by simply newing up a T (which EF), using the expression and otherwise discarding the object
public interface IDescritible<T> where T : IDescritible<T>
{
Expression<Func<T, string>> DescriptionExpression { get; }
}
public class Foo : IDescritible<Foo>
{
public string Name { get; set; }
public Expression<Func<Foo, string>> DescriptionExpression
{
get { return x => x.Name; }
}
}
public abstract class AbstractSpecification<T>
{
public abstract Expression<Func<T, bool>> IsSatisfiedBy();
}
public class DescriptionMatch<T> : AbstractSpecification<T>
where T : IDescritible<T>, new()
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
Expression<Func<T, string>> lambda = new T().DescriptionExpression;
return Expression.Lambda<Func<T, bool>>(
Expression.Call(
lambda.Body,
"Contains",
Type.EmptyTypes,
Expression.Constant(
this.Query,
typeof(string)
)
),
lambda.Parameters
);
}
}
public class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression _toBeReplaced;
private readonly Expression _replacement;
public ExpressionReplacer(Expression toBeReplaced, Expression replacement)
{
if (toBeReplaced.Type != replacement.Type)
{
throw new ArgumentException();
}
this._toBeReplaced = toBeReplaced;
this._replacement = replacement;
}
public override Expression Visit(Expression node)
{
return Object.ReferenceEquals(node, this._toBeReplaced) ? this._replacement : base.Visit(node);
}
}

Creating generic Expression

I have a problem regarding generic expressions.
I have an Expression<Func<T, bool>> and I want to convert the Expression<Func<T, bool>> to Expression<Func<Y, bool>>. The object of class T has the same properties as the class Y.
Does anybody know how to solve this?
The "preferred" way to do this is use a Interface that contains the property you care about.
interface IMyProp
{
string SomeProp {get;}
}
class T : IMyProp
{
public string SomeProp
{
get
{
//Some complicated logic
}
}
}
class Y : IMyProp
{
public string SomeProp {get; set;}
}
The just code your expression to Expression<Func<IMyProp, bool>>
However it is understandable that you can not always do this, for situations like this you can use a library like AutoMapper
class T
{
public string SomeProp
{
get
{
//Some complicated logic
}
}
}
class Y
{
public string SomeProp {get; set;}
}
//Some initiation code somewhere else in your project
public static void InitializeMappings()
{
Mapper.CreateMap<T, Y>();
}
public static IQueryable<Y> FilterOnTAndMapToY(IQueryable<T> source, Expression<Func<T,bool>> filter)
{
return source.Where(filter).Project().To<Y>();
}
Now this does not exactly turn your Expression<Func<T, bool>> in to a to Expression<Func<Y, bool>> but it does let you use your T expression and use it to get a Y result after the filtering has been applied.
The way AutoMapper's Queryable Extensions work is the querys and casts to go from T to Y happen all server side when you are doing LinqToEntities. So you could even do
public static IQueryable<Y> MultiFilterCast(IQueryable<T> source, Expression<Func<T,bool>> tTypeFilter, Expression<Func<Y,bool>> yTypeFilter)
{
var filteredStage1 = source.Where(tTypeFilter);
var castToY = filteredStage1.Project().To<Y>();
var filteredStage2 = castToY.Where(yTypeFilter);
return filteredStage2;
}
both tTypeFilter and yTypeFilter will be applied server side before you get the result set.

Generating Expression<Func<T, object>> arguments

I have a method like this:
public static MvcHtmlString Pager<T>(T urlParams, Expression<Func<T, object>> pageProperty) where T : class
{
string pagingProp = Helpers.PropertyToString(pageProperty.Body);
//set property on object using reflection.
PropertyInfo prop = type.GetProperty(urlParams.GetType());
}
The purpose of the expression is to know what property of urlParams is the one that should be used for paging.
Lets say I have the class:
public class Pagination
{
public int PageIndex {get; set; }
}
I would like to call it like this:
Html.Pager(new Pagination{ PageIndex = 1 }, new Expression<Func<Pagination>>(p => p.PageIndex))
Problem: Expression<Func<Pagination>>() does take a constructor, how do I tell the expression I want to use the PageIndex property?
Assuming:
void M(Expression<Func<User, object>> f) { /* ... some implementation ... */ }
Then:
M(u => u.Password);

Class type inference from static method / caller?

Given the following two classes:
public class ABC
{
public void Accept(Ordering<User> xyz)
{
// Do stuff with xyz...
}
}
public class Ordering<TEntity>
where TEntity : class
{
private readonly Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> Transform;
private Ordering(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> transform)
{
this.Transform = transform;
}
public static Ordering<TEntity> By<TKey>(Expression<Func<TEntity, TKey>> expression)
{
return new Ordering<TEntity>(query => query.OrderBy(expression));
}
public static Ordering<TEntity> ByDescending<TKey>(Expression<Func<TEntity, TKey>> expression)
{
return new Ordering<TEntity>(query => query.OrderByDescending(expression));
}
public Ordering<TEntity> ThenBy<TKey>(Expression<Func<TEntity, TKey>> expression)
{
return new Ordering<TEntity>(query => this.Transform(query).ThenBy(expression));
}
public Ordering<TEntity> ThenByDescending<TKey>(Expression<Func<TEntity, TKey>> expression)
{
return new Ordering<TEntity>(query => this.Transform(query).ThenByDescending(expression));
}
public IOrderedQueryable<TEntity> Apply(IQueryable<TEntity> query)
{
return Transform(query);
}
}
Used in the following way:
ABC abc = new ABC();
abc.Accept(Ordering<User>.By(u => u.Id));
Is there any way to infer the type of T like so:
abc.Accept(Ordering.By(u => u.Id));
You can do it, but not in a generic type. Generic type inference like this only occurs for generic methods. Declare a separate non-generic type, with a generic method:
public class XYZ
{
public static XYZ Action<T, TKey> (TKey key, T element)
{
return new XYZ<T>(element);
}
}
EDIT: Responding to the question edit.
No, you can't do something like this:
abc.Accept(Ordering.By(u => u.Id));
The problem is the inner expression:
Ordering.By(u => u.Id)
What's the type of u here? It could be any class with an Id property. Note that the C# compiler will need to work out the type of this expression before it looks at abc.Accept. Even if abc.Accept only worked for an Ordering<User>, it would fail.
There are three options here:
Use a static method in the generic class, specifying the source type argument explicitly, and inferring the key type argument:
Ordering<User>.By(u => u.Id)
Use a generic method in a non-generic class, specifying both type arguments explicitly:
Ordering.By<User, string>(u => u.Id)
Use a generic method in a non-generic class, specifying the type of the lambda parameter explicitly, and letting the compiler infer the key type argument:
Ordering.By((User u) => u.Id)
Obviously, all of these cases require you to specify the type explicitly somewhere.
One other option which is a little bit weird is relevant if you've typically already got an instance of User (or at least a variable of that type). You can use that as a sort of example, which gets ignored:
public static Ordering<T> By<T,TKey>(Expression<Func<T, TKey>> func, T example)
{
return By<T, TKey>(func);
}
...
Ordering.By(u => u.Id, dummyUser);

Categories