LINQ, can I write cleaner joins with method syntax? - linq

Using pubs
If I want to join using query syntax I would do this.
from a in db.authors
join ta in db.titleauthors on a.au_id equals ta.au_id
join t in db.titles on ta.title_id equals t.title_id
join s in db.sales on t.title_id = s.title_id
select new { a.au_lname, t.title1, s.qty }
Using method syntax
db.authors
.Join(db.titleauthors,
a => a.au_id,
ta => ta.au_id,
(a, ta) => new {a, ta})
.Join(db.titles,
z => z.ta.title_id,
t => t.title_id,
(z, t) => new { z.a, z.ta, t })
.Join(db.sales,
z => z.t.title_id,
s => s.title_id,
(z, s) => new { z.a, z.ta, z.t, s })
.Select(z => new { z.a.au_lname, z.t.title1, z.s.qty })
I was wondering if there was an elegant way of dealing with this line
(z, X) => new { z.Y1, z.Y2, z.Y3... , X }
maybe something like
(z, X) => z.push(X)
so I don't have to write everything out.
Does something like that exist or is possible?

No. Basically query expressions exist in order to keep this stuff away from you. There's no particularly simple way of mimicking transparent identifiers.
I find that when you hit transparent identifiers, it's almost always cleaner to use query expression syntax. It's definitely worth knowing both, as very simple queries are cleaner using method syntax, but the more complicated the query, the more likely it is to be easier to read using query expressions.
That's assuming it can all be represented with method expressions, of course. Don't forget you can break up queries into separate statements without changing the meaning, so if you do need to call methods which don't have query expression equivalents, I sometimes find it best to separate it out like this:
var foo = from x in y
join a in b on x.Z equals a.Z
select new { a, x };
var bar = foo.Skip(5)
.Take(10)
.ToList();
I find that cleaner than just using brackets to mush the two syntax forms together.

Related

Why is the "Select" of a Method Syntax is in another parenthesis?

var sample = db.Database.OrderByDescending(x => x.RecordId).Select(y => y.RecordId).FirstOrDefault();
I don't know if my title is correct / right. Just want to ask why this query the select is in another ( )?. As for the example .Select(y => y.RecordId) unlike the query I use to be
var sample = (from s in db.Databse where s.RecordId == id select s) I know this is the same right?. Then what is the why it is in another parenthesis?. Anyone has an idea or can anyone explain it why?. Thanks a lot.
In your first example, you're using "regular" C# syntax to call a bunch of extension methods:
var sample = db.Database
.OrderByDescending(x => x.RecordId)
.Select(y => y.RecordId)
.FirstOrDefault();
(They happen to be extension methods here, but of course they don't have to be...)
You use lambda expressions to express how you want the ordering and projection to be performed, and the compiler converts those into expression trees (assuming this is EF or similar; it would be delegates for LINQ to Objects).
The second example is a query expression, although it doesn't actually match your first example. A query expression corresponding to your original query would be:
var sample = (from x in db.Database
orderby x.RecordId descending
select x.RecordId)
.FirstOrDefault();
Query expressions are very much syntactic sugar. The compiler effectively converts them into the first form, then compiles that. The range variable declared in the from clause (x in this case) is used as the parameter name for the lambda expression, so select x.RecordId becomes .Select(x => x.RecordId).
Things become a bit more complicated with joins and multiple from clauses, as then the compiler introduces transparent identifiers to allow you to work with all the range variables that are in scope, even though you've really only got a single parameter. For example, if you had:
var query = from person in people
from job in person.Jobs
order by person.Name
select new { Person = person, Job = job };
that would be translated into the equivalent of
var query = people.SelectMany(person => person.Jobs, (person, job) => new { person, job } )
.OrderBy(t => t.person.Name)
.Select(t => new { Person = t.person, Job = t.job });
Note how the compiler introduces an anonymous type to combine the person and job range variables into a single object, which is used later on.
Basically, query expression syntax makes LINQ easier to work with - but it's just a translation into other C# code, and is neatly wrapped up in a single section of the C# specification. (Section 7.16.2 of the C# 5 spec.)
See my Edulinq blog post on query expressions for more detail on the precise translation from query expressions to "regular" C#.

Can you convert this Linq statement into Lambda without using join statements?

I see this all over the web, however I'm curious if there isn't an easier way to write this in lambda?
var x = from c in db.Client
from p in db.Prospects
from ct in db.Countys
select new ViewModelExcelReport
{
client = c.ClientName,
cntyCounty = ct.County,
sctSection = p.Section
};
I would like to see a lambda expression that does NOT use joins, as though i am almost certain that i have seen one without the joins, but if this isn't possible ofcouse i'd like to see one with, thanks.
Basically multiple from clauses contribute SelectMany calls. So your code is something like:
var x = db.Client
.SelectMany(c => db.Prospects, (c, p) => new { c, p })
.SelectMany(z => db.Countys, (z, ct) => new { z, ct })
.Select(zz => new ViewModelExcelReport
{
client = zz.z.c.ClientName,
cntyCounty = zz.ct.County,
sctSection = zz.z.p.Section
});
Note how this is rather more longwinded than the query expression - the compiler takes care of keeping track of all the range variables via transparent identifiers. Why do you want this in lambda form? What do you see as being the benefit? The query expression will be translated into exactly the same code, so you should use whichever one is clearer - which in this case looks like the query expression, IMO.
As an aside, I'd strongly recommend that you change your property names (in ViewModelExcelReport) to more idiomatic ones if you possibly can.

Is it possible to write join with a boolean evaluation?

I want to write
CompareInfo myCompIntl = CompareInfo.GetCompareInfo( "es-ES" );
var SharedYomi = from ObjA in ClassListA
join ObjB in ClassListB
where CompareInfo.Compare(ObjA.Name, ObjB.Name) == 0
select new {stringA = stringA, string = string};
Linq forces me to write join with equals. I can not pass in a Boolean evaluation.
How can I do that?
You cannot write that using the LINQ lambda query syntax. The join keyword requires you to specify exactly two properties that are compared using the equals keyword, because this maps to the first overload of Join that uses the default comparer to compare keys.
However, there is an overload of Join that accepts an IEqualityComparer that will probably work for you, you just need to use method query syntax.
Since you sound like you're not familiar with the method syntax, here's a good starting article from MSDN:
http://msdn.microsoft.com/en-us/library/vstudio/bb397947.aspx
But, basically, the syntax you think of as "LINQ" is just one way to refer to the LINQ extensions, and is really just syntactic sugar around the IEnumerable extension methods that implement LINQ. So, for example, the query:
from x in y where x.IsActive orderby x.Name select x
it basically identical to
y.Where(x => x.IsActive).OrderBy(x => x.Name).Select(x => x);
For the most part, each query clause maps to a particular overload of a particular IEnumerable method, but those methods have a number of other overloads that take different numbers and types of parameters.
The Join methods are a bit complex, because they take two sequences as input and let you combine individual elements of them using expressions, but the idea is exactly the same. A typical join would look like this:
from x in y
join a in b on x.Id equals a.ParentId
select new { x.Id, x.Name, a.Date }
becomes
y.Join(
b,
x => x.Id,
a => a.ParentId,
(x, a) => new { x.Id, x.Name, a.Date });
This will join a.ParentId and x.Id using the default comparison for their data type (int, string, whatever). The compiler directly translates the query syntax into method syntax, so the two behave exactly the same. (Nitpicking my own answer: Technically, the methods are on the Enumerable class, so you are really calling Enumerable.Join. But as they were implemented as extension methods, you can call them either way and the compiler will figure it out.)
In your case, what you need is to pass in a different comparison method, so you can call string.Compare with the explicit encoding. The other overload of Join lets you supply an implementation of IEqualityComparer<T> to use instead of the default. This will require you to implement IEqualityComparer<string> in a separate class, since there's no easy way to create an anonymous interface implementation (perhaps the only feature I miss from Java). For your example, you want something like this:
public class ComparerWithEncoding : IEqualityComparer<string>
{
private CompareInfo compareInfo
public ComparerWithEncoding ( string encoding )
{
this.compareInfo = CompareInfo.GetCompareInfo(encoding);
}
public bool Equals ( string a, string b )
{
return CompareInfo.Compare(a, b) == 0
}
public int GetHashCode(string a)
{
return a.GetHashCode();
}
}
classListA.Join(
ClassListB,
ObjA => ObjA.Name,
ObjB => ObjB.Name,
(ObjA, ObjB) => new { stringA = ObjA.Foo, stringB = ObjB.Bar },
new ComparerWithEncoding("es-ES"));

How can I Check for item existence in 2 observableCollection linq

Just a bit rusty on the old linq.
If I have 2 collections EG NewCustomerList and OldCustomerList and see if a surname exists already how would I do it in linq .I am sure there are many ways. SelectMany rings a bell but forgot how to do it!
In a forEach I would do something like that. What is the equivalent in linq?
foreach (var oldCustomer in OldCustomerList)
{
foreach (var newCustomer in NewCustomerList.Where(x => x.Surname == oldCustomer.Surname))
{
break;
}
}
Any Suggestions? Thanks a lot
So you're trying to see whether any of the old customer surnames are in the new customer list?
One simple option: do a join and see if it's empty:
if (OldCustomerList.Join(NewCustomerList, x => x.Surname, x => x.Surname,
(x, y) => null).Any())
{
...
}
(I've used a null projection because we really don't care about the join result.)
Another option:
var oldSurnames = new HashSet<string>(OldCustomrList.Select(x => x.Surname));
if (NewSurnameList.Any(x => oldSurnames.Contains(x.Surname))
{
...
}
I suspect you may find that you actually want the result in terms of which surnames are in common though... if you can give us more context, we may be able to help you more.
You can do this by:
NewCustomerList.Where(n => OldCustomerList.Any(o => o.Surname == n.Surname))
Here's another approach, which also has O(n + m) complexity + quick-pass semantics:
OldCustomerList.Select(cust => cust.Surname)
.Intersect(NewCustomerList.Select(cust => cust.Surname))
.Any();
IMO, it is more readable than an explicit Enumerable.Join: "test if the projection of surnames from the old customer-list has any elements in common with the projection of surnames from the new customer-list", which is pretty close to the problem-statement.

conditional include in linq to entities?

I felt like the following should be possible I'm just not sure what approach to take.
What I'd like to do is use the include method to shape my results, ie define how far along the object graph to traverse. but... I'd like that traversal to be conditional.
something like...
dealerships
.include( d => d.parts.where(p => p.price < 100.00))
.include( d => d.parts.suppliers.where(s => s.country == "brazil"));
I understand that this is not valid linq, in fact, that it is horribly wrong, but essentially I'm looking for some way to build an expression tree that will return shaped results, equivalent to...
select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
and s.country = 'brazil'
with an emphasis on the join conditions.
I feel like this would be fairly straight forward with esql but my preference would be to build expression trees on the fly.
as always, grateful for any advice or guidance
This should do the trick:
using (TestEntities db = new TestEntities())
{
var query = from d in db.Dealership
select new
{
Dealer = d,
Parts = d.Part.Where
(
p => p.Price < 100.0
&& p.Supplier.Country == "Brazil"
),
Suppliers = d.Part.Select(p => p.Supplier)
};
var dealers = query.ToArray().Select(o => o.Dealer);
foreach (var dealer in dealers)
{
Console.WriteLine(dealer.Name);
foreach (var part in dealer.Part)
{
Console.WriteLine(" " + part.PartId + ", " + part.Price);
Console.WriteLine
(
" "
+ part.Supplier.Name
+ ", "
+ part.Supplier.Country
);
}
}
}
This code will give you a list of Dealerships each containing a filtered list of parts. Each part references a Supplier. The interesting part is that you have to create the anonymous types in the select in the way shown. Otherwise the Part property of the Dealership objects will be empty.
Also, you have to execute the SQL statement before selecting the dealers from the query. Otherwise the Part property of the dealers will again be empty. That is why I put the ToArray() call in the following line:
var dealers = query.ToArray().Select(o => o.Dealer);
But I agree with Darren that this may not be what the users of your library are expecting.
Are you sure this is what you want? The only reason I ask is, once you add the filter on Parts off of Dealerships, your results are no longer Dealerships. You're dealing in special objects that are, for the most part, very close to Dealerships (with the same properties), but the meaning of the "Parts" property is different. Instead of being a relationship between Dealerships and Parts, it's a filtered relationship.
Or to put it another way, if I pull a dealership out of your results and passed to a method I wrote, and then in my method I call:
var count = dealership.Parts.Count();
I'm expecting to get the parts, not the filtered parts from Brazil where the price is less than $100.
If you don't use the dealership object to pass the filtered data, it becomes very easy. It becomes as simple as:
var query = from d in dealerships
select new { DealershipName = d.Name,
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };
If I just had to get the filtered sets like you asked, I'd probably use the technique I mentioned above, and then use a tool like Automapper to copy the filtered results from my anonymous class to the real class. It's not incredibly elegant, but it should work.
I hope that helps! It was an interesting problem.
I know this can work with one single Include. Never test with two includes, but worth the try:
dealerships
.Include( d => d.parts)
.Include( d => d.parts.suppliers)
.Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))
Am I missing something, or aren't you just looking for the Any keyword?
var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) ||
d.parts.suppliers.Any(s => s.country == "brazil"));
Yes that's what I wanted to do I think the next realease of Data Services will have the possiblity to do just that LINQ to REST queries that would be great in the mean time I just switched to load the inverse and Include the related entity that will be loaded multiple times but in theory it just have to load once in the first Include like in this code
return this.Context.SearchHistories.Include("Handle")
.Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);
before I tried to load for any Handle the searchHistories that matched the logic but don't know how using the Include logic you posted so in the mean time I think a reverse lookup would be a not so dirty solution

Resources