Simple question really but it seems tricky to do:
I am trying to convert the following SQL Query to LINQ expression:
select SUM(timespan) as timespan from TimeRegistrations as tr
where Activity_Id_FK =1 and tr.date>= DATEADD(DAY,-30,GETDATE());
This is what I have so far :
var total_hours_spent = from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e.Sum(e.timespan);
The compiler complains at e.Sum(e.timespan). I don't know how to select the Sum of the timespan from the TimeRegistrations table. Any help will be greatly appreciated.
In your query e is a single TimeRegistration entity with its Activity_Id_FK, date and other fields. So, you problem is here:
e.Sum(e.timespan)
Here you have two issues - first one is calling Sum on entity (it's not queryable set of entities - there is nothing to sum in single entity). And you missed syntax of Sum method - it should accept lambda with property selector Sum(e => e.timespan).
You should select timespan values from TimeRegistrations table, and then call Sum() method:
(from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >= date.AddDays(30)
select e.timespan).Sum()
SQL query will be generated only when Sum() is called, so it will end up with query you want. You also can select whole TimeRegistrations entity and then select which field to sum (same query will be generated):
(from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >= date.AddDays(30)
select e).Sum(e => e.timespan)
I don't like to mix query syntax with method calls, so here is pure lambda syntax:
DB.TimeRegistrations
.Where(e => e.Activity_Id_FK == activity_id && e.date >= date.AddDays(30))
.Sum(e => e.timespan)
BTW: Small advise - choose variable names which correlate with entity they represent. E.g. instead of e I'do go with variable r or tr to represent TimeRegistration entity.
Apply sum after query like this one,
var total_hours_spent = (from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e).Sum(e => e.timespan);
Hope this will solve your issue
Use following code to find total hours spent
var total_hours_spent = (from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e).Sum(e => e.timespan.TotalHours);
Maybe a more readable syntax
var total_hours_spent = DB.TimeRegistrations
.Where(e => e.Activity_Id_FK ==activity_id &&
e.date >=date.AddDays(30))
.Sum(e => e.timespan);
Related
I wrote a T-SQL code which has used case when in select scope. We couldn't use t-sql or store procedure in application, because of that I need to convert follong code to LINQ. Is there any way to change this code to linq quickly?
SELECT
T.TaskID,
SUM(CASE WHEN T.LogDate<#fromDate AND T.TaskStatusID=2 THEN ISNULL(DA_CHILD.Score,0)*(T.DoneScore/100) ELSE 0 END) PreAmount,
SUM(CASE WHEN T.LogDate>=#fromDate AND T.LogDate<=#toDate AND T.TaskStatusID=2 THEN ISNULL(DA_CHILD.Score,0)*(T.DoneScore/100) ELSE 0 END) CurAmount
FROM
NetTasks$ T
INNER JOIN NetDeviceActions DA ON DA.DeviceActionID=T.DeviceActionID
LEFT JOIN NetFinancialInfoDetail FID ON FID.TaskID=T.TaskID
INNER JOIN NetActionParents AP ON AP.ParentID=DA.ActionID
INNER JOIN NetDeviceActions DA_CHILD ON DA_CHILD.ActionID=AP.ChildID AND
DA_CHILD.DeviceID=DA.DeviceID AND
DA_CHILD.ContractInfoID=DA.ContractInfoID
WHERE
T.ParentTaskID = 0 AND
T.FinishDate<=#toDate AND
DA.ContractInfoID=9
GROUP BY
T.TaskID, T.DoneScore,T.FinishDate
In LINQ you can use C# statements so CASE WHEN is actually not hard.
Assuming you have finished all the joining into a query object called values, you can use something like below for the grouping and select:
var q = from a in values
group a by new {a.TaskID, a.DoneScore, a.FinishDate} into g
select new {
g.Key.TaskID,
PreAmount = g.Where(x => x.LogDate < fromDate && x.TaskStatusID == 2 && x.DA_CHILD.HasValue).Select(x => x.DoneScore).Sum(),
CurAmount = g.Where(x => x.LogDate >= fromDate && x.LogDate < toDate && x.TaskStatusID == 2 && x.DA_CHILD.HasValue).Select(x => x.DoneScore).Sum()
};
And of course, a friendly reminder, left joining in LINQ is very tedious.
Are you just looking for a simple where clause in your statement? (Though I admit this LINQ query is not going to be particularly simple.)
https://msdn.microsoft.com/en-us/library/bb397927.aspx
I advise building it up slowly.
With such good looking SQL, would you not be happier using QueryFirst and forgetting about Linq? You run your SQL directly in your C# app.
disclaimer : I wrote QueryFirst
Here is my query
return (from l in Context.DctLink
join t in Context.DctTabel on l.BType equals t.DocType
join t2 in Context.DctTabel on l.TabelNr equals t2.TabelNr
where l.AType == docType || l.AType == 0
select new { t.TabelNr, t2.Naam, t.Titel })
.Union(from l in Context.DctLink
join t in Context.DctTabel on l.AType equals t.DocType
join t2 in Context.DctTabel on l.TabelNr equals t2.TabelNr
where l.BType == docType || l.BType == 0
select new { t2.TabelNr, t2.Naam, t.Titel })
.Join(Context.TimIcon.Where(q => q.Timweb && q.ShowId.ToInt32() > 0),
x => x.TabelNr,
y => y.TabelNr,
(x, y) => new LookupItem
{
Id = x.TabelNr,
Name = x.Titel,
Tag = x.Naam
}).ToList();
I want to be able to do this q.ShowId.ToInt32() > 0. But I get a System.Unsupported Exception. Isn't this possible in a link query or am I just overlooking something simple
Thanks in advance
You need to fetch the data from db using AsEnumerable or ToList, then you can use any method you want. Otherwise it's not possible because EF Query Provider can't know how to translate your method into SQL.
It depends on your LINQ provider. LINQ to Objects supports pretty much anything. The one you're using (LINQ to Entities or LINQ to SQL or something similar) doesn't support everything, because it needs to understand your expression and translate it to SQL (it can't do that with any kind of expression).
The simplest way to fix this is to call AsEnumerable() at some point, in order to convert the sequence (up to that point) to an in-memory sequence, so you'll fall back to LINQ to Objects and you can perform the (previously unsupported) logic on it.
Here is the problematic line:
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeIds.Contains(new int[] {a.VariantId, a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
variantGeotpeIds is of type List<int[]>. Both a.VariantId and a.GenotypeId are of type int.
I cannot figure out why it why it will not do the comparison. Is this a deferred execution issue? It doesn't seem like it should be...
Thanks in advance.
List<T>.Contains only takes a single parameter of type T. In your case, T is Int32 but you're passing in a Int32[].
If you want to check that both values are in the list, you have to break the calls apart:
where d.DrugId == drugId &&
variantGenotypeIds.Contains(a.VariantId) &&
variantGenotypeIds.Contains(a.GenotypeId)
EDIT
If variantGenotypeIds is actually a List<Int32[]>, then there's another issue. LINQ to SQL will try to convert your query into its SQL equivalent. In this case, there's no way to translate your query into SQL so LINQ to SQL will throw an Exception.
If you really need to query this way, you'll have to read the records into memory first and then query using LINQ to Objects (which may or may not be a big deal depending on how many rows you are reading):
var query =
from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants
on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId
select new { a, d }
var originalSummaryCandidates =
(from q in query.AsEnumerable()
where variantGenotypeIds.Contains(new [] { q.a.VariantId, q.a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
Array comparison uses reference equality by default. It's possible that linq-to-sql just tries to translate that into SQL that compares the values, but you'd have to look at the generated SQL to be sure. Another option would be to use Any instead:
where d.DrugId == drugId &&
variantGenotypeIds.Any(v => v[0] == a.VariantId && v[1] == a.GenotypeId)
but I'm not sure if Linq-to-Sql will be able to translate that to the correct SQL either. Another option would be to project the List` to a > and then do a string comparison:
variantGenotypeStrings = variantGenotypeIds.Select(v => string.Format("{0}|{1}", v[0],v[1]);
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeStrings.Contains(string.Format("{0}|{1}", a.VariantId, a.GenotypeId))
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
I had tried to join two table conditionally but it is giving me syntax error. I tried to find solution in the net but i cannot find how to do conditional join with condition. The only other alternative is to get the value first from one table and make a query again.
I just want to confirm if there is any other way to do conditional join with linq.
Here is my code, I am trying to find all position that is equal or lower than me. Basically I want to get my peers and subordinates.
from e in entity.M_Employee
join p in entity.M_Position on e.PostionId >= p.PositionId
select p;
You can't do that with a LINQ joins - LINQ only supports equijoins. However, you can do this:
var query = from e in entity.M_Employee
from p in entity.M_Position
where e.PostionId >= p.PositionId
select p;
Or a slightly alternative but equivalent approach:
var query = entity.M_Employee
.SelectMany(e => entity.M_Position
.Where(p => e.PostionId >= p.PositionId));
Following:
from e in entity.M_Employee
from p in entity.M_Position.Where(p => e.PostionId >= p.PositionId)
select p;
will produce exactly the same SQL you are after (INNER JOIN Position P ON E..PostionId >= P.PositionId).
var currentDetails = from c in customers
group c by new { c.Name, c.Authed } into g
where g.Key.Authed == "True"
select g.OrderByDescending(t => t.EffectiveDate).First();
var currentAndUnauthorised = (from c in customers
join cd in currentDetails
on c.Name equals cd.Name
where c.EffectiveDate >= cd.EffectiveDate
select c).OrderBy(o => o.CoverId).ThenBy(o => o.EffectiveDate);
If you have a table of historic detail changes including authorisation status and effective date. The first query finds each customers current details and the second query adds all subsequent unauthorised detail changes in the table.
Hope this is helpful as it took me some time and help to get too.
I am trying to convert an old raw Sql query in Linq with Entity Framework here.
It was using the IN operator with a collection of items. The query was something like that:
SELECT Members.Name
FROM Members
WHERE Members.ID IN ( SELECT DISTINCT ManufacturerID FROM Products WHERE Active = 1)
ORDER BY Members.Name ASC
Since the return of the subquery is not a single string but a collection of strings I can't use the String.Contains() method.
I thought about doing something like :
var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);
and then
var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));
but it stops at the contains saying it has invalid arguments ... I can't select activeProducts.ManufacturerID because obviously the proprety is not there since it returns an IQueryable...
Bottom line what I'm trying to do here is to return a list of members who have at least one active product.
Any hint ?
[edit]
Here's the full query code ... I tried with the contains on the second expression, Linq didn't seem to like it :
Server Error in '/' Application.
LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Linq.IQueryable``1[System.String], System.String)' method, and this method cannot be translated into a store expression.
var activeProduct =(from product in Master.DataContext.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID.ToString() );
var activeArtists = from artist in Master.DataContext.ContactSet
where activeProduct.Contains(artist.ID.ToString())
select artist;
NumberOfArtists = activeArtists.Count();
artistsRepeater.DataSource = activeArtists;
artistsRepeater.DataBind();
[More details]
ManufacturerID is a nullable GUID apparently...
For some reason the ContactSet class do not contain any reference to the products I guess I will have to do a join query, no clues here.
var activeMembers = (
from member in db.ContactSet
where activeProducts.Select(x=>x.ID).Contains(member.ID));
Try where activeProducts.Contains(member.ID).
EDIT: Did you try it without any ToStrings?
You can do it in one query:
var q = from member in db.ContactSet
where member.Products.Any(p => p.IsActive)
select member;
Try the solution posted by Colin Meek at: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/095745fe-dcf0-4142-b684-b7e4a1ab59f0/. It worked for me.
What about this:
from m in members
where products.FirstOrDefault(prod => prod.IsActive == 1 && prod.Id == m.Id) != null
select m;
you can chain any number of conditions required in the where clause using &&
Ash..
from m in members
where products.Any(p => p.Active && p.ManufacturerID == m.ID)
select m
or
from m in members
join p in products on m.ID equals p.ManufacturerID
where p.Active
select m
Instead of this:
var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));
Try this:
var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID));
What if you swap the statement (untested)?
where activeProducts.Contains(member.ID)
How about this...
var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);
var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID.ToString()));
A helper or extension method will work fine when querying against objects in memory. But against an SQL database, your LINQ code will be compiled into an expression tree, analysed and translated into an SQL command. This functionality has no concept of custom-made extension methods or methods of other objects like .Contains(...).
It could be easily implemented into the standard LINQ-To-SQL functionality by Microsoft though. But as long as they don't want, we're helpless as long it's not an open source functionality.
All you can do is create your own QueryProvider that goes against an SQL database. But it will be hard and it would be only for that one in feature alone that you're missing.
However, if you really wanna go that route, have fun: LINQ: BUILDING AN IQUERYABLE PROVIDER SERIES
Finally I managed to code something really ugly, but that actually works! (lol)
var activeProduct =(from product in Master.DataContext.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID ).Distinct();
var artists = from artist in Master.DataContext.ContactSet
select artist;
List<Evolution.API.Contact> activeArtists = new List<Evolution.API.Contact>();
foreach (var artist in artists)
{
foreach(var product in activeProduct)
{
if (product.HasValue && product.Value == artist.ID)
activeArtists.Add(artist);
}
}
NumberOfArtists = activeArtists.Count();
artistsRepeater.DataSource = activeArtists;
artistsRepeater.DataBind();
I have already posted about the same at
http://www.codeproject.com/Tips/336253/Filtering-records-from-List-based-similar-to-Sql-I
var q = (from p in db.DOCAuditTrails
where p.ActionUser == "MyUserID"
&& p.ActionTaken == "Not Actioned"
&& p.ActionDate > DateTime.Parse("2011-09-13")
select p.RequisitionId).Distinct();
var DocAuditResults = db.DOCAuditTrails.Where(p
=> q.ToArray().Contains(p.RequisitionId));
Without know the exact mappings it is hard to tell what can be done and what can't. I will assume that there isn't any casting involved. Firstly you have to remember that everything in the Linq Expression tree must have an equivalent in SQL. As some others have noted, you have a object.ToString() in your Linq Statements.
However it seems that what people have neglected to mention is that you have TWO usages of object.ToSting(), both of which must be removed.
I would also make an extra variable to change the closure's capture type to be explicitly of DataContext (since the Linq statement is like a lambda, and delayed evaluated. It will need to take the whole of the Master variable. Earlier I stated that everything in your Linq must have an equivalent in SQL. Given that Master can't possibly exist in SQL, there is no DataContext property/column/mapping for the type of Master).
var context = Master.DataContext;
var activeProduct = from product in context.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID;
var activeArtists = from artist in context.ContactSet
where activeProduct.Contains(artist.ID)
select artist;
I hope the above changes work for you.
In many cases issues with Linq to ORMs can be traced back to your Linq Expression capturing a non primative (DateTime, int, string etc) and non ORM based class (DataContext/EntityObject etc). The other major gotcha is usage of functions and operators that aren't exposed by the ORM (it is possible to map user defined functions to .net function through the ORM, but I would not recommend it due to indexing issues).