I have a fairly complex sql statement with joins. Here is my code using EF:
var MicrositeResponseAdors = _context.MicrositeResponseAdor
.Include(user => user.UserTracking)
.ThenInclude(tracking => tracking.AddTradeIn)
.Include(user => user.UserTracking)
.ThenInclude(tracking => tracking.TrackingFinance)
.Include(user => user.UserTracking)
.ThenInclude(tracking => tracking.SearchTracking)
.Include(user => user.UserTracking)
.ThenInclude(tracking => tracking.TrackingType)
.Include(user => user.UserTracking)
.ThenInclude(tracking => tracking.Vehicle)
.FirstOrDefault(m => m.Id == id && m.FirstName.ToLower() != "test" && m.LastName.ToLower() != "test");
I need to add another join to a table that has no primary key, which Entity Framework doesn't allow. I wasn't even able to fake it. So I was told to make something more custom using what I believe is called LINQ?
I cannot figure out how to assign my data values with the original EF entity classes. Here is what I have so far:
var MicrositeResponseAdors = (from p in _context.MicrositeResponseAdor
join c in _context.UserTracking on p.Id equals c.UserTrackingId
into pc
select new {
MicrositeResponseAdor = p, MicrositeResponseAdor.UserTracking = pc
}
).FirstOrDefault();
This gives:
Invalid anonymous type member declarator.
I think that is because of the first part of MicrositeResponseAdor.UserTracking. I am not sure how to assign UserTracking under the MicrositeResponseAdor.
The error you've given is a C# compilation error, as you're trying to name a property MicrositeResponseAdor.UserTracking. Property names can't contain dots.
This would at least compile:
var micrositeResponseAdor = (
from p in _context.MicrositeResponseAdor
join c in _context.UserTracking on p.Id equals c.UserTrackingId into pc
select new {
MicrositeResponseAdor = p,
UserTracking = pc
}
).FirstOrDefault();
This will make your UserTracking appear in two places:
micrositeResponseAdor.UserTracking
micrositeResponseAdor.MicrositeResponseAdor.UserTracking
It's in the first because you've put it there, and it's in the second because there's a relationship between the entities anyway, so EF is conveniently populating it if it has loaded it in that context (and it has, because you asked for it explicitly).
So you don't have to assign loaded navigational properties in your query, you just have to make sure the related entities are loaded, either with a manual join, an Include or even a second query.
The question is why you do this at all. You've already had the Include version. The join variant is just more verbose, but it doesn't add anything.
Not that EF will always have a key on every entity, whether the database thinks it is one or not. Technically, that's unrelated Include vs join though.
Related
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);
According to this StackOverflow answer:
Linq to Entities - how to filter on child entities
you should be able to filter down the list of related entities in Entity Framework by utilizing a projection, like I've done here:
Company company = _context.Company
.Where(g => g.CompanyId == id)
.Select(comp => new
{
group = comp,
operators = comp.Operator,
formFamilies = comp.FormFamily.Where(ff => ff.IsActive ?? false)
}).AsEnumerable().Select(i => i.group).FirstOrDefault();
To give a quick overview of what I'm trying to obtain here, I'm trying to get a list of all of the active form families associated with this company object, however, whenever I restrict the results in any way, the result set is empty.
If the line were formFamilies = comp.FormFamily then it returns two results, one active one inactive
If the line is formFamilies = comp.FormFamily.Where(ff => true) then it returns nothing
If the line is formFamilies = comp.FormFamily.OrderBy(ff => ff.FormFamilyId) then it returns nothing.
Any sort of modification that I do to comp.FormFamily means the result set returns nothing, I've dug through the deepest sections of SA to try to find a solution, and tried every solution I've found, but nothing seems to cause this list to return anything.
Assuming that Company and FormFamily entities has one to many relationship I would suggest to use a join statement.Something like this should give you what you are looking for.
var company = from c in _context.Company
join f in _context.FormFamily
on c.Id equals f.CompanyId
where c.Id == id
select new Company()
{
Id = c.Id,
operators = c.Operator.ToList(),
formFamilies = c.FormFamily.Where(x=>x.IsActive ==
false).ToList()
} .FirstOrDefault();
Hope this helps.
I didn't quite understand what is your query is supposed to do. But it seems to me that you cannot just call Select method on another Select result method.
Anyway, you could simply use Include methods instead of projecting.
var company = _context.Company
.Where(c => c.Id == id)
.Include(c => c.FormFamily).Where(ff => ff.IsActive ?? false)
.ToList();
Did not test it. To prove it works or not be sure put an entity model in the question. Then I may produce more accurate answer.
Consider following LINQ query:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select new
{
ItemProp1 = obj,
ItemProp2 = obj.NavProp2.Any(n => n.Active)
}).SingleOrDefault();
This runs as expected, but item.ItemProp1.NavProp1 is NULL.
As it explains here this is because of the query actually changes after using Include(). but the question is what is the solution with this situation?
Edit:
When I change the query like this, every things works fine:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select obj).SingleOrDefault();
Regarding to this article I guess what the problem is... but the solution provided by author not working in my situation (because of using anonymous type in final select rather than entity type).
As you mentioned, Include is only effective when the final result of the query consists of the entities that should include the Include-d navigation properties.
So in this case Include has effect:
var list = _db.SampleEntity.Include(s => s.NavProp1).ToList();
The SQL query will contain a JOIN and each SampleEntity will have its NavProp1 loaded.
In this case it has no effect:
var list = _db.SampleEntity.Include(s => s.NavProp1)
.Select(s => new { s })
.ToList();
The SQL query won't even contain a JOIN, EF completely ignores the Include.
If in the latter query you want the SampleEntitys to contain their NavProp1s you can do:
var list = _db.SampleEntity
.Select(s => new { s, s.NavProp1 })
.ToList();
Now Entity Framework has fetched SampleEntitys and NavProp1 entities from the database separately, but it glues them together by a process called relationship fixup. As you see, the Include is not necessary to make this happen.
However, if Navprop1 is a collection, you'll notice that...
var navprop1 = list.First().s.Navprop1;
...will still execute a query to fetch Navprop1 by lazy loading. Why is that?
While relationship fixup does fill Navprop1 properties, it doesn't mark them as loaded. This only happens when Include loaded the properties. So now we have SampleEntity all having their Navprop1s, but you can't access them without triggering lazy loading. The only thing you can do to prevent this is
_db.Configuration.LazyLoadingEnabled = false;
var navprop1 = list.First().s.Navprop1;
(or by preventing lazy loading by disabling proxy creation or by not making Navprop1 virtual.)
Now you'll get Navprop1 without a new query.
For reference navigation properties this doesn't apply, lazy loading isn't triggered when it's enabled.
In Entity Framework core, things have changed drastically in this area. A query like _db.SampleEntity.Include(s => s.NavProp1).Select(s => new { s }) will now include NavProp1 in the end result. EF-core is smarter in looking for "Includable" entities in the end result. Therefore, we won't feel inclined to shape a query like Select(s => new { s, s.NavProp1 }) in order to populate the navigation property. Be aware though, that if we use such a query without Include, lazy loading will still be triggered when s.NavProp1 is accessed.
I know this will probably get a few laughs, but don't forget the obvious like i just did. The row in the database didn't actually have a foreign key reference! I should have checked the dam data first before thinking EF Include wasn't working! Grrr. 30 minutes of my life I won't get back.
If your model is defined properly it should work without any problems.
using System.Data.Entity;
var item = _db.SampleEntity
.Include(p => p.NavigationProperty)
.Select(p => new YourModel{
PropertyOne = p.Something,
PropertyTwo = p.NavigationProperty.Any(x => x.Active)
})
.SingleOrDefault(p => p.Something == true);
How did you find that item.ItemProp1.NavProp1 is null. EF uses proxies to load all required properties when you try to access it.
What about
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select obj).SingleOrDefault();
Assert.IsNotNull(obj.NavProp1);
Assert.IsNotNull(obj.NavProp2);
You can also try with
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select new
{
ItemProp1 = obj,
NavProp1 = obj.NavProp1,
ItemProp2 = obj.NavProp2.Any(n => n.Active)
}).SingleOrDefault();
Assert.IsNotNull(item.NavProp1)
Of course I assume that you don't have any problems with EF navigation property mappings.
So I am converting a old project with ordinary SQL queries to a ORM using the Entity Framework. So I have created database model like this:
So I had this old query which I want to translate to a linq expression
SELECT UGLINK.USERNAME
FROM GMLINK
INNER JOIN UGLINK
ON GMLINK.GROUPID = UGLINK.GROUPID
WHERE (((GMLINK.MODULEID)=%ID%))
And the problem I have is that I can't figure out how to do a join query using the objects.
Instead I have to go though the properties like this (which seems to be working):
// So this is one of the module objects that is located in a listView in the GUI
Module m = ModuleList.selectedItem as Module;
/* Now I want to fetch all the User objects that,
* via a group, is connected to a certain module */
var query = context.gmLink
.Join(context.ugLink,
gmlink => gmlink.GroupId,
uglink => uglink.GroupId,
(gmlink, uglink) => new { gmLink = gmlink, ugLink = uglink })
.Where(gmlink => gmlink.gmLink.ModuleId == m.ModuleId)
.Select(x => x.ugLink.User);
So as I said this works, but as you see I kind of have to connect the modules via the link tables properties .GroupId and .ModuleId and so on. Instead I would like to go through the objects created by EF.
I wanted to write a question a bit like this, but can't figure out how to do it, is it at all possible?
var query = context.User
.Select(u => u.ugLink
.Select(uglink => uglink.Group.gmLink
.Where(gmLink => gmLink.Module == m)));
This should be working:
var query = context.gmLink
.Where(gmlink => gmlink.ModuleId == m.ModuleId)
.SelectMany(gmlink => gmlink.Group.ugLink)
.Select(uglink => uglink.User);
It's impossible to filter gmLinks using .Where(gmlink => gmlink.Module == m) in EF, so this comparison needs to be done using identifiers. Another option is .Where(gmlink => gmlink.Module.ModuleId == m.ModuleId)
If you have lazy loading enabled, you do not need to apply specific join notation (you can access the navigation properties directly) - but the queries that are ran against SQL are inefficient (generally the results are returned in a number of different select statements).
My preference is to disable lazy loading on the context, and use .Include() notation to join tables together manually, resulting in generally more efficient queries. .Include() is used to explicitly join entities in Entity Framework.
Join() is misleading, and not appropriate for joining tables in EF.
So, to replicate this statement:
SELECT UGLINK.USERNAME
FROM GMLINK
INNER JOIN UGLINK
ON GMLINK.GROUPID = UGLINK.GROUPID
WHERE (((GMLINK.MODULEID)=%ID%))
You would use the following:
var query = context.gmLink
.Include(x => x.Group.gmLink)
.Where(x => x.ModuleId == myIdVariable)
.Select(x => new {
UserName = x.Group.ugLink.UserName
});
Assuming that your navigation properties are correctly set up. I have not tested this, so I'm not 100% on the syntax.
You should really run SQL profiler while you write and run LINQ to Entity queries against your database, so you can understand what's actually being generated and run against your database. A lot of the time, an EF query may be functioning correctly, but you may experience performance issues when deployed to a production system.
This whitepaper might help you out.
I haven't tested it, but something like this:
var users = context.User
.Where(x => x.ugLink
.Any(y => context.gmLink
.Where(z => z.ModuleId == m)
.Select(z => z.GroupId)
.Contains(y.GroupId)
)
)
.ToList();
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.