I am trying to convert an MSSQL statement into LINQ. The overall functionality is broken up into many LINQ statements which are combined together, so the result must return type IQueryable.
Included in this question is an example of the schema and data. I am trying to see if the most recent (by DateCreated) value of Number is equal to 400, if so return the Table1Id.
The following is the MSSQL statement.
select * from Table1 t1
where 400 in (
select top 1 t2.Number
from Table2 t2
where t2.Table1Id = t1.id
order by t2.DateCreated desc
)
The LINQ I have come up with so far is:
//initial query
var query1 = _table1Repository.Table
//chain our query
var query2 = from t1 in query1
where ((from t2 in _table2Repository.Table
where t2.Table1Id == t1.Id
orderby t2.DateCreated descending
select t2.Number)
.Take(1)).Contains(400)
select t1;
//execute the query (may be more queries between last query and this)
var queryResult = query2.ToList();
Though upon executing the following error is thrown:
Unable to create a constant value of type 'Project.Domain.Table2'. Only primitive types or enumeration types are supported in this context.
Table 1 data
| Id |
|----|
| 1 |
| 2 |
| 3 |
Table 2 data
| Id | DateCreated | Table1Id | Number |
|----|------------ |-----------|---------|
| 1 | 1/1/2014 | 1 | 100 |
| 2 | 2/1/2014 | 1 | 200 |
| 3 | 3/1/2014 | 1 | 300 |
| 4 | 1/1/2014 | 2 | 200 |
| 5 | 2/1/2014 | 2 | 300 |
| 6 | 3/1/2014 | 2 | 400 |
| 7 | 1/1/2014 | 3 | 400 |
| 8 | 2/1/2014 | 3 | 300 |
| 9 | 3/1/2014 | 3 | 200 |
Expected result
| Id |
|----|
| 2 |
Assuming you still want to use the In keyword, the Lambda version would look like:
var intList = new List<int>(1) { 400 };
var test = _table1Repository.Table
.Where(t1 => intList.Contains(_table2Repository.Table
.Where(t2 => t2.Table1Id == t1.id)
.OrderByDescending(t2 => t2.Number)
.First()));
The only Caveat is that if there are no values in Table2, First() with throw an exception.
An example of mix'd Linq and Lambda:
var query1 = _table1Repository.Table
.OrderBy(t1 => t1.id);
var query2 = (from t1 in query1
select new { Id = t1.Id, Number = number } );
var query3 = query2.ToList();
Related
I am new to Writing Linq Queries and would like to write a query as below.
Brief Info on Requirement:
I need to get a count of distinct group keys for a user with a join to another table having its name
TABLE - 1: Table - 2:
--------------- -------------
| Id | GrpKey | prdId | UserId| | Id | GrpKey | GrpName | UserId
| 1 | 123455 | Test1 | 11111 | 1 | 123455 | GroupOne | 1111
| 2 | 123455 | Test2 | 22222 | 2 | 551234 | GroupTwo | 1111
| 3 | 123455 | Test3 | 22222
| 4 | 551234 | Test4 | 11111
| 5 | 551234 | Test5 | 11111
| 6 | DBNULL | Test4 | 11111
| 7 | DBNULL | Test5 | 11111
REQD. RESULT for UserId : 11111
--------------------------------
GrpKey | GrpName | Count(GrpKey)
DBNULL | DBNULL | 2
551234 | GroupTwo| 2
123455 | GroupOne| 1
Queries Tried:
1)
from grp in table2
join item in table1 on grp.GrpKey equals item.GrpKey into j1
where grp.UserId == "1111"
select new Group
{
Count = j1.Count(),
Name = grp.GrpName,
Key = grp.GrpKey,
}).ToList();
2)
var result = (from item in table1
join grp in table2 on item.GrpKey equals grp.GrpKey into j1
from rt in j1.DefaultIfEmpty()
where item.userId == userId
select new Group
{
GrpKey = item.GrpKey,
userId = item.userId,
Count = j1.Count(),
GrpName = rt.GroupName
}).ToList();
Issues With TriedQuery1:
With the above LINQ query, I am able to get the count of all values except the rows having
GrpKey and GrpName as NULL. Can Anybody help me with a query to get data as per my required data set
Issues With TriedQuery2:
Count of Rows having null or coming Zero even though there are rows with values as NULL.
Thanks in Advance
For the second query, you could add Group by for lefted join result, like the following code :
var result = (from item in table1
join grp in table2 on item.GrpKey equals grp.GrpKey into j1
from rt in j1.DefaultIfEmpty()
where item.userId == 11111
group rt by rt?.GrpKey into g
select new
{
GrpKey = g.Key,
GrpName = g.First()?.GrpName,
Count = g.Count(),
}).ToList();
I hope you find this helpful.
I have three data tables that basically contain the following data:
table 1:
ID FContractID | WaitingTime
1 | 1 | 85
2 | 1 | 98
3 | 1 | 122
4 | 1 | 45
5 | 1 | 234
6 | 1 | 101
etc.
Table 2:
PricingCriterionItemId PricingCriterionName PricingCriterionUnit
1 | WaitingTimeMax | min
2 | WaitingTimePeriod | min
3 | WaitingTimeOverdue | €/period
4 | OverDuePriceMax | €
Table 3:
PricingCriterionId ContractID PricingCriterionItemId PricingCriterionValue
1 | 1 | 1 | 70
2 | 1 | 2 | 30
3 | 1 | 3 | 30,00
4 | 1 | 4 | 120,00
I want to add to the table 1 a column that contains waiting time cost. The waiting time cost would be calculated like
WaitingTimeCost = min(((WaitingTime - WaitingTimeMax) / WaitingTimePeriod) * WaitingTimeOverdue, OverDuePriceMax)
I can easily join tables 2 and 3 into one table:
Table 4
PricingCriterionId ContractID PricingCriterionName PricingCriterionValue PricingCriterionUnit
1 | 1 | WaitingTimeMax | 70 | min
2 | 1 | WaitingTimePeriod | 30 | min
3 | 1 | WaitingTimeOverdue | 30,00 | €/period
4 | 1 | OverDuePriceMax | 120,00 | €
Is it possible using linq to assign a column's value on a certain row using other columns value?
Something like
var result = from WaitingData in table1
join PricingCriteria in table4
on WaitingData.ContractId equals PricingCriteria.ContractId
let WaitingTimeMax = (select PricingCriterionValue from table4 where PricingCriterionName = "WaitingTimeMax")
let ...
let WaitingTimeCost = min(((WaitingTime - WaitingTimeMax) / WaitingTimePeriod) * WaitingTimeOverdue, OverDuePriceMax)
select new
{
ID,
WaitingTimeCost
}
How to formulate this properly using linq?
You don't need to do everything in the database query.
Database is only IO device, which responsible only for reading and writing data.
Load all required data and calculate.
var contractsId = waitingData.Select(data => data.ContractId).ToList();
var pricingCriterias = table4.Where(criteria => contractsId.Contains(criteria.ContractId)
.ToLookup(criteria => criteria.ContractId);
var maxWaitingTime =
pricingCriterias.SelectMany(group => group)
.Where(criteria => criteria.PricingCriterionName = "WaitingTimeMax")
.Max(criteria => criteria.PricingCriterionValue);
foreach (var waitingItem in waitingData)
{
// Calculate others waiting values
var waitingPerPeriod = (WaitingTime - maxWaitingTime) / WaitingTimePeriod);
var waitingPrice = waitingPerPeriod * WaitingTimeOverdue;
var WaitingTimeCost = Math.Min(waitingPrice, OverDuePriceMax)
}
Below example with DataTables.
However DataTable is heavy and not strongly typed data structure and as developer it takes more time to deal with it.
Instead transform data in DataTable to the plain strong typed objects with descriptive property names - you will get IntelliSense for free ;)
var contractsId =
waitingData.AsEnumerable()
.Select(row => row.Field<int>("ContractId"))
.ToList();
var pricingCriterias =
table4.AsEnumerable()
.Where(row => contractsId.Contains(row => row.Field<int>("ContractId"))
.ToLookup(row => row.Field<int>("ContractId"));
var maxWaitingTime =
pricingCriterias.SelectMany(group => group)
.Where(row => row.Field<string>("PricingCriterionName") = "WaitingTimeMax")
.Max(row => row.Field<int>("PricingCriterionValue"));
foreach (var waitingItem in waitingData)
{
// Calculate others waiting values
var waitingPerPeriod = (WaitingTime - maxWaitingTime) / WaitingTimePeriod);
var waitingPrice = waitingPerPeriod * WaitingTimeOverdue;
var WaitingTimeCost = Math.Min(waitingPrice, OverDuePriceMax)
}
I need to concatenate multiple columns values to single value (separated with coma for example). I can do it by string_agg(u.name, ',') as Users in PostgreSql. I tried do it in linq query syntax but I failed - all time I just get list with split values instead of one row with values concatenated in one field.
Let's assume that I have only three tables:
Doc User Doc_User
+----+--------------------+ +----+-----------+ +----+-----------+
| Id | Path | | Id | Name | | DocId | UserId |
+----+--------------------+ +----+-----------+ +----+-----------+
| 1 | "C:\\Files\\A.txt" | | 1 | "Adam" | | 1 | 1 |
| 2 | "C:\\Files\\B.txt" | | 2 | "Benny" | | 1 | 2 |
| 3 | "C:\\Files\\C.txt" | | 3 | "Charlie" | | 2 | 1 |
+----+--------------------+ +----+-----------+ | 2 | 2 |
| 2 | 3 |
+-------+--------+
At the start I was trying simple join:
var model = (from d in db.Docs
join du in db.DU on d.Id equals du.DocId
join u in db.Users on du.UserId equals u.Id
select new DataModel() { DocPath = d.Path, UserName = u.Name }).ToList();
As I suspected I got list with separated rows:
C:\Files\A.txt | Adam
C:\Files\A.txt | Benny
C:\Files\B.txt | Adam
C:\Files\B.txt | Benny
C:\Files\B.txt | Charlie
But what I want to obtain is:
C:\Files\A.txt | Adam, Benny
C:\Files\B.txt | Adam, Benny, Charlie
string.Join() is not recognized by EF and I can't use it in linq queries, Aggregate() not working too. How can I achieve this?
I've prepared example for play with it: .Net Fiddle.
The code below groups the common documents by path using LINQ and then joins the grouped document's UserNames, using string.Join. I also think you don't need ToList() in this line select new DataModel() { DocPath = d.Path, UserName = u.Name }).ToList(); if you are going to use this solution because we are going to use LINQ again.
var grouped = model.GroupBy(x => x.DocPath);
foreach (var iGrouped in grouped){
string.Join(",",iGrouped.Select(x => x.UserName));
Console.WriteLine(iGrouped.Key + " | " + string.Join(",",iGrouped.Select(x => x.UserName)));
}
I have multiple tables and i have a search ASP.NET page with a GridView for the results.
The GridView must contain Name, School, State, Country
I have multiple tables that only contains the data:
index_States
indexID| State
----------------
1 | state1
2 | state2
3 | state3
index_Contries
indexID| Country
----------------
1 | country1
2 | country2
3 | country3
index_Schools
indexID| School
----------------
1 | school1
2 | school2
3 | school3
Then i have the tables that contains the indexID as reference
General_Info
idKey | Name
--------------
1 | John
2 | McClane
3 | Jr.
Academic_XP
id | idSchool | idState | idCountry | idKey
--------------------------------------------
1 | 1 | 3 | 20 | 2
2 | 1 | 5 | 146 | 3
3 | 2 | 1 | 65 | 9
And THEN I have the table that contains UserType as only certain type of user will be searched
Users
id | UserType | idKey
-----------------------
1 | 1 | 1
2 | 3 | 2
3 | 3 | 3
4 | 1 | 4
I've already tried multiple queries but none seem to be working.
Last query that seem to be working was with INNER JOIN
SELECT Name, State
FROM General_Info A, Academic_XP B
INNER JOIN index_States D ON B.idState = D.indexID
GROUP BY A.id;
but it doesn't work as soon as I add a second INNER JOIN or a WHERE clause.
SELECT Name, State
FROM General_Info A, Academic_XP B, Users
INNER JOIN index_States D ON B.idState = D.indexID
INNER JOIN index_School E ON B.idSchool = E.indexID
GROUP BY A.id
WHERE Users.UserType = 3;
I don't know how can I do that.
So i guess the question is
How can I made a query that returns from all those tables something like this?
Name | State | School | Country
---------------------------------------
McClane | state3 | school1 | country20
Jr. | state1 | school5 | country146
Note that McClane and Jr. are both UserType 3.
I will appreciate any help.
You are producing a cartesian product between tables without joins. I think this is what you're looking for using additional JOINs:
SELECT DISTINCT
G.Name,
S.State,
C.Country,
SC.School
FROM Academic_XP A
JOIN Users U ON A.idKey = U.idKey
JOIN General_Info G ON A.id = G.idKey
JOIN Index_States S ON A.idState = S.indexID
JOIN Index_Contries C ON A.idCountry = C.indexID
JOIN Index_Schools SC ON A.idSchool = SC.indexID
WHERE U.UserType = 3
If some tables don't have matching keys, you'll need to use an OUTER JOIN.
A Visual Explanation of SQL Joins
I have two datasets that look like this:
+------------------------------------+
| Products |
+------------------------------------+
| Id | Name | Price |
+------------------------------------+
| 1 | apples | 1.00 |
| 2 | oranges | 2.00 |
| 3 | pomengrate | 3.00 |
+------------------------------------+
+-------------------------------+
| Sales |
+-------------------------------+
| CustId | ProductId | Quantity |
+-------------------------------+
| 1 | 1 | 5 |
| 1 | 2 | 4 |
| 1 | 3 | 2 |
| 2 | 1 | 8 |
| 2 | 3 | 7 |
+-------------------------------+
I want to get the amount that each customer is spending, essentially ending up with a result like this:
+----------------+
| CustId | Total |
+----------------+
| 1 | 19.00 |
| 2 | 29.00 |
+----------------+
I can do a sum across a single table but the Sum method in LINQ takes a lambda with only one argument: the reference to the table that the values being summed are in. These values are in different tables. How do I sum these together?
var totals =
from sale in Sales
group sale by sale.CustId into custSales
select new
{
CustId = custSales.Key,
Total = (
from custSale in custSales
select custSale.Product.Price *
custSale.Quantity)
.Sum()
};
Give this a shot. It gives the results you were looking for:
var results = sales.Join(products,
sale => sale.ProductID,
product => product.ID,
(sale, product) => new { CustID = sale.CustID, Total = sale.Quantity * product.Price })
.GroupBy(r => r.CustID)
.Select(g => new { CustID = g.Key, Total = g.Sum(gt => gt.Total) });
For completeness sake, here's the query syntax version (utilizing joins as opposed to a subselect):
var totals =
from sale in sales
join prod in product on sale.ProductId equals prod.Id
let saleProds = new { sale.CustId, Total = prod.Price * sale.Quantity }
group saleProds by saleProds.CustId into custSale
select new { Customer = custSale.Key, Total = custSale.Sum(tr => tr.Total) };
The key part is that you need to somehow transform the joined collection results (sale and prod) into a single entity that can then be grouped by.