Performance: LinqPad and Entity Framework generate different SQL for same Linq request - entity-framework

I think I understand from LinqPad doc that it uses a different linq to tsql translator than Entity Framework. LinqPad's is actually more efficient in at least one case! Here's the details:
LinqPad generates the following simple sql query:
SELECT [t0].[personId]
FROM [Person] AS [t0]
WHERE (NOT (([t0].[deleted]) = 1)) AND ([t0].[masterPersonId] = #p0)
ORDER BY [t0].[personId] DESC
For this C# linq code:
int? state = null;
string realmId = null;
int? reviewerId = null;
bool? deleted = false;
long? parentPersonId = 1275660779659;
var query = from person in Persons
where
(!deleted.HasValue || person.Deleted == deleted) &&
(!state.HasValue || person.personState == state) &&
(!parentPersonId.HasValue || person.masterPersonId == parentPersonId) &&
(realmId == null || person.realmId == realmId) &&
(reviewerId == null ||(person.reviewerId == reviewerId ))
orderby person.personId descending
select person.personId;
So you can see LinqPad translates the linq statement above and removes extraneous sql when a parameter value is null. Nice!
EF however always generates this regardless of null parameters:
SELECT
[Extent1].[personId] AS [personId]
FROM [dbo].[Person] AS [Extent1]
WHERE (#p__linq__0 IS NULL OR [Extent1].[deleted] = #p__linq__1) AND
(#p__linq__2 IS NULL OR [Extent1].[personState] = #p__linq__3) AND
(#p__linq__4 IS NULL OR [Extent1].[masterPersonId] = #p__linq__5) AND
(#p__linq__6 IS NULL OR [Extent1].[realmId] = #p__linq__7) AND
((#p__linq__8 IS NULL) OR ([Extent1].[reviewerId] = #p__linq__9))
It makes for a slower query. We were hoping to use LinqPad to evaluate generated sql for EF, but obviously not if the results will be different. It looks like we can target a custom assembly for an EF connection in LinqPad. I'll play around with that to see if we can at least bring the sql queries together.
Anyone ever travel this road or know of an EF setting we can take advantage of? We're running EF4.
Thanks in advance.

I found this excellent webcast from the author of LinqPad where he mentions this very issue. http://oreilly.com/pub/e/1295
I was using Linq To SQL translation not EF's.

Related

How to write subquery in select list in EF Core?

Select *,
(Select DefaultStartDay from Scheduler.ProgramSettings ps where ps.DefaultStartDay = s.Id ) [DefaultStartDay]
from Scheduler.Schedules s
where ScheduleType = 2;
I want to write above SQL query in EF Core, Specially I need subquery in select list to get data from another table with specific condition.
please refer image.Sample Data with SQL Query
I have tried below EF Core but getting wrong result.
var model = _context.Schedules
.Where(s => s.ScheduleType == 2)
.Select(rv => new ProgramSetting
{
Id = rv.Id,
ProgramTemplateId = rv.ProgramTemplateId,
IsActive = rv.IsActive,
DefaultStartDay = rv.Id
}).ToArray();
The SQL query is wrong and this is a misuse of EF Core.
First, that SQL will fail if there's more than 1 result from the subquery. Even in SQL you'd need a different query. An INNER JOIN would return the same results without failing if there are multiple matches.
Select s.*,ps.DefaultStartDay
from Scheduler.Schedules s
inner join Scheduler.ProgramSettings ps on ps.DefaultStartDay = s.Id
where ScheduleType = 2;
Second, using LINQ to emulate SQL is a misuse of both EF Core and LINQ. EF isn't a replacement for SQL, it's an ORM. Its job is to give the impression of working with in-memory objects instead of tables, not allow you to write SQL queries in C#
It's the ORM's job to generate JOINs as needed from the relations between entities (not tables). In this case, if Schedule has a ProgramSettins property, EF would generate the necessary joins automatically. Loading an entire schedule object could be as simple as :
var schedules=_context.Schedules
.Incule(sch=>sch.ProgramSettings)
.Where(s => s.ScheduleType == 2)
.ToArray();
Include is used to eagerly load the settings, not to force a JOIN.
If a Select clause is used that requires a property from ProgramSettings, the JOIN will be generated automatically, eg :
var namesAndDays=_context.Schedules
.Where(s => s.ScheduleType == 2)
.Select(s=>new {
Name = s.Name,
StartDay = s.ProgramSettings.DefaultStartDay
})
.ToArray();

Not getting values from linq query in aspnetzero framework

I am executing following query. I am doing this in aspnetzero framework and doing this in UserAppService.cs file.
var rt = _dbContextProvider.GetDbContext().Users
.Where(e => e.Id == 11)
.FirstOrDefault();
But this is always returning null, in database there is record with Id = 11.
So anyone has any idea what is wrong in this.
Thanks
As Svyatoslav Danyliv said in the comment, the issue is related to the query filters, try to add the .IgnoreQueryFilters() to ignore the query filters.

GroupJoin: exception thrown: System.InvalidOperationException

I'm trying to write a query to get all restaurant tables if exists or not a opened sale on it.
if a sale exists on a table I want to get the sum and couple details.that is my code:
db.SALETABLES
.GroupJoin(
db.SALES.Where(c => c.CLOSEDTIME == null),
t => t.ID,
sa => sa.ID_TABLE,
(ta, s) => new
{
ta.ID,
ta.DESCRIPTION,
NR_SALE = s.Any() ? s.First().NR_SALE : 0,
IDSALE = s.Any() ? s.First().ID : 0,
IDUSER = s.Any() ? s.First().IDUSER : 0,
USERNAME = s.Any() ? s.First().USERS.USERNAME :"" ,
SALESUM = s.Any() ? s.First().SALES_DETAIL.Sum(p => p.PRICE * p.CANT) : 0
}
but got this error:
Exception thrown: 'System.InvalidOperationException' in
System.Private.CoreLib.dll
thanks for any help
You don't specify the exception, but I assume it's about client-side evaluation (CSE), and you configured EF to throw an exception when it occurs.
It may be First() that triggers CSE, or GroupJoin. The former can easily be fixed by using FirstOrDefault(). The GroupJoin has more to it.
In many cases it isn't necessary to use GroupJoin at all, of Join, for that matter. Usually, manually coded joins can and should be replaced by navigation properties. That doesn't only make the code better readable, but also avoids a couple of issues EF 2.x has with GroupJoin.
Your SaleTable class (I'm not gonna follow your database-driven names) should have a property Sales:
public ICollection<Sale> Sales { get; set; }
And if you like, Sale could have the inverse navigation property:
public SaleTable SaleTable { get; set; }
Configured as
modelBuilder.Entity<SaleTable>()
.HasMany(e => e.Sales)
.WithOne(e => e.SaleTable)
.HasForeignKey(e => e.SaleTableId) // map this to ID_TABLE
.IsRequired();
Now using a table's Sales property will have the same effect as GroupJoin —a unique key, here a SaleTable, with an owned collection— but without the issues.
The next improvement is to simplify the query. In two ways. 1. You repeatedly access the first Sale, so use the let statement. 2. The query is translated into SQL, so don't worry about null references, but do prepare for null values. The improved query will clarify what I mean.
var query = from st in db.SaleTables
let firstSale = st.Sales.FirstOrDefault()
select new
{
st.ID,
NrSale = (int?)firstSale.NrSale ?? 0,
IdSale = (int?)firstSale.ID ?? 0,
...
SalesSum = (int?)firstSale.SalesDetails.Sum(p => p.Price * p.Cant) ?? 0
}
Using NrSale = firstSale.NrSale, would throw an exception for SaleTables without Sales (Nullable object must have a value).
Since the exception is by the EF Core infrastructure, apparently you are hitting current EF Core implementation bug.
But you can help EF Core query translator (thus avoiding their bugs caused by missing use cases) by following some rules when writing your LINQ to Entities queries. These rules will also eliminate in most of the cases the client evaluation of the query (or exception in EF Core 3.0+).
One of the rules which is the origin of issues with this specific query is - never use First. The LINQ to Objects behavior of First is to throw exception if the set is empty. This is not natural for SQL which naturally supports and returns NULL even for values which normally do not allow NULL. In order to emulate the LINQ to Objects behavior, EF Core has to evaluate First() client side, which is not good even if it works. Instead, use FirstOrDefault() which has the same semantics as SQL, hence is translated.
To recap, use FirstOrDefault() when you need the result to be a single "object" or null, or Take(1) when you want the result to be a set with 0 or one elements.
In this particular case, it's better to incorporate the 0 or 1 related SALE rule directly into the join subquery, by removing the GroupJoin and replacing it with SelectMany with correlated Where. And the Any() checks are replaced with != null checks.
With that said, the modified working and fully server translated query looks like this:
var query = db.SALETABLES
.SelectMany(ta => db.SALES
.Where(s => ta.ID == s.ID_TABLE && s.CLOSEDTIME == null).Take(1), // <--
(ta, s) => new
{
ta.ID,
ta.DESCRIPTION,
NR_SALE = s != null ? s.NR_SALE : 0,
IDSALE = s != null ? s.ID : 0,
IDUSER = s != null ? s.IDUSER : 0,
USERNAME = s != null ? s.USERS.USERNAME : "",
SALESUM = s != null ? s.SALES_DETAIL.Sum(p => p.PRICE * p.CANT) : 0
});

Entity Framework DB select with Skip and Take fetches the entire table

I have the following EF code
Func<CxForumArticle, bool> whereClause = a => a.CreatedBy == authorId;
IEnumerable<CxForumArticle> articlesCol = ctx.Articles
.Where(whereClause)
.Where(a => a.PublishingStatus == EnPublishStatus.PUBLISHED)
.OrderByDescending(a => a.ModifiedOn).Skip(offset).Take(pageSize);
It produces the following SQL
SELECT
[Extent1].[ArticleId] AS [ArticleId],
[Extent1].[Alias] AS [Alias],
[Extent1].[MigratedId] AS [MigratedId],
[Extent1].[Title] AS [Title],
[Extent1].[Teaser] AS [Teaser],
[Extent1].[ClobId] AS [ClobId],
[Extent1].[UnifiedContentId] AS [UnifiedContentId],
[Extent1].[EditorComments] AS [EditorComments],
[Extent1].[CreatedOn] AS [CreatedOn],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[ModifiedOn] AS [ModifiedOn],
[Extent1].[ModifiedBy] AS [ModifiedBy],
[Extent1].[PublishingStatus] AS [PublishingStatus]
FROM [dbo].[ForumArticle] AS [Extent1]
As you see, there is no ordering and paging in this SQL. So EF orders and pages data in memory.
This doesn't seem to be a good thing to do.
I read an article, claiming that I have to use expression in OrderBy clause. I did that
Func<CxForumArticle, bool> whereClause = a => a.CreatedBy == authorId;
Expression<Func<CxForumArticle, DateTime>> orderByFunc = a => a.ModifiedOn;
IEnumerable<CxForumArticle> articlesCol = ctx.Articles
.Where(whereClause)
.Where(a => a.PublishingStatus == EnPublishStatus.PUBLISHED)
.OrderByDescending(orderByFunc.Compile()).Skip(offset).Take(pageSize)
;
But I got the same result. Any ideas how can I force EF to sort and page the data in DB?
I know this is pretty old, but you actually need to have the whereClause use an expression as well. Entity Framework only knows how to convert expressions to SQL. If you use a delegate instead of an expression, it is going to query the entire table and then then filter using LINQ to Objects.

Why null and reference to null not the same thing

Why these two methods work differently:
public List<Foo> GetFoos()
{
int? parentId = null;
var l = _dataContext.Foos.Where(x => x.ParentElementId == parentId).ToList();
return l;
}
public List<Foo> GetFoos()
{
var l = _dataContext.Foos.Where(x => x.ParentElementId == null).ToList();
return l;
}
The first one returns nothing. Second returns what was expected. Data comes from EF. ParentElementId is nullable.
That is because you can't compare to null in SQL, it has the special IS NULL operator to check for null values.
The first query will be translated into a comparison, where the parameter is null:
WHERE ParentElementId = #param
This doesn't work, because comparing two null values doesn't yield true.
The second query will be translated into a null check, because the null value is a constant:
WHERE ParentElementId IS NULL
This works because EF is not fooled to translate it into a comparison.
I know, you got your answer but here is some additional insight:
This issue has been discussed on MSDN forums. Some people believe it's a bug, others say this is intentional behaviour due to performance reasons
It's always helps running EFProf or Sql Server Profiler (in case you are working with SQL Server. For example your two examples translate into two following statements respectively:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[ParentElementId] AS [ParentElementId]
FROM [dbo].[Foo] AS [Extent1]
WHERE [Extent1].[ParentElementId] = NULL
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[ParentElementId] AS [ParentElementId]
FROM [dbo].[Foo] AS [Extent1]
WHERE [Extent1].[ParentElementId] IS NULL
This technique (looking at generated SQL) is often very useful when dealing with problems in EF.
Captain Obvious: because parentId is not null, probably.
Response to edit: first one is not compilable. Type cannot be infered for null.
Response to another edit: Because EF query translates nullable types incorrectly probably