product sale qty sum and name in linq group query - linq

I have a product sales data and want to show the summary of sale grouped by product id.
Summary result should show product name and total sales. How can I select a field along with groupby result and that field is not the key field.
public partial class SaleOrderDetail
{
public int Id { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal LineTotal { get; set; }
}
var query = from saleorder in _dbContext.SaleOrderDetail
group saleorder by saleorder.ProductId into salesummary
select new
{
productid = salesummary.Key,
prdouctname = salesummary.First().ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};
I got the error invalidoperationException because of First() for product name.

You have to include ProductName in grouping Key.
var query =
from saleorder in _dbContext.SaleOrderDetail
group saleorder by new {saleorder.ProductId, saleorder.ProductName} into salesummary
select new
{
productid = salesummary.Key.ProductId,
prdouctname = salesummary.Key.ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};

Making SaleOrderDetail as AsEnumerable() did the trick. For SQL Expression it will work if make it as AsEnumerable() or .ToList<> etc.
var query = from saleorder in _dbContext.SaleOrderDetail.AsEnumerable()
group saleorder by saleorder.ProductId into salesummary
select new
{
productid = salesummary.Key,
prdouctname = salesummary.First().ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};

Related

Linq query to select any in list against a list

Using EF Core code-first, and I want to find any record with a similar list of a foreign entities to the entity I already have.
public class ClownModel {
public int Id { get; set; }
public List<CarModel> Cars { get; set; }
}
public class CarModel {
public int Id { get; set; }
}
var MyClown = new ClownModel() { /*add properties*/ }
//or maybe an existing record selected from database, just some ClownModel instance
Basically, "Select all the ClownModels where they have any Cars.Id that are in my MyClown.Cars"
Assuming that ClownModel has unique CarModel Id's, you can use the following query:
Matches All Ids
var ids = MyClown.Cars.Select(c => c.Id).ToList();
var query =
from cm in ctx.ClownModel
where cm.Cars.Where(c => ids.Contains(c.Id)).Count() == ids.Count
select cm;
Matches Any Ids
var ids = MyClown.Cars.Select(c => c.Id).ToList();
var query =
from cm in ctx.ClownModel
where cm.Cars.Where(c => ids.Contains(c.Id)).Any()
select cm;

LINQ - Left Join And Group By

I have a problem. This is my products class
public int ProductID { get; set; }
public string ProductName { get; set; }
public int CategoryID { get; set; }
public decimal Price { get; set; }
public int CompanyID { get; set; }
public string QuantityPerUnit { get; set;
And this is OrderDetail Class
public int DetailID { get; set; }
public int OrderID { get; set; }
public int ProductID { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public bool Discount { get; set; }
so ı wrote this query in mssql. So, how can I write this query on linq ?
select Products.ProductID, Sum(OrderDetails.Quantity) as 'NumberOfOrdered' from Products
left join OrderDetails on Products.ProductID = OrderDetails.ProductID
group by Products.ProductID
order by Products.ProductID
I Wrote this query but it doesn't work.
from p in _context.Products
join d in _context.OrderDetails on p.ProductsID equals d.ProductsID into t1
from d1 in t1.DefaultIfEmpty()
group new { p, d } by new { p.ProductsID, d.ProductsID } into g
orderby g.Key.ProductsID
select new ProductsOrderDetails
{
ProductsID = g.Key.ProductsID,
QuantityToplam = g.(x=>x.d.Quantity)
}).ToList();
So you have Products, and OrderDetails. Every Product has zero or more OrderDetails, every OrderDetail is the detail of exactly one Product, namely the Product that OrderDetail.ProductId belongs to.
Requirement: From every Product, give me the Id and the sum of the Quantities of all its OrderDetails
Whenever you need the sequence of items, every item with its sub-items, like Schools with their Students, Authors with their Books, Orders with their OrderDetails, consider using one of the overloads of Enumerable.GroupJoin
In this case, I don't want a simple Product with its OrderDetails, I want to compose a special result. For every Product I want the Id and the sum of Quantities of all its OrderDetauls. Therefore I use the Overload with a parameter resultSelector.
var result = dbContext.Products.GroupJoin( // GroupJoin Products
dbContext.OrderDetails, // with Orderdetails
product => product.Id, // From every Product take the Id
orderDetail => orderDetail.ProductId, // From every OrderDetail that the foreign key
// resultSelector: for every Product and all its zero or more OrderDetails
// create one new object
(product, orderDetailsOfThisProduct) => new
{
Id = product.Id,
QuantitiesToPlam = orderDetailsOfThisProduct
.Select(orderDetail => orderDetail.Quantity)
.Sum(),
});
For property QuantitiesToPlam: for every OrderDetail that belongs to this Product: take the value of property Quantity, and Sum them.

EF Core - many queries sent to database for subquery

Using EF Core 2.2.2, I have a table in my database which is used to store notes for many other tables. In other words, it's sortof like a detail table in a master-detail relationship, but with multiple master tables. Consider this simplified EF Model:
public class Person
{
public Guid PersonID { get; set; }
public string Name { set; set; }
}
public class InvoiceItem
{
public Guid InvoiceItemID { get; set; }
public Guid InvoiceID { get; set; }
public string Description { get; set; }
}
public class Invoice
{
public Guid InvoiceID { get; set; }
public int InvoiceNumber { get; set; }
public List<Item> Items { get; set; }
}
public class Notes
{
public Guid NoteID { get; set; }
public Guid NoteParentID { get; set; }
public DateTime NoteDate { get; set; }
public string Note { get; set; }
}
In this case, Notes can store Person notes or Invoice notes (or InvoiceItem notes, though let's just say that the UI doesn't support that).
I have query methods set up like this:
public IQueryable<PersonDTO> GetPersonQuery()
{
return from p in Context.People
select new PersonDTO
{
PersonID = p.PersonID,
Name = p.Name
};
}
public List<PersonDTO> GetPeople()
{
return (from p in GetPersonQuery()
return p).ToList();
}
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber
};
}
public List<InvoiceDTO> GetInvoices()
{
return (from i in GetInvoiceQuery()
return i).ToList();
}
These all work as expected. Now, let's say I add InvoiceItems to the Invoice query, like this:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList()
};
}
That also works great, and issues just a couple queries. However, the following:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in Context.Notes
where i.InvoiceID = n.NoteParentID
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};
}
sends a separate query to the Note table for each Invoice row in the Invoice table. So, if there are 1,000 invoices in the Invoice table, this is sending something like 1,001 queries to the database.
It appears that the Items subquery does not have the same issue because there is an explicit relationship between Invoices and Items, whereas there isn't a specific relationship between Invoices and Notes (because not all notes are related to invoices).
Is there a way to rewrite that final query, such that it will not send a separate note query for every invoice in the table?
The problem is indeed the correlated subquery versus collection navigation property. EF Core query translator still has issues processing such subqueries, which are in fact logical collection navigation properties and should have been processed in a similar fashion.
Interestingly, simulating collection navigation property with intermediate projection (let operator in LINQ query syntax) seems to fix the issue:
var query =
from i in Context.Invoices
let i_Notes = Context.Notes.Where(n => i.InvoiceID == n.NoteParentID) // <--
select new InvoiceDTO
{
InvoiceID = i.InvoiceID,
InvoiceNumber = i.InvoiceNumber,
Items = (from ii in i.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in i_Notes // <--
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};

Linq sort by products but display cateogires

So I have an issue where the code is Selecting a Category
and I need to change sort order to sort by product.Name Then By category.name.
But the problem is I still want to select a category, but how do I
sort by product.name first without adding an extra join or select.
from category in categories
select category.name
orderby category.Name //orderby category name
Later on in view I loop foreach(category.products) and pass in category.product[i] to view to display
But sort order is wrong, order is always by Category.Name
How do I sort by Product.Name first and then by Category.Name?
Will SelectMany help? Again I do not want to disrupt the select
part of my query, just the Order by stuff.
class Product
{
public string Name { get; set; }
public int CategoryID { get; set; }
}
class Category
{
public string Name { get; set; }
public int ID { get; set; }
}
// Specify the first data source.
static List categories = new List()
{
new Category(){Name="Beverages", ID=001},
new Category(){ Name="Condiments", ID=002},
new Category(){ Name="Vegetables", ID=003},
new Category() { Name="Grains", ID=004},
new Category() { Name="Fruit", ID=005}
};
// Specify the second data source.
static List products = new List()
{
new Product{Name="Cola", CategoryID=001},
new Product{Name="Tea", CategoryID=001},
new Product{Name="Mustard", CategoryID=002},
new Product{Name="Pickles", CategoryID=002},
new Product{Name="Carrots", CategoryID=003},
new Product{Name="Bok Choy", CategoryID=003},
new Product{Name="Peaches", CategoryID=005},
new Product{Name="Melons", CategoryID=005},
};
Oh, I saw you query, it was badly formatted.
You need to order by Product.Name group by Category.Name
//LinqPad code
class Category
{
public string Name { get; set; }
public int ID { get; set; }
public List<Product> products { get; set;}
}
class Product
{
public string Name { get; set; }
public int ID { get; set; }
}
void Main()
{
// Specify the second data source.
List<Product> BevProducts = new List<Product>()
{
new Product{Name="ZCola"},
};
// Specify the third data source.
List<Product> CondProducts = new List<Product>()
{
new Product{Name="Sugar"},
};
// Specify the first data source.
List<Category> categories = new List<Category>()
{
new Category(){Name="Beverages", ID=001, products=BevProducts},
new Category(){ Name="Condiments", ID=002, products=CondProducts},
};
var sortedCats = categories.OrderBy(c => c.ID).ToList();
foreach (var category in sortedCats)
{
//display category
System.Console.Out.WriteLine(category.Name);
//Assuming each category contains exactly one product in the list ie 1 to 1 relationship
// how can I sort by product.Name, so if ZCola comes before Sugar, ZCola's Category (Beverages) sorts before Condiments
// so in this Case Beverages, Condiments is the right order, because ZCola comes after Sugar.
var sortedProductsPerCategory = category.products.OrderBy(p => p.Name).ToList();
foreach (var product in sortedProductsPerCategory)
{
//display product
System.Console.Out.WriteLine(" " + product.Name);
}
}
}
class Category
{
public string Name { get; set; }
public int ID { get; set; }
public List<Product> products { get; set;}
}
class Product
{
public string Name { get; set; }
public int ID { get; set; }
}
void Main()
{
// Specify the second data source.
List<Product> BevProducts = new List<Product>()
{
new Product{Name="ZCola"},
};
// Specify the third data source.
List<Product> CondProducts = new List<Product>()
{
new Product{Name="Sugar"},
};
// Specify the first data source.
List<Category> categories = new List<Category>()
{
new Category(){Name="Beverages", ID=001, products=BevProducts},
new Category(){ Name="Condiments", ID=002, products=CondProducts},
};
var sortedCats = categories.OrderBy(c => c.products.Min(p => p.Name)).ToList();
foreach (var category in sortedCats)
{
//display category
System.Console.Out.WriteLine(category.Name);
//Assuming each category contains exactly one product in the list ie 1 to 1 relationship
// how can I sort by product.Name, so if ZCola comes before Sugar, ZCola's Category (Beverages) sorts before Condiments
// so in this Case Beverages, Condiments is the right order, because ZCola comes after Sugar.
var sortedProductsPerCategory = category.products.OrderBy(p => p.Name).ToList();
foreach (var product in sortedProductsPerCategory)
{
//display product
System.Console.Out.WriteLine(" " + product.Name);
}
}
}

Group records by id

I am getting records like this returned from a stored procedure:
Id FullName Review Rating
---------------------------------------------
1 john sk 4.5
1 john hhh 3.5
1 john jhj 1.5
2 rig www 3.5
2 rig eee 1.5
This is my query which return records like above:
var empDetails = context.Database.SqlQuery<SearchWorkerDetail>("exec SearchWorkerDetail #param1", new SqlParameter("param1", searchKeyword) )
.Select( d => new
{
id = d.Id,
FullName = d.FullName,
Email = d.Email,
ServiceDescription = d.ServiceDescription,
Skills = d.Skills,
Name = d.Name,
r = d.Review,
averagerating = d.Rating
}).ToList();
Now I want to group the records by Id and want to select data.
Expected output:
SearchWorkerDetail=
[0]:{
Id:1
FullName:John
Email:john#yahoo.com
ReviewList:
{
[0]: Review=sk
rating=4.5
[1]: Review=hhh
rating=3.5
[2]: Review=jhj
rating=1.5
}
[1]:{
Id:2
FullName:rig
Email:rig#yahoo.com
ReviewList:
{
[0]: Review=www
rating=3.5
[1]: Review=eee
rating=1.5
}
}
For corresponding id I want list of reviews (1 to many relation between worker and review)
My class structure:
[DataContract]
public class SearchWorkerDetail
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FullName { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string ServiceDescription { get; set; }
[DataMember]
public string Skills { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Review { get; set; }
[DataMember]
public Nullable<decimal> Rating { get; set; }
[DataMember]
public List<ReviewModel> RatingList { get; set; }
}
My query is like this:
var data = empDetails.GroupBy(m=>m.id)
.Select(g => new SearchWorkerDetail
{
id =g.Key,
FullName = g.FullName,
Reviewlist =
}
).ToList();
Here it is not allowing me to select FullName and other (eg: Email, skillDescription, Skills etc)
How do I do this?
There is no FullName property on g because it's a group not a single object. However you can get the FullName of first record in the group then create a ReviewList for each record in the group by using another Select:
empDetails.GroupBy(m => m.id)
.Select(g => new SearchWorkerDetail
{
id = g.Key,
FullName = g.First().FullName,
Reviewlist= g.Select(x => new ReviewModel
{
Review = x.Review,
rating = x.Rating
}.ToList()
}).ToList();
Or you can also group by based on FullName:
empDetails.GroupBy(m => new { m.id, m.FullName })
.Select(g => new SearchWorkerDetail
{
id = g.Key.id,
FullName = g.Key.FullName,
/* rest is the same */
}).ToList();

Resources