Building a tree in C# and fill it with recursion - c#

I have to build a tree, similar as filesystem structure of files and folders in the hard drive.
Instead of files and folders I have folders, and equipments.
I have created this class
public class TreeModel
{
public int Id { get; set; }
public string Name { get; set; }
public int? IdParent { get; set; }
public int? Level { get; set; }
public bool IsEquipment { get; set; }
public List<TreeModel> Children { get; set; }
}
I have a list of N root which can have childrens of TreeModel elements.
I have tryed this recursive function which makes the program crash
private TreeModel GetChild(TreeModel tree, ref List<ShipSparePartsCategory> folders, List<ShipSparePartsCategory> equipments)
{
if (tree == null)
{
return tree;
}
var combined = folders.Concat(equipments);
var nodes = combined
.Where(x => x.IdParent == tree.Id && x.IsActive == true)
.Select(y => new TreeModel()
{
Id = y.Id,
IdParent = y.IdParent,
Name = y.Name,
Level = y.Level,
IsEquipment = equipments.Any(z => z.Id == y.Id) ? true : false,
Children = equipments.Any(z => z.Id == y.Id) ? new List<TreeModel>() : new List<TreeModel>(),
})
.ToList();
tree.Children.AddRange(nodes);
return GetChild(tree, ref folders, equipments);
}
I have no idea how to fix it and call it in the correct way.

Related

Linq Sorting on list and its nested collection of same class

I have the following class
public class Node
{
public string Name { get; set; }
public string Image { get; set; }
public string Symbol { get; set; }
public string KaartNum { get; set; }
public List<Node> children { get; set; }
}
It's a tree list, which holds the node within the node, I want to get the list ordered by KaartNum, then by Name, I tried the following for sorting to get the desired results.
List<Node> SortedList = obNodeList
.OrderBy(o => Convert.ToInt16(o.Kaart))
.ThenBy(o => o.Name))
.ThenBy(c=>c.children.OrderBy(p=>p.N))
.ToList();
It works only at root class level and not working on nested children
the property, I only need to sort on Name property.
Recursion to the rescue:
List<Node> SortParents(List<Node> source) {
if(source == null || source.Count == 0) return source;
return
source
.OrderBy(o => Convert.ToInt16(o.KaartNum))
.ThenBy(o => o.Name)
.Select(o => new Node() {
Name = o.Name,
Image = o.Image,
Symbol = o.Symbol,
KaartNum = o.KaartNum,
children = SortChildren(o.children)
})
.ToList();
}
List<Node> SortChildren(List<Node> source) {
if(source == null || source.Count == 0) return source;
return
source
.OrderBy(o => o.Name)
.Select(o => new Node() {
Name = o.Name,
Image = o.Image,
Symbol = o.Symbol,
KaartNum = o.KaartNum,
children = SortChildren(o.children)
})
.ToList();
}
You can merge both methods into one with an additional bool sortOnlyByName parameter if you want to.

EF get list of parent entities that have list of child

I have to next 2 entities in my project
public class Product
{
public Product()
{
this.ProductImages = new HashSet<ProductImage>();
this.ProductParams = new HashSet<ProductParam>();
}
public int ID { get; set; }
public int BrandID { get; set; }
public int CodeProductTypeID { get; set; }
public string SeriaNumber { get; set; }
public string ModelNumber { get; set; }
public decimal Price { get; set; }
public bool AvailableInStock { get; set; }
public virtual Brand Brand { get; set; }
public virtual CodeProductType CodeProductType { get; set; }
public virtual ICollection<ProductImage> ProductImages { get; set; }
public virtual ICollection<ProductParam> ProductParams { get; set; }
}
public class ProductParam
{
public int Id { get; set; }
public int ProductId { get; set; }
public int CodeProductParamId { get; set; }
public string Value { get; set; }
public virtual Product Product { get; set; }
public virtual CodeProductParam CodeProductParam { get; set; }
}
and I want to get list of Products which has list of specified parameters
var prodParamCritria = new List<ProductParam>()
{
new ProductParam(){CodeProductParamId =1, Value="Black" },
new ProductParam(){CodeProductParamId =2, Value="Steal"}
};
in sql I can do it by using EXISTS clause twise
SELECT *
FROM Products p
WHERE EXISTS (
SELECT *
FROM ProductParams pp
WHERE pp.ProductId = p.ID
AND (pp.CodeProductParamId = 1 AND pp.[Value] = N'Black')
)
AND EXISTS (
SELECT *
FROM ProductParams pp
WHERE pp.ProductId = p.ID
AND pp.CodeProductParamId = 2
AND pp.[Value] = N'Steal'
)
How can i get same result by EF methods or linq
Try this:
var products= db.Products.Where(p=>p.ProductParams.Any(pp=>pp.CodeProductParamId == 1 && pp.Value == "Black") &&
p.ProductParams.Any(pp=>pp.CodeProductParamId == 2 && pp.Value == "Steal"));
Update
The problem in work with that list of ProductParam to use it as a filter is that EF doesn't know how to translate a PodructParam object to SQL, that's way if you execute a query like this:
var products2 = db.Products.Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));
You will get an NotSupportedException as you comment in the answer of #BostjanKodre.
I have a solution for you but probably you will not like it. To resolve that issue you could call the ToList method before call the Where. This way you will bring all products to memory and you would work with Linq to Object instead Linq to Entities, but this is extremely inefficient because you are filtering in memory and not in DB.
var products3 = db.Products.ToList().Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));
If you want filter by one criteria then this could be more simple and you are going to be able filtering using a list of a particular primitive type. If you, for example, want to filter the products only by CodeProductParamId, then you could do this:
var ids = new List<int> {1, 2};
var products = db.Products.Where(p => ids.All(i=>p.ProductParams.Any(pp=>pp.CodeProductParamId==i))).ToList();
This is because you are working with a primitive type and not with a custom object.
I suppose something like that should work
db.Product.Where(x => x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 1) != null && x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 2) != null).ToList();
or better
db.Product.Where(x => x.ProductParams.Any(y => y.CodeProductParamId == 1) && x.ProductParams.Any(y => y.CodeProductParamId == 2)).ToList();
Ok, if you need to make query on parameters in list prodParamCriteria it will look like this:
db.Product.Where(x => prodParamCritria.All(c=> x.ProductParams.Any(p=>p.CodeProductParamId == c.CodeProductParamId && p.Value== c.Value))).ToList();
I forgot that complex types cannot be used in query database, so i propose you to convert your prodParamCriteria to dictionary and use it in query
Dictionary<int, string> dctParams = prodParamCritria.ToDictionary(x => x.CodeProductParamId , y=>y.Value);
db.Product.Where(x => dctParams.All(c => x.ProductParams.Any(p=> p.CodeProductParamId == c.Key && p.Value== c.Value))).ToList();
another variation:
IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
x => new {
p = x,
cs = x.ProductParams.Where(y => lis.Contains(y.Id))
}
).Where(y => y.cs.Count() == lis.Count()).
ToList();
with a named class like (or maybe without, but not in linqpad)
public class daoClass {
public Product p {get; set;}
public Int32 cs {get; set;}
}
IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
x => new daoClass {
p = x,
cs = x.ProductParams.Where(y => lis.Contains(y.Id))
}
).Where(y => y.cs.Count() == lis.Count()).
SelectMany(y => y.p).
ToList();

Entityframework select all childs related data when call parent

I have categories table which contains :
public partial class C_Categories
{
public int CatId { get; set; }
public Nullable<int> ParentId { get; set; }
public string ImageUrl { get; set; }
public virtual ICollection<C_Node> C_Node { get; set; }
}
And i have node table which contains :
public partial class C_Node
{
public int NodeId{ get; set; }
public Nullable<int> CatId { get; set; }
public System.DateTime PostDate { get; set; }
public virtual C_Categories C_Categories { get; set; }
}
And my controller :
public ActionResult Index(int? catId)
{
IQueryable<C_Node> moduleItems = db.C_Node;
if (catId != null)
{
//here i want to check if category is parent , get all node related to his child categories
moduleItems = moduleItems.Where(x => x.CatId == catId);
}
return View(moduleItems.ToList());
}
At my controller i want to check if category is parent , get all node table related to his child categories,
I tried to use any , but it failed .
to explain my question : i have category : electronics and electronics have childs computers, mobiles . i have products on node table under computers and mobiles , if catId is electronics i want all products under its childs computers, mobiles
You first need to find all the categories under the parent; if there are only 2 levels this is simple:
...
if (catId != null)
{
// Find the child categories for which this is the parent
var childCatIds = db.C_Categories
.Where(cat => cat.ParentId == catId)
.Select(cat => cat.CatId)
.ToList();
if (childCatIds.Count == 0)
// Not a parent category: Just find the items for the category as before
moduleItems = moduleItems.Where(x => x.CatId == catId);
else
// Parent category: Find the items for the child categories
moduleItems = moduleItems.Where(x => childCatIds.Contains(x.CatId));
}
If there are more than 2 levels, you will need to find the child ids recursively.
private List<int> GetChildCatIds(List<int> parentCatIds)
{
var childCatIds = db.C_Categories
.Where(cat => cat.ParentId.HasValue && parentCatIds.Contains(cat.ParentId.Value))
.Select(cat => cat.CatId)
.ToList();
if (childCatIds.Count == 0)
// Reached the end of the tree: no more children
return parentCatIds;
else
// Recursive call to find the next child level:
return GetChildCatIds(childCatIds);
}
...
if (catId != null)
{
var childCatIds = GetChildCatIds(new List<int>{catId.Value});
moduleItems = moduleItems.Where(x => childCatIds.Contains(x.CatId));
}
How about this:
moduleItems = dbcontext.C_Nodes.Where(n => n.CatId == catId);

Issue Related to SelectMany function in LINQ

I have two tables in Database:
PostCalculationLine
PostCaluclationLineProduct
PostCalculationLineProduct(table2) contains Foriegn key of PostCalucationLineId(table1)
In C# code I have two different Models for these two tables as follows:
public class PostCalculationLine : BaseModel
{
public long Id{ get; set; }
public string Position { get; set; }
public virtual Order Order { get; set; }
public virtual Task Task { get; set; }
//some other properties go here
public virtual IList<PostCalculationLineProduct> PostCalculationLineProducts { get; set; }
}
and
public class PostCalculationLineProduct : BaseModel
{
public long Id {get;set;}
public string Description { get; set; }
//some other properties go here
}
Now in Entityframework code, I fetch data from PostCalculationLineProduct as follows:
PostCalculationLineRepository pclr = new PostCalculationLineRepository();
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts)
.Where(c => c.Product.ProductType.Id == 1 && c.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.Id,
Date = c.From,
EmployeeName = c.Employee != null ?c.Employee.Name:string.Empty,
Description= c.Description,
ProductName = c.Product != null?c.Product.Name :string.Empty,
From = c.From,
To = c.Till,
Quantity = c.Amount,
LinkedTo = "OrderName",
Customer ="Customer"
PostCalculationLineId = ____________
})
.ToDataSourceResult(request);
In the above query I want to get PostCalculationLineId(from Table1) marked with underLine. How can I achieve this?
Thanks
You can use this overload of SelectMany to achieve this:-
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts,
(PostCalculationLineProductObj,PostCalculationLineObj) =>
new { PostCalculationLineProductObj,PostCalculationLineObj })
.Where(c => c.PostCalculationLineProductObj.Product.ProductType.Id == 1
&& c.PostCalculationLineProductObj.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.PostCalculationLineProductObj.Id,
Date = c.PostCalculationLineProductObj.From,
//Other Columns here
PostCalculationLineId = c.PostCalculationLineObj.Id
};
This will flatten the PostCalculationLineProducts list and returns the flattened list combined with each PostCalculationLine element.

Grouping and flattening list with linq and lambda

I have the following class
public class SolicitacaoConhecimentoTransporte
{
public long ID { get; set; }
public string CodigoOriginal { get; set; }
public DateTime Data { get; set; }
public List<CaixaConhecimentoTransporte> Caixas { get; set; }
}
I would like to know if there is a way of achiveing the same behavior of the code below using Linq (with lambda expression syntax),
List<SolicitacaoConhecimentoTransporte> auxList = new List<SolicitacaoConhecimentoTransporte>();
foreach (SolicitacaoConhecimentoTransporte s in listaSolicitacao)
{
SolicitacaoConhecimentoTransporte existing =
auxList.FirstOrDefault(f => f.CodigoOriginal == s.CodigoOriginal &&
f.Data == s.Data &&
f.ID == s.ID);
if (existing == null)
{
auxList.Add(s);
}
else
{
existing.Caixas.AddRange(s.Caixas);
}
}
return auxList;
In other words, group all entities that have equal properties and flat all lists into one.
Thanks in advance.
Use anonymous object to group by three properties. Then project each group to new SolicitacaoConhecimentoTransporte instance. Use Enumerable.SelectMany to get flattened sequence of CaixaConhecimentoTransporte from each group:
listaSolicitacao.GroupBy(s => new { s.CodigoOriginal, s.Data, s.ID })
.Select(g => new SolicitacaoConhecimentoTransporte {
ID = g.Key.ID,
Data = g.Key.Data,
CodigoOriginal = g.Key.CodigoOriginal,
Caixas = g.SelectMany(s => s.Caixas).ToList()
}).ToList()

Categories

Resources