I’d like to consult about a problem I have faced. I've started working on a project with a very difficult database: many tables in the DB don’t have primary keys or have multiple PKs, so I can't add correct associations for all entities in my edmx. However, for some entities it’s possible and I managed to do so. Thus, I have two entities with an association between them: Vulner and VulnerDescription. And I have a "bad" connection table for Vulners called VulnerObjectTie (with a mental FK: VulnerObjectTie.Vulner = Vulner.Id), which I can’t add correct associations to. So, I decided to do add the following LinqtoEntities query:
var vulerIdList = from vulner in _businessModel.DataModel.VulverSet.Include("Descriptions")
join objectVulnerTie in _businessModel.DataModel.ObjectVulnerTieSet on vulner.Id equals objectVulnerTie.Vulner
where softwareId.Contains(objectVulnerTie.SecurityObject)
select vulner;
where description is Navigation Property for an association with the VulnerDescription table. The query works, but it does not load the Descriptions property. However, if I remove the join operator, then Descriptions are loaded correctly.
The most obvious solution for this problem is to divide one query into the next two queries:
var vulerIdList = from vulner in _businessModel.DataModel.VulverSet
join objectVulnerTie in _businessModel.DataModel.ObjectVulnerTieSet on vulner.Id equals objectVulnerTie.Vulner
where softwareId.Contains(objectVulnerTie.SecurityObject)
select vulner.Id;
var query = from vulner in _businessModel.DataModel.VulverSet.Include("Descriptions")
where vulerIdList.Contains(vulner.Id)
select vulner;
But I think it looks ugly. Can anyone suggest a more simple solution for this problem, or is it just a special feature of EF4??
thankyouplease :))
It's a known 'feature' or limitation, depending on how you look at it. Here's an interesting discussion on the topic, I'm sure there are more references to find: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/d700becd-fb4e-40cd-a334-9b129344edc9/
The problem here is that EF is not very well suited for "bad databases". EF (and especially all automation tools like model wizard) expects clear and correct database design.
Include is not supported in queries using custom joins or projections. Not supported in this case means that it is completely omitted.
Related
We are working on a Project in which there are many linq queries are not optimized, because as they started on the project they used the property virtual for all of their models.
My task is to optimize the max number of the queries, in order to enhance the app performance.
The problem is if I use the Include function and delete all virtual properties from the model, lot of things stop working and the number of affected functions is huge.
So I thought if I can find some thing resemble to "exclude" to exclude the unnecessary sub queries in some cases.
(with the assumption of your result set implements ienumerable)
My first choice would be:
ListMain.Except(ItemsToExclude);
Or, I would go with (not) "Contains" as follows and have check in-between to exclude the records. This may not be the best way out there but I could work.
!ListMain.Contains(ItemsToExclude)
I don't know if I got the question right, but to avoid loading certain attributes or related objects, you can make an additional Select() including only what's needed.
Example:
A simple ToList() will bring the entire object from the table:
var resultList = await dbContext.ABTests.AsNoTracking().ToListAsync();
It will derives in the query:
SELECT [a].[Id], [a].[AssignedUsers], [a].[EndDate], [a].[Groups], [a].[Json], [a].[MaxUsers], [a].[Name], [a].[NextGroup], [a].[StartDate]
FROM [ABTests] AS [a]
(which includes all mapped fields of the ABTest object)
To avoid fetching all, you can do as follow:
var resultList = await dbContext.ABTests.AsNoTracking().Select(x => new ABTest
{
Id = x.Id,
Name = x.Name
}).ToListAsync();
(supposing that you only wan the fields Id and Name to be eagerly loaded)
The resulting SQL Query will be:
SELECT [a].[Id], [a].[Name]
FROM [ABTests] AS [a]
Good day,
I have a diagram with 5 tables.
As you can see, I have a category table and medical studies with details.
Also two similar tables : OtherStudy and OtherMedicalStudyDetails.
These tables also have some foreing keys that come from other tables that are not included in this diagram.
For the sake of simplicity I provided a database diagram that does not look good but makes it simpler to explain my problem (somebody could tell me I should use just the medicalStudy and medicalStudy details tables and do not use the other two, but I would like to keep the diagram like this please).
What I am trying to do is, after adding (using c# entityframework) a medicalStudy with its MedicalStudyDetails, compare both detail tables and bring a list of all "OtherStudy" where (MedicalStudyDetails.FK_otherTable==OtherMedicalStudyDetails.FK_otherTable&& MedicalStudyDetails.FK_anotherTable==OtherMedicalStudyDetails.FK_anotherTable).
. Note that this compairson should be done foreach MedicalStudyDetails added.
is there a way to bring the mentioned list using a sintax like this?
var otherStudy= _dbContext.MedicalStudyDetails.Include(...)
.Where(...)
.ToList();
Wouldn't you need a multiple clause inner join to achive this??
from msd in _dbContext.MedicalStudyDetails
join omsd in _dbContext.OtherMedicalStudyDetails on
new {msd.FK_otherTable, msd.FK_anotherTable} equals new { omsd.FK_otherTable, omsd.FK_anotherTable}
where ...
select ...
I want to execute a complex query using Entity Framework (Code First).
I know it can be done by LINQ, but what is the best way to write LINQ query for complex data and inner joining?
I want to execute the following query:
var v = from m in WebAppDbContext.UserSessionTokens
from c in WebAppDbContext.Companies.Include(a => a.SecurityGroups)
from n in WebAppDbContext.SecurityGroups.Include(x => x.Members)
where m.TokenString == userTokenString &&
n.Members.Contains(m.User) &&
c.SecurityGroups.Contains(n)
select c;
Is this the best way to do this?
Does this query get any entire list of records from the db and then executes the filtration on this list? (might be huge list)
And most important: Does it query the database several times?
In my opinion and based on my own experiences, talking about performance, especially joining data sets, it's faster when you write it in SQL. But since you used code first approach then it's not an option. To answer your questions, your query will not query DB several times (you can try debugging and see Events log in VS). EF will transform your query into SQL statement and execute it.
TL:DR; don't micromanage the robots. Let them do their thing and 99% of the time you'll be fine. Linq doesn't really expose methods to micromanage the underlying data query, anyway, its whole purpose is to be an abstraction layer.
In my experience, the Linq provider is pretty smart about converting valid Linq syntax into "pretty good" SQL. Your looks like your query is all inner joins, and those should all be on the primary indexes/foreign keys of each table, so it's going to come up with something pretty good.
If that's not enough to soothe your worries, I'd suggest:
put on a SQL Trace to see the actual query that's being presented to the Database. I bet it's not as simple as Companies join SecurityGroups join Members join UserTokens, but it's probably equivalent to it.
Only worry about optimizing if this becomes an actual performance problem.
If it does become a performance problem, consider refactoring your problem space. It looks like you're trying to get a Company record from a UserToken, by going through three other tables. Is it possible to just relate Users and Companies directly, rather than through the security model? (please excuse my total ignorance of your problem space, I'm just saying "maybe look at the problem from another angle?")
In short, it sounds like you're burning brain cycles attempting to optimize something prematurely. Now, it's reasonable to think if there is a performance problem, this section of the code could be responsible, but that would only be noticeable if you're doing this query a lot. Based on coming from a security token, I'd guess you're doing this once per session to get the current user's contextual information. In that case, the problem isn't usually with Linq, but with your approach to solving the problem for finding Company information. Can you cache the result?
My current database solution includes three tables called Establishment, Feature, and a linking many-to-many table called EstablishmentFeature (since an establishment can have many features, and a feature can exists across multiple establishments).
I need to generate a query that returns establishments that meet only certain criteria, namely, which establishments have X features based on a collection of featureId's being passed in. The establishment must have ALL features that are being search, i.e.. AND not OR condition.
I got the SQL to achieve the desired result, but I am pulling my hair out trying to work out the LINQ (lambra prefereably) equivalent. The T-SQL is:
SELECT e.[EstablishmentId], e.[Name], e.[Description]
FROM Establishment e
INNER JOIN EstablishmentFeature ef
ON e.[EstablishmentId] = ef.[EstablishmentId]
WHERE ef.[FeatureId] in ('20', '15', '72')
GROUP BY e.[EstablishmentId], e.[Name], e.[Description]
HAVING COUNT(*) = 3
I tried to use Linqer to convert the SQL but Linqer crashes when it attempts the conversion. I tried reinstalling Linqer, but it crashes without fail when trying to compile the LINQ syntax. (Simpler conversions work though). Also tried to work out the LINQ equivalent using LinqPad, but I just ended up chasing my tail...
Is this something I will have to use PredicateBuilder for? Somewhat exhausted, I don't want to go through the PredicateBuilder learning curve if there is a simple solution that is escaping me.
I'd try this (For all given ids there is any (= at least one) feature that has this given id):
var establishments = context.Establishments
.Where(e => featureIds.All(fid => e.Features.Any(f => f.FeatureId == fid)))
.ToList();
(featureIds is an IEnumerable<int> with the Ids being searched for.)
Let's say I have two tables:
Report
Comment
And assuming I have a database context:
var reports = db.Reports();
How can I make sure all Comments for each report are loaded as well?
At this point I want to disconnect from the database but still
have access to the comments. (For example:)
reports[0].Comments[0].Subject
I'm assuming that there is an 1-M FK relationship between reports and comments (1 Report can have many Comments)?
One option is to use the DataLoadOptions.LoadWith method - something like the following:
var reports = db.Reports();
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Reports>(r => r.Comments); // Ask for Comments along with reports
reports.LoadOptions = dlo;
Now, every time you select a report on that data context, the comments will be fetched from the db along with it.
Just beware that ALL fields from comments will be selected - there is no way using this method to select a subset of fields.
Another option is to be specific about what you want to select in the Linq query, e.g.
var myReportsList = from report in db.Reports
select new { // Using anonymous type, but could use a custom class
Report = report,
Comment = report.Comment.Detail, // for example
Subject = report.Comment.Subject
};
To understand when the query gets run and the database connection closed, you will need to understand:
The deferred execution model of Linq and Linq To Sql (Basically, for Linq to SQL, the query only runs when the results are asked for e.g. by iterating over the collection or binding to a grid)
The difference between IQueryable and IEnumerable
Jon Skeets "C# in depth" gives a great overview of these, and i've also heard very good things about "Linq in Action" - plus there are plenty of blog posts about these concepts which do the subjects more justice than I can do here ;o)
Keep in mind that if you use LoadOptions to define a multi-hop path (Reports, comments, anotherentity), the 3rd and further hops are loaded (if related over 1:n relationships) by code which is very inefficient: they'll execute one query per parent. For reports-comments, it's ok, they'll be fetched in 2 queries.