linq query help - c#

I have a search method below using linq, my search citeria is based on one table so far but I have a particular citeria which requires another table :
Method:
public void search(string s)
{
var db = new CommerceEntities();
var products =
from p in db.Products
where (p.ModelName != null && p.ModelName.Contains(s))
|| SqlFunctions.StringConvert((double) p.ProductID).Contains(s)
|| (p.ModelNumber != null && p.ModelNumber.Contains(s))
|| (p.Description != null && p.Description.Contains(s))
|| SqlFunctions.StringConvert((double) p.CategoryID).Contains(s)
|| //stuck - See comment below
/* so far all in 'products' table, but I have one more citeria here that needs 'categories' table.
Ok, the query in SQL statement will be like this:
select ProductID, ModelName, ProductImage, Unitcost, products.Categoryid
from categories
join products
on (categories.CategoryID = products.categoryID)
where categories.categoryname = 'necklace' (replace necklace with my parameter 's')
order by products.ModelName
I am not sure how to 'integrate' it with my existing linq query. Please kindly advice. Thanks.
*/
select new
{
// Display the items
p.ProductID,
p.ModelName,
p.ProductImage,
p.UnitCost,
p.CategoryID,
};
ListView_Products.DataSourceID = null;
ListView_Products.DataSource = products;
}

var products = from p in db.Products
join c in categories on c.CategoryID equals p.categoryID
where (/* all you conditions in either tables */)
select new
{
p.ProductID,
p.ModelName,
p.ProductImage,
p.UnitCost,
p.CategoryID
/* plus what ever you want from categories */
};

Related

C# LINQ select from table and join multiple table and join a SQL query view in SQL server

I have a code that selecting from table and joining multiple tables and joining dbContext.Database.sqlQuery from view in sql server.
But it gives me this error
Unable to create a constant value of type
'ITManagement.Models.Employee'. Only primitive types or enumeration
types are supported in this context.
My code
public JsonResult getEmployeeAsset(EmployeeController employee)
{
var employeeID = Request.QueryString["employeeID"];
var devices = (from asset in db.Devices
where asset.EmployeeID == employeeID
join brand in db.DeviceBrands on asset.Brand equals brand.ID
join model in db.DeviceModels on asset.Model equals model.ID
join type in db.DeviceTypes on asset.DeviceType equals type.ID
join room in db.Rooms on asset.FullRoomCode equals room.FullCode
//if device has last employee
join lsEmp in db.Database.SqlQuery<LDAPUsers>("SELECT * FROM V_LDAP_Users") on asset.LastEmployeeID equals lsEmp.employeeID into lstEmp
join sysUser in db.AspNetUsers on asset.sscUser equals sysUser.Id
from lastEmployee in lstEmp.DefaultIfEmpty()
select new
{
deviceID = asset.ID,
SerialNumber = asset.SerialNumber,
Type = type.Type,
BrandName = brand.BrandName,
ModelName = model.ModelName,
MaccCode = asset.MaccCode,
PONumber = asset.PONumber,
WarrantyDate = asset.WarrantyDate.ToString(),
MacAddress = asset.MacAddress,
WIFIMacAddress = asset.WIFIMacAddress,
PCName = asset.PCName,
LastEmployee = asset.LastEmployeeID + "-" + lastEmployee.employeeName,
Shared = asset.Shared == 1 ? "True" : "False",
Location = room.RoomName,
RecordedBy = sysUser.Name,
requestID = (from request in db.StoreRequests where request.DeviceID == asset.ID && request.State == 1 && request.VoucherType == "ASD" orderby request.ID select request.ID).FirstOrDefault()
}).DefaultIfEmpty();
return Json(new { assets = devices == null ? null : devices }, JsonRequestBehavior.AllowGet);
}
Your help please, thanks.
First of all, have you tried nested queries by commenting them out?
for example;
//join lsEmp in db.Database.SqlQuery<LDAPUsers>("SELECT * FROM V_LDAP_Users") on asset.LastEmployeeID equals lsEmp.employeeID into lstEmp
//requestID = (from request in db.StoreRequests where request.DeviceID == asset.ID && request.State == 1 && request.VoucherType == "ASD" orderby request.ID select request.ID).FirstOrDefault()
If there is no problem in these two, you can quickly find out which one is causing the problem by commenting the fields.
Tip: Also, more than 3 joins will affect your query performance. Try to split your queries as much as possible.

Convert SQL WHERE with COALESCE into LINQ

I'm trying to produce a filtered list in a controller to pass to a MVC view.
I know I can do this in SQL: (I've shortened the SQL somewhat...)
SELECT ItemID, Item, SupplierID, Supplier, BrandID, Brand, CategoryID, Category, SubCategoryID, SubCategory, TypeID, Type
FROM Table1 join Table 2... etc
WHERE (supplierid = #SID OR #SID = -99) AND
(brandid = #BID OR #BID = -99) AND
(categoryid = #CID OR #CID = -99) AND ....
but I would like to know the best way to do it in C# & Linq (or if it's just better to call a stored proc based on the above and use that instead)
Here is the start of my controller:
public async Task<ActionResult> Index(int? supplierid, int? itembrandid, int? categoryid, int? subcategoryid, int? itemtypeid)
{
List<DTOClasses.ItemsListDTO> items = new List<DTOClasses.ItemsListDTO>();
items = await (from i in db.Items
join b in db.ItemBrand on i.BrandID equals b.BrandID
join s in db.Suppliers on b.SupplierID equals s.SupplierID
join sc in db.SubCategories on i.SubCategoryID equals sc.SubCategoryID
join c in db.Categories on sc.CategoryID equals c.CategoryID
where // get stuck at this point
select new DTOClasses.ItemsListDTO
{
ItemId = i.ItemID
//etc..
}
).ToListAsync();
return View(items);
}
I wanted to avoid having to write potentially 25 nested if statements if possible.
** Edit
for example - to keep the linq query neat and tidy, have each combination of input params with their own query to execute.
if( BID != null && SID != null && CID != null)
{ query 1}
else
if (BID != null && SID != null && CID == null)
{query 2}
C# has the equivalent of COALESCE using the null coalescing operator ??, but I am not sure how that is relevant to your question. C# also has the exact equivalent of multiple conditions with AND and OR in a where, so just like SQL you can do:
items = await (from i in db.Items
join b in db.ItemBrand on i.BrandID equals b.BrandID
join s in db.Suppliers on b.SupplierID equals s.SupplierID
join sc in db.SubCategories on i.SubCategoryID equals sc.SubCategoryID
join c in db.Categories on sc.CategoryID equals c.CategoryID
where ((b.BrandID == BID || BID == -99) &&
(s.SupplierID == SID || SID == -99) &&
(sc.CategoryID == CID || CID == -99))
select new DTOClasses.ItemsListDTO
{
ItemId = i.ItemID
//etc..
}
).ToListAsync();
If the repetitiveness bothers you as much as it does me, you could hide it in a helper extension method, but that assumes the magic number stays the same -99 for all the ID types (and ignores how bad using a magic number actually is).
static class IDExt {
static bool IDMatches(this int anID, int testID) => testID == anID || testID == -99;
}
then you have
where b.BrandID.IDMatches(BID) && s.SupplierID.IDMatches(SID) && sc.CategoryID.IDMatches(SID)

how can i write this query entity framework

how can write this in CS what is should replace **** with ??
MSSQL Query:
SELECT *FROM Advertisement INNER JOIN
SubCategory ON Advertisement.SubCategoryID = SubCategory.SubCategoryID INNER JOIN
CategoryHaveSubCategory ON SubCategory.SubCategoryID = CategoryHaveSubCategory.SubCategoryID INNER JOIN
Category ON CategoryHaveSubCategory.CategoryID = Category.CategoryID WHERE (CategoryHaveSubCategory.CategoryID = 1)
C# Query:
query = (from a in db.Advertisements
orderby a.Date descending
where **** && a.Deleted == false
select a).Skip(skip).Take(PAGE_SIZE);
You didn't include an important part of your Advertisement entity - the navigation properties. From the relation shown I assume you have something like this
class Advertisement
{
// ...
public SubCategory SubCategory { get; set; }
}
Then the condition you are asking for should be something like this
where a.SubCategory.Categories.Any(c => c.CategoryID == 1) && a.Deleted == false
query = (from a in db.Advertisements
orderby a.Date descending
where a.SubCategory.Categories.Any(c => c.CategoryID == 1)
&& a.Deleted == false
select a).Skip(skip).Take(PAGE_SIZE);

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.

How to rewrite a SQL query in LINQ to Entities?

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
};

Categories