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
Related
I have three tables, Chart, ChartQuestion, ChartAnswer
A Chart has many ChartQuestions, a ChartQuestion has many ChartAnswers.
I'd like to do a linq query that gives me an object containing all of this data, so it'd be a ChartObject containing List<ChartQuestion> and each ChartQuestion contains a List<ChartAnswer>
I started with this:
(from chart in db.Chart
join chartQuestion in db.chartQuestion on chart.ChartId equals chartQuestion.ChartId into chartQuestions)
This seems to be the first step. However I want to include the ChartAnswers now, so I have to do another join to pull back the ChartAnswers but I don't know how to do this.
I can't do a join, and while I can do a from, I am not sure of the exact syntax.
(from chart in db.Chart
join chartQuestion in db.chartQuestion on chart.ChartId equals chartQuestion.ChartId into chartQuestions
from chartQuestionsSelection in chartQuestions
join chartAnswer in context.ChartAnswers on chartQuestions.ChartAnswerId equals chartAnswer.ChartAnswerId into chartAnswers // This is wrong
)
With that code above you end up as chartAnswers being separate to the chartQuestions rather than belonging to them, so I don't think it is correct.
Any idea?
joining a table destructors it i.e. you now have the row as a variable if you need it in a nested fashion then select in like this
(from chart in db.Chart
select new { //can also be your own class/record, perhaps a DTO
chart.Id,
chart.Name,
questions = (from chartQuestion in db.chartQuestion
where chart.ChartId == chartQuestion.ChartId
select new { //perhaps a dto
chartQuestion.Id,
chartQuestion.Question,
Answers =
(from chartAnswer in context.ChartAnswers
where chartAnswer.ChartAnswerId == chartQuestion.ChartAnswerId
select chartAnswer).ToList() //this is translated and not evaluated
}).ToList() //this is translated not evaluated
}).ToListAsync(cancellationToken) //this will evaluate the expression
If you need it by joins then you can group join it like this:
from m in _context.Chart
join d in _context.ChartQuestions
on m.ID equals d.ID into mdJoin
select new
{
chartId = m.ID,
chartName = "m.name",
quess = from d in mdJoin
join dd in _context.ChartAnswer
on d.Iddomain equals dd.DomainId into anJoin
select new
{
quesId = d.ID,
quesName = d.Question,
anss = anJoin
}
}
A better way: If you edit your DbConfigurations to include navigation properties of each then the code will become way simpler.
example:
db.Chart
.Include(x => x.chartQuestions)
.ThenInclude(x => x.chartAnswers)
You can search more about how to do navigation properties in EFCore, but ofcourse if the code base is large then this might not be feasible for you.
Try following :
(from chart in db.Chart
join chartQuestion in db.chartQuestion on chart.ChartId equals chartQuestion.ChartId
join chartAnswer in context.ChartAnswers on chartAnswer.ChartAnswerId equals chartQuestion.ChartAnswerId
)
Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for
I am trying to take the TSQL below and convert it into LINQ. I'm obviously not very good at this and not sure how to go about doing a join in Linq. My confusion goes a little beyond just the simple expression. In my basic uses my result set is a class that of course represents a table row...but a join would not have this one to one ratio so do I have to create a custom result set?
TSQL:
SELECT
SimEA.*,
SimE.*
FROM
dbo.SSimEmailsAdressees SimEA
JOIN dbo.SSimEmails SimE ON
SimEA.EmailID = SimE.EmailMsgID
WHERE
SimEA.UserId = var
The closest I have come:
this.GetAll<SSimEmails>()
.Where(e => e.SSimEmailsAdressees.Any(p => p.UserId.ToString() == usrID));
Which of course return an object that is a mimic of the single table being queried. So I need help getting the join added and I'm guessing I'll have to create a return object something like this?
this.GetAll<MyJoinResultsObject>......
I think that you should write something like:
var q =
from SimEA in SSimEmailsAdressees
where SimEA.UserId = usrID
join SimE in SSimEmails on SimEA.EmailID equals SimE.EmailMsgID
into joinSimE
from SimE in joinSimE.DefaultIfEmpty()
select new { EmailID = SimEA.EMailID, UserId = SimEA.UserId, OtherField = SimE.OtherField };
var result = from ea in SimEA
join e in SimE
on ea.EmailID equals e.EmailMsgID
where ea.UserId = userId
select new { properties you want here or new object based on properties}
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;
I have two IEnumerables called BaseReportDefinitions and InputReportDefinitions. I need to do a left outer join where i want all the InputReportDefinitions and whichever BaseReportDefinitions that match. Both IEnumberables contain ReportDefinition objects that contain ParentName and ReportName properties that need to be used as the join key. I want to return the ReportDefinition object for each (in the case of BaseReportDefinition entry it may be null) in an anonymous object.
I have seen many examples of linq outer joins and outer joins with a static second condition that often gets put into a where condition but nothing that really uses two conditions fully for the join.
var items = inputReportDefinitions.GroupJoin(
baseReportDefinitions,
firstSelector => new {
firstSelector.ParentName, firstSelector.ReportName
},
secondSelector => new {
secondSelector.ParentName, secondSelector.ReportName
},
(inputReport, baseCollection) => new {inputReport, baseCollection})
.SelectMany(grp => grp.baseCollection.DefaultIfEmpty(),
(col, baseReport) => new
{
Base = baseReport,
Input = col.inputReport
});
I believe this ends up being a left outer join. I don't know how to convert this monstrosity to a query statement. I think if you add AsQueryable() to the end it could be used in Linq-to-SQL, but honestly, I have little experience with that.
EDIT: I figured it out. Much easier to read:
var otherItems = from i in inputReportDefinitions
join b in baseReportDefinitions
on new {i.ParentName, i.ReportName}
equals new {b.ParentName, b.ReportName} into other
from baseReport in other.DefaultIfEmpty()
select new
{
Input = i,
Base = baseReport
};