LINQ understading Non-Equijoins - c#

I use asp.net 4, ef 4 and c#, LINQ and Non-Equijoins.
Here below I wrote two examples of Non-Equijoins.
Both are working fine in my model.
Because I'm pretty new to Linq, I would like ask you:
Which syntax typology would you advice me to adopt in my code?
Which code performance faster?
Thanks for your help:
Here some useful links:
http://msdn.microsoft.com/en-us/library/bb882533.aspx
http://msdn.microsoft.com/en-us/library/bb311040.aspx
http://msdn.microsoft.com/en-us/library/bb310804.aspx
// Query sintax
var queryContents =
from cnt in context.CmsContents
let cntA =
from a in context.CmsContentsAssignedToes
select a.CmsContent.ContentId
where cntA.Contains(cnt.ContentId) == false
select cnt;
// Query method
var queryContents02 =
from c in context.CmsContents
where !(
from a in context.CmsContentsAssignedToes
select a.ContentId).Contains(c.ContentId)
select c;

I'd prompt for a third option:
var validContentIds = from a in context.CmsContentsAssignedToes
select a.ContentId;
var queryContents = from cnt in context.CmsContents
where !validContentIds.Contains(cnt.ContentId)
select cnt;
Or alternatively (and equivalently):
var validIds = context.CmsContentsAssignedToes.Select(a => a.ContentId);
var queryContents = context.CmsContents
.Where(cnt => !validIds.Contains(cnt.ContentId));
I wouldn't expect the performance to be impacted - I'd expect all of these to end up with the same SQL.

I like the first query syntax (it is better readable for me but this part of question is subjective) and I think the perforamance will be the same because queries are actually the same. let keyword just stores subexpression to variable but generated SQL query should be the "same".

Related

Linq IN Clause in Where

I want to know how to use IN clause in Linq. Here is my Code :-
int empCount = ctx.tblEmpTran
.Count(
e => e.Id == Id &&
e.Month == selectedMonth &&
e.Year == selectedYear &&
e.employeeId.contains()
);
The Following query is supposed to be in IN
SELECT A.Id FROM dbo.EmployeeDetail A WHERE A.CompanyId = 1 AND A.COLS > 0
In the above code, contains method do not popup in intellisense.
This is because you are trying to convert from SQL to Linq, and you couldn't try a worse approach.
You should try to write your LINQ query starting from what you need it to do, forgetting SQL altogether.
In Linq there is no "IN" operator, to achieve the same thing you take your collection and check if it Contains the value.
So in your scenario you should simply generate your collection of valid values and then in your query do:
myCollection.Contains(e.employeeId)
It is "collection CONTAINS value" the logic, not "value is found IN collection". Again if you insist to start from SQL when using Linq you will always incur in this kind of problems.
Check Albahari tutorial on how to approach Linq correctly and your productivity will skyrocket.
You should create a collection of employee IDs that you want to check, and the code would be
employees.contains(e.employeeId)
Instead of this e.employeeId.contains(), you should use this:
listOfIds.Contains(e.employeeId)
where listOfIds would be a list of int, List<int> and would contain the ids you would place between the parentheses after IN(...).
considering that you have a tblEmployeeDetail in the same DbSet and them having a relation through employeeId you can write you query like.
var q = from e in ctx.tblEmployeeDetail where e.Transactions.Any(t => t.Month == selectedMonth &&
t.Year == selectedYear);
int empCount = q.Count();
this is pseudo-code but this is how you would use the LINQ effectively, (Exists is better than In check)

LinqToSql - SQL generated by CONCAT (UNION)

Simply put, is there any way to coerce LinqToSql into generating chained concats without nesting the UNION ALL statements?
Example:
a.Concat(b).Concat(c)
results in something semantically similar to:
SELECT * FROM (
SELECT * FROM A
UNION ALL
SELECT * FROM B
)
UNION ALL
SELECT * FROM C
It would be much more readable/preferable if I could convince it to do:
SELECT * FROM A
UNION ALL
SELECT * FROM B
UNION ALL
SELECT * FROM C
I understand why it does it (and I'm not even sure these two things are exactly the same semantically) but is there any way to make this happen? It would make a bunch of our generated queries significantly easier to read and debug.
The SQL generated from LINQ is not designed to be readable, so I don't think there is a way to do that. It would be nice, I agree. This is one reason I am intrigued by the concept of micro-ORMs like Dapper, so I can just write my own SQL.
I had issues concatenating a large autogenerated set of iqueryables, as the nesting behavior of the generated sql query simply got too deep for sql to handle. I solved it by concatenating the items in a binare-tree-like fashion like so:
public static IQueryable<T> BinaryConcatenation<T>(this IEnumerable<IQueryable<T>> queries)
{
var count = queries.Count();
var firsthalf = queries.Take(count / 2).ToArray();
var secondhalf = queries.Skip(count / 2).ToArray();
if (firsthalf.Length == 0 || secondhalf.Length == 0) return queries.Aggregate((src, next) => src.Concat(next));
var first = BinaryConcatenation(firsthalf);
var second = BinaryConcatenation(secondhalf);
return first.Concat(second);
}
This way the generated SQL nesting depth becomes drastically lower (log(n)?).

A C# Linq to Sql query that uses SUM, Case When, Group by, outer join, aggregate and defaults

I've been searching for possible solutions and attempting this for several hours without luck. Any help will be greatly appreciated.
I've got a Sql statement which I'm trying to put together as a C# LINQ query.
Here is the working SQL:
SELECT up.UserProfileID
,up.FirstName
,up.LastName
,SUM(CASE WHEN ul.CompletionDate IS NULL THEN 0
ELSE ISNULL(ul.Score, 0)
END) AS TotalScore
FROM dbo.UserProfile up
LEFT OUTER JOIN dbo.UserLearning ul ON up.UserProfileID = ul.UserProfileID
WHERE up.ManagerUserProfileID IS NULL
GROUP BY up.UserProfileID, up.FirstName, up.LastName
I've tried several different ways but seem to end up with either a statement that doesn't return what I want or doesn't execute successfully
My current (non-working) code looks something like this:
var pd = from up in db.UserProfiles
join ul in db.UserLearnings on up.UserProfileID equals ul.UserProfileID into temp
from upJOINul in temp.DefaultIfEmpty(new UserLearning() { Score = 0 })
where up.ManagerUserProfileID.Equals(null)
group up by new
{
UserProfileID = up.UserProfileID,
FirstName = up.FirstName,
LastName = up.LastName,
TotalScore = up.UserLearnings.Sum(u => u.Score)
};
Thank you for any help
After several more attempts and further use of google I finally managed to get a working solution. I hope it'll be of use to someone else.
var pd = db.UserProfiles.AsEnumerable()
.Where(up => up.ManagerUserProfileID.Equals(null))
.Select(up => new
{
UserProfileID = up.UserProfileID,
FirstName = up.FirstName,
LastName = up.LastName,
TotalScore = up.UserLearnings
.Where(ul => ul.CompletionDate.HasValue && ul.Score.HasValue)
.DefaultIfEmpty()
.Sum(ul => ul != null && ul.Score.HasValue ? ul.Score : 0)
});
Not what you asked for, but if you have a working complex SQL query, that is fairly static, put it in a stored proc, and drag that SP to your LINQ DataContext.
The LINQ provider has to compile your query to sql every time it's called, and that takes time, and server CPU cycles. If it's a complex query, it can eat up significant resources. Also may miss some optimizations you can do with straight SQL.
Unless of course there is a purpose to it.
If you have ORM problem, grap the actual SQL commands, take a look at it, and compare with what you want to achieve. Can you show the generated SQL as well, so we can find the difference easier?

How to force LINQ to SQL to evaluate the whole query in the database?

I have a query which is fully translatable to SQL. For unknown reasons LINQ decides the last Select() to execute in .NET (not in the database), which causes to run a lot of additional SQL queries (per each item) against database.
Actually, I found a 'strange' way to force the full translation to SQL:
I have a query (this is a really simplified version, which still does not work as expected):
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
Translation = Translations.Select(t => new
{
Name = t.Name,
// ...
}).FirstOrDefault()
})
It will generates a lot of SQL queries:
SELECT [t0].[CatalogPlacementId] AS [PlacementId]
FROM [dbo].[MainCategories] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
...
However, if I append another Select() which just copies all members:
.Select(e => new
{
PlacementId = e.PlacementId,
Translation = new
{
Name = e.Translation.Name,
// ...
}
})
It will compile it into a single SQL statement:
SELECT [t0].[CatalogPlacementId] AS [PlacementId], (
SELECT [t2].[Name]
FROM (
SELECT TOP (1) [t1].[Name]
FROM [dbo].[Translations] AS [t1]
) AS [t2]
) AS [Name]
FROM [dbo].[MainCategories] AS [t0]
Any clues why? How to force the LINQ to SQL to generate a single query more generically (without the second copying Select())?
NOTE: I've updated to query to make it really simple.
PS: Only, idea I get is to post-process/transform queries with similar patterns (to add the another Select()).
When you call SingleOrDefault in MyQuery, you are executing the query at that point which is loading the results into the client.
SingleOrDefault returns IEnumerable<T> which is no longer an IQueryable<T>. You have coerced it at this point which will do all further processing on the client - it can no longer perform SQL composition.
Not entirely sure what is going on, but I find the way you wrote this query pretty 'strange'. I would write it like this, and suspect this will work:
var q = from e in MainCategories
let t = Translations.Where(t => t.Name == "MainCategory"
&& t.RowKey == e.Id
&& t.Language.Code == "en-US").SingleOrDefault()
select new TranslatedEntity<Category>
{
Entity = e,
Translation = new TranslationDef
{
Language = t.Language.Code,
Name = t.Name,
Xml = t.Xml
}
};
I always try to separate the from part (selection of the datasources) from the select part (projection to your target type. I find it also easier to read/understand, and it generally also works better with most linq providers.
You can write the query as follows to get the desired result:
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
TranslationName = Translations.FirstOrDefault().Name,
})
As far as i'm aware, it's due to how LINQ projects the query. I think when it see's the nested Select, it will not project that into multiple sub-queries, as essentially that would be what would be needed, as IIRC you cannot use multiple return columns from a sub-query in SQL, so LINQ changes this to a query-per-row. FirstOrDefault with a column accessor seems to be a direct translation to what would happen in SQL and therefore LINQ-SQL knows it can write a sub-query.
The second Select must project the query similar to how I have written it above. It would be hard to confirm without digging into a reflector. Generally, if I need to select many columns, I would use a let statement like below:
from e in MainCategories
let translation = Translations.FirstOrDefault()
select new
{
PlacementId = e.CatalogPlacementId,
Translation = new {
translation.Name,
}
})

Help Converting T-SQL to LINQ

Have the following (non-straightforward) T-SQL query, which i'm trying to convert to LINQ (to be used in a L2SQL expression):
declare #IdAddress int = 481887
select * from
(
select top 3 p.*
from tblProCon p
inner join vwAddressExpanded a
on p.IdPrimaryCity = a.IdPrimaryCity
where a.AddressType = 3
and p.IsPro = 1
and a.IdAddress = #IdAddress
order by AgreeCount desc
) as Pros
union
select * from
(
select top 3 p.*
from tblProCon p
inner join vwAddressExpanded a
on p.IdPrimaryCity = a.IdPrimaryCity
where a.AddressType = 3
and p.IsPro = 0
and a.IdAddress = #IdAddress
order by AgreeCount desc
) as Cons
order by ispro desc, AgreeCount desc
In a nutshell, i have an #IdAddress - and i'm trying to find the top 3 pro's and top 3 con's for that address.
The above query does work as expected. I'm not entirely sure how to convert it to a LINQ query (never done unions before with LINQ). I don't even know where to start. :)
Query-style/Lambda accepted (prefer query-style, for readability).
Also - i have LinqPad installed - but i'm not sure how to "convert T-SQL to Linq" - is there an option for that? Bonus upvote will be awarded for that. :)
The above T-SQL query performs well, and this L2SQL query will be executed frequently, so it needs to perform pretty well.
Appreciate the help.
var baseQuery = (from p in db.tblProCon
join a in db.vwAddresssExpanded
on p.IdPrimaryCity equals a.IdPrimaryCity
where a.AddressType == (byte) AddressType.PrimaryCity &&
a.IdAddress == idAddress
order by p.AgreeCount descending
select p);
var pros = baseQuery.Where(x=> x.IsPro).Take(3);
var cons = baseQuery.Where(x=> !x.IsPro).Take(3);
var results = pros
.Union(cons)
.OrderByDescending(x => x.IsPro)
.ThenByDescending(x => x.AgreeCount)
.ToList();
You can call (some query expression).Union(other query expression).
You can also (equivalently) write Enumerable.Union(some query expression, other query expression).
Note that both expressions must return the same type.
AFAIK, there are no tools that automatically convert SQL to LINQ.
(For non-trivial SQL, that's a non-trivial task)

Categories

Resources