I use this method to retrieve Items for a Tree component in Blazor Serverside, In the DAL I have:
public List<TreeItem> GetTreeItems()
{
var tree = new List<TreeItem>();
TreeItem item = new TreeItem()
{
DepartmentID = 0,
CategoryID = 0,
Text = "Root",
Childs = context.Departments.OrderBy(d => d.Order).Select(d => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = 0,
Text = d.Title,
Childs = d.Categories.OrderBy(c => c.Order).Select(c => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = c.Id,
Text = c.Title
}).ToList()
}).ToList()
};
tree.Add(item);
return tree;
}
The TreeItem class is the following, (The model shared by the blazor Component and Dal Class):
public class TreeItem
{
public int DepartmentID { get; set; }
public int CategoryID { get; set; }
public string Text { get; set; }
public List<TreeItem> Childs { get; set; }
}
But when I was to retrieve Items for the tree in the blazor component I get the exception: Operation is not valid due to the current state of the object., admin is the DAL class I inject to Blazor component as follows:
private void GetTreeModel()
{
try
{
Items = admin.GetTreeItems();
TreeSuccess = true;
TreeMessage = "Success";
return;
}
catch (Exception ex) // Error here
{
TreeSuccess = false;
TreeMessage = "Can not load tree items";
return;
}
}
What is this error and How to solve it?
I solved my problem using First loading entities and then using Linq to Objects, Like this:
var tree = new List<TreeItem>();
var departments = context.Departments.OrderBy(d => d.Order).ToList();
var categories = context.Categories.OrderBy(c => c.Order).ToList();
TreeItem item = new TreeItem()
{
DepartmentID = 0,
CategoryID = 0,
Text = "Root",
Childs = departments.Select(d => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = 0,
Text = d.Title,
Childs = categories.Where(c => c.DepartmentID == d.Id).OrderBy(c => c.Order).Select(c => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = c.Id,
Text = c.Title
}).ToList()
}).ToList()
};
tree.Add(item);
return tree;
}
Consider tables
Inventions, list of inventions
Components, list of all components available for use in inventions, and
InventionComponents, list of utilized components, with count, in an invention
For a given invention, &inventionID, I would like to do a 'covering' left join to all of the components instead of just the utilized components.
SQL would be something like
select
I.name as inventionName
, C.name as componentName
, coalese (IC.count, 0) as componentCount
from
(select &inventionID as inventionID, ID, name from components) C -- all components applied to some &inventionID
left join
inventionComponents IC
on
C.ID = IC.ComponentID
and C.inventionID = IC.inventionID
join
inventions I
on
I.ID = C.inventionID
Sample data and Linq query in .NET fiddle at https://dotnetfiddle.net/cx4bHp results in an exception
[System.NullReferenceException: Object reference not set to an instance of an object.]
Question: How should the Linq query be modified to perform the desired covering query ?
For completeness, the C# fiddle code is repeated here
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var components = new List<Component>{
new Component { ID=1, Name = "Florgebit" },
new Component { ID=2, Name = "Phadron" },
new Component { ID=3, Name = "Goobstem" },
new Component { ID=4, Name = "Larchwren" },
new Component { ID=5, Name = "Zangponder" },
new Component { ID=6, Name = "Spoofork" },
new Component { ID=7, Name = "Forkoon" },
new Component { ID=8, Name = "Blidget" },
new Component { ID=9, Name = "Wazzawim" },
new Component { ID=10, Name = "Klackberg" },
};
var inventions = new List<Invention>{
new Invention { ID=21, Name = "Swazzlute" },
new Invention { ID=22, Name = "Corpocran" },
new Invention { ID=23, Name = "Fillyboof" },
};
var inventionComponents = new List<InventionComponent>{
new InventionComponent { ID=100, InventionID=21, ComponentID=1, Count=1 },
new InventionComponent { ID=101, InventionID=21, ComponentID=2, Count=2 },
new InventionComponent { ID=102, InventionID=21, ComponentID=8, Count=3 },
new InventionComponent { ID=103, InventionID=23, ComponentID=5, Count=4 },
new InventionComponent { ID=104, InventionID=23, ComponentID=6, Count=5 },
new InventionComponent { ID=105, InventionID=23, ComponentID=3, Count=4 },
new InventionComponent { ID=106, InventionID=21, ComponentID=4, Count=3 },
new InventionComponent { ID=107, InventionID=22, ComponentID=5, Count=2 },
new InventionComponent { ID=108, InventionID=22, ComponentID=4, Count=1 },
new InventionComponent { ID=109, InventionID=22, ComponentID=1, Count=6 },
new InventionComponent { ID=110, InventionID=22, ComponentID=7, Count=1 },
new InventionComponent { ID=111, InventionID=21, ComponentID=9, Count=1 },
};
var details =
from A in inventions
join B in inventionComponents on A.ID equals B.InventionID
join C in components on B.ComponentID equals C.ID
orderby A.Name, C.Name
select new {
InventionName = A.Name,
ComponentName = C.Name,
ComponentCount = B.Count
};
/*
foreach(var d in details)
{
Console.WriteLine("Invention: {0}, Component: {1}, Count: {2}", d.InventionName, d.ComponentName, d.ComponentCount);
}
*/
var inventionID = 22;
var index = 1;
// want full coverage of inventionID, componentID with applied counts
// 22,1,6
// 22,2,**0**
// 22,3,**0**
// 22,4,1
// 22,5,2
// 22,6,**0**
// 22,7,1
// 22,8,**0**
// 22,9,**0**
// 22,10,**0**
var corpcheck =
from C in components select new { InventionID = inventionID, ComponentID = C.ID, ComponentName = C.Name } into allcomps
join B in inventionComponents on new { allcomps.InventionID, allcomps.ComponentID } equals new { B.InventionID, B.ComponentID } into join1
// from j1 in Join1 // inner join
from j1 in join1.DefaultIfEmpty() // causes exception
orderby allcomps.ComponentName
select new {
RowNum = index++,
InventionID = allcomps.InventionID,
ComponentName = allcomps.ComponentName,
ComponentCount = j1.Count,
};
foreach(var x in corpcheck)
{
Console.WriteLine("InventionID: {0}, RowNum: {1}, ComponentName: {2}, Count: {3}", x.InventionID, x.RowNum, x.ComponentName, x.ComponentCount);
}
}
public class Invention
{
public int ID { get; set; }
public string Name { get; set; }
}
public class InventionComponent
{
public int ID { get; set; }
public int InventionID { get; set; }
public int ComponentID { get; set; }
public int Count { get; set; }
}
public class Component
{
public int ID { get; set; }
public string Name { get; set; }
}
}
By adding DefaultIfEmpty(), j1 can be null for some components. If j1 is null, I assume you want count to be 0:
from C in components select new { InventionID = inventionID, ComponentID = C.ID, ComponentName = C.Name } into allcomps
join B in inventionComponents on new { allcomps.InventionID, allcomps.ComponentID } equals new { B.InventionID, B.ComponentID } into join1
from j1 in join1.DefaultIfEmpty()
orderby allcomps.ComponentName
select new {
RowNum = index++,
InventionID = allcomps.InventionID,
ComponentName = allcomps.ComponentName,
ComponentCount = j1 == null ? 0 : j1.Count, // add null check
};
In LINQ-to-objects you could also use ComponentCount = j1?.Count ?? 0. but I assume you're going to use this in LINQ to a SQL backend.
I need some help to simplify a linq query. I have 2 classes Invoice and Customer.
The Invoice have a property CustomerId and a property Customer.
I need to get all invoices and include the Customer object.
I don't like my query, as it needs to change if new properties are added to the Invoice object.
I can't join the invoice and customer earlier than this stage so that is not an alternative.
My query.
var customers = GetCustomers();
var invoices = GetInvoices();
var joinedList = (from x in invoices
join y in customers on x.CustomerId equals y.CustomerId
select new Invoice
{
Amount = x.Amount,
CustomerId = x.CustomerId,
Customer = y,
InvoiceId = x.InvoiceId
}).ToList();
The classes
public class Invoice
{
public int InvoiceId { get; set; }
public double Amount { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
}
private static IEnumerable<Invoice> GetInvoices()
{
yield return new Invoice
{
Amount = 34,
CustomerId = 1,
InvoiceId = 1
};
yield return new Invoice
{
Amount = 44.7,
CustomerId = 1,
InvoiceId = 2
};
yield return new Invoice
{
Amount = 67,
CustomerId = 2,
InvoiceId = 3
};
yield return new Invoice
{
Amount = 89,
CustomerId = 3,
InvoiceId = 4
};
}
private static IEnumerable<Customer> GetCustomers()
{
yield return new Customer
{
CustomerId = 1,
Name = "Bob"
};
yield return new Customer
{
CustomerId = 2,
Name = "Don"
};
yield return new Customer
{
CustomerId = 3,
Name = "Alice"
};
}
Why not just a simple foreach loop:
// Dictionary for efficient look-up
var customers = GetCustomers().ToDictionary(c => c.CustomerId);
var invoices = GetInvoices().ToList();
//TODO: error checking
foreach(var i in invoices)
i.Customer = customers[i.CustomerId];
i have a list of generic class which consists of 2 string property and 1 List as a property
code snipnets is as follows:
public Class abc
{
public int ID { get; set; }
public String Name { get; set; }
List<String> myList;
public List<String> Subjects
{
get
{
if (myList == null)
{
myList = new List<string>();
}
return myList;
}
}
public abc()
{
}
public abc(int id, String name, params string[] subjects)
{
Subjects.AddRange(subjects.AsEnumerable<String>());
ID = id;
Name = name;
}
}
List<abc> myList = new List<abc>();
myList.Add(new abc(1, "p1", "Maths", "Science"));
myList.Add(new abc(2, "p2", "Maths", "Art"));
myList.Add(new abc(3, "p3", "Art", "Science"));
myList.Add(new abc(4, "p4", "Geometry", "Maths"));
I need the output as
Subject Count Person
Maths 3 p1,p2,p4
Science 2 p1,p3
Art 2 p2,p3
Geometry 1 p4
Looks like you want something like:
var query = from item in myList
from subject in item.Subjects
group item.Name by subject into g
select new { Subject = g.Key,
Count = g.Count(),
Person = string.Join(",", g) };
(Change g into g.ToArray() in the string.Join call if you're using .NET 3.5.)
var result =myList.SelectMany(p => p.Subjects
.Select(q => new{Person = p.Name, Subject = q, ID = p.ID}))
.GroupBy(p => p.Subject)
.Select(p => new {Name = p.Key, Count = p.Count(), Persons = p
.Aggregate("", (a, b) => a + b.Person
+ ",").TrimEnd(',')}).OrderBy( p => p.Count);
Iterate over this collection, and print result as needed - properties of a result are Name, Count, Persons
Linq to Objects - Where search within a list
internal class ProdQtyByWarehouse
{
public int id { get; set; }
public List<ProdWarehouseQty> ProdWarehouseQtys { get; set; }
}
internal class ProdWarehouseQty
{
public int id { get; set; }
public string PName { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
var list1 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 3,
PName = "list1PN1"
},
new ProdWarehouseQty
{
id = 4,
PName = "list1PN2"
}
};
var list2 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 5,
PName = "list2PN1"
},
new ProdWarehouseQty
{
id = 6,
PName = "list2PN2"
}
};
var prodQtyByWarehouses = new List<ProdQtyByWarehouse>
{
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list1},
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list2}
};
List<int> integers = new List<int>{2,3,4,6};
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id = 3})
select c).ToList(); // no object is returned
}
How can i achieve:
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id in integers})
select c).ToList();
List<ProdQtyByWarehouse> list =
(
from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Exists(x => integers.Contains(x.id))
select c
).ToList();