This question already has answers here:
How to do joins in LINQ on multiple fields in single join
(13 answers)
Closed 8 years ago.
I'm trying to create a linq join statement which joins an object from a table based on two conditions
MESSAGES
======
ID (int)
UserID (Guid)
MESSAGEPART
======
MessageID (int)
IsPlaintext (bool)
MessageContent (nvarchar(max))
Here's the query I want to write, essentially:
var messages = from m in db.Message
join p in db.MessagePart on m.ID equals p.MessageID
and p.IsPlaintext equals false
Unfortunately, this doesn't work. This is the best I can do.
var messages = from m in db.Message
join p in
(from x in db.MessagePart where x.IsPlaintext == false select x)
on m.ID equals p.MessageID
This seems a bit longwinded. Is there a more elegant way of achieving it?
An elegant solution.
var messages = from m in db.Message
join p in db.MessagePart on m.ID equals p.MessageID
where p.IsPlaintext == false
You could try this one:
var messages = from m in db.Message
join p in db.MessagePart
on new { m.ID, false } equals { p.MessageID, p.IsPlaintext }
or you could try this one:
var messages = db.Message.Join(db.MessagePart.Where(x=>x.IsPlainText==false),
x=>x.ID,
y=>y.Id,
(x,y)=>new {});
Inside the new { } you will declare the properties of the anonymous type you select.
ps. If you update your post and show there which fields you want to select, I could update also mine.
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
)
This question already has answers here:
An anonymous type cannot have multiple properties with the same name
(2 answers)
Closed 6 years ago.
I have a method with which is the following:
using (ubmmsEntities db = new ubmmsEntities())
{
var result = (from l in db.log_documents
join t in db.teams on l.op_user_team equals t.id
where l.tracking_id == trackingID
select new { l.op_code, l.op_date, l.op_description, l.op_refer_code, l.op_refer_comments, t.team_name, l.id });
return result.
Now I have to add another column it, and in simple SQL all I would need is to add two more inner joins, but I am quite lost how to do it in EF a the below results a error under t2.team_name "An anonymous type cannot have multiple properties with the same name".
using (ubmmsEntities db = new ubmmsEntities())
{
var result = (from l in db.log_documents
join t in db.teams on l.op_user_team equals t.id
join r in db.refers_codes on l.op_refer_code equals r.code
join t2 in db.teams on r.id equals t2.id
where l.tracking_id == trackingID
select new { l.op_code, l.op_date, l.op_description, l.op_refer_code, l.op_refer_comments, t.team_name, l.id, t2.team_name });
return result.ToList();
}
So, I looked over here and there, and found this thread, but I am unable to figure out how to apply the solution to my method. I googled for "EF naming types" and similar to try to understand what is being asked from me, but honestly I was unable to figure out by myself.
So, I believe the problem is because EF wants me to set a unique name to my db.team... which I thought I'd given it by calling it t2. This approach does work on SQL, but I do not understand how to apply the same to my method. I did find some clues such as new{t2=t.id} but different errors starting to pop-up in different places.
Help/Directions please?
You can give names to properties in your projectction ie
.Select(x => new { firstItem = x.y, secondItem = x.x });
You can use this to get around your problem.
Or, as you are doing it:
select new { firstItem = x.y, secondItem = x.x } .....
Hoping that I'm just missing something obvious but here's my query
var data = (from project in _db.Projects
where project.Id == id
let primaryCategory = (from c in _db.Categories
where c.CategoryID == project.PrimaryCategory
select c.Name)
let categories = (from c in _db.ProjectCategories
join pc in _db.Projects_ProjectCategories on c.ProjectCategoryID equals pc.ProjectCategoryID
where pc.ProjectID == project.ProjectID
select c.Name)
let owner = (from o in _db.Owners
join po in _db.App_Projects_Owners on o.OwnerID equals po.OwnerID
where po.ProjectID == project.ProjectID
select new OwnerModel
{
Owner = o,
Project = project,
PrimaryCategory = primaryCategory.FirstOrDefault(),
Categories = categories.ToList()
})
select new
{
owner,
project
}).FirstOrDefault();
In there OwnerModel.Categories is a List of strings. I can't use ToList() in the query because it gives a materialization error. I've added a custom setter that takes the IQueryable, but that still makes another round trip to the database for every owner that the query returns.
So how are you supposed to assign basic lists in a subquery?
EDIT AND ANSWER (since Robert McKee lead me to the answer in his comment).
The answer is to use the group by clause like so
var data = (from project in _db.Projects
where project.Id == id
let primaryCategory = (from c in _db.Categories
where c.CategoryID == project.PrimaryCategory
select c.Name)
let categories = (from c in _db.ProjectCategories
join pc in _db.Projects_ProjectCategories on c.ProjectCategoryID equals pc.ProjectCategoryID
where pc.ProjectID == project.ProjectID
group c.Name by pc.ProjectCategoryID into x
select x.ToList())
let owner = (from o in _db.Owners
join po in _db.App_Projects_Owners on o.OwnerID equals po.OwnerID
where po.ProjectID == project.ProjectID
select new OwnerModel
{
Owner = o,
Project = project,
PrimaryCategory = primaryCategory.FirstOrDefault(),
Categories = categories
})
select new
{
owner,
project
}).FirstOrDefault();
Specifically note the bits involving
group c.Name by pc.ProjectCategoryID into x
select x.ToList()
It may seem counter-intuitive that it works like this until you dig into exactly what's going on. The method that I was calling above with categories.ToList() was trying to use the System.Collection.Generics ToList function, and that list didn't have any way to convert the expression to sql. However by using the group by clause I was creating a specialized Enumerable IGrouping and calling the ToList function on that. This function is able to be translated into a sql statement and thus doesn't throw the exception.
Learn something new every day.
Set up your navigation properties, and then your query becomes something like:
var data=db.Projects
.Include(p=>p.PrimaryCategory)
.Include(p=>p.Categories)
.Include(p=>p.Owner) // or .Include(p=>p.Owners) if projects can have multiple owners
.First(p=>p.Id == id);
As for child objects, you are looking for the group by clause or .GroupBy method.
I've been scratching my head for some hours trying to figure this one out. I need to search in several tables, using Entity Framework 6 and I've tried to make it work using joins with no luck at all. After a while, I decided to simply make it into several queries, which I'm not sure if ideal at all.
The following code works, but it's not pretty, I think (searchTerm is my search query which is a string):
// Search shareholders
var shareholders =
(from sh in ctx.FirmTypeShareholders.Where(x =>
x.CustomerID.ToString().Equals(searchTerm) ||
x.Firm_FirmType.Firm.ID.ToString().Equals(searchTerm))
join firm in ctx.Firms on sh.Firm_FirmType.FirmID equals firm.ID
select sh).ToList();
// Power plants by shareholder id
var powerPlantsByShareholder = (from sh in ctx.FirmTypeShareholders
join firm in ctx.Firms on sh.Firm_FirmType.FirmID equals firm.ID
from o in firm.Ownerships
join ppu in ctx.PowerPlantUnits on o.PowerPlantUnitID equals ppu.ID
join powerPlant in ctx.PowerPlants on ppu.PowerPlantID equals powerPlant.ID
where sh.CustomerID == 34
select new
{
Shareholder = sh,
PowerPlant = powerPlant
});
// Search persons
var persons = (from p in ctx.People.Where(x =>
x.ID.ToString().Equals(searchTerm) || x.Mobile.ToString().Equals(searchTerm) ||
x.Email.Equals(searchTerm) || x.Name.Contains(searchTerm))
select p).ToList();
// Search power plants
var powerPlants = (from pp in ctx.PowerPlants.Where(x =>
x.ID.ToString().Equals(searchTerm) || x.GSRN.ToString().Equals(searchTerm) ||
x.EDIEL.ToString().Equals(searchTerm))
select pp).ToList();
Is there a way to make this work with a single query?
Thanks in advance :-)
Linq to EF, I'm using asp.net 4, EF 4 and C#.
Here are two ways I came up with to query my data. Ways A and C are working fine. B however needs to implement and additional WHERE statement (as "where c.ModeContent == "NA").
My question is:
Regarding this kind of join (outer join, I suppose) what is the best approach in term of performance?
Could you show me some code to implement additional WHERE statement in B?
Any way to improve this code?
Thanks for your time! :-)
// A
var queryContents = from c in context.CmsContents
where c.ModeContent == "NA" &&
!(from o in context.CmsContentsAssignedToes select o.ContentId)
.Contains(c.ContentId)
select c;
// B - I need to implent where c.ModeContent == "NA"
var result01 = from c in context.CmsContents
join d in context.CmsContentsAssignedToes on c.ContentId equals d.ContentId into g
where !g.Any()
select c;
// C
var result02 = context.CmsContents.Where(x => x.ModeContent == "NA").Where(item1 => context.CmsContentsAssignedToes.All(item2 => item1.ContentId != item2.ContentId));
Regarding query B you can apply the condition like this:
var result01 = from c in context.CmsContents where c.ModeContent == "NA"
join d in context.CmsContentsAssignedToes on c.ContentId equals d.ContentId into g
where !g.Any()
select c;
Your query will be far more readable and maintainable (and perform at least as well) if you use your association properties instead of join:
var result = from c in context.CmsContents
where c.ModeContent == "NA"
&& !c.AssignedToes.Any()
select c;
I'm guessing that the navigation on CmsContent to CmsContentsAssignedToes is called AssignedToes. Change the name in my query if it's actually called something else.
This query can be read out loud and you know exactly what it means. The join versions you have to think about.