Group Linq Query Results in a String - c#

Here is what I am trying to accomplish:
I have a list of companies that offer different services. I am trying to group the services together of a company in a string format so when I can export to excel it shows up in one column.
Right now if a company has 4 services, they will show up 4 different times in query. Which is logical, just want to group them together.
Here is what I have tried and get "A lambda expression with a statement body cannot be converted to an expression tree"
Services = (from cc in CharityCategories join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.Trim()}).Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString() ,
LinqPad shows the error on the line, right after StringBuilder(), "Aggregate(new StringBuilder(), (a, b)"
I can get them to group in a link and when clicked, that link lists them in a format like { myservice = 1} - This is why I am using .Append

use below code then change your logic accordingly, hopefully this will work
Correction in cg.Key.ToString().Trim()
Services = (from cc in CharityCategories
join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.ToString().Trim() }).Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString();
I don't know your requirement, we can correct this if you can provide exact expected result

Well as the error says you cannot convert a lambda expression with a statement body to an expression tree. Entity framework uses expression trees to convert to sql statements. However, you can materialize the results first with (AsEnumerable) and then use your code with linq to objects.
Services = (from cc in CharityCategories
join c in Cao_Categories on cc.CategoryID equals c.CategoryID
join chy in CharityYears on cc.CharityYearID equals chy.CharityYearID
where chy.CampYearID == 5 && chy.StatusID == 1
group c by c.Category into cg
select new { Categories = cg.Key.ToString().Trim() })
.AsEnumerable()
.Aggregate(new StringBuilder(), (a, b) =>
{
if (a.Length > 0)
a.Append(",");
a.Append(b.ToString().Split('=')[1].Replace(" }", ""));
return a;
}).ToString();
Important: The aggregation will take place after all the rows were retrieved from the DB.

Assuming your result set can be represented in the form of the following class
public class Category
{
public string Name{get;set;}
public string Product{get;set;}
}
You could write the following LINQ query to get the result in the way you wished to:
var testList = new List <Category> {
new Category {Name = "A",Product = "decks"},
new Category {Name = "B",Product = "cards"},
new Category {Name = "C",Product = "paper"},
new Category {Name = "A",Product = "scissor"},
new Category {Name = "B",Product = "crates"},
new Category {Name = "C",Product = "rocks"}
};
var finalList = testList
.GroupBy(x => x.Name)
.Select(x => new {
Category =x.Key,
Items = String.Join(",", x.Select(y => y.Product))
});
Thus the result set would be in the form of:
Category A -> decks, scissor
Category B-> cards,crates
Category C-> paper, rocks
Do mark as answer if this helps.

Related

Linq using join and grouping

I'm trying to do something very simple.
I have two tables in my database that I would like to query using linq.
Table of Books, and table of GenreTypes. The result of this query would go to my web Api.
Here is a code snippet:
public List<BooksChart> GetBooksChart()
{
var results = from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select (z => new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
return results;
}
public class BooksChart
{
public string category_name;
public int value;
}
The results of the grouping "n" I would like to store them in BooksChart class to construct the Api.
This code is not compiling.
Previously, I was querying only one table of Books which I have divided into Books and GenreTypes.
My previous working code for querying Books was :
var results = _dbcontext
.Books
.GroupBy(x => x.GenreType)
.Select(z => new BooksPieChart
{
category_name = z.Key,
value = z.Count()
}).ToList();
return results;
EDIT
What I want to achieve in SQL is the following:
select count(*), g.Name
from books b, GenreTypes g
where b.GenreTypeId = g.Id
group by g.Name;
You are mixing the two syntax options of query and method. For query syntax you need to do the projection (select) like this:
return (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart {
category_name = n.Key,
value = n.Count()
}).ToList();
The format of (z =>....) is the declaration of the labmda passed to the Select method.
Site notes:
As #Rabbi commented, since you are using EF, consider properly defining navigation properties. It will make querying simpler.
Side note for the sql - consider using joins instead of multiple tables in the from: INNER JOIN ON vs WHERE clause
The parentheses must surround the whole query, like so:
var results = (from b in _dbcontext.Books
join g in _dbcontext.GenreTypes
on b.GenreTypeId equals g.Id
group g by g.Name into n
select new BooksChart
{
category_name = n.Key,
value = n.Count()
}).ToList();
The compilation error is due to this (z => which is not needed at all.

Select by ID from a list

I have a table of products and a table of categories, I can select by the ID of the Category like this:
var result = db.tblProducts.Where(p => p.tblCategories.Any(c => c.ID == 1));
However, I want to be able to select based on a list of Categories:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => r.tblCategories.Any(t => catIDs.Contains(t.ID)));
I get the following error:
LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method, and this method cannot be translated into a store expression.
Presumably because I am using Contains to compare entities to local variables. Is there a way to do this?
Try create Expression from values. F.e.:
static Expression MakeOrExpression<T, P>(Expression<Func<T, P>> whatToCompare, IEnumerable<P> values)
{
Expression result = Expression.Constant(true);
foreach (var value in values)
{
var comparison = Expression.Equal(whatToCompare, Expression.Constant(value));
result = Expression.Or(result, comparison);
}
return result;
}
How to use:
var results = db.tblProducts.Where(r => r.tblCategories.Any(MakeOrExpression(t => t.ID, catIDs)));
The method MakeOrExpression will create an expression t.ID == 1 || t.ID == 2 || t.ID == 3 for list { 1, 2, 3 } dynamically, and then EF will translate it to SQL condition.
Maybe you can use this:
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblCategories
.Where(t => catIDs.Contains(t.ID))
.SelectMany(t => t.tblProducts)
.Distinct();
Try this:
var query=from p in db.tblProducts
from c in p.tblCategories
where catIDs.Contains(c.ID)
select p;
If at least one of the categories of the product is in the catIDs list, then the product will be seleted.
Another option could be start by the categories (I'm guessing you have a many to many relationship between Product and Category and you have a collections of products in your Category entity):
var query=db.tblCategories.Where(c => catIDs.Contains(c.ID)).SelectMany(c=>c.tblProducts).Distinct();
Try this code :
var catIDs = new List<int>() { 1,2,3 };
var results = db.tblProducts.Where(r => catIDs.Any(c => c == r.tblCategories.Id));

How to convert Generic List<anonymous type > to Generic List <Classname>?

I have a database, where the CPU usage is inserted for every 'n' seconds and my task is to get the latest CPU entry from the database. I would like to display the latest CPU entry from the database tat belongs to the particular server , and I would like to save the result into a generic list.
what I have tried,
string match = "ServerX"
List<UsageCPU> cpus = (from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select new { ServerID = b.Key,
Usage = b.FirstOrDefault() }).ToList();
and I would like to loop through the result cpus to perform further other steps.
but I get the following error
Cannot implicitly convert type
'System.Collections.Generic.List<AnonymousType#1>' to
'System.Collections.Generic.List<FNC_RAQIT_HM_UI.UsageCPU>'.
EDIT
UPDATE
The following query from Satpal worked for me.. but using var
var cpus = (from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select new
{
ServerID = b.Key,
Usage = b.FirstOrDefault().Usage
}).ToList();
foreach (var item in cpus)
{
if(bla bla..)
}
kindly help !!
Use
List<UsageCPU> cpus = (from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select new UsageCPU //Added UsageCPU
{
ServerID = b.Key,
Usage = b.FirstOrDefault().Usage
}).ToList();
Update as per error
The entity cannot be constructed in a LINQ to Entities query
List<UsageCPU> cpus = (from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select new
{
ServerID = b.Key,
Usage = b.FirstOrDefault().Usage
})
.AsEnumerable()
.Select(x => new UsageCPU
{
ServerID ,
Usage
}).ToList();
try to select a not new
when you select new you get anonymous type which can't be casted to UsageCPU type
(from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select a)
or use var and then loop through the results
var cpus = (from a in db.UsageCPUs
where a.ServerID.Contains(match)
group a by a.ServerID into b
orderby b.Key
select new { ServerID = b.Key,
Usage = b.FirstOrDefault() }).ToList();

How to perform Join between multiple tables in LINQ lambda

I am trying to perform a Join between multiple tables in LINQ. I have the following classes:
Product {Id, ProdName, ProdQty}
Category {Id, CatName}
ProductCategory{ProdId, CatId} //association table
And I use the following code (where product, category and productcategory are instances of the above classes):
var query = product.Join(productcategory, p => p.Id, pc => pc.ProdID, (p, pc) => new {product = p, productcategory = pc})
.Join(category, ppc => ppc.productcategory.CatId, c => c.Id, (ppc, c) => new { productproductcategory = ppc, category = c});
With this code I obtain an object from the following class:
QueryClass { productproductcategory, category}
Where producproductcategory is of type:
ProductProductCategoryClass {product, productcategory}
I do not understand where the joined "table" is, I was expecting a single class that contains all the properties from the involved classes.
My aim is to populate another object with some properties resulting from the query:
CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });
how can I achieve this goal?
For joins, I strongly prefer query-syntax for all the details that are happily hidden (not the least of which are the transparent identifiers involved with the intermediate projections along the way that are apparent in the dot-syntax equivalent). However, you asked regarding Lambdas which I think you have everything you need - you just need to put it all together.
var categorizedProducts = product
.Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
.Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new { ppc, c })
.Select(m => new {
ProdId = m.ppc.p.Id, // or m.ppc.pc.ProdId
CatId = m.c.CatId
// other assignments
});
If you need to, you can save the join into a local variable and reuse it later, however lacking other details to the contrary, I see no reason to introduce the local variable.
Also, you could throw the Select into the last lambda of the second Join (again, provided there are no other operations that depend on the join results) which would give:
var categorizedProducts = product
.Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
.Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new {
ProdId = ppc.p.Id, // or ppc.pc.ProdId
CatId = c.CatId
// other assignments
});
...and making a last attempt to sell you on query syntax, this would look like this:
var categorizedProducts =
from p in product
join pc in productcategory on p.Id equals pc.ProdId
join c in category on pc.CatId equals c.Id
select new {
ProdId = p.Id, // or pc.ProdId
CatId = c.CatId
// other assignments
};
Your hands may be tied on whether query-syntax is available. I know some shops have such mandates - often based on the notion that query-syntax is somewhat more limited than dot-syntax. There are other reasons, like "why should I learn a second syntax if I can do everything and more in dot-syntax?" As this last part shows - there are details that query-syntax hides that can make it well worth embracing with the improvement to readability it brings: all those intermediate projections and identifiers you have to cook-up are happily not front-and-center-stage in the query-syntax version - they are background fluff. Off my soap-box now - anyhow, thanks for the question. :)
What you've seen is what you get - and it's exactly what you asked for, here:
(ppc, c) => new { productproductcategory = ppc, category = c}
That's a lambda expression returning an anonymous type with those two properties.
In your CategorizedProducts, you just need to go via those properties:
CategorizedProducts catProducts = query.Select(
m => new {
ProdId = m.productproductcategory.product.Id,
CatId = m.category.CatId,
// other assignments
});
take look at this sample code from my project
public static IList<Letter> GetDepartmentLettersLinq(int departmentId)
{
IEnumerable<Letter> allDepartmentLetters =
from allLetter in LetterService.GetAllLetters()
join allUser in UserService.GetAllUsers() on allLetter.EmployeeID equals allUser.ID into usersGroup
from user in usersGroup.DefaultIfEmpty()// here is the tricky part
join allDepartment in DepartmentService.GetAllDepartments() on user.DepartmentID equals allDepartment.ID
where allDepartment.ID == departmentId
select allLetter;
return allDepartmentLetters.ToArray();
}
in this code I joined 3 tables and I spited join condition from where clause
note: the Services classes are just warped(encapsulate) the database operations
public ActionResult Index()
{
List<CustomerOrder_Result> obj = new List<CustomerOrder_Result>();
var orderlist = (from a in db.OrderMasters
join b in db.Customers on a.CustomerId equals b.Id
join c in db.CustomerAddresses on b.Id equals c.CustomerId
where a.Status == "Pending"
select new
{
Customername = b.Customername,
Phone = b.Phone,
OrderId = a.OrderId,
OrderDate = a.OrderDate,
NoOfItems = a.NoOfItems,
Order_amt = a.Order_amt,
dis_amt = a.Dis_amt,
net_amt = a.Net_amt,
status=a.Status,
address = c.address,
City = c.City,
State = c.State,
Pin = c.Pin
}) ;
foreach (var item in orderlist)
{
CustomerOrder_Result clr = new CustomerOrder_Result();
clr.Customername=item.Customername;
clr.Phone = item.Phone;
clr.OrderId = item.OrderId;
clr.OrderDate = item.OrderDate;
clr.NoOfItems = item.NoOfItems;
clr.Order_amt = item.Order_amt;
clr.net_amt = item.net_amt;
clr.address = item.address;
clr.City = item.City;
clr.State = item.State;
clr.Pin = item.Pin;
clr.status = item.status;
obj.Add(clr);
}
var query = from a in d.tbl_Usuarios
from b in d.tblComidaPreferidas
from c in d.tblLugarNacimientoes
select new
{
_nombre = a.Nombre,
_comida = b.ComidaPreferida,
_lNacimiento = c.Ciudad
};
foreach (var i in query)
{
Console.WriteLine($"{i._nombre } le gusta {i._comida} y naciĆ³ en {i._lNacimiento}");
}
it has been a while but my answer may help someone:
if you already defined the relation properly you can use this:
var res = query.Products.Select(m => new
{
productID = product.Id,
categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
}).ToList();

Linq: doing a Where() clause on a nested model

This may look complicated, but I am really just trying to select all records and their children from A where certain conditions exist in their children. Looking for the syntax I can add to the A variable that allows me to filter a bunch of conditions (like a specification pattern?)
If you have a nested view like this:
var a = from Arow in Atable
where ???
select new AViewModel { // (image Products)
id = Arow.id,
name = Arow.Name,
Brows = (from Brow in Arow.Brows
select new BViewModel { // (sold in different regions)
id = Brow.id,
name = Brow.Name,
Crows = (from Crow in Brow.Crows
select new CViewModel { // (and in many stores)
id = Crow.id,
name = Crow.Name
}
}
};
And text representing queries from a web page like this (Specification Pattern?)
We used JQuery selectors for the operators (like ^= means "starts with")
filter[] = { "Brow.Name = Joe", "Crow.Name = Kim" }
How could you filter the expression A with those criteria? In other words, can you have a where expression like a.Where() that can filter nested properties?
My poor attempt:
var b = from row in a
where a.Brow.Where(b => b.name == "Joe") &&
a.Brow.Crow.Where(c => c.name == "Kim")
select row;
What I really need is something like this:
Select *
from A join B on A.key = B.key join C on B.key = C.key -- propagated keys
where exists (select null from B where A.key = B.key and B.Name = "Joe") and
exists (select null from C where A.key = C.key and C.Name = "Kim")
Especially if I can do this:
var result = a.Where(first).Where(second);
Your "poor attempt" isn't so far off. Just substitute Any() for Where():
var b = from row in a
where a.Brow.Any(b => b.name == "Joe") &&
a.Brow.Crow.Any(c => c.name == "Kim")
select row;
Edited to add:
You might want to do a case-insensitive comparison, though, e.g., .Any(b => b.name.Equals("Joe", StringComparison.CurrentCultureIgnoreCase)).
Or for fun, you can do the lambda equivilant:
var b = a.where(x => x.Brow.Any(b => b.name == "Joe")
&& x.Brow.Crow.Any( c => c.name == "Kim")
.Select(y => y.row);

Categories