How to make nested list from one base list - c#

I would like to make nested list from this one list
public class Product
{
string Id;
string ProductName;
decimal Price;
string Supplier;
int Quantity;
string VersionId;
string TypeId;
}
public class ProductTypeDto
{
public string TypeId { get; set; }
public string ProductName { get; set; }
public string Price { get; set; }
public List<ProductVersionDto> ProductVersions { get; set; }
}
public class ProductVersionDto
{
public string VersionId { get; set; }
public string ProductName { get; set; }
public string Supplier { get; set; }
public int Quantity { get; set; }
}
I would like to know how I can create a list of ProductTypeDto using linq c# .
I need to get all products with the same TypeId , and ProductVersions field should contain products with same version Id (and of course same TypeId).
I don't want to use foreach loops, and I dont want to loop over products twice to make this nested list.. I think there would be a better way using Linq, would be great if you can help..
Edit: I add here what I have done so far, but it is not the way I want this to be fixed.
List<ProductTypeDto> products = this._dbContext
.Products
.Where(product => product.TypeId == query.TypeId)
.Select(product => new ProductTypeDto()
{
TypeId = product.TypeId,
ProductName = product.ProductName,
Price = product.Price,
ProductVersions = product.Products.Where(p => p.TypeId == product.TypeId)
.Select(p => new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
}).ToList()
})
.ProjectTo<ProductTypeDto>(this._config)
.ToListAsync(cancellationToken);
This is the result I want to get:
var product1 = new Product() { Id = 1, ProductName = "foo", Price = 20, Supplier = "test1", Quantity = 3, VersionId = "1", TypeId = "1" };
var product2 = new Product() { Id = 2, ProductName = "foo1", Price = 60, Supplier = "test2", Quantity = 9, VersionId = "1", TypeId = "1" };
var product3 = new Product() { Id = 3, ProductName = "foo2", Price = 30, Supplier = "test3", Quantity = 5, VersionId = "2", TypeId = "1" };
var product4 = new Product() { Id = 4, ProductName = "foo3", Price = 10, Supplier = "test4", Quantity = 4, VersionId = "1", TypeId = "2" };
var product5 = new Product() { Id = 5, ProductName = "foo4", Price = 50, Supplier = "test5", Quantity = 8, VersionId = "1", TypeId = "3" };
List<ProductVersionDto> p1 = {
new ProductVersionDto { ProductName = "foo", Quantity= 3, Supplier ="test1"}
new ProductVersionDto { ProductName = "foo1", Quantity= 9, Supplier ="test2"}
};
List<ProductVersionDto> p2 = {
new ProductVersionDto { ProductName = "foo3", Quantity= 4, Supplier ="test4"}
};
List<ProductVersionDto> p3 = {
new ProductVersionDto { ProductName = "foo4", Quantity= 8, Supplier ="test5"}
};
List<ProductTypeDto> products = {
new ProductTypeDto{ ProductName = "foo", Price =20, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo1", Price =60, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo3", Price =10, ProductVersions = p2}
new ProductTypeDto{ ProductName = "foo4", Price =50, ProductVersions = p3}
}

Try this:
var dtos =
(
from p in products
group new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
} by new { p.TypeId, p.ProductName, p.Price } into g
select new ProductTypeDto()
{
TypeId = g.Key.TypeId,
ProductName = g.Key.TypeId,
Price = g.Key.Price,
ProductVersions = g.ToList(),
}
).ToList();
It appears that you meant Price to be a decimal in ProductTypeDto, BTW.

Related

How to get data from 3 table into 1 list

Sorry for my bad English.
Here is my SQL Design.
I have 3 table in Sqlsever. Each table has 4 column with same name, same datatype.
And i want to get data from 4 column "Id, Name, Quantity, IdCategory" from 3 table into 1 list object same as returning value in this code below:
public async Task<IEnumerable<Shirt>> LoadAllShirt()
{
return await _dbContext.Shirt.ToListAsync();
}
I use .NET Core 6 Mvc - code first. Thanks for your help.
I have 3 table in Sqlsever. Each table has 4 column with same name,
same datatype. And I want to get data from 4 column "Id, Name,
Quantity, IdCategory" from 3 table into 1 list, I use .NET Core 6 Mvc - code first.
Well, lot of way around to handle this kind of scenario. Most easy and convenient way I would prefer to use View model or using Linq query.
Lets assume you have below Models:
Models:
public class Bags
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
public class Shirts
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
public class Shoes
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
Seeds In Models:
List<Bags> listBags = new List<Bags>();
listBags.Add(new Bags() { Id = 101, Name = "Bag A", Quantity =10, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 102, Name = "Bag B", Quantity =15, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 103, Name = "Bag C", Quantity =20, Category = "Cat-A"});
List<Shirts> listShirts = new List<Shirts>();
listShirts.Add(new Shirts() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-B" });
List<Shoes> listShoes = new List<Shoes>();
listShoes.Add(new Shoes() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-S" });
Way: 1 using ViewModel:
public class AllViewModel
{
public List<Bags> Bags { get; set; }
public List<Shirts> Shirts { get; set; }
public List<Shoes> Shoes { get; set; }
}
Query Using ViewModel:
var allTableUsingViewModel = new AllViewModel();
allTableUsingViewModel.Bags = listBags;
allTableUsingViewModel.Shirts = listShirts;
allTableUsingViewModel.Shoes = listShoes;
Output Using ViewModel:
Way: 2 using Linq Annonymous Type:
Query Using Linq Annonymous Type:
var AllTableListUsingLinq = from a in listBags
join b in listShirts on a.Id equals b.Id
join c in listShoes on b.Id equals c.Id
select new
{
FromBagsID = a.Id,
FromBagsName = a.Name,
FromBagsQuantity = a.Quantity,
FromBagsCategory = a.Category,
FromShirtsID = b.Id,
FromShirtsName = b.Name,
FromShirtsQuantity = b.Quantity,
FromShirtsCategory = b.Category,
FromShoesID = c.Id,
FromShoesName = c.Name,
FromShoesQuantity = c.Quantity,
FromShoesCategory = c.Category
};
Output Using Linq Annonymous Type:
Full Controller:
[HttpGet("GetFrom3Tables")]
public IActionResult GetFrom3Tables()
{
List<Bags> listBags = new List<Bags>();
listBags.Add(new Bags() { Id = 101, Name = "Bag A", Quantity =10, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 102, Name = "Bag B", Quantity =15, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 103, Name = "Bag C", Quantity =20, Category = "Cat-A"});
List<Shirts> listShirts = new List<Shirts>();
listShirts.Add(new Shirts() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-B" });
List<Shoes> listShoes = new List<Shoes>();
listShoes.Add(new Shoes() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-S" });
//Way: 1 Linq Query
var AllTableListUsingLinq = from a in listBags
join b in listShirts on a.Id equals b.Id
join c in listShoes on b.Id equals c.Id
select new
{
FromBagsID = a.Id,
FromBagsName = a.Name,
FromBagsQuantity = a.Quantity,
FromBagsCategory = a.Category,
FromShirtsID = b.Id,
FromShirtsName = b.Name,
FromShirtsQuantity = b.Quantity,
FromShirtsCategory = b.Category,
FromShoesID = c.Id,
FromShoesName = c.Name,
FromShoesQuantity = c.Quantity,
FromShoesCategory = c.Category
};
//Way: 2 : ViewModel
var allTableUsingViewModel = new AllViewModel();
allTableUsingViewModel.Bags = listBags;
allTableUsingViewModel.Shirts = listShirts;
allTableUsingViewModel.Shoes = listShoes;
return Ok(AllTableListUsingLinq);
}
Note: If you need more information you could check our official document for View Model and Linq Projction here
The following sample query will list your 3 types of data into a single result set.
var allResults = resultSet1.Concat(resultSet2);
For the return type create a class which will be the parent class for all your products (Bag,Shirt,Shoes) Which will help you to return data in a single Generic data.
If you use any non-generic list to send the data like hashtable or Arraylist then then there will be no issue.
In my way I will suggest to use generic data list as it will help you fetch data in better time complexity.
In this case you may need to define additional indirect base class with these 4 parameters. Than you can create Collection of this base class, and concatinate all 3 tables into.
public class BaseEntity
{
public string Name {get;set;}
}
public class Shoes : BaseEntity
{
}
...
public IEnumerable<BaseEntity> GetAllTables()
{
var shirts = await _dbContext.Shirt.ToListAsync();
var shoes = await _dbContext.Shoes.ToListAsync();
var bags = await _dbContext.Bags.ToListAsync();
return shirts.Concat(shoes).Concat(bags);
}
Similar example but witout casting to base class is shown in Enumerable.Concat documentation: https://learn.microsoft.com/pl-pl/dotnet/api/system.linq.enumerable.concat?view=net-7.0

Why am I getting an exception when filling the database with test data (EF Core - ASP-NET Core)

I am getting an exception: "Object reference not set to an instance of an object". I can't understand, why. What's wrong with my connections between tables? I wanted to establish a relationship between the "Country" and "Company" tables in a one-to-many relationship, to avoid duplicating countries in the Company table ( This is what the table looked like before - Image3. I want to create 3 table.
Information about exception:
Image1
Information2:
Image2
The table before:
Image3
An exception occurs in the tables: "Product" - "Company" - "Country".
Seed Database:
using E_Store2021.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace E_Store2021.Data
{
public static class ApplicationDbContextSeed
{
public static async Task SeedAsync(ApplicationDbContext _context, ILoggerFactory loggerFactory)
{
try
{
_context.Database.EnsureCreated();
await SeedCategoriesAsync(_context);
await SeedSubCategories(_context);
await SeedCountry(_context);
await SeedCompany(_context);
await SeedProduct(_context);
}
catch(Exception ex)
{
var log = loggerFactory.CreateLogger<ApplicationDbContext>();
log.LogError(ex.Message);
}
}
private static async Task SeedCategoriesAsync(ApplicationDbContext _context)
{
if (_context.Categories.Any()) return;
var categories = new List<Category>()
{
new Category
{
Description = "Some category",
CategoryName = "Electronics",
ImagePath = "category_1.webp"
},
new Category
{
Description = "Some category",
CategoryName = "Computers",
ImagePath = "category_2.webp"
},
new Category
{
Description = "Some category",
CategoryName = "Music",
ImagePath = "category_3.webp"
},
new Category
{
Description = "Some category",
CategoryName = "Software",
ImagePath = "category_4.webp"
},
new Category
{
Description = "Some category",
CategoryName = "Arts",
ImagePath = "category_5.webp"
},
new Category
{
Description = "Some category",
CategoryName = "Games",
ImagePath = "category_6.webp"
},
};
_context.Categories.AddRange(categories);
await _context.SaveChangesAsync();
}
private static async Task SeedSubCategories(ApplicationDbContext _context)
{
if (_context.SubCategories.Any()) return;
var categories = await _context.Categories.ToListAsync();
var subCategories = new List<SubCategory>
{
new SubCategory
{
CategoryID = categories.FirstOrDefault(c => c.CategoryName == "Electronics").CategoryID,
SubCategoryName = "Camera",
Description = "Some"
},
new SubCategory
{
CategoryID = categories.FirstOrDefault(c => c.CategoryName == "Electronics").CategoryID,
SubCategoryName = "Phones",
Description = "Some"
},
new SubCategory
{
CategoryID = categories.FirstOrDefault(c => c.CategoryName == "Electronics").CategoryID,
SubCategoryName = "Headphones",
Description = "Some"
},
new SubCategory
{
CategoryID = categories.FirstOrDefault(c => c.CategoryName == "Electronics").CategoryID,
SubCategoryName = "Video Game Consoles && Accessories",
Description = "Some"
},
};
_context.SubCategories.AddRange(subCategories);
await _context.SaveChangesAsync();
}
private static async Task SeedProduct(ApplicationDbContext _context)
{
if (_context.Products.Any()) return;
var subCategories = await _context.SubCategories.ToListAsync();
var companies = await _context.Companies.ToListAsync();
var products = new List<Product>
{
new Product
{
ProductName = "iPhone 12 Pro Max",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Phones").SubCategoryID,
UnitPrice = 1695.66,
ImagePath = "iphone12.webp",
Description = "Some phone",
OnSale = true,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Apple").CompanyID,
},
new Product
{
ProductName = "iPhone 11 Pro Max",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Phones").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "iphone11.jpg",
Description = "Some phone",
OnSale = false,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Apple").CompanyID,
},
new Product
{
ProductName = "Xiaomi 9 Pro",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Phones").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "iphone11.jpg",
Description = "Some phone",
OnSale = false,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Xiaomi").CompanyID,
},
new Product
{
ProductName = "Canon PowerShot SX540 Digital Camera",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Camera").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "canon_sx540.jpg",
Description = "Some phone",
OnSale = false,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Canon").CompanyID,
},
new Product
{
ProductName = "Panasonic LUMIX FZ300",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Camera").SubCategoryID,
UnitPrice = 397,
ImagePath = "panasonic_fz300.jpg",
Description = "Some phone",
OnSale = false,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Panasonic").CompanyID,
},
new Product
{
ProductName = "4K Digital Camera 48MP Camera",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Camera").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "4k_digitalcamera_48mp.jpg",
Description = "Some phone",
Star = 4.5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "CEDITA").CompanyID,
},
new Product
{
ProductName = "TOZO T10 Bluetooth 5.0",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Headphones").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "tozo_t10.jpg",
Description = "Some phone",
OnSale = false,
Star = 4.6,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "TOZO").CompanyID,
},
new Product
{
ProductName = "TOZO T6 True Wireless",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Headphones").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "tozo_t6.jpg",
Description = "Some phone",
OnSale = false,
Star = 4.5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "TOZO").CompanyID,
},
new Product
{
ProductName = "Razer Kraken Tournament Edition",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Headphones").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "razer_kraken.jpg",
Description = "Some phone",
OnSale = false,
Star = 4.5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Razer").CompanyID,
},
new Product
{
ProductName = "Sony PlayStation 4 Pro (Red Dead Redemption 2 Bundle)",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Video Game Consoles && Accessories").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "playstation-4.jpg",
Description = "Some phone",
OnSale = false,
Star = 4,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Sony").CompanyID,
},
new Product
{
ProductName = "PlayStation 4 Pro 1TB Limited Edition",
SubCategoryID = subCategories.FirstOrDefault(s => s.SubCategoryName == "Video Game Consoles && Accessories").SubCategoryID,
UnitPrice = 1395.66,
ImagePath = "playstation-spider.jpg",
Description = "Some phone",
OnSale = false,
Star = 5,
Quantity = 10,
CompanyID = companies.FirstOrDefault(c => c.CompanyName == "Sony").CompanyID,
},
};
_context.Products.AddRange(products);
await _context.SaveChangesAsync();
}
private static async Task SeedCompany(ApplicationDbContext _context)
{
if (_context.Companies.Any()) return;
var countries = await _context.Countries.ToListAsync();
List<Company> companies = new List<Company>
{
new Company
{
CompanyName = "Apple",
Year = 1976,
CountryID = countries.FirstOrDefault(c => c.CountryName == "USA").CountryID
},
new Company
{
CompanyName = "Xiaomi",
Year = 2010,
CountryID = countries.FirstOrDefault(c => c.CountryName == "China").CountryID
},
new Company
{
CompanyName = "Canon",
Year = 1987,
CountryID = countries.FirstOrDefault(c => c.CountryName == "China").CountryID
},
new Company
{
CompanyName = "Panasonic",
Year = 1918,
CountryID = countries.FirstOrDefault(c => c.CountryName == "Japan").CountryID
},
new Company
{
CompanyName = "CEDITA",
Year = 2005,
CountryID = countries.FirstOrDefault(c => c.CountryName == "United Kingdom").CountryID
},
new Company
{
CompanyName = "TOZO",
Year = 2016,
CountryID = countries.FirstOrDefault(c => c.CountryName == "Spain").CountryID
},
new Company
{
CompanyName = "Razer",
Year = 2010,
CountryID = countries.FirstOrDefault(c => c.CountryName == "USA").CountryID
},
new Company
{
CompanyName = "Sony",
Year = 2000,
CountryID = countries.FirstOrDefault(c => c.CountryName == "Sony").CountryID
},
};
_context.Companies.AddRange(companies);
await _context.SaveChangesAsync();
}
private static async Task SeedCountry(ApplicationDbContext _context)
{
if (_context.Countries.Any()) return;
var countries = new List<Country>
{
new Country
{
CountryName = "Spain"
},
new Country
{
CountryName = "United Kingdom"
},
new Country
{
CountryName = "Japan"
},
new Country
{
CountryName = "China"
},
new Country
{
CountryName = "USA"
},
};
_context.Countries.AddRange(countries);
await _context.SaveChangesAsync();
}
}
}
Class Product:
using E_Store2021.ViewModels;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace E_Store2021.Models
{
public class Product
{
public int ProductID { get; set; }
[Required]
[StringLength(100)]
[Display(Name = "Name")]
public string ProductName { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
[Display(Name = "Price")]
public double UnitPrice { get; set; }
public bool OnSale { get; set; }
public double Star { get; set; }
public int Quantity { get; set; }
public int? SubCategoryID { get; set; }
public int? CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual SubCategory SubCategory { get; set; }
}
}
Class Company:
using System.Collections.Generic;
namespace E_Store2021.Models
{
public class Company
{
public int CompanyID { get; set; }
public string CompanyName { get; set; }
public int Year { get; set; }
public int? CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
Class Country:
using System.Collections.Generic;
namespace E_Store2021.Models
{
public class Country
{
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
}
Thank you for any help.
Declare your collection navigation properties like this:
public virtual ICollection<Product> Products { get; } = new HashSet<Product>();
So they start out empty, not null.
You have the error:
new Company
{
CompanyName = "Sony",
Year = 2000,
CountryID = countries.FirstOrDefault(c => c.CountryName == "Sony").CountryID
},

Group Products by Brand then by Category - linq

There is three table:
Brand Category
----------------------- --------------------------------
BrandID BrandName CategoryID CategoryName
----------------------- --------------------------------
1 Brand1 1 cat1
2 Brand2 2 cat2
Product
------------------------------------------------------------
ProductID BrandID CategoryID ProductName
------------------------------------------------------------
1 1 1 Product1
2 1 1 Product2
3 1 2 Product3
4 2 1 Product4
I want to write Llinq query to get output like below:
Brand1
|__ cat1
| |__ Product1
| |__ Product2
|
|__ Cat2
| |__ Product3
|
|__ Brand2
|__ cat1
| |__ Product4
I Wrote this query to get Product group by Category:
var query2 = db.Brands
.GroupBy(x => new { x.BrandID, x.BrandName })
.Select(x => new
{
x.Key.BrandID,
x.Key.BrandName,
ProductsNames = db.Products
.Where(p => p.BrandID == x.Key.BrandID)
.Select(p => p.ProductName).ToList()
}).ToList();
How can Group Products by Brand then by Category?
You can do this in 2 steps
//Step 1 - Join 3 lists
var query = (from p in products
join b in brands on p.BrandId equals b.Id
join c in categories on p.CategoryId equals c.Id
select new
{
b.BrandName,
c.CategoryName,
p.ProductName
}).ToList();
//Step 2 - query required results
var results = query
.GroupBy(x => new { x.BrandName, x.CategoryName })
.Select(x => new
{
Brand = x.Key.BrandName,
Category = x.Key.CategoryName,
Product = x.Select(y=>y.ProductName).ToList()
});
Console.WriteLine(JsonConvert.SerializeObject(results));
OUTPUT
[
{
"Brand": "Brand1",
"Category": "cat1",
"Product": [
"Product1",
"Product2"
]
},
{
"Brand": "Brand1",
"Category": "cat2",
"Product": [
"Product3"
]
},
{
"Brand": "Brand2",
"Category": "cat1",
"Product": [
"Product4"
]
}
]
I wrote a little console program:
class Program
{
static void Main(string[] args)
{
var products = new List<Product>{
new Product { ProductID = 1, BrandID = 1, CategoryID = 1, ProductName = "Product1" },
new Product { ProductID = 2, BrandID = 1, CategoryID = 1, ProductName = "Product2" },
new Product { ProductID = 3, BrandID = 1, CategoryID = 2, ProductName = "Product3" },
new Product { ProductID = 4, BrandID = 2, CategoryID = 1, ProductName = "Product4" }
};
var groupedByBrand =
products
.GroupBy(p => p.BrandID)
.Select(g => g.GroupBy(p => p.CategoryID));
foreach (var groupByBrand in groupedByBrand)
foreach (var groupByCategory in groupByBrand)
foreach (var product in groupByCategory)
Console.WriteLine($"{product.BrandID} - {product.ProductName}");
}
}
public class Product
{
public int ProductID { get; internal set; }
public int BrandID { get; internal set; }
public int CategoryID { get; internal set; }
public string ProductName { get; internal set; }
}
I grouped by BrandId and then inside this group grouped by categoryId.
If it is just about the grouping you can use the BrandID and CategoryID and don't even need to join the tables. if you want to sort by name then of course you need to join.
Maybe you wanted to do some sorting on top of / instead of grouping?
In one step , more compact manner
public class Product
{
public int ProductID { get; internal set; }
public string ProductName { get; internal set; }
public Brand brand { get; set; }
public Category cat { get; set; }
public Product()
{
brand = new Brand();
cat = new Category();
}
}
public class Brand
{
public int BrandID { get; internal set; }
public string BrandName { get; internal set; }
}
public class Category
{
public int CategoryID { get; internal set; }
public string CategoryName { get; internal set; }
}
private void a1()
{
var products = new List<Product>{
new Product { ProductID = 1,
brand =new Brand { BrandID = 1, BrandName = "Brand1" },
cat = new Category { CategoryID = 1, CategoryName = "cat1" },
ProductName = "Product1" },
new Product { ProductID = 2,
brand =new Brand { BrandID = 1, BrandName = "Brand1" },
cat = new Category { CategoryID = 1, CategoryName = "cat1" },
ProductName = "Product2" },
new Product { ProductID = 3,
brand =new Brand { BrandID = 1, BrandName = "Brand1" },
cat = new Category { CategoryID = 2, CategoryName = "cat2" },
ProductName = "Product3" },
new Product { ProductID = 4,
brand =new Brand { BrandID = 2, BrandName = "Brand2" },
cat = new Category { CategoryID = 1, CategoryName = "cat1" },
ProductName = "Product4" }
};
var g1 = products.GroupBy(p => new { p.brand, p.cat, p })
.Select(y => new
{
k1 = y.Key.brand.BrandName,
k2 = y.Key.cat.CategoryName,
k3 = y.Key.p.ProductName
}
);
}
// Result/Output as below
{ k1 = "Brand1", k2 = "cat1", k3 = "Product1" }
{ k1 = "Brand1", k2 = "cat1", k3 = "Product2" }
{ k1 = "Brand1", k2 = "cat2", k3 = "Product3" }
{ k1 = "Brand2", k2 = "cat1", k3 = "Product4" }

why can't i access CategoryName in this example?

Consider the following classes:
public class Pie
{
public int Id { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public ICollection<Pie> Pies { get; set; }
}
I have the following data coming from hardcoded files:
public class MockPieRepository : IPieRepository
{
private readonly ICategoryRepository _categoryRepository = new MockCategoryRepository();
private List<Pie> pies = new List<Pie>() {
new Pie { Id = 1, Name = "Apple Pie", Price = 12.95M, IsPieOfTheWeek = true, ShortDescription = "Yummy Apple Pie", LongDescription = "Lengthy apple pie", ImageThumbnailUrl = "images/thumbs/apple.jpg", ImageUrl = "images/apple.jpg", CategoryId = 1},
new Pie { Id = 2, Name = "Rhubarb Pie", Price = 11.95M, IsPieOfTheWeek = false, ShortDescription = "Yummy Rhubarb Pie", LongDescription = "Lengthy Rhubarb pie", ImageThumbnailUrl = "images/thumbs/Rhubarb.jpg", ImageUrl = "images/Rhubarb.jpg", CategoryId = 1},
new Pie { Id = 3, Name = "Cheesecake", Price = 8.95M, IsPieOfTheWeek = false, ShortDescription = "Yummy Cheesecake Pie", LongDescription = "Lengthy Cheesecake pie", ImageThumbnailUrl = "images/thumbs/Cheesecake.jpg", ImageUrl = "images/Cheesecake.jpg", CategoryId = 2},
new Pie { Id = 4, Name = "Chocolate Cake", Price = 4.95M, IsPieOfTheWeek = false, ShortDescription = "Yummy Chocolate Pie", LongDescription = "Lengthy Chocolate pie", ImageThumbnailUrl = "images/thumbs/Chocolate.jpg", ImageUrl = "images/Chocolate.jpg", CategoryId = 3},
new Pie { Id = 5, Name = "Mince Beef", Price = 6.99M, IsPieOfTheWeek = false, ShortDescription = "Yummy Mince Beef Pie", LongDescription = "Lengthy Mince Beef pie", ImageThumbnailUrl = "images/thumbs/Beef.jpg", ImageUrl = "images/Beef.jpg", CategoryId = 3}
};
public IEnumerable<Pie> GetPies()
{
return pies.OrderBy(p => p.Id);
}
public Pie GetPieById(int pieId)
{
return pies.FirstOrDefault(p => p.Id == pieId);
}
}
public class MockCategoryRepository : ICategoryRepository
{
public IEnumerable<Category> Categories {
get {
return new List<Category>
{
new Category { CategoryId = 1, CategoryName = "Fruit Pies", Description = "All fruity pies"},
new Category { CategoryId = 2, CategoryName = "Cheesecakes", Description = "Cheesecakes to make your heart sing"},
new Category { CategoryId = 3, CategoryName = "Seasonal Pies", Description = "Christmas, Halloween or Spring"}
};
}
}
}
When I try to get CategoryName from #pie.Category.CategoryName the value is null. Clearly I'm doing something wrong trying to navigate to Category from Pie, but can anyone tell me what it is?
Thanks.
You never set pie.Category = category.
One of the things that EF will automatically do when it loads data, is that it will 'resolve' navigation properties that it has detected in the data.
If you save pie with a Category set, then that data will be persisted in the database, and next time pie is loaded, you will have pie.Category also loaded. As you haven't stored (and retrieved) data from a database, this has not happened.

LINQ Group & Sum on a list of Models where a property is another model

I havent been able to find any answers to this specific question on LINQ Group & Aggregation so am hoping someone here can help. I have a list of models of such:
public class BasketProduct
{
public ProductItem Product { get; set; }
public int Quantity { get; set; }
public decimal SubTotal { get; set; }
public DateTime DateAdded { get; set; }
}
where the first property is another model:
public class ProductItem
{
public int ID { get; set; }
public string Description { get; set; }
public char Item { get; set; }
public decimal Price { get; set; }
public string ImagePath { get; set; }
public string Barcode { get; set; }
}
I basically want to be able to Group and Aggregate on this list:
List<BasketProduct> allBasketProducts =
Using the following:
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Product.ID = y.First().Product.ID,
Product.Item = y.First().Product.Item,
Product.Description = y.First().Product.Description,
Quantity = y.Sum(z => z.Quantity),
Product.ImagePath = y.First().Product.ImagePath,
Product.Price = y.First().Product.Price,
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();
However it seriously doesn't like this (as per red squigly lines and even red'er text):
Can someone help please?
Your issue isn't actually related to LINQ, it's your ProductItem constructor. You need to construct its nested Product object explicitly, like this:
allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal),
Product = new ProductItem
{
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
}
}).ToList();
var totals =
(from b in allBasketProducts
group new { b.Quantity, b.SubTotal, Product= b.Product } by b.Product.ID into g
select new BasketProduct
{
Product = g.First().Product,
SubTotal = g.Sum(z => z.SubTotal),
Quantity = g.Sum(z => z.Quantity)
}).ToList();
Try following :
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct()
{
Product = new ProductItem() {
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
},
Quantity = y.Sum(z => z.Quantity),
SubTotal
When you specify the type of your Select, the compiler expects only the properties of that type. So you can only set the properties Product, Subtotal, Quantity and DateAdded in that code of yours.
You can find the Product simply by selecting the first Product that has an ID that matches your grouping Key:
var allBasketProductsGroupedByProductID = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Product = y.First(i => i.Product.ID == y.Key).Product,
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();
try this
List allBasketProducts = new List();
allBasketProducts = new List<BasketProduct>()
{
new BasketProduct()
{
Product = new ProductItem()
{
ID = 1,
Price = 5,
},
Quantity = 2,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 1,
Price = 5,
},
Quantity = 4,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 2,
Price = 10,
},
Quantity = 3,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 3,
Price = 20,
},
Quantity = 3,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 2,
Price = 20,
},
Quantity = 3,
SubTotal = 2,
}
};
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct()
{
Product = new ProductItem()
{
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
},
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();

Categories

Resources