How to rewrite a SQL query in LINQ to Entities? - c#

I'm trying to rewrite a SQL query in LINQ to Entities. I'm using LINQPad with a typed datacontext from my own assembly to test things out.
The SQL query I'm trying to rewrite:
SELECT DISTINCT variantID AS setID, option_value AS name, option_value_description AS description, sort_order as sortOrder
FROM all_products_option_names AS lst
WHERE lst.optionID=14 AND lst.productID IN (SELECT productID FROM all_products_option_names
WHERE optionID=7 AND option_value IN (SELECT name FROM brands
WHERE brandID=1))
ORDER BY sortOrder;
The LINQ to Entities query I've come up with so far (which doesn't work due to a timeout error):
from a in all_products_option_names
where a.optionID == 14 && all_products_option_names.Any(x => x.productID == a.productID && x.optionID == 7 && brands.Any(y => y.name == x.option_value && y.brandID == 1))
select new
{
id = a.variantID,
name = a.option_value,
description = a.option_value_description,
sortOrder = a.sort_order,
}
This is the error I get when I run the above query: An error occurred while executing the command definition. See the inner exception for details.
And the inner exception is: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Edit:
I use MySQL and probably that's why LINQPad doesn't show me the generated SQL.
The SQL version doesn't time out.
Edit 2:
I solved the problem by completely changing the query, so this question is irrelevant now.
I marked Steven's response as the correct one, because he was closest to what i was trying to achieve and his response gave me the idea which led me to the solution.

Try this:
var brandNames =
from brand in db.Brands
where brand.ID == 1
select name;
var brandProductNames =
from p in db.all_products_option_names
where p.optionID == 7
where brandNames.Contains(p.option_value)
select p.productId;
var results =
from p in db.all_products_option_names
where p.optionID == 14
where brandProductNames.Contains(p.productId)
select new
{
setID = p.variantID,
name = p.option_value,
description = p.option_value_description,
sortOrder = p.sort_order
};

I would recommend doing joins rather than sub-select's as you have them. Sub-selects are not very efficient when you look at performance, it's like having loops inside of loops when you code , not a good idea. This could actually cause that timeout your getting if your database is running slowly even thou that looks like a simple query.

I would try using joins with a distinct at the end like this:
var results =
(from p in db.all_products_option_names
join p2 in db.all_products_option_names on p.productId equals p2.productId
join b in db.Brands on p2.option_value equals b.name
where p.optionID == 14
where p2.optionID == 7
where b.BrandID == 1
select new
{
setID = p.variantID,
name = p.option_value,
description = p.option_value_description,
sortOrder = p.sort_order
}).Distinct();
Or you could try using joins with the into and with an any like so
var results =
from p in db.all_products_option_names
join p2 in (from p3 in db.all_products_option_names.Where(x => x.optionId == 7)
join b in db.Brands.Where(x => x.BrandID == 1) on p3.option_value equals b.name
select p3) into pg
where p.optionID == 14
where pg.Any()
select new
{
setID = p.variantID,
name = p.option_value,
description = p.option_value_description,
sortOrder = p.sort_order
};

Related

SQL Query to Linq to Entities - C#

I have been trying to convert this SQL statement into a linq as i am trying to move the functionality into a program.
Here is the SQL statement
SELECT cust.sg_group_name AS customer,
(SELECT Sum(du.used_space)
FROM sg_groups AS clnt
LEFT JOIN client_disk_usage AS du
ON clnt.sg_group_id = du.sg_group_id
AND clnt.group_role_id = 3
WHERE clnt.parent_group_id = cust.sg_group_id
AND du.day_of_month = 15
AND du.month_of_year = 05
AND du.used_space_year = 2016) AS disk_usage
FROM sg_groups AS cust
WHERE cust.group_role_id = 2
ORDER BY cust.sg_group_name
Essentially the output is just a list with two columns
customer disk_usage
Customer1 136401537652
Customer2 42208008210
If possible i just want to convert this to a linq statement. I have tried putting the query into LinqPad, but it doesn't seem to want to convert from SQL to Linq (just comes up with a blank white page). I have had a crack at the query myself, but i either get something that doesn't work altogether, or an incorrect number of results.
If anyone has any suggestions that would be great!
disk_usage(Sub Query) is a bit Complicated Part. Converted over here. Try this out
var CoreList = (from clnt in EntityName.sg_groups
join du in EntityName.client_disk_usage
on new { GrpId = clnt.sg_group_id, RoleId = clnt.group_role_id } equals new { GrpId = du.sg_group_id, RoleId = 3 } into LJ
from RT in LJ.DefaultIfEmpty()
where du.day_of_month == 15 && du.month_of_year == 05 && du.used_space_year == 2016
select new {clnt, du, RT}
).ToList();
var CoreListSet = CoreList.Select(i=> new YourEntityClass
{
//Fetch the ParentGroupId & UsedSpace
}).ToList();
var CoreListComplete = (from cl in CoreListSet
join cust in EntityName.sg_groups
on cust.sg_group_id equals cl.parent_group_id).ToList();
Now get the sum of CoreListComplete & just implement the base Select Query in Linq!
Apologies for the delayed response. I've marked #Anil answer up as this is the one that helped me find the answer. You solution did work #Sathish but it can be accomplished in a single command. Here is my final solution. Many thanks for your help!
storeGridUsage = (
from cust in db.sg_groups
from client in db.sg_groups
join du in db.client_disk_usage on client.SG_GROUP_ID equals du.SG_GROUP_ID
where client.GROUP_ROLE_ID == 3
where client.PARENT_GROUP_ID == cust.SG_GROUP_ID && du.DAY_OF_MONTH == day && du.MONTH_OF_YEAR == month && du.USED_SPACE_YEAR == year
where cust.GROUP_ROLE_ID == 2
orderby cust.SG_GROUP_NAME
group new {cust, du} by cust.SG_GROUP_NAME
into g
select new StoreGridUsage
{
CustomerName = g.Key,
DiskUsageInBytes = g.Sum(o => o.du.USED_SPACE)
}).ToList();

Using SQL instead of LINQ in Telerik Open Access

I'm using Telerik Open Access. I have two separate projects that have Open Access data and then a third project that has the bulk of my code. I've been working on a way to convert a simple (at least I thought it was) SQL query to LINQ so that I can get the data I need. I have not been successful. I've had to break a single LINQ query into separate queries, because of the need for the Trim() function (I think). This has led to a lengthy piece of code and I'm still not getting the same results as my SQL query.
So my question is, is there anyway to use SQL instead of LINQ to access the data in the Open Access projects? If so, can you show me the syntax to do that for my query?
If it is not possible to use SQL, can you show me show me the proper way to convert my SQL query into LINQ so that I get the same results?
Thank you.
My SQL query is
SELECT DISTINCT us2.ccustno, us2.dispname, us2.csiteno, so.s1_name
FROM [DALubeDeacom].[dbo].[dmbill] bi
INNER JOIN [DALubeDeacom].[dbo].[dmso1] so
ON bi.bi_s1id = so.s1_id
INNER JOIN [DALubeNew].[dbo].[usersecurity] us2
ON so.s1_name = us2.cparentno
WHERE
us2.ctype = 'JOBSITE'
AND us2.csiteno is not null
AND us2.csiteno != ''
AND bi.bi_smid = '22'
ORDER BY us2.csiteno
My LINQ query is
public List<DataModelSample> GetLocationsBySalesNo(string salesNo)
{
int iSalesNo = int.Parse(salesNo.Trim());
try
{
var dmso = (
from so in deacom.Dmso1
join qt in deacom.Dmbills
on so.S1_id equals qt.Bi_s1id
where qt.Bi_smid == iSalesNo
select new Dmso1
{
S1_id = so.S1_id
, S1_name = so.S1_name.Trim()
}
);
var usec = (
from us in dbContext.Usersecurities
where us.Cparentno != null && us.Cparentno.Trim() != "" && us.Ctype.Trim() == "JOBSITE" && us.Csiteno.Trim() != ""
select new Usersecurity
{
Ccustno = us.Ccustno.Trim(),
Csiteno = us.Csiteno.Trim(),
Dispname = us.Dispname.Trim(),
Cparentno = us.Cparentno.Trim()
}
);
var customers =
(
from us in usec
join so in dmso
on us.Cparentno equals so.S1_name
select us
);
customers = customers.GroupBy(x => x.Csiteno).Select(x => x.First());
List<DataModelSample> listLocations =
(
from c in customers
select new DataModelSample
{
customerID = c.Ccustno
,
origLocationName = c.Csiteno + " " + c.Dispname
,
origLocationID = c.Csiteno
}
).OrderBy(x => x.origLocationID).ToList();
return listLocations.ToList();
}
catch (Exception ex)
{
throw ex;
}
} // GetLocationsBySalesNo(userInfo.csalesno)
Edit 1 - 2-19-16
Tried a suggestion by ViktorZ. His query was similar to the one I first tried. It returned the error "Identifier 'Ctype' is not a parameter or variable or field of 'DALube_DeacomModel.Dmbill'. If 'Ctype' is a property please add the FieldAlias or Storage attribute to it or declare it as a field's alias." From an online search, it looked like this was do to "extended fields". I don't seemed to be using such fields. The only way I could get around this error was to break it into the smaller LINQ queries in my original question, which didn't produce the right results. Any suggestions?
Here's the code:
var query = (from bill in deacom.Dmbills
join so in deacom.Dmso1 on bill.Bi_s1id equals so.S1_id
join us in dbContext.Usersecurities on so.S1_name equals us.Cparentno
where us.Ctype == "JOBSITE"
&& us.Csiteno != null
&& us.Csiteno != string.Empty
&& bill.Bi_smid == iSalesNo
select new
{
ccustno = us.Ccustno.Trim(),
dispname = us.Dispname.Trim(),
csiteno = us.Csiteno.Trim(),
s1_name = so.S1_name.Trim()
}).Distinct();
One very crude approximation of your SQL query is:
var query = (from bill in deacom.Bills
join so in deacom.LubeDeacom on bill.bi_s1id equals so.s1_id
join us in deacom.UserSecurity on so.s1_name equals us.cparentno
where us.ctype = "JOBSITE"
&& us.csiteno != null
&& us.csiteno != string.Empty
&& bill.smid = '22'
order by us.csiteno
select new
{
us.ccustno.Trim(),
us.dispname.Trim(),
us.csiteno.Trim(),
so.s1_name.Trim()
}).Distinct();
// to check the translation result
string sql = query.ToString()
// to get the results
var result = query.ToList()
If this is not working for you, you can always fall back to Telerik Data Access ADO.NET API. Here is a documentation article how to use it.

left join in Linq query

I'm trying to do a left join, not an inner join in a linq query. I have found answers related to using DefaultIfEmpty() however I can't seem to make it work. The following is the linq query:
from a in dc.Table1
join e in dc.Table2 on a.Table1_id equals e.Table2_id
where a.Table1_id == id
orderby a.sort descending
group e by new
{
a.Field1,
a.Field2
} into ga
select new MyObject
{
field1= ga.Key.Field1,
field2= ga.Key.Field2,
manySubObjects = (from g in ga select new SubObject{
fielda= g.fielda,
fieldb= g.fieldb
}).ToList()
}).ToList();
The query only gives me the rows from table 1 that have a corresponding record in table 2. I would like every record in table 1 populated into MyObject and a list of 0-n corresponding records listed in manySubObjects for each MyObject.
UPDATE:
I tried the answer to the question that is a "possible duplicate", mentioned below. I now have the following code that does give me one record for each item in Table1 even if there is no Table2 record.
from a in dc.Table1
join e in dc.Table2 on a.Table1_id equals e.Table2_id into j1
from j2 in j1.DefaultIfEmpty()
where a.Table1_id == id
orderby a.sort descending
group j2 by new
{
a.Field1,
a.Field2
} into ga
select new MyObject
{
field1= ga.Key.Field1,
field2= ga.Key.Field2,
manySubObjects = (from g in ga select new SubObject{
fielda= g.fielda,
fieldb= g.fieldb
}).ToList()
}).ToList();
However, with this code, when there is no record in table2 I get "manySubObject" as a list with one "SubObject" in it with all null values for the properties of "SubObject". What I really want is "manySubObjects" to be null if there is no values in table2.
In reply to your update, to create the null listing, you can do a ternary in your assignment of manySubObjects.
select new MyObject
{
field1= ga.Key.Field1,
field2= ga.Key.Field2,
manySubObjects =
(from g in ga select g).FirstOrDefaut() == null ? null :
(from g in ga select new SubObject {
fielda= g.fielda,
fieldb= g.fieldb
}).ToList()
}).ToList();
Here is a dotnetfiddle that tries to do what you're attempting. https://dotnetfiddle.net/kGJVjE
Here is a subsequent dotnetfiddle based on your comments. https://dotnetfiddle.net/h2xd9O
In reply to your comments, the above works with Linq to Objects but NOT with Linq to SQL. Linq to SQL will complain that it, "Could not translate expression ... into SQL and could not treat as a local expression." That's because Linq cannot translate the custom new SubObject constructor into SQL. To do that, you have to write more code to support translation into SQL. See Custom Method in LINQ to SQL query and this article.
I think we've sufficiently answered your original question about left joins. Consider asking a new question about using custom methods/constructors in Linq to SQL queries.
I think the desired Result that you want can be given by using GroupJoin()
The code Below will produce a structure like so
Field1, Field2, List < SubObject > null if empty
Sample code
var query = dc.Table1.Where(x => Table1_id == id).OrderBy(x => x.sort)
.GroupJoin(dc.Table2, (table1 => table1.Table1_id), (table2 => table2.Table2_id),
(table1, table2) => new MyObject
{
field1 = table1.Field1,
field2 = table1.Field2,
manySubObjects = (table2.Count() > 0)
? (from t in table2 select new SubObject { fielda = t.fielda, fieldb = t.fieldb}).ToList()
: null
}).ToList();
Dotnetfiddle link
UPDATE
From your comment I saw this
ga.Select(g = > new SubObject(){fielda = g.fielda, fieldb = g.fieldb})
I think it should be (depends on how "ga" is built)
ga.Select(g => new SubObject {fielda = g.fielda, fieldb = g.fieldb})
Please update your question with the whole query, it will help solve the issue.
** UPDATE BIS **
sentEmails = //ga.Count() < 1 ? null :
//(from g in ga select g).FirstOrDefault() == null ? null :
(from g in ga select new Email{
email_to = g.email_to,
email_from = g.email_from,
email_cc = g.email_cc,
email_bcc = g.email_bcc,
email_subject = g.email_subject,
email_body = g.email_body }).ToList()
Should be:
sentEmails = //ga.Count() < 1 ? null :
((from g in ga select g).FirstOrDefault() == null) ? null :
(from g in ga select new Email{
email_to = g.email_to,
email_from = g.email_from,
email_cc = g.email_cc,
email_bcc = g.email_bcc,
email_subject = g.email_subject,
email_body = g.email_body }).ToList()
Checks if the group has a First, if it doesn't the group doesn't have any records so the Action.Name for a Time Stamp has no emails to send. If the First isn't null the loop throw the group elements and create a list of Email,
var results =
(
// Use from, from like so for the left join:
from a in dc.Table1
from e in dc.Table2
// Join condition goes here
.Where(a.Id == e.Id)
// This is for the left join
.DefaultIfEmpty()
// Non-join conditions here
where a.Id == id
// Then group
group by new
{
a.Field1,
a.Field2
}
).Select(g =>
// Sort items within groups
g.OrderBy(item => item.sortField)
// Project required data only from each item
.Select(item => new
{
item.FieldA,
item.FieldB
}))
// Bring into memory
.ToList();
Then project in-memory to your non-EF-model type.

Get Count from one value in three table linq2sql select?

I just want the apartment complex count along with the other values. Only adding the count breaks the code. The error I get is "Sequence operators not supported for type 'System.String'." I have also tried changing apartCount to an int with no luck. Any help would be appreciated
using (var db = new DataClasses2DataContext())
{
var zips = (from s in db.ZipCodeServiceAvailabilities
join b in db.ZipCodeBoundaries on s.ZipCode equals b.ZipCode
join a in db.pdx_apart_views on s.ZipCode equals a.Zip_Code
where (s.IsServiced == 1 && b.Ordering % 10 == 0)
orderby b.ZipCode
select new
{
zipCode = b.ZipCode.Trim(),
latitude = b.Latitude,
longitude = b.Longitude,
apartCount = a.Apartment_complex.Count()
}).ToArray();
}
I think you miss group by clause in your query.
or you can use corolated sub query in select clause. please explain your question more

LINQ to Entities - Multiple Joins - Null Reference Exception on 'Select'

I am trying to convert a SQL query to a LINQ to entities query, but am having some problems with the LINQ select block.
Here is the SQL query which performs as expected:
SELECT distinct( p.PendingID,
p.Description,
p.Date,
f.Status,
u.UserName,
m.MapID
FROM Pending p
JOIN Users u
ON p.UserID = u.UserID
LEFT JOIN Forks f
ON p.PendingID = f.PendingID
LEFT JOIN Maps m
ON f.ForkID = m.ForkID
ORDER BY p.Date DESC
Here is the LINQ to entities query as I have it thus far:
var pList = (from pending in pendingItems
// JOIN
from user in userList.Where(u => pending.UserID == u.UserID)
// LEFT OUTER JOIN
from fork in forkList.Where(f => pending.ID == f.PendingID)
.DefaultIfEmpty()
// LEFT OUTER JOIN
from map in mapList.Where(m => fork.ID == m.ForkID)
.DefaultIfEmpty()
orderby pending.Date descending
select new
{
ItemID = pending.ID, // Guid
Description = pending.Description, // String
Date = pending.Date, // DateTime
Status = fork.Status, // Int32 (*ERROR HERE*)
UserName = user.UserName, // String
MapID = map.ID // Guid (*ERROR HERE*)
})
.Distinct()
.ToList();
The LINQ query fails on either of the following 2 lines, which attempt to assign values retrieved from left outer join results. If the following lines are omitted, the LINQ query completes without errors:
Status = fork.Status,
MapID = map.ID
Why are those 2 property assignments failing within the LINQ query's select block?
The problem is that due to your outer joins, fork and map may be null. Of course when they're null, you can't access their properties. You may need something like this:
Status = (fork == null) ? null : fork.Status,
MapID = (map == null) ? null : map.ID

Categories