Reputation: 40162
I'd like to pass IQueryable
as a parameter to a function that would then call the Entity Framework provider to execute a query as follows:
public ICollection<MyRowType> RunCommand(IQueryable<MyRowType> query)
{
using (var db = new MyDbContext())
{
var result = db.MyRowType
.Include(r => r.RelatedTableA)
.Include(r => r.RelatedTableB)
.Include(r => r.RelatedTableC)
// How do I make the value in `query` run here?
.ToList();
}
}
I'm basically wrapping my database calls to this method to return all the necessary includes but I don't want to lose the flexibility of making custom queries via IQueryable<MyRowType>
.
Here's an example of what I'd like to achieve:
public ICollection<MyRowType> SomeOtherMethod(string id, SomeValue value = null, SomeOtherValue otherValue = null)
{
var query = Enumerable.Empty<MyRowType>()
.Where(r => r.Id == di);
if (value != null)
query = query.Where(r => r.PropertyOne == value);
if (otherValue != null)
query = query.Where(r => r.PropertyTwo == otherValue);
return RunCommand(query);
}
Answer update:
I combined the answers of Yacoub Massad and dasblinkenlight into these two methods:
public static ICollection<MyRowType> RunCommand(Expression<Func<MyRowType, bool>> condition)
{
return Get(t => t.Where(condition));
}
public static ICollection<MyRowType> RunCommand(Func<IQueryable<MyRowType>, IQueryable<MyRowType>> query)
{
using (var db = new DbContext())
{
var includes = db.MyRowType
.Include(r => r.RelatedTableA)
.Include(r => r.RelatedTableB)
.Include(r => r.RelatedTableC)
var result = query(includes)
.ToList();
return result;
}
}
Upvotes: 0
Views: 197
Reputation: 726489
It is not easy to extract the expression from IQueryable<T>
, and you wouldn't be able to apply the queryable
that you pass into the method directly. However, since it appears that you are trying to limit the query by passing IQueryable<T>
without changing the output type, it may be easier to change the API to take a condition instead of IQueryable<T>
, like this:
public ICollection<MyRowType> RunCommand(Expression<Func<MyRowType,bool>> condition) {
using (var db = new MyDbContext()) {
var result = db.MyRowType
.Include(r => r.RelatedTableA)
.Include(r => r.RelatedTableB)
.Include(r => r.RelatedTableC)
.Where(condition)
.ToList();
}
}
If you use LINQ to Entities predicate combiner you would be able to combine your custom conditions with the common preparation steps performed inside your RunCommand
method:
public ICollection<MyRowType> SomeOtherMethod(string id, SomeValue value = null, SomeOtherValue otherValue = null) {
Expression<Func<MyRowType,bool>> condition =
r => r => r.Id == di;
if (value != null)
condition = condition.And(r => r.PropertyOne == value);
if (otherValue != null)
condition = condition.And(r => r.PropertyTwo == otherValue);
return RunCommand(query);
}
Upvotes: 1
Reputation: 27861
You should pass Func<IQueryable<MyRowType>,IQueryable<MyRowType>>
instead of IQueryable<MyRowType>
like this:
public ICollection<MyRowType> RunCommand(Func<IQueryable<MyRowType>,IQueryable<MyRowType>> func)
{
using (var db = new MyDbContext())
{
IQueryable<MyRowType> q = db.MyRowType
.Include(r => r.RelatedTableA)
.Include(r => r.RelatedTableB)
.Include(r => r.RelatedTableC);
var result = func(q);
return result.List();
}
}
And then you can use it like this:
var result = RunCommand(q => q.Where(....));
Or use other LINQ methods like this:
var result = RunCommand(q => q.Where(....).OrderBy(x => ...).Take(10));
Please note that you cannot pass a query that will select a different type than MyRowType
. If you want that then you can make the method generic like this:
public ICollection<T> RunCommand<T>(Func<IQueryable<MyRowType>,IQueryable<T>> func)
{
using (var db = new MyDbContext())
{
IQueryable<MyRowType> q = db.MyRowType
.Include(r => r.RelatedTableA)
.Include(r => r.RelatedTableB)
.Include(r => r.RelatedTableC);
var result = func(q);
return result.List();
}
}
Now you can use it like this:
var result = RunCommand<SomeOtherType>(q => q.Where(x => ....).Select(x => ....));
Upvotes: 1