How to use Func with IQueryable that returns IOrderedQueryable - linq

I'm doing some research about EF and came across a function that accepts
Func<IQueryable<Student>, IOrderedQueryable<Student>>
and just wondering how to call that function that accepts that kind of parameter?

imagine function is something like that, and you've got a property Id in Student class.
public static class Helper {
public static void Test(Func<IQueryable<Student>, IOrderedQueryable<Student>> param)
{
var test = 0;
}
}
then you could use it this way
var student = new List<Student>().AsQueryable();//non sense, just for example
Helper.Test(m => student.OrderBy(x => x.Id));
m => student.OrderBy(x => x.Id) is a
Func<IQueryable<Student>, IOrderedQueryable<Student>>
(IQueryable<student> as parameter, returning a IOrderedQueryable<Student>)
or just
Helper.Test(m => m.OrderBy(x => x.Id));
In fact this doesn't make much sense without a "real" function...

define a method.
public IOrderedQueryable<Student> OrderingMethod(IQueryable<Student> query)
{
return query.OrderBy(student => student.Name);
}
Now this assignment is legal:
Func<IQueryable<Student>, IOrderedQueryable<Student>> orderingFunc = this.OrderingMethod;
And now that you have it in a variable, it's easy to pass it to the method.
You could also do it all inline:
Func<IQueryable<Student>, IOrderedQueryable<Student>> orderingFunc =
query => query.OrderBy(student => student.Name);

Related

Distinct/GroupBy in WhenAll result Async

I am writing a method in which i am using async prog.
var tasks = new List<Task<List<SomeClass>>>();
tasks.Add(this.Method1());
tasks.Add(this.Method2());
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
i want distinct records from this result. How to achieve that.
currently i have written
return results.SelectMany(s => s).GroupBy(x => x.Name).Select(x => x.FirstOrDefault()).ToList();
But i am not sure with SelectMany, will this give correct result.
SelectMany(s => s) is a "flatten" operation. It takes a sequence of sequences and flattens them to a single sequence.
The LINQ "distinct" operator is called Distinct. If your SomeClass overrides equality to be based on Name, then that's all you need:
return results.SelectMany(s => s).Distinct().ToList();
But if SomeClass doesn't define equality that way, you'll need to do another kind of distinct.
One option is to use the Distinct overload that takes an equality comparer. Then you can pass in an equality comparer that determines equality by Name. To do this, first define an equality comparer:
public sealed class NameEqualityComparer: IEqualityComparer<SomeClass>
{
public int GetHashCode(SomeClass obj) => EqualityComparer<string>.Default.GetHashCode(obj.Name);
public bool Equals(SomeClass x, SomeClass y) => EqualityComparer<string>.Default.Equals(x.Name, y.Name);
}
and then you can invoke the correct overload:
return results.SelectMany(s => s).Distinct(new NameEqualityComparer()).ToList();
I have a library that helps define custom comparers (properly handling all edge cases), which I prefer to use for things like this. With the Nito.Comparers library, you don't need to define a custom NameEqualityComparer; instead, you can define comparers in-line like this:
return results.SelectMany(s => s).Distinct(b => b.EquateBy(x => x.Name)).ToList();
or separately, if desired:
var comparer = EqualityComparerBuilder.For<SomeClass>().EquateBy(x => x.Name);
return results.SelectMany(s => s).Distinct(comparer).ToList();
A completely different option is to add a new "Distinct-By" operator that acts the way you want. This is part of MoreLINQ or you can add it yourself:
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> #this, Func<T, TKey> selector)
{
var keys = new HashSet<TKey>();
foreach (var item in #this)
{
if (keys.Add(selector(item)))
yield return item;
}
}
Then you can use the new operator like this:
return results.SelectMany(s => s).DistinctBy(x => x.Name).ToList();
All of these options are more efficient than grouping.

Linq Parsing Error when trying to create seperation of concerns

I am in the middle of a refactoring cycle where I converted some extension methods that used to look like this:
public static IQueryable<Family> FilterOnRoute(this IQueryable<Family> families, WicRoute route)
{
return families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
}
to a more fluent implementation like this:
public class SimplifiedFamilyLinqBuilder
{
private IQueryable<Family> _families;
public SimplifiedFamilyLinqBuilder Load(IQueryable<Family> families)
{
_families = families;
return this;
}
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
return this;
}
public IQueryable<Family> AsQueryable()
{
return _families;
}
}
which I can call like this: (note this is using Linq-to-Nhibernate)
var families =
new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
this produces the following SQL which is fine with me at the moment: (of note is that the above Linq is being translated to a SQL Query)
select ... from "Family" family0_
where exists (select pods1_.PODID from "POD" pods1_
inner join Route wicroute2_ on pods1_.RouteID=wicroute2_.RouteID
where family0_.FamilyID=pods1_.FamilyID
and wicroute2_.RouteID=#p0);
#p0 = 1
my next effort in refactoring is to move the query part that deals with the child to another class like this:
public class SimplifiedPODLinqBuilder
{
private IQueryable<POD> _pods;
public SimplifiedPODLinqBuilder Load(IQueryable<POD> pods)
{
_pods = pods;
return this;
}
public SimplifiedPODLinqBuilder OnRoute(WicRoute route)
{
_pods = _pods.Where(pod => pod.Route.RouteID == route.RouteID);
return this;
}
public IQueryable<POD> AsQueryable()
{
return _pods;
}
}
with SimplifiedFamilyLinqBuilder changing to this:
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam =>
_podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any()
);
return this;
}
only I now get this error:
Remotion.Linq.Parsing.ParserException : Cannot parse expression 'value(Wic.DataTests.LinqBuilders.SimplifiedPODLinqBuilder)' as it has an unsupported type. Only query sources (that is, expressions that implement IEnumerable) and query operators can be parsed.
I started to implement IQueryable on SimplifiedPODLinqBuilder(as that seemed more logical than implementing IEnumberable) and thought I would be clever by doing this:
public class SimplifiedPODLinqBuilder : IQueryable
{
private IQueryable<POD> _pods;
...
public IEnumerator GetEnumerator()
{
return _pods.GetEnumerator();
}
public Expression Expression
{
get { return _pods.Expression; }
}
public Type ElementType
{
get { return _pods.ElementType; }
}
public IQueryProvider Provider
{
get { return _pods.Provider; }
}
}
only to get this exception (apparently Load is not being called and _pods is null):
System.NullReferenceException : Object reference not set to an instance of an object.
is there a way for me to refactor this code out that will parse properly into an expression that will go to SQL?
The part fam => _podLinqBuilder.Load(fam.PODs.AsQueryable() is never going to work, because the linq provider will try to parse this into SQL and for that it needs mapped members of Family after the =>, or maybe a mapped user-defined function but I don't know if Linq-to-Nhibernate supports that (I never really worked with it, because I still doubt if it is production-ready).
So, what can you do?
To be honest, I like the extension methods much better. You switched to a stateful approach, which doesn't mix well with the stateless paradigm of linq. So you may consider to retrace your steps.
Another option: the expression in .Any(pod => pod.Route.RouteID == route.RouteID)); could be paremeterized (.Any(podExpression), with
OnRoute(WicRoute route, Expression<Func<POD,bool>> podExpression)
(pseudocode).
Hope this makes any sense.
You need to separate methods you intend to call from expressions you intend to translate.
This is great, you want each of those methods to run. They return an instance that implements IQueryable<Family> and operate on that instance.
var families = new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
This is no good. you don't want Queryable.Where to get called, you want it to be an expression tree which can be translated to SQL. But PodLinqBuilder.Load is a node in that expression tree which can't be translated to SQL!
families = _families
.Where(fam => _podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any();
You can't call .Load inside the Where expression (it won't translate to sql).
You can't call .Load outside the Where expression (you don't have the fam parameter).
In the name of "separation of concerns", you are mixing query construction methods with query definition expressions. LINQ, by its Integrated nature, encourages you to attempt this thing which will not work.
Consider making expression construction methods instead of query construction methods.
public static Expression<Func<Pod, bool>> GetOnRouteExpr(WicRoute route)
{
int routeId = route.RouteID;
Expression<Func<Pod, bool>> result = pod => pod.Route.RouteID == route.RouteID;
return result;
}
called by:
Expression<Func<Pod, bool>> onRoute = GetOnRouteExpr(route);
families = _families.Where(fam => fam.PODs.Any(onRoute));
With this approach, the question is now - how do I fluidly hang my ornaments from the expression tree?

How to create a collection of Expression<Func<T, TRelated>>?

I have a repository with the following method:
IEnumerable<T> FindAll<TRelated>(Specification<T> specification,
Expression<Func<T, TRelated>> fetchExpression);
I need to pass in more than one expression. I was thinking about changing the signature to:
IEnumerable<T> FindAll<TRelated>(Specification<T> specification,
IEnumerable<Expression<Func<T, TRelated>>> fetchExpression);
Is this possible?
How do I create an array, say, of expressions to pass into this method?
Currently I'm calling the method from my service layer like this:
var products = productRepository.FindAll(specification,p => p.Variants);
But I'd like to pass p => p.Variants and p => p.Reviews for example. And then in the repository I'd like to iterate through the expression and add them to a query.
For a bit of background on why I am doing this see Ben Foster's blog post on Eager loading with NHibernate.
You could use params to do it:
IEnumerable<T> FindAll(Specification<T> specification,
params Expression<Func<T, object>>[] fetchExpressions)
{
var query = GetQuery(specification);
foreach(var fetchExpression in fetchExpressions)
{
query.Fetch(fetchExpression);
}
return query.ToList();
}
You can call this like so:
var products = productRepository.FindAll(specification,
p => p.Variants, p => p.Reviews );
You can change your call to this:
var products = productRepository.FindAll(specification,
new [] { p => p.Variants,
p => p.Reviews });
But this will only work if the T is the same in both!

How can I create an Expression within another Expression?

Forgive me if this has been asked already. I've only just started using LINQ. I have the following Expression:
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = // This is a Person entity, no idea how to create it
});
}
I want to be able to populate LastSalesContact, which is a Person entity.
The details that I wish to populate come from m.LatestPerson, so how can I map over the fields from m.LatestPerson to LastSalesContact. I want the mapping to be re-useable, i.e. I do not want to do this:
LastSalesContact = new Person()
{
// Etc
}
Can I use a static Expression, such as this:
public static Expression<Func<TblUser, User>> SelectToUser()
{
return x => (new User()
{
// Populate
});
}
UPDATE:
This is what I need to do:
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = new Person()
{
PersonId = m.LatestPerson.PersonId,
PersonName = m.LatestPerson.PersonName,
Company = new Company()
{
CompanyId = m.LatestPerson.Company.CompanyId,
etc
}
}
});
But I will be re-using the Person() creation in about 10-15 different classes, so I don't want exactly the same code duplicated X amount of times. I'd probably also want to do the same for Company.
Can't you just use automapper for that?
public static Expression<Func<TblCustomer, CustomerSummary>> SelectToSummary()
{
return m => Mapper.Map<TblCustomer, CustommerSummary>(m);
}
You'd have to do some bootstrapping, but then it's very reusable.
UPDATE:
I may not be getting something, but what it the purpose of this function? If you just want to map one or collection of Tbl object to other objects, why have the expression?
You could just have something like this:
var customers = _customerRepository.GetAll(); // returns IEnumerable<TblCustomer>
var summaries = Mapper.Map<IEnumerable<TblCustomer>, IEnumerable<CustomerSummary>>(customers);
Or is there something I missed?
I don't think you'll be able to use a lambda expression to do this... you'll need to build up the expression tree by hand using the factory methods in Expression. It's unlikely to be pleasant, to be honest.
My generally preferred way of working out how to build up expression trees is to start with a simple example of what you want to do written as a lambda expression, and then decompile it. That should show you how the expression tree is built - although the C# compiler gets to use the metadata associated with properties more easily than we can (we have to use Type.GetProperty).
This is always assuming I've understood you correctly... it's quite possible that I haven't.
How about this:
public static Person CreatePerson(TblPerson data)
{
// ...
}
public static Expression<Func<TblPerson, Person>> CreatePersonExpression()
{
return d => CreatePerson(d);
}
return m => (new CustomerSummary()
{
ID = m.ID,
CustomerName = m.CustomerName,
LastSalesContact = CreatePerson(m.LatestPerson)
});

Call method on LINQ query results succinctly

I'd like to call MyMethod on each object from a LINQ Query, what is the best way to do this?
Currently I am using:
.Select(x => { x.MyMethod (); return x; }).ToArray();
ToArray() is needed for immediate execution.
Is there a simpler way to write this (without a foreach)
Thanks
You could specify your own reusable extension method that runs an Action<> on each element, and yield returns it.
IEnumerable<T> Do(this IEnumerable<T> vals, Action<T> action) {
foreach(T elem in vals) {
action(elem);
yield return elem;
}
}
Such method is included in the Rx library, under the System.Interactive namespace.
Then you can simply do
myCollection.Do(x => x.MyMethod()).ToArray();
xList already has the method you need: .ForEach(). It calls an Action on each list member.
List<x> fooList = ....Select(x => x).ToList();
fooList.ForEach(x => DoSomething(x));
I created an Apply extension method :
public static IEnumerable<T> Apply<T>(this IEnumerable<T> source, Action<T> action)
{
foreach(var item in source)
{
action(item);
yield return item;
}
}
You can use it as follows :
var results = query.Apply(x => x.MyMethod()).ToArray();
Actually, that's similar to the List<T>.ForEach method, excepts that it returns the items of the source so that you can continue to apply sequence operators on it
A for each is probably going to be the easiest way to do this, you could write an extension method that does the for each, but
You really wouldn't gain anything.
In my opinion, you don't need to call .To___ conversion methods since you are expecting side-effects only. Reactive Extension's Do() method would be a viable option.
By using Do() method, you have two advantages (as far as I'm concerned),
1) Defer execution (You can defer the immediate execution if you want).
2) Do() method has different overloads to let you have more controls over iteration.
For example: Do(onNext, OnError, OnCompeleted) overload
var deferQuery = query.Do(x => a.MyMethod(), ex => Console.WriteLine(ex.Message), () => Console.WriteLine("Completed"));
var immediateQuery = query.Do(x => a.MyMethod(), ex => Console.WriteLine(ex.Message), () => Console.WriteLine("Completed")).Run();

Resources