Do sub-queries hit database multiple times if using Entity Framework - entity-framework-6

Might seem like a silly question but please read it... I am writing some services and there comes a situation where I have to use sub-query with Entity Framework. Now I have written a query with sub-query but the way I have written it, got me thinking that whether my query is hitting database two times?
Below is the pseudo code of actual query....
var data = databaseContext.FirstTable
.Where(x => x.Group_Id == (databaseContext.SecondTable
.Where(y => y.DomainName == "gmail.com")
.Select(x => x.Group_Id)
.FirstAndDefault()))
.ToList();
This query is giving my expected result but I think is hitting the database twice. Am I correct ?
Now I don't want a yes no answer but a short description and can I turn this query into a join one. And some tools or tips to check database hits in SQL Server like a tracer or something.

Yes it does. When you call first or default the Iqueryable will be send to the database. You can trick it by using transactions. Also it's recommended for what you are doing here to use transactions. Also for what you are trying to do i recommend using transitional properties of EF.
var data = databaseContext.FirstTable
.Include(x=>x.SecondTable)
.Where(x => x.SecondTable.DomainName == "gmail.com")
The SQL server profiler will show you all queries on the database. Not recommended to use it on anything else than local host of course.

Related

EF CORE 3.0 Cannot use multiple DbContext instances within a single query execution

After upgrading from .Net Core 2.2 to 3.0, the apps is throwing this err message.
Cannot use multiple DbContext instances within a single query execution
What is the workaround?
IQueryable<ApplicationUser> query;
var queryJoin = from ci in _courseInstructorRepository.Table
join uc in _userCourseRepository.Table on ci.CourseId equals uc.CourseId
select new { ci, uc };
if (userId > 0)
queryJoin = queryJoin.Where(x => x.ci.UserId == userId);
if (courseId > 0)
queryJoin = queryJoin.Where(x => x.uc.CourseId == courseId);
if (classId > 0)
queryJoin = queryJoin.Where(x => x.uc.CourseClassId == classId);
query = queryJoin.Select(x => x.uc.User).Distinct();
if (!string.IsNullOrEmpty(studentFirstChar))
query = query.Where(x => x.FirstName.StartsWith(studentFirstChar));
if (schoolId > 0)
query = query.Where(x => x.SchoolId == schoolId);
query = query.OrderBy(x => x.UserName);
return new PagedList<ApplicationUser>(query, pageIndex, pageSize);
You have a couple of design flaws in your code that EF core 2 swept under the carpet.
Your repositories don't share one context instance. EF core 2 couldn't create one SQL query from your code either, but it silently switched to client-side evaluation. That is, it just executed two SQL queries and joined them in memory. This must have been highly inefficient. One of the best design decisions in EF core 3 was to abandon automatic client-side evaluation, so now you're getting this error.
You don't use navigation properties. Using an ORM like EF, using manual joins should hardly ever be necessary. The Instructor class should have a navigation property like Courses, and Course a navigation property like Instructor.
Don't use this redundant repository layer anyway. As you're already experiencing in this small piece of code, it usually makes things harder than necessary without any added value.
One of your variables was created using another instance of DBContext, so when you try to use it as part of another DBContext's query it throws. The work around is to close the first DBContext and invoke DBContext.Attach(model) on the second.

Using navigation property in orderby clause of breeze query

I am using breeze to call a web api method which is:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => sa.Appeal).Include("Case");
My breeze query looks like:
var query = EntityQuery.from('GetEditShipmentAppeals')
.withParameters({ shipmentID: shipmentID, caseID: caseID })
.orderByDesc("Case.ID")
GetEditShipmentAppeals is a web api method that contains the first query. In spite of using .Include("Case") in the query I am not able to use "Case.ID" in the order by clause of breeze query.
var query = EntityQuery.from('Appeals')
.expand("Case,Patient")
.orderByDesc("Case.ID").inlineCount();
Even if I use navigation property on a breeze query that does not involve a EF query in web api, it does not work. In above query Case is a navigation property in Appeal table.
If I understand your example correctly, then I think that this is an Entity Framework issue. My understanding is that Entity Framework does not support "Includes" on a projection. See http://connect.microsoft.com/VisualStudio/feedback/details/347543/entity-framework-eager-loading-not-working-in-some-projection-scenarios.
To confirm this, I would try executing your EF query in isolation and see if the "Include" is actually doing anything. My guess is that it isnt.
However, you can still accomplish what you want with a slightly different server side projection. ( I'm not sure what object 'Case' is a property of and the syntax may be a bit off but...) Something like:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => new
{ Appeal: sa.Appeal, Case: sa.Appeal.Case, CaseId: sa.Appeal.Case.Id });
Note that Breeze will return a collection of 'anonymous' javascript objects from this query, but each of the 'entities' within each of these objects (i.e. Appeals and Cases) will be full Breeze entities and will be part of the EntityManager cache.

LINQ custom function in .where

can I, and if I can, how, write LINQ statement like this:
public IQueryable<Advert> SearchSimilarAdverst(string query)
{
Levenshtein compute = new Levenshtein();
return _db.Adverts.Where(a => a.IsActive &&
(compute.FindSimilarity(a.Name, query) <= 2));
}
Thanks
EDIT
I've tired solution Jeffery suggested and it worked but when I've tried this line of code I got EntityCommandExecutionException, does anybody know why ?
adverts.Where(a => a.WhoAmILookingForTags.Any
(t => compute.FindSimilarity(t.Name,query) <= 2));
Tags and Adverts are connected with many to many relation, and WhoAmILookingForTags is list of tags
EF will not be able to translate compute.FindSimilarity(a.Name, query) to SQL, so you're going to have to take the performance hit and do the following.
public IEnumerable<Advert> SearchSimilarAdverst(string query)
{
Levenshtein compute = new Levenshtein();
var adverts = _db.Adverts.Where(a => a.IsActive).ToList();
return adverts.Where(a => compute.FindSimilarity(a.Name, query) <= 2));
}
Note the return type of the method also needed to be changed to reflect the return type
As commented below, the query should be forced to run before filtering using FindSimilarity because of delayed execution. query.ToList() is one of many options.
In short, no - because EF needs to be able to convert your Linq predicate into T-SQL (or whatever dialect of SQL your RDBMS uses). Only a subset of .NET BCL functions are supported (such as String.Contains and custom user code is right-out).
For complicated predicates I recommend writing your own SQL by hand - you'll also get considerably better performance, EF can be slow at generating SQL.
I hope I'm wrong on this, but I do not believe that you would be allowed to that in a Linq-to-Entities query. In those kinds of LINQ queries it has to translate everything in the Where function into a SQL statement to be run on the underlying database. Since your method is not something that is on the SQL side of things, it will cause an exception when you try this.
For a regular LINQ query (i.e. after everything has already been put into memory), there should be no problem with accomplishing this.
You could also try it and see if it does or does not work...

Error with multiple open data readers for child entity

I have a ef 4 model and I'm using self tracking entities. In this model there is an entity called Organisation. Each Organisation can have many Locations (addresses). If I try to select a single location (so I can delete it), thus:
var location = _container.Locations.FirstOrDefault(l => l.Id == id);
I get an erorr that there is already a data reader open so I cant open another. If I do the following:
var location = _container.Locations.Include("Organisation").FirstOrDefault(l => l.Id == id);
Then it all works just fine.
Using Intellitrace I can see that with the failed query it executes an ADO command to get just the location then does another command to get the location and the organisation.
Is this a bug or something I need to do differently for selftracking entities?
No it is not a bug. This usually happens if you iterate result of one query and in this iteration you execute another query (it can also happen due to lazy loading but lazy loading is not supported with self tracked entities). The simplest solution is simply allowing multiple active data readers (your database must support it). In case of SQL Server 2005 and newer you can simply add to your connection string this part: MultipleActiveResultSets=true;

EF4 - Self tracking entities and inheritance and eager loading

I know this has been asked before in several ways but none of the answers seem applicable to me - or correct - or current, so I'll try again.
I have a large model with several intstances of inherited entities. One example is a Timetable that contains a collection of TimetableEvents. There are several sub-types of TimetableEvent, such as an InterviewTimetableEvent, BreakTimetableEvent and an ExercisetimeTableEvent. ExerciseTimetableEvent has a relationship to an Exercise entity.
I need to use self-tracking entities as I'm using a WCF back end to serve up data to several WPF clients in a stateless fashion.
So, I need to eager load everything and I thought that self-tracking entities would automatically do this but it appears they dont.
So, to get a timetable I need to do something like this:
var tt = (from s in ESSDataContainer.Timetables
.Include("TimetableEvents")
where s.TimetableId == timetableid
select s).FirstOrDefault();
This will give me the TimetableEvents but not the Exercises that are related to the ExerciseTimetableEvents. Ive tried the following (and several other suggestions) without luck:
var tt = (from s in ESSDataContainer.Timetables
.Include("TimetableEvents")
.Include("ExerciseTimetableEvents.Exercise")
where s.TimetableId == timetableid
select s).FirstOrDefault();
Is there a solution to this?
If not I'll go back to normal context tracking and connect to the database from a local container rather than using WCF.
Cheers
It's a bit tricky, but possible:
var tt = (from s in ESSDataContainer.Timetables
where s.TimetableId == timetableid
select new
{
TimeTable = s,
Events = s.TimeTableEvents,
Exercise = s.TimeTableEvents.OfType<ExerciseTimetableEvents>()
.Select(ett => ett.Exercise)
}).Select(s => s.TimeTable)
.AsEnumerable()
.FirstOrDefault();
Clear as mud, but, hey: No magic strings! Also, it has the advantage that it actually works....
There is a Proposal for this issua at Microsoft Connect:. If you think this worthy you can vote for it.

Resources