Linq to Entities selecting properties from related objects - c#

I have several tables that are properly linked and mapped through FKs / link tables.
ie: link_table -> 1 to many relationship to table_one and table_two -> many to many relationship to table_three
var results = db.link_table.Where(l => l.table_one.RandomProperty == "value");
The above obviously works but I want to be able to make a select like this:
var results = db.link_table.Where(l => l.table_one.RandomProperty == "value" && l.table_two.table_three.RandomProperty == "anothervalue");
This fails because it seems not possible to access the properties that belong to table_three. The SQL query I would like should resemble something like this (the WHERE part is the most important):
SELECT * FROM link_table
LEFT JOIN table_one ON table_one.assoc = link_table.assoc
LEFT JOIN table_two ON table_two.assoc = link_table.assoc
LEFT JOIN table_three ON table_two.assoc = table_three.assoc
WHERE table_one.RandomProperty = "value" AND table_three.RandomProperty = "AnotherValue"
How would I be able to access the "RandomProperty" of table_three through the relational mapping of Linq2Entities? Is it even possible in one single line of code and thus getting the result I want in one automatically generated SQL query?

First declare all your FK properties as virtual in all model classes.
And then use this code:-
var results = db.link_table.Where(l => l.table_one.RandomProperty == "value" && l.table_two.table_three.RandomProperty == "value").FirstOrDefault();

The in-depth answer is given to this question: linq to entities, a where in where clause? (inner where). It involves using the .Any() command to search through the related many-to-many table data.
i.e.:
var results = db.link_table.Where(l => l.table_one.RandomProperty == "value" && l.table_two.table_three.Any(t => t.RandomProperty == "value"));

Related

ServiceStack ORMLite: Best way to alias primary table and select list in query with join?

I have the following ORMLite query in which I wish to return the columns from the primary table, i.e. the one referenced by the From<T>() method, filtered by a join to a secondary table.
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>((o, q) => o.SurveyQuestionId == q.Id, conn.JoinAlias("q"))
.Where<SurveyQuestionDTO>(q => q.SurveyId == surveyId);
return conn.Select(query);
This generates the following SQL query
SELECT "Id", "SurveyQuestionId", "Option", "Selected", "Sequence"
FROM "dbo"."SurveyQuestionOptions"
INNER JOIN "dbo"."SurveyQuestions" q
ON ("dbo"."SurveyQuestionOptions"."SurveyQuestionId" = "q"."Id")
WHERE ("dbo"."SurveyQuestions"."SurveyId" = #0)
This would be fine except that both tables have Id and Sequence columns so the query fails with ambiguous column references. If I was hand-coding the SQL I would simply alias the SurveyQuestionOptions table, for instance with o and use that alias on each column in the select list, like o.Id, o.SurveyQuestionId, o.Option, o.Selected, o.Sequence or even just o.* as all columns are being returned. My question is, what is the best way to make ORMLite generate such code?
I have found a way to do it, by adding a Select<T>() method returning an anonymous class, as follows
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>(
(o, q) => o.SurveyQuestionId == q.Id && q.SurveyId == surveyId,
conn.JoinAlias("q"))
.Select<SurveyQuestionOptionDTO>(o => new
{
o.Id,
o.SurveyQuestionId,
o.Option,
o.Selected,
o.Sequence
});
return conn.Select(query);
This works, but it seems like a lot of extra code to achieve a simple result, and because columns are explicitly returned, requires this code to change if the table ever gets a new column and the DTO class is re-generated. Is there a better, simpler way?
I have found a simpler way that also resolves future impact of column changes. Instead of returning a new anonymous class from the Select<T>() method you can simply return the instance that's passed in. So the code now looks like this, and still works as expected.
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>(
(o, q) => o.SurveyQuestionId == q.Id && q.SurveyId == surveyId,
conn.JoinAlias("q"))
.Select<SurveyQuestionOptionDTO>(o => o);
return conn.Select(query);

Select parent but order by certain child with dynamic linq

I'am trying to write following in Dynamic-linq. I have following statement in ordinary linq
var result = DBContext.Report
.Include(h => h.ReportRoleMemberships)
.Join(DBContext.ReportRoleMemberships.Where(Rrm =>Rrm.ValidTo==null && Rrm.UserId==userId),
t=>t.Id,
y=>y.Report_Id,
(t,y) => new { Ha=t, Rrm=y })
.OrderBy(h => h.Rrm.ReportRoleValue);
userId is a int with the UserId in the code above.
I want to sort on ReportRoleValue and feels this feel a little bit over the top, but I havent a clue how I should write this is dynamic linq , since it is orderby on one-to-many parent-child relationship.
Assuming that there is a navigation property from ReportRoleMemberships to Reports, you can turn the query around and by that remove the separate Join, e.g.:
var result = DBContext.ReportRoleMemberships
.Include(h => h.Report)
.Where(Rrm => Rrm.ValidTo == null && Rrm.UserId == userId)
.OrderBy(h => h.ReportRoleValue);
This query selects the entries from ReportRoleMemberships and applies the conditions to them and also populates the Report part so that you retrieve both the ReportRoleMembership and Report information in one query.

ASP.NET MVC C# Select and Where Statements

I'm having trouble understanding .Select and .Where statements. What I want to do is select a specific column with "where" criteria based on another column.
For example, what I have is this:
var engineers = db.engineers;
var managers = db.ManagersToEngineers;
List<ManagerToEngineer> matchedManager = null;
Engineer matchedEngineer = null;
if (this.User.Identity.IsAuthenticated)
{
var userEmail = this.User.Identity.Name;
matchedEngineer = engineers.Where(x => x.email == userEmail).FirstOrDefault();
matchedManager = managers.Select(x => x.ManagerId).Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
}
if (matchedEngineer != null)
{
ViewBag.EngineerId = new SelectList(new List<Engineer> { matchedEngineer }, "PersonId", "FullName");
ViewBag.ManagerId = new SelectList(matchedManager, "PersonId", "FullName");
}
What I'm trying to do above is select from a table that matches Managers to Engineers and select a list of managers based on the engineer's id. This isn't working and when I go like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
I don't get any errors but I'm not selecting the right column. In fact the moment I'm not sure what I'm selecting. Plus I get the error:
Non-static method requires a target.
if you want to to select the manager, then you need to use FirstOrDefault() as you used one line above, but if it is expected to have multiple managers returned, then you will need List<Manager>, try like:
Update:
so matchedManager is already List<T>, in the case it should be like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
when you put Select(x=>x.ManagerId) after the Where() now it will return Collection of int not Collection of that type, and as Where() is self descriptive, it filters the collection as in sql, and Select() projects the collection on the column you specify:
List<int> managerIds = managers.Where(x => x.EngineerId == matchedEngineer.PersonId)
.Select(x=>x.ManagerId).ToList();
The easiest way to remember what the methods do is to remember that this is being translated to SQL.
A .Where() method will filter the rows returned.
A .Select() method will filter the columns returned.
However, there are a few ways to do that with the way you should have your objects set up.
First, you could get the Engineer, and access its Managers:
var engineer = context.Engineers.Find(engineerId);
return engineer.Managers;
However, that will first pull the Engineer out of the database, and then go back for all of the Managers. The other way would be to go directly through the Managers.
return context.Managers.Where(manager => manager.EngineerId == engineerId).ToList();
Although, by the look of the code in your question, you may have a cross-reference table (many to many relationship) between Managers and Engineers. In that case, my second example probably wouldn't work. In that case, I would use the first example.
You want to filter data by matching person Id and then selecting manager Id, you need to do following:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).Select(x => x.ManagerId).ToList();
In your case, you are selecting the ManagerId first and so you have list of ints, instead of managers from which you can filter data
Update:
You also need to check matchedEngineer is not null before retrieving the associated manager. This might be cause of your error
You use "Select" lambda expression to get the field you want, you use "where" to filter results

Entity Framework Union and Projection fail

This is a multi-progned question so please bear with me! I have two queries:
var dynamicResult = Repository.Select<Table1>()
.Where(b => b.InactivatedD == null)
.Select(b => b.Table2);
var staticResult = Repository.Select<Table2>()
.Where(b => b.column == "CONSTANT");
return dynamicResult.Union(staticResult).ToList();
This works fine. Now I have added an additional property to the the Table2 class and have instructed EF to ignore the field within my configuration like so:
Ignore(e => NewColumn);
This too is working well as I can set the field properly without EF throwing an exception. Now I don't know if there is an easy way to do what I want. In my first query Table1 has a column that I want to use to hydrate this new column on Table2 but I don't know of an easy way to do this. The only thing I have been able to come up with is:
var dynamicResult = Repository.Select<Table1>()
.Where(b => b.InactivatedD == null)
.Select(b => new Table2 { Column1 = b.Table2.Column1, NewColumn = b.SomeColumn ... <additional initialization> });
This is a little messy and the initialization would get pretty long since this entity has about 15 columns I'd need to hydrate. I could, of course, just traverse the association between Table2 and Table1 in my property rather than trying to set it in the above query but that seems like additional work and one more query to maintain. Additionally, when using the method above, my union no longer works. If my queries look like this:
var dynamicResult = Repository.Select<Table1>()
.Where(b => b.InactivatedD == null)
.Select(b => new Table2 { Column1 = b.Table2.Column1, NewColumn = b.SomeColumn })
var staticResult = Repository.Select<Table2>()
.Where(b => b.column == "CONSTANT")
.Select(b => new Table2 { Column1 = b.Table2.Column1, NewColumn = b.SomeColumn })
return dynamicResult.Union(staticResult).ToList();
I get an exception that the Entity or Complex type, Table 2, can not be constructed by an Entity Framework query which kind of has me at a loss. I understood why I was getting this error before I told EF to ignore the NewColumn but now I am not sure why this error is popping up.
In summation: is there a better way to hydrate my new column then what I have proposed above and can anyone identify why I am unable to union entities created using new from within a query?
Thanks!
It is never allowed to create an entity type in an EF query irrespective of which properties you address. The reason is that EF has no way to track such entities, because it did not materialize them itself.
You should define a type that looks like Table2, including the new column and project to that type in both queries. You will be able to union the queries.

Entity framework. Need help filtering results

Need to select data in entity framework but need to filter on the childrent and grandchildren
i have 4 tables. Parent -> Child -> GrandChild -> GreatGrandChild I want to return all the parents but filter on child and greatgrandchildren.
in other words (for example)
SELECT Parent.*
FROM Parent
INNER JOIN Child
INNER JOIN Grandchild
INNER JOIN GreatGrandChild
WHERE child.Column5 = 600 AND
GreatGrandChild.Column3 = 1000
it cant be anomymous type because i need to update the data and saveChanges to the db.
using vs 2010 and EF 4.0
Using linq you should need something like this.
var q = from q1 in dbContext.Parent
join q2 in dbContext.Children
on q1.key equals q2.fkey
join q3 in ........
where q4.col1 == 3000
select q1;
This query should do what you want. Yes, it is a bit of a mess because it is so deeply nested.
var result = context.Parent
.Where(parent => parent.Child
.Any(child => (child.Column5 == 600) &&
child.GrandChild
.Any(grandchild => grandchild.GreatGrandChild
.Any(greatgrandchild => greatgrandchild.Column3 == 1000))));
Your table structure - if your example is not just for illustration, leads me to think you may want to think more about your model here (i.e. are children separate entity types or should they be a defined relationship?)
What you describe is a simple joins and where clause though, and is written essentially the same way: assuming you are returning DBSet from your DBContext:
_context.Parents.Join(context.Child, p=>p.Parent.ID, c=>c.ParentID)
.Join(...Grandchild...).Where(o=>o.Column5=600)
.Join(...GreatGrandChild...).Where(o=>o.Column3=1000)
EDIT to get back the strongly typed entities you might need to do something like:
var greatgrandchildren = context.GreatGrandchildren.Where(o=>o.Column3=1000).ToList();
var grandchildren = context.Grandchildren.Where(o=>o.Column3=600 and greatgrandchildren.contains(o)).ToList();
var children = context.Children.Where(o=>grandchildren.Contains(o)).ToList();
var parents = context.Parent(o=>children.Contains(o).ToList();
My syntax might be off, and someone can add, can I avoid .ToList() to prevent roundtrips until the last call?

Categories