Linq Entity Framework -- can I use a recursive method?
Following on from the excellent answer to my previous question:
Linq Entity Framework generic filter method
I am now trying to understand how I can apply something like recursion to my solution.
To recap, instead of multiple similar declarations of this method:
protected IQueryable<Database.Product> GetActiveProducts( ObjectSet<Database.Product> products ) {
var allowedStates = new string[] { "Active" , "Pending" };
return (
from product in products
where allowedStates.Contains( product.State )
&& product.Hidden == "No"
select product
);
}
I now have a single implementation that accepts an interface type (IHideable) to operate on:
protected IQueryable<TEntity> GetActiveEntities<TEntity>( ObjectSet<TEntity> entities ) where TEntity : class , Database.IHideable {
var allowedStates = new string[] { "Active" , "Pending" };
return (
from entity in entities
where allowedStates.Contains( entity.State )
&& entity.Hidden == "No"
select entity
);
}
This works well and the solution is clean and understandable ( big 开发者_如何学JAVAthanks to https://stackoverflow.com/users/263693/stephen-cleary ).
What I am trying to do now is to apply a similar (or the same?) method to any EntityCollections associated to an EntityObject that also happen to implement IHideable.
I currently make use of GetActiveEntities() like this:
var products = GetActiveEntities( Entities.Products );
return (
from product in products
let latestOrder = product.Orders.FirstOrDefault(
candidateOrder => (
candidateOrder.Date == product.Orders.Max( maxOrder => maxOrder.Date )
)
)
select new Product() {
Id = product.Id ,
Name = product.Name,
LatestOrder = new Order() {
Id = latestOrder.Id ,
Amount = latestOrder.Amount,
Date = latestOrder.Date
}
}
);
In this example I would like to have the Orders EntityCollection also filter by GetActiveEntities(), so that the latest order returned can never be a "hidden" one.
Is it possible to have all EntityCollections implementing IHideable to be filtered - maybe by applying some reflection/recursion inside GetActiveEntities() and calling itself? I say recursion because the best solution would go multiple levels deep walking through the entity graph.
This stuff is stretching my brain!
UPDATE #1 (moved my comments up to here)
Thanks Steve.
Making the method accept IQuerable as suggested gives this error:
'System.Data.Objects.DataClasses.EntityCollection<Database.Order>' does not contain a definition for 'GetActiveEntities' and no extension method 'GetActiveEntities' accepting a first argument of type 'System.Data.Objects.DataClasses.EntityCollection<Database.Order>' could be found (are you missing a using directive or an assembly reference?)
I presume this is because EntityCollection does not implement IQueryable.
I was able to get further by creating a second extension method that explicitly accepted EntityCollection and returned IEnumerable. That compiled but at runtime gave this error:
LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1[Database.Order] GetActiveEntities[Order](System.Data.Objects.DataClasses.EntityCollection`1[Database.Order])' method, and this method cannot be translated into a store expression.
I also tried calling AsQueryable() on the EntityCollection and returning IQueryable but the same error came back.
Edited to use ObjectSet instead of EntityCollection
I recommend using my solution, but as an extension method, which can be applied to any query:
public static IQueryable<TEntity> WhereActive<TEntity>(
this IQueryable<TEntity> entities)
where TEntity : class , Database.IHideable
{
var allowedStates = new string[] { "Active", "Pending" };
return (
from entity in entities
where allowedStates.Contains(entity.State)
&& entity.Hidden == "No"
select entity
);
}
This can then be used as such:
var products = Entities.Products.WhereActive();
return (
from product in products
let latestOrder = (
from order in Entities.Orders.WhereActive()
where order.ProductId == product.Id
orderby candidateOrder.Date descending
select order).FirstOrDefault()
select new Product()
{
Id = product.Id,
Name = product.Name,
LatestOrder = new Order()
{
Id = latestOrder.Id,
Amount = latestOrder.Amount,
Date = latestOrder.Date
}
}
);
精彩评论