Combining Includes with Entity Framework - entity-framework

I generally use a generic repository to boilerplate my EF queries so I have to write limited code and also use caching. The source code for the repository can be found here.
The backbone query within the code is this one below. FromCache<T>() is an IEnumerable<T> extension method that utilizes the HttpContext.Cache to store the query using a stringified representation of the lambda expression as a key.
public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null)
where T : class, new()
{
// Check for a filtering expression and pull all if not.
if (expression == null)
{
return this.context.Set<T>()
.AsNoTracking()
.FromCache<T>(null)
.AsQueryable();
}
return this.context.Set<T>()
.AsNoTracking<T>()
.Where<T>(expression)
.FromCache<T>(expression)
.AsQueryable<T>();
}
Whilst this all works it is subject to the N+1 problem for related tables since If I were to write a query like so:
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false)
.Include(p => p.Author);
The Include() will have no effect on my query since it has already been run in order to be cached.
Now I know that I can force Entity Framework to use eager loading within my model by removing the virtual prefix on my navigation properties but that to me feels like the wrong place to do it as you cannot predict the types of queries you will be making. To me it feels like something I would be doing in a controller class. What I am wondering is whether I can pass a list of includes into my Any<T>() method that I could then iterate though when I make the call?

ofDid you mean something like...
IQueryable<T> AnyWithInclude<T,I>(Expression<Func<T,bool>> predicate,
Expression<Func<T,I>> includeInfo)
{
return DbSet<T>.where(predicate).include(includeInfo);
}
the call
Context.YourDbSetReference.AnyWithInclude(t => t.Id==someId, i => i.someNavProp);
In response to extra question on as collection.
I realised late, there was an overload on Property. You can just pass a string
This might work but call is not easy. Well I find it hard.
IQueryable<T> GetListWithInclude<I>(Expression<Func<T, bool>> predicate,
params Expression<Func<T, I>>[] IncludeCollection);
so i tried
public virtual IQueryable<T> GetListWithInclude(Expression<Func<T, bool>> predicate,
List<string> includeCollection)
{ var result = EntityDbSet.Where(predicate);
foreach (var incl in includeCollection)
{
result = result.Include(incl);
}
return result;
}
and called with
var ic = new List<string>();
ic.Add("Membership");
var res = context.DbSte<T>.GetListWithInclude( t=>t.UserName =="fred", ic);
worked as before.

In the interest of clarity I'm adding the solution I came up with based upon #soadyp's answer.
public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null,
params Expression<Func<T, object>>[] includeCollection)
where T : class, new()
{
IQueryable<T> query = this.context.Set<T>().AsNoTracking().AsQueryable<T>();
if (includeCollection.Any())
{
query = includeCollection.Aggregate(query,
(current, include) => current.Include(include));
}
// Check for a filtering expression and pull all if not.
if (expression != null)
{
query = query.Where<T>(expression);
}
return query.FromCache<T>(expression, includeCollection)
.AsQueryable<T>();
}
Usage:
// The second, third, fourth etc parameters are the strongly typed includes.
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false,
p => p.Author);

Related

Merging Entity Framework Expression Trees

I'm looking for a way to merge multiple expression trees in order to build selectors for an Entity Framework query. The query knows which columns to select based on user-provided parameters. For example, a basic query returns ID/Name columns of an entity. If a parameter is explicitly set to also retrieve the Description column, then the query will return ID/Name/Description.
So, what I need it the code for the MergeExpressions method in the following code.
Expression<Func<T, TDto>> selector1 = x => new TDto
{
Id = x.Id,
Name = x.Name
}
Expression<Func<T, TDto>> selector2 = x => new TDto
{
Description = x.Description
}
var selector = selector1;
if (includeDescription)
selector = MergeExpressions(selector1, selector2);
var results = repo.All().Select(selector).ToList();
Thank you.
Not sure for general case, but merging MemberInitExpression bodied lambdas like in your sample is relatively easy. All you need is to create another MemberInitExpression with combined Bindings:
static Expression<Func<TInput, TOutput>> MergeExpressions<TInput, TOutput>(Expression<Func<TInput, TOutput>> first, Expression<Func<TInput, TOutput>> second)
{
Debug.Assert(first != null && first.Body.NodeType == ExpressionType.MemberInit);
Debug.Assert(second != null && second.Body.NodeType == ExpressionType.MemberInit);
var firstBody = (MemberInitExpression)first.Body;
var secondBody = (MemberInitExpression)second.Body.ReplaceParameter(second.Parameters[0], first.Parameters[0]);
var body = firstBody.Update(firstBody.NewExpression, firstBody.Bindings.Union(secondBody.Bindings));
return first.Update(body, first.Parameters);
}
Note that the lambda expressions must be bound to one and the same parameters, so the above code uses the following parameter replacer helper to rebind second lambda body to the first lambda parameter:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Check out PredicateBuilder.
Example:
Expression<Func<Customer, bool>> expr1 = (Customer c) => c.CompanyName.StartsWith("A");
Expression<Func<Customer, bool>> expr2 = (Customer c) => c.CompanyName.Contains("B");
var expr3 = PredicateBuilder.And(expr1, expr2);
var query = context.Customers.Where(expr3);
or
var expr3 = expr1.And(expr2);
var query = context.Customers.Where(expr3);
I do this kind of thing with extension methods. Its syntactically a bit nicer than using expression trees everywhere. I call this composable repositories.
I also wrote a tool (LinqExpander) to combine the expression trees of different extension methods togeather, which is especially useful for doing projection (selects) from your database. This is only nessacary when you are doing things with sub-entities. (see my post here: Composable Repositories - Nesting extensions)
usage would be something along the lines of:
var dtos = context.Table
.ThingsIWant() //filter the set
.ToDtos() //project from database model to something else (your Selector)
.ToArray();//enumerate the set
ToDtos might look something like:
public static IQueryable<DtoType> ToDtos(this IQueryable<DatabaseType> things)
{
return things.Select(x=> new DtoType{ Thing = x.Thing ... });
}
You want to merge two selects togeather (im assuming to avoid an underfetch but this seems a bit wierd). I would do this by using a projection like this:
context.Table
.AsExpandable()
.Select(x=>new {
Dto1 = x.ToDto1(),
Dto2 = x.ToDto2()
})
.ToArray();
if you really wanted it to return a single entity like this you could probably do something like:
context.Table
.AsExpandable()
.Select(x=> ToDto1(x).ToDto2(x));
but I havent ever tried this.
As this uses a sub projection you will need the .AsExpandable extensions.

2nd level caching EF codeFirst and Predicate not working on a where clause

I have a question about caching with Entity Framework code first.
I need to cache my query's results and I came about something that I didn't know.
Func<T, bool> predicate does not work when filtering whilst Expression<Func<T, bool>> predicate does.
Maybe I'm missing the obvious.
Here is my scenario:
Whenever I call a method, e.g."GetOrders", I use a method called "GetCachedModels" internally to get the cached version.
When subsequently many calls are made with
"GetOrders(customerNo)" it checks the cache and the get it from there if it's there. That is the theory.
However when using Func predicate it does not find the item, but when using the Expression version it does?
My question is how do you use "Expressions" with a Where clause on a list?
The other solution would be to have a method for each search, e.g. "myservice.GetCustomer(etc..) or myservice.GetOrders(etc..) instead of generic
myservice.GetAll(); Which means adding many methods to the interface.
mycode:
public interface IGenericRepository
{
IList<T> GetAll<T>() where T : class;
IList<T> Find<T>(Func<T, bool> predicate) where T : class; //I use this so that I could use the predicate in an where clause against a list.
etc....
}
In my repository I have something like:
public IList<T> Find<T>(Func<T, bool> predicate) where T : class
{
List<T> models = GetCachedModels<T>().ToList();
var result= models.Where(predicate).ToList(); --Does not work!!! tried also with(expression.Compile()).ToList(); !!still no results!!!!
return result;
}
internal IList<T> GetCachedModels<T>() where T : class
{
IList<T> models;
Type typeParameterType = typeof(T);
string cacheName = string.Format("{0}Cache", typeParameterType.Name);
object cacheView = DbCache.Get(cacheName);
if (cacheView == null)
{
models = Set<T>().ToList();
DbCache.Add(cacheName, models, DateTime.Now.AddHours(1));
}
else
{
models = (IList<T>)cacheView;
}
return models;
}
//below method works but cannot use cache!!!!
public IList<T> Find<T>(Expression<Func<T, bool>> predicate) where T : class
{
return Set<T>().Where(predicate).ToList();
}
Expression predicates works only for IQueryable interface. List doesn't inherit it, so if you want to use this expression, you need to return IQueryable in GetCachedModels method, and return Set, so it can query this data. And then you can place it.
Otherwise if you want to cache all items from Set, then you need to pass Func instead of Expression, and then use it in Where extension method, like this - http://dotnetfiddle.net/5YsIy3

Entity Framework non generic Include on dynamic ObjectSet

I've ran into a problem while creating an data access layer for my application. I am working with derived entities in Entity Framework. If I try to create an ObjectSet for a derived Entity, I get this Exception:
Blockquote There are no EntitySets defined for the specified entity type 'Type Name'. If 'Type Name' is a derived type, use the base type instead.
I've tried to resolve this problem via Reflection. (if the Entity Type is derived by an Entity Type => get an ObjectSet for the base Type)
I found this: How can I obtain ObjectSet<T> from Entity-Framework at runtime where T is dynamic? but I haven't found how to use Include and Where on the built up ObjectSet.
protected IEnumerable<IDataObject> GetData(Type entityType, Expression<Func<dynamic, bool>> whereClause, Expression<Func<dynamic, dynamic>>[] includes)
{
if (typeof(IDataObject).IsAssignableFrom(entityType.BaseType))
{
return GetData(entityType.BaseType, whereClause, includes);
}
var contextType = this.Context.GetType();
MethodInfo createObjectSetMethod = contextType.GetMethod("CreateObjectSet", new Type[] {}).MakeGenericMethod(entityType);
// Builds up an ObjectSet<EntityType>
dynamic objectSet = createObjectSetMethod.Invoke(this.Context, new object[] { });
dynamic query = objectSet;
if (includes != null)
{
foreach (var include in includes)
{
query = query.Include(include);
}
}
if (whereClause == null)
{
whereClause = (item) => true;
}
query = query.Where(whereClause);
return query.ToList().OfType<IDataObject>();
}
The code runs as intended, as long I don't use the Includes and WhereClause.
When I call this function I dont know the resolved ObjectSet (T-Parameter) at compile time.
Is there any way to use dynamic Expressions in an generic ObjectSet?
Thanks in advance.
The issue looks to be with the
query = query.Include(include);
the var include would be an Expression<Func<dynamic,dynamic>>
The Include Method you access is expecting a string or
Expression<Func<T, TProperty>>
Have you tried to pass the include as a string path? I would expect that to work.
Otherwise you need to construct the expression tree with code
since you cant pass Dynamic objects to
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class;
There is also dynamic linq library to can check out.
System.Linq.Dynamic can be found at following links
http://msdn.microsoft.com/en-US/vstudio/bb894665.aspx
with an intro here http://www.scottgu.com/blogposts/dynquery/dynamiclinqcsharp.zip
This is an easier alternative using text to build expression trees.
Building expressions trees with code is more powerful and flexible but harder.

Usage of "Expression<Func<Object, bool>>"

please take a look at the following snippet:
public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
{
return filter != null ? this.ObjectSet.Where(filter) : null;
}
As for example:
public IQueryable<Department> Query(Expression<Func<Department, bool>> filter)
{
return _context.Departments.Where(filter);
}
(original source: http://blogs.microsoft.co.il/blogs/gilf/archive/2010/06/21/revisiting-the-repository-and-unit-of-work-patterns-with-entity-framework.aspx)
Can anyone provide some concrete examples of how to go about actually calling the second method with an actual query?
Thanks!
To say get all departments with Sales over $100
var deparments = deparmentsRepository.Query(d => d.TotalSales > 100.0m);
Func and Expression> can both be declared the same way using lambda syntax.
In fact there's an example in the article (in the last code snippet) you linked to already for this.
foreach(var department in uow.Departments.Query(d => d.Budget > 150000)) { ... }
Given that Query returns IQueryable there's no reason you can't build into more complex queries.
var deparments = deparmentsRepository.Query(d => d.TotalSales > 100.0m)
.Select(d => new { ... });
Assuming Manager and Department have a foreign key you don't need that join.
var deparments = deparmentsRepository.Query(d => d.Manager.Name.StartsWith("A"));

Entity Framework using Generic Predicates

I use DTO's to map between my Business and Entity Framework layer via the Repository Pattern.
A Standard call would look like
public IClassDTO Fetch(Guid id)
{
var query = from s in _db.Base.OfType<Class>()
where s.ID == id
select s;
return query.First();
}
Now I wish to pass in filtering criteria from the business layer so I tried
public IEnumerable<IClassDTO> FetchAll(ISpecification<IClassDTO> whereclause)
{
var query = _db.Base.OfType<Class>()
.AsExpandable()
.Where(whereclause.EvalPredicate);
return query.ToList().Cast<IClassDTO>();
}
The Call from the business layer would be something like
Specification<IClassDTO> school =
new Specification<IClassDTO>(s => s.School.ID == _schoolGuid);
IEnumerable<IClassDTO> testclasses = _db.FetchAll(school);
The problem I am having is that the .Where clause on the EF query cannot be inferred from the usage. If I use concrete types in the Expression then it works find but I do not want to expose my business layer to EF directly.
Try making FetchAll into a generic on a class instead, like this:-
public IEnumerable<T> FetchAll<T> (Expression<Func<T,bool>> wherePredicate)
where T:IClassDTO //not actually needed
{
var query = _db.Base.OfType<T>()
.AsExpandable()
.Where(wherePredicate);
return query;
}
pass in school.Evalpredicate instead. FetchAll doesn't appear to need to know about the whole specification, it just needs the predicate, right? If you need to cast it to IClassDTO, do that after you have the results in a List.