Encapsulating LINQ queries in navigation properties for re-use? - c#

I'm using Entity Framework Code First with SQL Server, with a domain entity that is similar to this:
public class Item
{
public ICollection<ItemLocation> ItemLocations { get; set; }
}
An item can be assigned to many locations throughout it's life, but only one can be active at any time, and we use this to get the actual location of the item:
public Location
{
get
{
return ItemLocations.Where(x => x.IsActive).Select(x => x.Location).FirstOrDefault()
}
}
This property works as expected if I load the entire item object:
var item = (from i in db.Items select i).FirstOrDefault();
Console.WriteLine(item.Location.Name);
However, I can't use this in my LINQ queries where I need to return an anonymous type, like this:
var items = from i in db.Items
select new
{
ItemId = i.ItemId,
LocationName = i.Location.Name
};
Instead, I have to use the full query every time:
var items = from i in db.Items
select new
{
ItemId = i.ItemId,
LocationName = i.ItemLocations.Where(x => x.IsActive).Select(x => x.Location).FirstOrDefault().Name
};
Ideally, I'd like to keep the logic for retrieving an item location in one place (like a property), rather than having to scatter these all over the place.
What is the best way to achieve this?

So to start with, if we want to be able to combine this sub-query with another query then we need to define it as an Expression object, rather than as C# code. If it has already been compiled into IL code then the query provider cannot inspect it to look at what operations are performed and translate that into SQL code. Creating an Expression representing this operation is easy enough:
public static readonly Expression<Func<Item, ItemLocation>> LocationSelector =
item => item.ItemLocations.Where(x => x.IsActive)
.Select(x => x.Location)
.FirstOrDefault();
Now that we have an expression to get a location from an item, we need to combine that with your custom expression for selecting out an anonymous object from an item, using this location. To do this we'll need a Combine method that can take one expression selecting an object into another object, as well as another expression that takes the original object, the result of the first expression, and computes a new result:
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
Internally, this simply replaces all instances of the parameter of the second expression with the body of the first; the rest of the code is simply ensuring a single parameter throughout and wrapping the result back into a new lambda. This code depends on the ability to replace all instances of one expression with another, which we can do using:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Now that we have our Combine method all we need to do is call it:
db.Items.Select(Item.LocationSelector.Combine((item, location) => new
{
ItemId = item.ItemId,
LocationName = location.Name
}));
And voila.
If we wanted, we could print out the expression generated by the call to Combine instead of passing it to Select. Doing that, it prints out:
param => new <>f__AnonymousType3`2(ItemId = param.ItemId,
LocationName = param.ItemLocations.Where(x => x.IsActive)
.Select(x => x.Location).FirstOrDefault().Name)
(whitespace added by myself)
That is exactly the query that you had specified out manually, however here we're re-using the existing sub-query without needing to type it out every single time.

Related

Entity Framework linq orderby function

I am new to Entity Framework and linq. I am working with asp.net mvc 5 and C#.
I write a query with dynamic sorting as follows:
public static IEnumerable<T> OrderByDynamic<T>(this IEnumerable<T> source, string propertyName, bool Ascending)
{
if (Ascending)
return source.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
else
return source.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
}
and in my repository I can write:
string sortExpr = "name";
_objDB.hotels.Where(s => (s.city_id = 1))
.OrderByDynamic(sortExpr, Ascending).ToList();
This code works fine when sorting is on a column of a table, but I need to sort by a SQL function. I entered the function into the .edmx model with the following code
[EdmFunction("hotelReservation.Core.Data", "getHotelMinPrice_cap")]
public static int getHotelMinPrice_cap(int Hotel_id, int capacity, DateTime enter_date, DateTime exit_date)
{
throw new NotSupportedException("Direct calls are not supported.");
}
and my SQL selection is something like:
select *
from hotel
where city_id = 1
order by dbo.getHotelMinPrice_cap(hotel.id,1,'2001-01-01', '2021-01-01')
How can I write the last SQL query with dynamic sorting in linq?
Your solution introduces several problems:
You OrderBy a property by name, hoping that the objects you order have this property. What if your objects don't have this property?
SQL does not understand functions like GetProperty(), so this ordering has to be done in local memory (AsEnumerable) instead of the much faster SQL server (AsQueryable).
You use a stored procedure to order by.
The first two problems can be solved easily by changing the parameter propertyName by a Func<TSource, TKey> keySelector:
public static IEnumerable<T, TKey> OrderByDynamic<T>(this IEnumerable<T> source,
Func<T, TKey> keySelector, System.ComponentModel.ListSortDirection sortDirection)
{
if (sortDirection == ListSortDirection.Ascending)
return source.OrderBy(keySelector);
else
return source.OrderByDescending(keySelector);
}
Usage would be like:
var result = Students.OrderByDynamis(student => student.Name, ListSortDirection.Ascending);
The advantage of this method is that your compiler will complain if you try to order by a non-existing property. Besides this OrderBy can be performed AsQueryable; it can be performed by your database instead of in local memory.
It is really a bad idea to use a string to select the property you want.
If you make a typing error you'll only detect this at run-time. Besides: if you know what to type as string for your propertyName during development of your code, you also know the type of objects you will be sorting, so you could write a keySelector instead.
Your second problem is calling the stored procedure as sort order. This is fairly easy to solve if you first call the stored procedure and then order by the returned value:
var result = Hotels.Where(hotel => hotel...)
.Select(hotel => new
{
StoredProcedureValue = CallStoredprocedure(hotel,...),
Hotel = hotel,
})
.AsEnumerable() // bring the result to local memory
.Orderby(item => item.StoredProcedureValue)
.Select(item => item.Hotel);
Only the hotels that will be in your end-result are transferred to local memory, together with the result of the StoredProcedures. They have only been called for the hotels you will use in your end result.
However, the sorting is done in local memory. If you also want this sorting to be done on database side you'll have to create a new stored procedure that will call the other stored procedure before performing the sort.
Thanks to Harald Coppoolse for the answer
I finally did it like this:
_objDB.hotels .Select(h=> new { id= h.id, name=h.name, star=h.star,
minPrice1 = hotel.getHotelMinPrice_cap(h.id, 1, model.date_check_in, model.date_check_out)})
.Where(s => (s.city_id = 1))
.OrderByDynamic(sortExpr, Ascending).ToList();
In this case, i can choose sortExpr = minPrice1; and it will be sort by the sql function.
also I changed the OrderByDynamic function as bellow:
public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
which i found on page:
https://stackoverflow.com/a/7265354/8509940

In a Linq Expression body how to use the value of a variable instead of a reference to it?

Here is my code:
IQueryable<DAL.TradeCard> data = dc.TradeCard.Include("Address").Include("Vehicle");
string orderNumber = "ORD_NR_2";
Expression<Func<DAL.TradeCard, bool>> whereClause = a => a.orderNumber == orderNumber;
// Expression<Func<DAL.TradeCard, bool>> whereClause = a => a.orderNumber == "ORD_NR_2";
List<DAL.TradeCard> dataAsList = data.Where(whereClause).ToList();
If I use the commented line, then the value of whereClause will look like this:
{a => (a.orderNumber == "ORD_NR_2")}
If instead of the commented line I use the other definiton then the value of whereClause will look like this:
{a => (a.orderNumber ==
value(app_Employee.UI.UserFunctions.LejelentettTetelek+<>c__DisplayClass0).orderNumber)}
This is a problem, because I want to save the whereClause and use it in other places where the orderNumber variable does not exist.
So how to use the value of the orderNumber variable instead of a reference to it in a Linq expression. I want to make "value(app_Employee.UI.UserFunctions.LejelentettTetelek+<>c__DisplayClass0).orderNumber" into "ORD_NR_2".
The code in this blog post (Link is dead, Archived version: https://web.archive.org/web/20160122054419/http://blogs.msdn.com/b/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx) provides a way to evaluate all sections of an expression into values, in all of the places that it can be done.
First it walks through the expression tree from the bottom up, indicating which objects don't have an parameter objects as any of their children. Then it walk through the tree from the top down, evaluating all expressions to a value that don't have a parameter in them.
We can also create an additional method specifically for an expression representing a Func with one parameter so that you don't need to do the cast when you call it:
public static Expression<Func<TIn, TOut>> Simplify<TIn, TOut>(
this Expression<Func<TIn, TOut>> expression)
{
return (Expression<Func<TIn, TOut>>)Evaluator.PartialEval(expression);
}
This allows you to write:
string orderNumber = "ORD_NR_2";
Expression<Func<DAL.TradeCard, bool>> whereClause = a => a.orderNumber == orderNumber;
string foo = whereClause.Simplify().ToString();
//foo will be "{a => (a.orderNumber == "ORD_NR_2")}"

Expression Tree Concatenation with LINQ to SQL

I'm having a go at making a flexible exporter to export info from a SQL db accessed via LINQ to SQL, whereby the program can dynamically choose which fields to select and do all the processing server side.
The final aim is to have a simple statement like:
var result = db.Product.Select(p => selector(p));
Where selector is a dynamically created Expression Tree describing the fields to select. Currently, I have each of the db fields assigned to it's own individual selector like:
Expression<Func<Product, string>> name = p => p.Name;
Expression<Func<Product, string>> createdAt = p => p.createdAt.ToString();
At the moment, I'm taking the chosen fields and trying to make one concatenated expression out of them that returns a comma delimited string result from the select statement, something like:
Expression<
Func<
Func<Product, string>,
Func<Product, string>,
Func<Product, string>>> combinator = (a, b) => (p) => a(p) + "," + b(p);
// So I can do something like...
Expression<Func<Product, string>> aggregate = p => "";
foreach (var field in fieldsToSelect)
aggregate = combinator(aggregate, field);
// This would create...
Expression<Func<Products, string>> aggregate = p => a(p) + "," + b(p);
Once I've built up my selector with however many fields, I can execute it in the statement and all the processing is done on the server. However, I've been unable to properly create an expression to concatenate the two child functions in such a manner that the result isn't a Func that's simply executed after the round trip to fetch the results:
var joinedSelector = combinator.Compile();
Func<Products, string> result = joinedSelector(firstSelector.Compile(), secondSelector.Compile());
var query = db.Product.Select(p => result(p)).ToList();
From my limited understanding of Expression Trees, this doesn't actually result in one as the statements are compiled to normal Funcs. I've looked at Expression.Coalesce() but am not sure if it's what I'm after (think it just does "??").
I'm pretty new to this sort of stuff, and would appreciate any help.
Even if people can think of better ways to attack the original problem, I'd still quite like an explanation of how to achieve what I'm trying to do just for the sake of learning how to use Expression Trees.
So you're looking to create a Combine method that can combine the results of two selectors. To do this we'll need a method that accepts two functions with the same input and same output, and then a function accepting two instances of that output type and returning a new value.
The function will need to replace all instances of the parameters of the selectors' body with a common parameter. It will then replace the two parameters of the result function with the corresponding bodies of the different selectors. Then we just wrap all of that up in a lambda.
public static Expression<Func<T, TResult>> Combine
<T, TIntermediate1, TIntermediate2, TResult>(
this Expression<Func<T, TIntermediate1>> first,
Expression<Func<T, TIntermediate2>> second,
Expression<Func<TIntermediate1, TIntermediate2, TResult>> resultSelector)
{
var param = Expression.Parameter(typeof(T));
var body = resultSelector.Body.Replace(
resultSelector.Parameters[0],
first.Body.Replace(first.Parameters[0], param))
.Replace(
resultSelector.Parameters[1],
second.Body.Replace(second.Parameters[0], param));
return Expression.Lambda<Func<T, TResult>>(body, param);
}
This uses the following method to replace all instances of one expression with another:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Now we can write the following:
Expression<Func<Product, string>> name = p => p.Name;
Expression<Func<Product, string>> createdAt = p => p.createdAt.ToString();
Expression<Func<Product, string>> aggregate =
Combine(name, createdAt, (a, b) => a + "," + b);
It actually comes out a bit simpler than in your mockup, as the result selector doesn't need to know anything about how its inputs are generated, or that they're actually selectors. This solution also allows for each selector to select out different types, and for them to differ from the result, simply because there's no real cost to adding all of this when the generic arguments are just going to be inferred.
And with this in place you can even easily aggregate an arbitrary number of selectors, given the type restrictions you've put in place:
IEnumerable<Expression<Func<Product, string>>> selectors = new[]{
name,
createdAt,
name,
};
var finalSelector = selectors.Aggregate((first, second) =>
Combine(first, second, (a, b) => a + "," + b));
This would let you, for example, have a params method accepting any number of selectors (of a common input and output type) and be able to aggregate all of their results together.

Dynamic Lambda Select Index

I need some help with a LINQ extension that I'm tying to write. I'm trying to create an extension that calculates the row index of a given Id within an IQueryable - Except that type can be any table. I think I've got most of the way there but I just can't seem to complete it. I'm getting the following error message on the line
Select(lambda)
The type arguments for method
'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable,
System.Func)' cannot be inferred from the usage.
Try specifying the type arguments
explicitly. c:\users\shawn_000\documents\visual studio
2013\projects\dexconstruktaweb\dexconstruktaweb\generalhelper.cs 157 17 DexConstruktaWeb
private class GetRowCountClass
{
public GetRowCountClass(int id, int index)
{
this.Id = id;
this.Index = index;
}
public int Id { get; set; }
public int Index { get; set; }
}
public static int GetRowCount<T>(this IQueryable<T> query, int id)
{
Type sourceType = typeof(T);
ParameterExpression[] parameter = new ParameterExpression[2];
parameter[0] = Expression.Parameter(sourceType, "x");
parameter[1] = Expression.Parameter(typeof(int), "index");
Type getRowCountType = typeof(GetRowCountClass);
ConstructorInfo constructor = getRowCountType.GetConstructor(new[] { typeof(int), typeof(int)} );
PropertyInfo pi = sourceType.GetProperty("Id");
Expression expr = Expression.Property(parameter[0], pi);
NewExpression member = LambdaExpression.New(constructor,new Expression[] { expr, parameter[1]});
LambdaExpression lambda = Expression.Lambda(member, parameter);
var item = query.AsEnumerable()
.Select(lambda);
}
I know that after the select I need the following line to get the index to return, but for now I'm stumped. Any help would be appreciated. Thanks.
.SingleOrDefault(x => x.Id == id).index;
Update
I've done some further digging and found that some LINQ statements do not work for LINQ to Entities, which is what I'm using:
http://msdn.microsoft.com/en-us/library/bb738550.aspx
http://msdn.microsoft.com/en-us/library/bb896317.aspx
In particular "Most overloads of the projection and filtering methods are supported in LINQ to Entities, with the exception of those that accept a positional argument."
To get around this I was using a call to AsEnumerable() to turn this into a generic Enumerable, then the call to Select and SingleOrDefault as described above. However, I have found that there is no difference in the SQL created between a call to AsEnumerable and ToList, so I have decided to simply call:
.ToList().FindIndex(e => e.Id == id)
directly on my IQueryable without creating an Extension as it is a small enough piece of code.
Thanks for all your help. If someone still sees a better way to do this please let me know.
cheers,
Update 2
As a bit of a learning exercise I took Servy's suggestion and this answer Creating Dynamic Predicates- passing in property to a function as parameter and came up with the following:
public static int GetRowIndex<T>(this IQueryable<T> query, Expression<Func<T, int>> property, int id)
{
var lambda = Expression.Lambda<Predicate<T>>(
Expression.Equal(property.Body, Expression.Constant(id)), property.Parameters);
return query.ToList().FindIndex(lambda.Compile());
}
This can be called like:
var result2 = query.GetRowIndex(x => x.Id, id);
Where query is of Type IQueryable.
There is very little point to it though and it is only really useful as a learning exercise.
Thanks.
Your lambda always returns GetRowCountClass and takes T so you can use generic version of Expression.Lambda method:
var lambda = Expression.Lambda<Func<T, GetRowCountClass>>(member, parameter);
var item = query.Select(lambda);
return item.SingleOrDefault(x => x.Id == id).Index;

Invoke a C# inner Expression with a member property of a parameter to an outer expression

I am using the Albaharis PredicateBuilder as found here http://www.albahari.com/nutshell/predicatebuilder.aspx to filter results in a Linq-to-SQL application. This has been working great.
What I am trying to do now, is reuse the existing filtering predicate expression to filter an object that has the existing filtered object as a property.
For example, I have 2 classes, Order and Customer. I already have a method that returns a Expression<Func<Customer, bool>>, which is built using the above mentioned predicate builder. I now want to reuse this in my Order filtering method, which will return a Expression<Func<Customer, bool>> by somehow passing the Order.Customer property (expression?) into my Customer filter method.
I have something like this (far from complete, but I hope you get the idea):
public class CustomerSearchCriteria
{
public Expression<Func<Customer, bool>> FilterPredicate()
{
// Start with predicate to include everything
var result = PredicateBuilder.True<Customer>();
// Build predicate from criteria
if (!String.IsNullOrEmpty(this.Name))
{
result = result.And(c => SqlMethods.Like(c.Name, this.Name));
}
// etc. etc. etc
}
public class OrderSearchCriteria
{
public Expression<Func<Order, bool>> FilterPredicate()
{
// Start with predicate to include everything
var result = PredicateBuilder.True<Order>();
// Build predicate from criteria
if (!String.IsNullOrEmpty(this.Reference))
{
result = result.And(o => SqlMethods.Like(o.Reference, this.Reference));
}
// etc. etc. etc
// This is where I would like to do something like:
// result = result.And(o => o.Customer "matches" this.CustomerCriteria.FilterPredicate()
}
Can any Linq expression guru help me?
Thanks in advance.
If you use the Albaharis' LinqKit, you should be able to do something like this:
var customerFilter = this.CustomerCriteria.FilterPredicate();
// create an expression that shows us invoking the filter on o.Customer
Expression<Func<Order, bool>> customerOrderFilter =
o => customerFilter.Invoke(o.Customer);
// "Expand" the expression: this creates a new expression tree
// where the "Invoke" is replaced by the actual predicate.
result = result.And(customerOrderFilter.Expand())

Categories