I see several StackOverflow questions on this already but not of them seem to match my scenario. I promise I looked.
I have some queries against my database that I'm using linq to do and I can't figure out why the incorrect SQL is being generated. This is happening in several places in my code. I'm hoping we're just falling into some well known gotcha but I can't wrap my head around why Linq seemingly decides my where clause is dumb and shouldn't add it to the generated SQL query.
Why is this?
Example:
var testing = (from i in context.TableName1 where i.Param1 == object1.GuidParam select i).ToList();
The above query returns the following SQL
{SELECT
[Extent1].[RecordId] AS [RecordId],
[Extent1].[AnotherId] AS [AnotherId],
[Extent1].[YetAnotherId] AS [YetAnotherId],
[Extent1].[WeLikeIds] AS [WeLikeIds],
[Extent1].[WeReallyLikeIds] AS [WeReallyLikeIds]
FROM [dbo].[SomeTable] AS [Extent1]}
However the following query:
var testing = (from i in context.TableName1 where i.Param1 == object1.GuidParam select i);
var testingToList = testing.ToList();
Generates the following correct SQL
{SELECT
[Extent1].[RecordId] AS [RecordId],
[Extent1].[AnotherId] AS [AnotherId],
[Extent1].[YetAnotherId] AS [YetAnotherId],
[Extent1].[WeLikeIds] AS [WeLikeIds],
[Extent1].[WeReallyLikeIds] AS [WeReallyLikeIds]
FROM [dbo].[SomeTable] AS [Extent1]
WHERE [Extent1].[RecordId] = '78e49f5c-0ff8-e311-93f4-00155d514a6d'}
I prefer the lambda notation, and I don't see why this wouldn't work...
var testing = context.TableName1.Where(i => i.Param1 == object1.GuidParam).ToList();
Cleaner, concise and it should work.
Related
I moved from Linq 2 SQL to Linq 2 Entities due to performance reasons for complex queries.
It's a little bit frustrating to get everything working.
In L2SQL I can run subqueries without any problems.
Now I figured out, that in L2E I have to place the subquery outside the query.
That's usually no problem, but how can I run a query if the subquery depends on the query?
For example:
I want to get the status of an document but for that I need the current documentID.
var query = from Dok in dbContext.Dokumente
orderby Dok.DokumentID descending
select new ClassDok() {
_Nr = Dok.Dokumentnr,
_StatusID = dbContext.Database.SqlQuery<int>("GetDokStatusID", Dok.DokumentID).Single()
};
I already tried to put
dbContext.Database.SqlQuery<int>("GetDokStatusID", Dok.DokumentID).Single()
into a method and return integer, but it doesn't work.
I guess it should be really simple, but i cannot find how to do it.
I have a linq query, that selects one column, of type int, and i need it sorted.
var values = (from p in context.Products
where p.LockedSince == null
select Convert.ToInt32(p.SearchColumn3)).Distinct();
values = values.OrderBy(x => x);
SearchColumn3 is op type string, but i only contains integers. So i thought, converting to Int32 and ordering would definitely give me a nice 1,2,3 sorted list of values. But instead, the list stays ordered like it were strings.
199 20 201
Update:
I've done some tests with C# code and LinqPad.
LinqPad generates the following SQL:
SELECT [t2].[value]
FROM (
SELECT DISTINCT [t1].[value]
FROM (
SELECT CONVERT(Int,[t0].[SearchColumn3]) AS [value], [t0].[LockedSince], [t0].[SearchColumn3]
FROM [Product] AS [t0]
) AS [t1]
WHERE ([t1].[LockedSince] IS NULL)
) AS [t2]
ORDER BY [t2].[value]
And my SQL profiler says that my C# code generates this piece of SQL:
SELECT DISTINCT a.[SearchColumn3] AS COL1
FROM [Product] a
WHERE a.[LockedSince] IS NULL
ORDER BY a.[SearchColumn3]
So it look like C# Linq code just omits the Convert.ToInt32.
Can anyone say something useful about this?
[Disclaimer - I work at Telerik]
You can solve this problem with Telerik OpenAccess ORM too. Here is what i would suggest in this case.
var values = (from p in context.Products
where p.LockedSince == null
orderby "cast({0} as integer)".SQL<int>(p.SearchColumn3)
select "cast({0} as integer)".SQL<int>(p.SearchColumn3)).ToList().Distinct();
OpenAccess provides the SQL extension method, which gives you the ability to add some specific sql code to the generated sql statement.
We have started working on improving this behavior.
Thank you for pointing this out.
Regards
Ralph
Same answer as one my other questions, it turns out that the Linq provider i'm using, the one that comes with Telerik OpenAccess ORM does things different than the standard Linq to SQL provider! See the SQL i've posted in my opening post! I totally wasn't expecting something like this, but i seem that the Telerik OpenAccess thing still needs a lot of improvement. So be careful before you start using it. It looks nice, but it has some serious shortcomings.
I can't replicate this problem. But just make sure you're enumerating the collection when you inspect it. How are you checking the result?
values = values.OrderBy(x => x);
foreach (var v in values)
{
Console.WriteLine(v.ToString());
}
Remember, this won't change the order of the records in the database or anywhere else - only the order that you can retrieve them from the values enumeration.
Because your values variable is a result of a Linq expression, so that it doest not really have values until you calling a method such as ToList, ToArray, etc.
Get back to your example, the variable x in OrderBy method, will be treated as p.SearchColumn3 and therefore, it's a string.
To avoid that, you need to let p.SearchColumn3 become integer before OrderBy method.
You should add a let statement in to your code as below:
var values = (from p in context.Products
where p.LockedSince == null
let val = Convert.ToInt32(p.SearchColumn3)
select val).Distinct();
values = values.OrderBy(x => x);
In addition, you can combine order by statement with the first, it will be fine.
I am trying to do a join with a sub query and can't seem to get it. Here is what is looks like working in sql. How do I get to to work in linq?
SELECT po.*, p.PermissionID
FROM PermissibleObjects po
INNER JOIN PermissibleObjects_Permissions po_p ON (po.PermissibleObjectID = po_p.PermissibleObjectID)
INNER JOIN Permissions p ON (po_p.PermissionID = p.PermissionID)
LEFT OUTER JOIN
(
SELECT u_po.PermissionID, u_po.PermissibleObjectID
FROM Users_PermissibleObjects u_po
WHERE u_po.UserID = '2F160457-7355-4B59-861F-9871A45FD166'
) used ON (p.PermissionID = used.PermissionID AND po.PermissibleObjectID = used.PermissibleObjectID)
WHERE used.PermissionID is null
Without seeing your database and data model, it's pretty impossible to offer any real help. But, probably the best way to go is:
download linqpad - http://www.linqpad.net/
create a connection to your database
start with the innermost piece - the subquery with the "where" clause
get each small query working, then join them up. Linqpad will show you the generated SQL, as well as the results, so build your small queries up until they are right
So, basically, split your problem up into smaller pieces. Linqpad is fantastic as it lets you test these things out, and check your results as you go
hope this helps, good luck
Toby
The LINQ translation for your query is suprisingly simple:
from pop in PermissibleObjectPermissions
where !pop.UserPermissibleObjects.Any (
upo => upo.UserID == new Guid ("2F160457-7355-4B59-861F-9871A45FD166"))
select new { pop.PermissibleObject, pop.PermissionID }
In words: "From all object permissions, retrieve those with at least one user-permission whose UserID is 2F160457-7355-4B59-861F-9871A45FD16".
You'll notice that this query uses association properties for navigating relationships - this avoids the need for "joining" and simplfies the query. As a result, the LINQ query is much closer to its description in English than the original SQL query.
The trick, when writing LINQ queries, is to get out of the habit of "transliterating" SQL into LINQ.
I have some linq that returns the correct data.
var numEmails = (from row in EmailBatchProposal
where row.EmailBatchId == emailBatchId
select row.EmailBatchProposalId).Count();
However, if I understand linq correctly, this does not perform optimally. It grabs all the data and then walks through the list and counts the rows. What I'd really like is for linq (in the background) to use like:
Select count(*) from ...
I trust the performance reasons are obvious.
Does anyone know the proper way to do this?
Actually, if the linq query is used with a collection that implements IQueryable and supports translation into underlying SQL variant, it is quite a basic functionality to translate the Count function from your example correctly.
People generally learn best by practicing. I would suggest you get a copy of LinqPad (free), enter in your Linq query, and see what SQL it generates. Then you can modify the Linq query until you get exactly what you want.
Actually, the LINQ-to-SQL is smart enough to know that it should do a count... For example, if I have the following query:
var v = (from u in TblUsers
select u).Count();
the SQL that actually executes when I run this is:
SELECT COUNT(*) AS [value]
FROM [tblUsers] AS [t0]
Amazing, huh? Another poster made the really good suggestion of getting LinqPad - it is a WONDERFUL tool. It will show you the exact SQL that gets executed. You can always confirm with SQL profiler too.
Check the Log of the SQL used in the query.
using (var dbc = new siteDataContext())
{
dbc.Log = Console.Out;
var bd = (from b in dbc.birthdays
select b).Count();
Console.WriteLine("{0}", bd);
}
gives the query:
SELECT COUNT(*) AS [value]
FROM [dbo].[birthdays] AS [t0]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1
You can just use the argument of .Count().
int numEmails = EmailBatchProposal.Count(x => x.EmailBatchId == emailBatchId)
As noted below, this doesn't actually resolve to any different SQL, but I think it's at least a cleaner looking alternative.
The following (cut down) code excerpt is a Linq-To-Entities query that results in SQL (via ToTraceString) that is much slower than a hand crafted query. Am I doing anything stupid, or is Linq-to-Entities just bad at optimizing queries?
I have a ToList() at the end of the query as I need to execute it before using it to build an XML data structure (which was a whole other pain).
var result = (from mainEntity in entities.Main
where (mainEntity.Date >= today) && (mainEntity.Date <= tomorrow) && (!mainEntity.IsEnabled)
select new
{
Id = mainEntity.Id,
Sub =
from subEntity in mainEntity.Sub
select
{
Id = subEntity.Id,
FirstResults =
from firstResultEntity in subEntity.FirstResult
select new
{
Value = firstResultEntity.Value,
},
SecondResults =
from secondResultEntity in subEntity.SecondResult
select
{
Value = secondResultEntity.Value,
},
SubSub =
from subSubEntity in entities.SubSub
where (subEntity.Id == subSubEntity.MainId) && (subEntity.Id == subSubEntity.SubId)
select
new
{
Name = (from name in entities.Name
where subSubEntity.NameId == name.Id
select name.Name).FirstOrDefault()
}
}
}).ToList();
While working on this, I've also has some real problems with Dates. When I just tried to include returned dates in my data structure, I got internal error "1005".
Just as a general observation and not based on any practical experience with Linq-To-Entities (yet): having four nested subqueries inside a single query doesn't look like it's awfully efficient and speedy to begin with.
I think your very broad statement about the (lack of) quality of the SQL generated by Linq-to-Entities is not warranted - and you don't really back it up by much evidence, either.
Several well respected folks including Rico Mariani (MS Performance guru) and Julie Lerman (author of "Programming EF") have been showing in various tests that in general and overall, the Linq-to-SQL and Linq-to-Entities "engines" aren't really all that bad - they achieve overall at least 80-95% of the possible peak performance. Not every .NET app dev can achieve this :-)
Is there any way for you to rewrite that query or change the way you retrieve the bits and pieces that make up its contents?
Marc
Have you tried not materializing the result immediately by calling .ToList()? I'm not sure it will make a difference, but you might see improved performance if you iterate over the result instead of calling .ToList() ...
foreach( var r in result )
{
// build your XML
}
Also, you could try breaking up the one huge query into separate queries and then iterating over the results. Sending everything in one big gulp might be the issue.