How to replace Take(1) from Linq query - c#

In a project I have to maintain, I have this Linq query using Take(1) :
(from workflow in db.Workflows
from search in db.Searches.Where(p => p.WorkflowId == workflow.WorkflowId).Take(1).DefaultIfEmpty()
select new { workflow, hasSearch = search.SearchId > 0 }).ToList();
When Take(1) is translated to SQL, it gives something like :
ROW_NUMBER() OVER(PARTITION BY WorkflowId ORDER BY WorkflowId)
which occurs to be lacking performance.
I guess the developer had a SELECT TOP 1 in mind. But if use FirstOrDefault it would not return an IQueryable anymore. So, my question is : how can I rewrite the Linq query to keep the same purpose and not using Take, if possible ?

If understand correctly needed result, query should be rewritten in this way:
var query =
from workflow in db.Workflows
select new
{
workflow,
hasSearch = db.Searches.Any(p => p.WorkflowId == workflow.WorkflowId)
};

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)

SQL query to linq to SQL query

I have the following query in SQL :
SELECT TOP 1 *
FROM sessions
ORDER BY start_time
How should I code it instead in LINQ-to-SQL
Use OrderBy for the ordering, and First or possibly FirstOrDefault for the equivalent of TOP 1:
var session = db.Sessions.OrderBy(x => x.StartTime).FirstOrDefault();
if (session != null)
{
// Use the session
}
else
{
// There weren't any sessions
}
You could use a query expression for the first part, but it seems pretty pointless - it would end up being more long-winded than using "dot notation", so I'd stick to that.
(from i in dc.sessions
orderby i.start_time).Take(1);
or
dc.sessions.OrderBy(x => x.start_time).Take(1);
Here's an example:
NorthwindDataContext db = new NorthwindDataContext();
var shippers = from shipper in db.Shippers
orderby shipper.CompanyName
select shipper;
return shippers.First();
However, using the syntax recommended by Jon Skeet above is probably most appropriate for your simple use case.

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)

linq to sql report tables in query

Here's the method i want to write:
public static IEnumerable<String> GetTableNames(this IQueryable<T> query)
{
//...
}
where the IQueryable is a linq-to-sql query (is there a more specific interface i should use?).
then if i had a query like this
var q = from c in db.Customers
from p in db.Products
from cp in db.CustomerProducts
where c.ID = 3 && cp.CustID == c.ID && p.ID == cp.ProdID
select new {p.Name, p.Version};
q.GetTableNames();// return ["Customers", "Products", "CustomerProducts"]
basically it would show all the tables that this query touches in the db, it is ok to execute the query to figure this out too (since that is going to happen anyway)? any ideas?
(EDIT: sorry if this is a little too "give me teh codez!", any tips, partial solutions, or explanations of why this is impossible will be considered Thanks! -Luke)
LINQ-to-SQL allows you to capture the TSQL easily - just set .Log. The problem then is parsing that. I would be tempted to capture the TSQL, and play it back in SSMS with SET STATISTICS IO ON - this gives easily parsed per-table results.
You could do DataContext.GetCommand(query).CommandText, then parse that looking for FROM and JOIN clauses (tricky would be cartesian (comma) joins).

Categories

Resources