Methods of writing LINQ C# - c#

I'm perplexed by the below.
I've been using LINQ in my projects using formats such as:
var query =
from obj_i in set1
join obj_j in set2 on
new {
JoinField1 = obj_i.SomeField1,
JoinField2 = obj_i.SomeField2,
JoinField3 = obj_i.SomeField3,
JoinField4 = obj_i.SomeField4
}
equals
new {
JoinField1 = obj_j.SomeOtherField1,
JoinField2 = obj_j.SomeOtherField2,
JoinField3 = obj_j.SomeOtherField3,
JoinField4 = obj_j.SomeOtherField4
}
But I was recently told that the below is also 'another way' of writing LINQ queries.
var query =
from obj_i in set1
join obj_j in set2 on
obj_i.SomeField1 = obj_j.SomeOtherField1 and
obj_i.SomeField2 = obj_j.SomeOtherField2 and
obj_i.SomeField3 = obj_j.SomeOtherField3 and
obj_i.SomeField4 = obj_j.SomeOtherField4
As I understand, using the single = is wrong (especially in the case where by == doesn't apply since you need to use equals, but also using and is wrong, since the correct keyword would be && if you were allowed to use anything but equals in this case.
I can understand the use of && and == in the where clause, which makes it even more outstanding that the above code can be used, since it doesn't even compile.
Am I missing something?
If so, could you point me to where I can learn of this alternate method of writing LINQ?

But I was recently told that the below is also 'another way' of writing LINQ queries.
No, the second syntax you show is incorrect. Just try it, you'll see that it doesn't compile.
The join clause in Linq query comprehension syntax is translated to a call to the Join extension method. For instance, this query:
var query =
from x in a
join y in b
on x.Foo equals y.Bar
select Baz(x, y);
Is translated to:
var query = a.Join(b, x => x.Foo, y => y.Foo, (x, y) => Baz(x, y));
So you can see that the parts on the left and right of equals correspond to different parameters of the Join method: the left part selects the join key for the first source, and the right part selects the key for the second source. These keys are then matched against each other to perform the join. This is the important part: the on clause doesn't specify a free-form join condition, it specifies how to extract the join key on each side. So the left and right key selectors have to be clearly separated; the second syntax you show can't work, because there's no way to extract full key information for either source: the join could only be performed by evaluating the join condition for each (x, y), which is an O(n²) operation instead of a (roughly) O(n) operation.
However, sometimes you need more flexibility than what an equi-join can give; in that case you can use a cross-join and filter the results:
var query =
from x in a
from y in b
where x.Foo == y.Bar
select Baz(x, y);
Or in your case:
var query =
from obj_i in set1
from obj_j in set2
where obj_i.SomeField1 == obj_j.SomeOtherField1
&& obj_i.SomeField2 == obj_j.SomeOtherField2
&& obj_i.SomeField3 == obj_j.SomeOtherField3
&& obj_i.SomeField4 == obj_j.SomeOtherField4;

Related

Sql to LINQ 'where in' query

I'm trying to convert this SQL query to LINQ
select * from fichepfe where valid = 1 and id_fiche in ( select fiche_id from enseignant_fiche where id_ens = '*variable*');
This query can return multiple rows.
This is what I tried but I keep getting this error
Operator '==' cannot be applied to operands of type 'method group' and 'IQueryable'
What I tried:
var fiches = (from fiche in _context.Fichepfes where fiche.Valid == true && fiche.IdFiche ==
(from fens in _context.enseignant_fiche where IdEns == *variable*
select fens.ficheId )
select fiche ).ToList();
Thanks in advance.
This should work:
var fiches =
(from fiche in _context.Fichepfes where
fiche.Valid == true &&
_context.enseignant_fiche.Any(fens => fens.IdEns == *variable* && fens.ficheId == fiche.IdFiche)
).ToList();
However this may cause part of the query to be executed by the client, since LINQ-to-SQL may be unable to convert the .Any() call to a raw query.
A better way would be to use a join:
var fiches =
(from fens in _context.enseignant_fiche where fens.IdEns == ens
join fiche in _context.Fichepfes on fens.ficheId equals fiche.IdFiche
where fiche.Valid
select fiche.IdFiche).ToList();
The join can be done the other way around, too, but it's around 5 to 6 times slower on my computer.
var fiches =
(from fiche in _context.Fichepfes where fiche.Valid
join fens in _context.enseignant_fiche on fiche.IdFiche equals fens.ficheId
where fens.IdEns == ens
select fiche.IdFiche).ToList();
So you have a table of Fiches (FichePfes), and a table of EnseignantFiches (enseignant_fiche).
There seems to be a relation between Fiches and EnseignantFiches: every Fiche has (owns?) exactly one EnseignantFiche, namely the EnseignantFichethat the foreign keyIdFiche` refers to.
Furthermore, every Fiche has a Boolean property Valid; every EnseignantFiche has a (string?) property IdEns.
Requirement: Give me all Valid Fiches, that owns an EnseignantFiche with a value of IdEns that equals "variable"
var validFiches = dbContext.Fiches.Where(fiche => fiche.Valid);
var variableEnseignantFiches = dbContext.EnseignantFiches
.Where(enseignantFiche => enseignantFiche.IdEns == "*variable*";
var requestedFiches = validFiches.Join(
variableEnseignantFiches,
fiche => validFiche.IdFiche, // from every Fiche take the foreign key
enseignantFiche => enseignantFiche.IdFiche, // from every EnseignatFiche take primary key
(fiche, enseignantFiche) => new // when they match, make one new object
{
// Select the fiche properties that yo plan to use
FicheId = fiche.Id,
FicheName = fiche.Name,
...
// Select the EnseignantFiche properties that you plan to use:
EnseignantName = enseignantFiche.Name,
...
});
In words:
From the table of Fiches, keep only the Valid ones.
From the table of EnseignantFiches, keep only those with an IdEns equal to "Variable"
Join these two tables on Primary key equals Foreign key, and Select the properties that you plan to use.
Of course you can do this in one big LINQ statement. Because the query is not executed yet, this won't enhance process speed. It will surely deteriorate readability, testability and reusability.

C# Linq Group by Object

I have an issue of using group by in LINQ to SQL statement.
The cod I have is
var combinedItems = (from article in articles
join author in authors
on article.AuthorId equals author.Id into tempAuthors
from tempAuthor in tempAuthors.DefaultIfEmpty()
select new { article , author = tempAuthor});
var groups1 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article into g
select g.Key).ToList();
var groups2 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article.Id into g
select g.Key).ToList();
I tried to group in two different ways. The first way, I group by an object and the second way I just group by a field in one of the objects.
When I run groups1, I got an error saying need to evaluate in client side, while when I use groups2, it works all good. Can I ask what could be wrong? If I want to group by object, is there any way to do it?
In case you want to group by object, as you've not overridden Equals and GetHashCode() in your Article class or implemented IEqualityComparer<Article> you're just getting the default comparison, which checks if the references are equal. So what you need is something like this:
class GroupItemComparer : IEqualityComparer<Article>
{
public bool Equals(Article x, Article y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Article obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
And then you need to change your query to lambda expression:
var groups1 = combinedItems.GroupBy(c => c.article , new GroupItemComparer())
.Select(c => c.Key).ToList();
In case you got any exception regarding translation your method to SQL, you can use AsEnumerable or ToList methods before your GroupBy method, with this methods after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.
As others have pointed out, the GroupBy is using reference equality by default, and you could get around it by specifying one or more properties to group by. But why is that an error?
The whole point of the query is to translate your Linq query into SQL. Since object reference equality on the client can't be easily translated to SQL, the translator doesn't support it and gives you an error.
When you provide one or more properties to group by, the provider can translate that to SQL (e.g. GROUP BY article.Id), and thus the second method works without error.

Linq to SQL - Query

I am trying to mimic below statement in Linq to SQL.
WHERE (rtrim(posid) like '%101' or rtrim(posid) like '%532')
I statement basically determine if posid ends with 101 or 532. In the above example I am only making 2 comparisons but their could be 1 to N comparisons all joined with OR. I store the comparison values (101,532,...) in a generic list that I send to my Linq to SQL method.
I have tried to mimic above SQL using a where clause unsuccessfully (example below):
var PosNum = new List<string>();
PosNum.Add("101");
PosNum.Add("532");
var q = (from a in context.tbl_sspos select a);
q = q.Where(p => PosNum.Contains(p.posid.Trim()));
The issue with the above where clause is that it tries to do an exact match rather I want an ends with comparison.
How would I mimic the SQL statement in Linq to SQL.
Thank You in advance for any help / advice you can provide.
I would use String.EndsWith();
This will check the end of the string rather than entire contents of it.
var q = (from a in context.tbl_sspos select a);
q = q.Where(p => p.posid.EndsWith("102") || p.posid.EndsWith("532"));
In EF 4 you can use the StartsWith / EndsWith methods by now. Might also work in LINQ to SQL.
UPDATE
Just realized that you are trying todo this against multiple values (PosNum), I don't think that this is directly supported currently. You can however concatenate multiple Where()clauses to get the result.
UPDATE 2
As AdamKing pointed out concatenating the where clauses was filtering against all PosNum values, here is the corrected version:
var baseQuery = (from a in context.tbl_sspos select a);
IEnumerable<YourType> q = null;
foreach(var pos in PosNum)
{
if(q == null)
q = baseQuery.Where(a => a.posid.EndsWith(pos));
else
q = q.Union(baseQuery.Where(a => a.posid.EndsWith(pos)));
}
This is not as pretty anymore, but works nonetheless.

How to select a variable plus some other fields in Linq to object?

Consider the following linq query:
from r in result
join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS
from oUS in jOnOrderUS.DefaultIfEmpty()
let OnOrderUS = oUS != null ? oUS.UnitQty : 0
select (new Func<ItemMinMaxView>(() =>
{
r.OnOrderUS = OnOrderUS;
return r;
})).Invoke()
I want to select r from result, but fill a field with the data from oUS. Is there a better way to do this?
I'd rather see your functionality split into its constituent parts. The initial query (join, projection, etc.), the mutation, and then return the specific results you need. Such as
var query = from r in result
join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS
from oUS in jOnOrderUS.DefaultIfEmpty()
let OnOrderUS = oUS != null ? oUS.UnitQty : 0
select new { r, OnOrderUS };
foreach (var item in query)
{
item.r.OnOrderUS = item.OnOrderUS;
}
return query.Select(item => item.r);
// assumes this is in a method returning IEnumerable<R>
If you do not want to code the foreach, you could write it like this. I don't like mutations from queries, but this will do what you want
Func<R, int, R> mutateR = (r, onOrderUS) => { r.OnOrderUS = onOrderUS; return r; };
var query = from r in result
join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS
from oUS in jOnOrderUS.DefaultIfEmpty()
let OnOrderUS = oUS != null ? oUS.UnitQty : 0
select mutateR(r, OnOrderUS);
I too would like to see the query split into more logical parts to aid readability. But I would then take advantage of the Reactive Extensions (Rx) to cause the side effect you want (ie the value assignment).
First up I would just create the query like Anthony suggested, but I want to check that your query is correct.
If there are more than one onOrderUS records returned then you'll only assign the last one found to the ItemMinMaxView record.
If there will only ever be zero or one record then your query is fine, but it could be simpler.
Either way, try this query instead:
var query =
from r in result
join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into goUSs
let OnOrderUS = goUSs.Sum(x => x.UnitQty)
select new
{
Result = r,
OnOrderUS
};
Then I'd get the final results (with the assignment side effect) like this:
var output = query
.Do(x => x.Result.OnOrderUS = x.OnOrderUS)
.Select(x => x.Result)
.ToArray();
The .Do(...) method is part of Rx and is explicitly there for these kinds of side effects.
You do get a load of good extension methods for IEnumerable<T> in Rx so if you haven't checked it out you should. It's even recently been promoted to a supported Microsoft developer tool.

How to do joins in LINQ on multiple fields in single join

I need to do a LINQ2DataSet query that does a join on more than one field (as
var result = from x in entity
join y in entity2
on x.field1 = y.field1
and
x.field2 = y.field2
I have yet found a suitable solution (I can add the extra constraints to a where clause, but this is far from a suitable solution, or use this solution, but that assumes an equijoin).
Is it possible in LINQ to join on multiple fields in a single join?
EDIT
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
is the solution I referenced as assuming an equijoin above.
Further EDIT
To answer criticism that my original example was an equijoin, I do acknowledge that, My current requirement is for an equijoin and I have already employed the solution I referenced above.
I am, however, trying to understand what possibilities and best practices I have / should employ with LINQ. I am going to need to do a Date range query join with a table ID soon, and was just pre-empting that issue, It looks like I shall have to add the date range in the where clause.
Thanks, as always, for all suggestions and comments given
var result = from x in entity
join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
var result = from x in entity1
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
You need to do this, if the column names are different in two entities.
The solution with the anonymous type should work fine. LINQ can only represent equijoins (with join clauses, anyway), and indeed that's what you've said you want to express anyway based on your original query.
If you don't like the version with the anonymous type for some specific reason, you should explain that reason.
If you want to do something other than what you originally asked for, please give an example of what you really want to do.
EDIT: Responding to the edit in the question: yes, to do a "date range" join, you need to use a where clause instead. They're semantically equivalent really, so it's just a matter of the optimisations available. Equijoins provide simple optimisation (in LINQ to Objects, which includes LINQ to DataSets) by creating a lookup based on the inner sequence - think of it as a hashtable from key to a sequence of entries matching that key.
Doing that with date ranges is somewhat harder. However, depending on exactly what you mean by a "date range join" you may be able to do something similar - if you're planning on creating "bands" of dates (e.g. one per year) such that two entries which occur in the same year (but not on the same date) should match, then you can do it just by using that band as the key. If it's more complicated, e.g. one side of the join provides a range, and the other side of the join provides a single date, matching if it falls within that range, that would be better handled with a where clause (after a second from clause) IMO. You could do some particularly funky magic by ordering one side or the other to find matches more efficiently, but that would be a lot of work - I'd only do that kind of thing after checking whether performance is an issue.
Just to complete this with an equivalent method chain syntax:
entity.Join(entity2, x => new {x.Field1, x.Field2},
y => new {y.Field1, y.Field2}, (x, y) => x);
While the last argument (x, y) => x is what you select (in the above case we select x).
I think a more readable and flexible option is to use Where function:
var result = from x in entity1
from y in entity2
.Where(y => y.field1 == x.field1 && y.field2 == x.field2)
This also allows to easily change from inner join to left join by appending .DefaultIfEmpty().
var result = from x in entity
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
select new
{
/// Columns
};
you could do something like (below)
var query = from p in context.T1
join q in context.T2
on
new { p.Col1, p.Col2 }
equals
new { q.Col1, q.Col2 }
select new {p...., q......};
Using the join operator you can only perform equijoins. Other types of joins can be constructed using other operators. I'm not sure whether the exact join you are trying to do would be easier using these methods or by changing the where clause. Documentation on the join clause can be found here. MSDN has an article on join operations with multiple links to examples of other joins, as well.
If the field name are different in entities
var result = from x in entity
join y in entity2 on
new {
field1= x.field1,
field2 = x.field2
}
equals
new {
field1= y.field1,
field2= y.myfield
}
select new {x,y});
As a full method chain that would look like this:
lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
(a, b) => new ResultItem
{
Id = a.Id,
ATotal = a.Total,
BTotal = b.Total
}).ToList();
I used tuples to do that, this is an example for two columns :
var list= list1.Join(list2,
e1 => (e1.val1,e1.val2),
e2 => (e2.val1,e2.val2),
(e1, e2) => e1).ToList();
from d in db.CourseDispatches
join du in db.DispatchUsers on d.id equals du.dispatch_id
join u in db.Users on du.user_id equals u.id
join fr in db.Forumreports on (d.course_id + '_' + du.user_id) equals (fr.course_id + '_'+ fr.uid)
this works for me
Declare a Class(Type) to hold the elements you want to join. In the below example declare JoinElement
public class **JoinElement**
{
public int? Id { get; set; }
public string Name { get; set; }
}
results = from course in courseQueryable.AsQueryable()
join agency in agencyQueryable.AsQueryable()
on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper }
equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

Categories