How to multiply and sum all numeric children properties in LINQ
I have an object as follows
public class ResourceTier
{
//primary key. no duplicate
public int Id { get; set; }
public int ParentId { get; set; }
public decimal Volume { get; set; } //has value
public decimal UnitRate { get; set; } //has value
public decimal TotalPrice { get; set; } //has no value
}
The TotalPrice default value is 0. This is the property where I want to fill the value. The TotalPrice should be filled by multiplying the UnitRate and Volume properties and then summing of all the children if any.
This is the data
| Id | ParentId | Volume | UnitRate | TotalPrice |
| 1 | 0 | - | - | 180 |
| 2 | 0 | - | - | 30 |
| 3 | 1 | - | - | 130 |
| 4 | 1 | 5 | 10 | 50 |
| 5 | 2 | 3 | 10 | 30 |
| 6 | 3 | - | - | 50 |
| 7 | 3 | 2 | 40 | 80 |
| 8 | 6 | 4 | 10 | 40 |
| 9 | 6 | 1 | 10 | 10 |
When I try this, it is not working. This code has only the sum of the direct children but not all children (grand grand children and so on)
List<ResourceTier> result = ...;
result.ForEach(x => x.TotalPrice =
result.Where(c => c.ParentId == x.Id).Count() == 0 ?
s.UnitRates * s.Volume :
result.Where(c => c.ParentId == x.Id).Select(s => (s.UnitRates * s.Volume)).Sum());
A solution using only Linq will be hard to achieve, because it will typically only respect one level of children. In order to get the total of the grandchildren too, you have to walk the tree completely in order to get the sum of the children (and their children).
You have to create a method that uses recursion which means that it will call itself again with a different set of parameters. This way, you can get the total of the children first and then assign the value of the current node, e.g.:
private decimal SetTotal(IEnumerable<ResourceTier> tiers, ResourceTier current)
{
current.Total = current.Volume * current.UnitRate;
// Get children of current node
var children = tiers.Where(x => x.ParentId == current.Id && x.Id != current.Id); // The second condition explicitely excludes the current node to avoid infinite loops
foreach(var child in children)
current.Total += SetTotal(tiers, child); // Call method again for children
return current.Total;
}
The first call to the function would use the ids of all items that do not have a parent, e.g.:
foreach(var topLevelNode in tiers.Where(x => x.ParentId == 0))
SetTotal(tiers, topLevelNode);
Please note that the code above should demonstrate the principle of recursion. For sure there are more efficient ways to solve this a bit faster.
As other answers you need to calculate totalprice recursively:
public static decimal total(ResourceTier rt, List<ResourceTier> data)
{
if (data.Where(x=>x.ParentId==rt.Id).Count()==0)
return rt.Volume*rt.UnitRate;
else
{
var sum= data.Where(x => x.ParentId == rt.Id).Select(x=>total( x,data)).Sum();
return rt.UnitRate*rt.Volume+sum;
}
}
And the use this method:
var data = new List<ResourceTier> {
new ResourceTier{ Id=1, ParentId=0, Volume=0, UnitRate=0, TotalPrice=0 },
new ResourceTier{ Id=2, ParentId=0, Volume=0, UnitRate=0, TotalPrice=0 },
new ResourceTier{ Id=3, ParentId=1, Volume=0, UnitRate=0, TotalPrice=0 },
new ResourceTier{ Id=4, ParentId=1, Volume=5, UnitRate=10, TotalPrice=0 },
new ResourceTier{ Id=5, ParentId=2, Volume=3, UnitRate=10, TotalPrice=0 },
new ResourceTier{ Id=6, ParentId=3, Volume=0, UnitRate=0, TotalPrice=0 },
new ResourceTier{ Id=7, ParentId=3, Volume=2, UnitRate=40, TotalPrice=0 },
new ResourceTier{ Id=8, ParentId=6, Volume=4, UnitRate=10, TotalPrice=0 },
new ResourceTier{ Id=9, ParentId=6, Volume=1, UnitRate=10, TotalPrice=0 },
};
var result = data.Select(x=>new ResourceTier { Id=x.Id, ParentId=x.ParentId, UnitRate=x.UnitRate, Volume=x.Volume, TotalPrice= total(x, data) }).ToList();
output :
LINQ can't change your source collection!
However you can create a new collection of ResourceTiers that match your requirements. After that you can decide to assign that to your output, or decide to save them in your database, table, etc.
IEnumerable<ResourceTier> sourceCollection = ...
IEnumerable<ResourceTier> resourceTiersWithTotalPrice = sourceCollection
.Select(resourceTier => new ResourceTier
{
Id = resourceTier.Id,
ParentId = resourceTier.ParentId,
...
TotalPrice = resourceTier.Volume * resourceTier.UnitRate,
});
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I need to select fields from unique records within a table with multiple where clauses. I currently am using C# and LINQ fluent syntax connected with NHibernate. So I am wondering if there is a way to create this query that way. Here is a test dataset:
+----+----------+------+------+
| Id | ParentId | Name | Type |
+----+----------+------+------+
| 1 | 100 | A | 1 |
| 2 | 100 | A | 2 |
| 3 | 100 | A | 3 |
| 4 | 200 | B | 1 |
| 5 | 300 | A | 1 |
| 6 | 300 | A | 2 |
| 7 | 400 | A | 1 |
| 8 | 400 | A | 2 |
| 9 | 400 | A | 3 |
| 10 | 400 | A | 4 |
+----+----------+------+------+
I can get the results I want using this SQL query:
SELECT ParentId, COUNT(Name) as Cnt, Max(Id) as Id, Max(Name) as Name, Max(Type) as Type FROM TestGroupBy Where Name = 'A' Group By ParentId;
This gives the result:
+----------+-----+----+------+------+
| ParentId | Cnt | Id | Name | Type |
+----------+-----+----+------+------+
| 100 | 3 | 3 | A | 3 |
| 300 | 2 | 6 | A | 2 |
| 400 | 4 | 10 | A | 4 |
+----------+-----+----+------+------+
I know how to make the group by query but I can't figure out how to do the multiple MAX selects. Is that just not possible with LINQ? If it's not, then what would be a way that I could go about this?
Here's a small snippet that shows you the linq query in context:
public class Row
{
public int Id;
public int ParentId;
public string Name;
public int Type;
public Row(int Id, int ParentId, string Name, int Type)
{
this.Id = Id;
this.ParentId = ParentId;
this.Name = Name;
this.Type = Type;
}
}
class Program
{
static void Main(string[] args)
{
List<Row> test = new List<Row>();
test.Add(new Row(1, 100, "A", 1));
test.Add(new Row(2, 100, "A", 2));
test.Add(new Row(3, 100, "A", 3));
test.Add(new Row(4, 200, "B", 1));
test.Add(new Row(5, 300, "A", 1));
test.Add(new Row(6, 300, "A", 2));
test.Add(new Row(7, 400, "A", 1));
test.Add(new Row(8, 400, "A", 2));
test.Add(new Row(9, 400, "A", 3));
test.Add(new Row(10, 400, "A", 4));
dynamic d = from row in test
where row.Name.Equals("A")
group row by row.ParentId into grp
select new {
ParentId = grp.Key,
Cnt = grp.Count(),
Id = grp.Max(x => x.Id),
Name = grp.Max(x => x.Name),
Type = grp.Max(x => x.Type)
};
}
}
When you have a queryable, you can call Select and pass a predicate. The new keyword constructs an object with the schema you prefer.
.Select( x => new
{
ParentId = x.ParentId
Cnt = x.Count(p => p.Name),
Id = x.Max( p => p.Id )
/*etcetera*/
} );
I am having issues with my nested foreach loop. I'm trying to populate data from by database to my list with information about car information (company, different car models). My issue has to do with my inner loop, and not being able to continue populating my list.
The results that I'm expecting is this:
"CompanyId": 1,
"CompanyName": "Toyota"
"ParentVehicleId": 2,
"ParentVehicleName": "Camry",
"ChildVehicleId": 4,
"ChildVehicleName":"Camry/Scepter"
"CompanyId": 1,
"CompanyName": "Toyota"
"ParentVehicleId": 4,
"ParentVehicleName": "Crown"
"ChildVehicleId": 0,
"ChildVehicleName":"N/A"
"CompanyId": 12,
"CompanyName": "Hyundai"
"ParentVehicleId": 13,
"ParentVehicleName": "Accent",
"ChildVehicleId": 0,
"ChildVehicleName":"N/A"
etc...
But what I'm actually getting is only these two:
"CompanyId": 1,
"CompanyName": "Toyota"
"ParentVehicleId": 2,
"ParentVehicleName": "Camry",
"ChildVehicleId": 3,
"ChildVehicleName":"Camry/Vista"
"CompanyId": 1,
"CompanyName": "Toyota"
"ParentVehicleId": 2,
"ParentVehicleName": "Camry",
"ChildVehicleId": 4,
"ChildVehicleName":"Camry/Scepter"
This is a snippet of my db table:
Vehicle Table
|----------------------------------------------|
| VehicleId | ManufactId | BrandName |
|----------------------------------------------|
| 1 | 1 | Toyota |
|----------------------------------------------|
| 2 | 1 | Camry |
|----------------------------------------------|
| 3 | 2 | Camry/Vista |
|----------------------------------------------|
| 4 | 2 | Camry/Scepter |
|----------------------------------------------|
| 5 | 4 | Crown |
|----------------------------------------------|
| 6 | 5 | Supra |
|----------------------------------------------|
C# code
public List<VehicleListModel>> VehicleMethod()
{
List<VehicleListModel> vehicleList = new List<VehicleListModel>();
foreach (var item in companyInfo)
{
var parentInfo = _context.VehicleTable.Where(y => item.VehicleId == y.ManufactId).ToList();
foreach (var item2 in parentInfo)
{
var childInfo = _context.VehicleTable.Where(y => item2.VehicleId == y.ManufactId).ToList();
foreach (var item3 in childInfo)
{
VehicleListModel vehList = new VehicleListModel();
//if ChildVehicleId does not exist, 0 & N/A are
//returned
vehList.CompanyId = item.VehicleId;
vehList.CompanyName = item?.BrandName ?? "N/A";
vehicleList.Add(vehList);
}
}
}
return vehicleList;
}
The problem is basically how your data is connected.
Let's take Toyota:
|----------------------------------------------|----------------|
| VehicleId | ManufactId | BrandId | BrandName |
|----------------------------------------------|----------------|
| 1 | null | 1 | Toyota |
|----------------------------------------------|----------------|
| 2 | 1 | 1 | Camry |
|----------------------------------------------|----------------|
| 3 | 2 | 1 | Camry/Vista |
|----------------------------------------------|----------------|
| 4 | 2 | 1 | Camry/Scepter |
As you can see, the model Camry is the relationship between the versions and the company.
When VehicleId is 2 (from Camry) you look for records where ManufactId is 2 (Vista and Scepter).
For Nissan instead:
|----------------------------------------------|----------------|
| VehicleId | ManufactId | BrandId | BrandName |
|----------------------------------------------|----------------|
| 9 | null | 9 | Nissan |
|----------------------------------------------|----------------|
| 10 | 9 | 9 | Datsun |
|----------------------------------------------|----------------|
| 11 | 9 | 9 | Datsun 13T |
Datsun doesn't have childs (no record have ManufactId equal to 10). Update Datsun 13 T record to ManufactId 10 to see it.
The same goes for the rest.
Moreover, because you hydrate the objects of the list inside the innermost foreach loop (and you never reach that code) you don't even get the empty objects.
If the data is wrong and you can't do anything about it, one possible way to handle these cases is to generate objects with the available info:
....
List<VehicleListModel> vehicleList = new List<VehicleListModel>();
var companies = _context.Where(x => x.ManufactId == null).ToList();
foreach (var company in companies)
{
var models = _context.Where(y => company.VehicleId == y.ManufactId).ToList();
if (models.Any())
{
foreach (var model in models)
{
var versions = _context.Where(y => model.VehicleId == y.ManufactId).ToList();
if (versions.Any())
{
foreach (var version in versions)
{
VehicleListModel vehList = new VehicleListModel();
vehList.CompanyId = company.VehicleId;
vehList.CompanyName = company?.BrandName ?? "N/A";
vehList.ParentVehicleId = model?.VehicleId ?? 0;
vehList.ParentVehicleName = model?.BrandName ?? "N/A";
vehList.ChildVehicleId = version?.VehicleId ?? 0;
vehList.ChildVehicleName = version?.BrandName ?? "N/A";
vehicleList.Add(vehList);
}
}
else
{
VehicleListModel vehList = new VehicleListModel();
vehList.CompanyId = company.VehicleId;
vehList.CompanyName = company.BrandName;
vehList.ParentVehicleId = model.VehicleId;
vehList.ParentVehicleName = model.BrandName;
vehList.ChildVehicleId = 0;
vehList.ChildVehicleName = "N/A";
vehicleList.Add(vehList);
}
}
}
else
{
VehicleListModel vehList = new VehicleListModel();
vehList.CompanyId = company.VehicleId;
vehList.CompanyName = company.BrandName;
vehList.ParentVehicleId = 0;
vehList.ParentVehicleName = "N/A";
vehList.ChildVehicleId = 0;
vehList.ChildVehicleName = "N/A";
vehicleList.Add(vehList);
}
}
....
Also, as #Yair suggested, you need to change Crown to ManufactId = 1
You are overriding the vehList instance in each iteration. Instead, you should move its initialization to the inner most loop, so a new instance is added to the list in each iteration:
foreach (var item in companyInfo)
{
var parentInfo = _context.VehicleTable.Where(y => item.VehicleId == y.ManufactId).ToList();
foreach (var item2 in parentInfo)
{
// This should be removed from the code:
// VehicleListModel vehList = new VehicleListModel();
var childInfo = _context.VehicleTable.Where(y => item2.VehicleId == y.ManufactId).ToList();
foreach (var item3 in childInfo)
{
// Instead, it's initialized here:
VehicleListModel vehList = new VehicleListModel();
//if ChildVehicleId does not exist, 0 & N/A are
//returned
vehList.CompanyId = item.VehicleId;
vehList.CompanyName = item?.BrandName ?? "N/A";
vehList.ParentVehicleId = item2?.VehicleId ?? 0;
vehList.ParentVehicleName = item2?.BrandName ?? "N/A";
vehList.ChildVehicleId = item3?.VehicleId ?? 0;
vehList.ChildVehicleName = item3?.BrandName ?? "N/A";
vehicleList.Add(vehList);
}
}
}
I have 3 tables:
Recipe
RecipeIngredient (fk RecipeId)
RecipeTag (fk RecipeId)
Recipe
+-----+------------+-------------+------+
| Id | Name | Ingredients | Tags |
+-----+------------+-------------+------+
| 99 | Mango Sago | | |
| 100 | Tuna Melt | | |
+-----+------------+-------------+------+
RecipeIngredient
+-----+----------+------------+---------------------------------------------+----------+
| Id | Quantity | UOM | Name | RecipeId |
+-----+----------+------------+---------------------------------------------+----------+
| 115 | 2 | Pieces | Whole Ripe Mangoes | 99 |
| 116 | 1 | Pieces | Jolly Coconut Milk, 400ml | 99 |
| 117 | 2 | Tablespoon | Sugar | 99 |
| 118 | 1 | Cup | Cooked Tapioca Pearls | 99 |
| 119 | NULL | NULL | Mango Cubes | 99 |
| 120 | 1 | Pieces | Doña Elena 100% Tuna Shredded 185g, drained | 100 |
| 121 | 2 | Tablespoon | White Onion, chopped | 100 |
| 122 | 2 | Tablespoon | Jolly Real Mayonnaise | 100 |
| 123 | 1 | Tablespoon | Celery or Pickle Relish, finely chopped | 100 |
| 124 | 8 | Pieces | White Bread | 100 |
| 125 | 4 | Pieces | Cheddar or Mozzarella Cheese | 100 |
+-----+----------+------------+---------------------------------------------+----------+
RecipeTag
+----+-----------------+----------+
| Id | Name | RecipeId |
+----+-----------------+----------+
| 72 | Filipino Desert | 99 |
| 73 | Quick Recipe | 99 |
| 74 | Quick Recipe | 100 |
+----+-----------------+----------+
How do I add all the RecipeIngredient to the Ingredients column in the Recipe table and add all the RecipeTag to the Tags column in the Recipe table in the controller?
public JsonResult GetAllRecipes()
{
var recipes = db.Recipes.OrderBy(a => a.Name).ToList();
return new JsonResult { Data = recipes, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public JsonResult GetAllRecipes()
{
var recipes = (from rec db.Recipes
join ing in db.Ingredients on rec.Id equals ing.RecipeId into subIngrs
from subIngr in subIngrs.DefaultIfEmpty()
join tag in db.RecipeTags on rec.Id equals tag.RecipeId into subTags
from subTag in subTags.DefaultIfEmpty()
order by rec.Name
select new
{
rec.Id,
rec.Name,
Quantity = subIngr == null ? null : subIngr.Quantity,
IngrName = subIngr == null ? null : subIngr.Name,
UOM = subIngr == null ? null : subIngr.UOM,
TagName = subTag == null ? null : subTag.Name
}).ToList()
.GroupBy(x => new { x.Id, x.Name }).Select(x => new
{
x.Key.Id,
x.Key.Name,
Ingredients = string.Join("," x.Where(y => y.IngrName != null).Select(y => $"{y.Quantity} {y.UOM} {y.Name}").Distinct()),
Tags = string.Join("," x.Where(y => y.TagName != null).Select(y => y.TagName).Distinct())
}).ToList();
return new JsonResult { Data = recipes, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
You can use STUFF if you want to do it in sql.
SELECT a.Id,
Recipe = a.Name,
Ingredient = STUFF((
SELECT ','
+ CAST(b.Quantity AS NVARCHAR(10)) + ' '
+ b.UOM + ' '
+ b.Name
FROM dbo.RecipeIngredient b
WHERE a.Id = b.RecipeId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
Tag = STUFF((
SELECT ',' + c.Name
FROM dbo.RecipeTag c
WHERE a.ID = c.RecipeId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM Recipe a
Here's a Demo.
To execute the stored procedure and return the results as a json in your controller method
var recipies = db.Database.SqlQuery<RecipeVM>("NameOfStoredProcedure");
return Json(recipes, JsonRequestBehavior = JsonRequestBehavior.AllowGet);
where RecipeVM is
public class RecipeVM
{
public int Id { get; set; }
public string Name { get; set; }
public string Ingredient { get; set; }
public string Tag { get; set; }
}
Assuming you have set up your navigation properties correctly (i.e. Recipe contains public virtual ICollection<RecipieIngredient> Ingredients { get; set; } etc) then to get the data in the concatenated format you want,
public JsonResult GetAllRecipes()
{
var recipes = db.Recipes
.OrderBy(r => r.Name)
.ToList() // this is necessary because we need Linq to Objects for the string formatting
.Select(r => new // can be anonymous objects because we are returning a JsonResult
{
Id = r.Id,
Name = r.Name,
Ingredients = r.Ingredients
.Select(i => string.Format("{0} {1} {2}", i.Quantity, i.UOM, i.Name).TrimStart())
.Aggregate((c, n) => c + ", " + n),
Tags = r.Tags
.Select(t => t.Name)
.Aggregate((c, n) => c + ", " + n)
});
return Json(recipes, JsonRequestBehavior = JsonRequestBehavior.AllowGet);
}
Side note, your RecipeIngredient table indicates nullable values for Quantity and UOM hence the TrimStart. I am assuming that if either Quantity or UOM is null, then the other is also null
Alternatively, (in LINQ to SQL) you can use
var recipes = (from r in db.Recipies
join i in db.RecipeIngredient on r.Id equals i.RecipeId into Ingedients
join t in db.RecipeTag on r.Id equals t.RecipeId into Tags
orderby r.Name
select new
{
Name = r.Name,
Ingedients = Ingedients,
Tags = Tags
}).ToList()
.Select(x => new
{
Name = x.Name,
Ingredients = x.Ingedients
.Select(y => string.Format("{0} {1} {2}", y.Quantity, y.UOM, y.Name).Trim())
.Aggregate((c, n) => c + ", " + n),
Tags = x.Tags
.Select(y => y.Name)
.Aggregate((c, n) => c + ", " + n)
});
return Json(recipes, JsonRequestBehavior = JsonRequestBehavior.AllowGet);
I'm not exactly sure what you're trying to accomplish, but if you're trying to programatically insert this data from user input, you could just iterate over the ingredients/tags and build a string to insert into the relevant tables.
If you're trying to do this from existing data, then I'm not sure EF is the tool to use. I would just write SQL scripts to handle that.
I have a problem with getting grouped columns in LINQ.
My class:
public class DTO_CAORAS
{
public int? iORAS_KEY_CON { get; set; }
public int? iMERC_KEY {get;set;}
public double? decD_ORAS_QUA {get;set;}
}
LINQ query:
var results =
from oras in listCAORAS_Delivered
group oras by new
{
oras.iORAS_KEY_CON,
oras.iMERC_KEY
}
into orasGroup
select new
{
decD_ORAS_QUA = orasGroup.Sum(x => x.decD_ORAS_QUA)
};
List results is filled only with one column - decD_ORAS_QUA. I don't know how to get columns, by which query is grouped - IORAS_KEY_CON and iMERC_KEY? I would like to fill results with iORAS_KEY_CON, iMERC_KEY and decD_ORAS_QUA.
Input data:
+---------------+-----------+---------------+
| iORAC_KEY_CON | iMERC_Key | decD_ORAS_QUA |
+---------------+-----------+---------------+
| 1 | 888 | 1 |
| 1 | 888 | 2 |
| 1 | 888 | 4 |
+---------------+-----------+---------------+
Desired output:
+---------------+-----------+---------------+
| iORAC_KEY_CON | iMERC_Key | decD_ORAS_QUA |
+---------------+-----------+---------------+
| 1 | 888 | 7 |
+---------------+-----------+---------------+
To also show the keys:
var results = from oras in listCAORAS_Delivered
group oras by new { oras.iORAS_KEY_CON, oras.iMERC_KEY } into g
select new DTO_CAORAS {
iORAS_KEY_CON = g.Key.iORAS_KEY_CON,
iMERC_KEY = g.Key.iMERC_KEY,
decD_ORAS_QUA = g.Sum(x => x.decD_ORAS_QUA)
};
As you are only grouping one column you can also:
var results = from oras in listCAORAS_Delivered
group oras.decD_ORAS_QUA by new { oras.iORAS_KEY_CON, oras.iMERC_KEY } into g
select new DTO_CAORAS {
iORAS_KEY_CON = g.Key.iORAS_KEY_CON,
iMERC_KEY = g.Key.iMERC_KEY,
decD_ORAS_QUA = g.Sum()
};
I want to make recursive category menu for product categories on my web page.Each category must retrieve related first item according to CategoryId on "Product" table but this category should be disappear if category hasn't any product.Actually, i can make to easily with using INNER JOIN for non-recursive category menu.How can i solve this issue?Is there any idea?
I can use a method as follow but this method both amateur and may be null first item.
Category table
+--------------+---------------+------------+
| CategoryId | CategoryName | ParentId |
+--------------+---------------+------------+
| 1 | Cookware | NULL |
+--------------+---------------+------------+
| 2 | Tableware | NULL |
+--------------+---------------+------------+
| 3 | Teapots | 1 |
+--------------+---------------+------------+
| 4 | Cutleries | 3 |
+--------------+---------------+------------+
| 5 | 30pcs Cutlery | 2 |
+--------------+---------------+------------+
Product table
+--------------+--------------+--------------------+------------+
| ProductId | ProductCode | ProductName | CategoryId |
+--------------+--------------+--------------------+------------+
| 1 | G110090 | Teapot | 3 |
+--------------+--------------+--------------------+------------+
| 2 | D220623 | Cutlery Set | 5 |
+--------------+--------------+--------------------+------------+
RecursiveCategory method
public string RecursiveCategory(IEnumerable<Category> category, int? parent)
{
string catNode = string.Empty;
if(category.Any(n=>n.ParentId == parent))
{
catNode += "<ul>";
foreach(Category c in category)
{
catNode += "<li>";
catNode += "<a href='/Detail/" + GetFirstItem(c.CategoryId).ProductId + "'>"+c.CategoryName+"</a>";
catNode += RecursiveCategory(category, c.ParentId);
catNode += "</li>";
}
catNode += "</ul>"
}
return catNode;
}
GetFirstItem method
public Product GetFirstItem(int categoryId)
{
Product prod = new Product();
foreach(Product p in db.Product.Where(n=>n.CategoryId == categoryId))
{
prod.ProductId = p.ProductId;
prod.ProductName = p.ProductName;
...
}
return prod;
}
Try this to construct a hierarchy from a given point (using null in the first call will give you the full tree with the products for each category). As a modification you could make product lazy load if you need to:
public class Category
{
IEnumerable<Category> Children {get;set;}
IEnumerable<Product> Products {get;set;}
}
public IEnumerable<Category> GetCategory(int? parent)
{
var result = new List<Category>();
foreach (var cat in categories.Where(p => p.parentId = parent)
{
var generatedCategory = new Category();
generatedCategory.Children = GetCategory(cat.id);
generatedCategory.Products = products.Where(p => p.CategoryId = cat.CategoryId);
result.Add(generatedCategory);
}
return result;
}
Note: I haven't tested the code just a guide about how to construct it easily.