I have an array of ListViewItems ( ListViewItem[]
), where I store a SalesOrderMaster
object in each ListViewItem.Tag for later reference.
I have some code that right now, goes through each ListViewItem
safely casts the .Tag property into a SalesOrderMaster object, then adds that object to a collection of SalesOrders, only after checking to make sure the order doesn't already exist in that collection.
The process to compare sales orders is expensive, and I would like to convert this to a LINQ expression for clarity and performance. ( I also have the Parallel Extensions to .NET Framework 3.5 installed so I can use that to further improve LINQ performance)
So without further ado: This is what I have, and then what I want. ( what I want won't compile, so I know I am doing something wrong, but I hope it illustrates the point )
What I have: ( Slow )
foreach (ListViewItem item in e.Argument as ListViewItem[])
{
SalesOrderMaster order = item.Tag as SalesOrderMaster;
if ( order == null )
{
return;
}
if (!All_SalesOrders.Contains(order))
{
All_SalesOrders.Add(order);
}
}
What I want: ( Theory )
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]).AsParallel()
select new { ((SalesOrderMaster)item.Tag) }).Distinct();
EDIT: I know the cast is cheap, I said the "Compare", which in this case translates to the .Contains(order) operation
EDIT: Everyone's answer was awesome! I wish I could mark more than one answer, but in the end I have to pick one.
EDIT : This is what I ended up with:
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]) select (SalesOrderMaster) item.Tag).GroupBy(item => item.Number).Select(x => x.First()).ToList();
You are allowed to use an anonymous type in LINQ. In LINQ, select clause generates anonymous type so that in a query you can include properties that are not defined in the class.
LINQ query operations are strongly typed in the data source, in the query itself, and in the query execution.
Anonymous types typically are used in the select clause of a query expression to return a subset of the properties from each object in the source sequence. For more information about queries, see LINQ in C#. Anonymous types contain one or more public read-only properties.
I see nobody has addressed your need to convert an anonymous type to a named type explicitly, so here goes... By using "select new { }
" you are creating an anonymous type, but you don't need to. You can write your query like this:
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]).AsParallel()
select (SalesOrderMaster)item.Tag)
.Distinct()
.ToList();
Notice that the query selects (SalesOrderMaster)item.Tag
without new { }
, so it doesn't create an anonymous type. Also note I added ToList()
since you want a List<SalesOrderMaster>
.
This solves your anonymous type problem. However, I agree with Mark and Guffa that using a parallel query here isn't you best option. To use HashSet<SalesOrderMaster>
as Guffa suggested, you can do this:
IEnumerable<SalesOrderMaster> query =
from item in (ListViewItem[])e.Argument
select (SalesOrderMaster)item.Tag;
HashSet<SalesOrderMaster> orders = new HashSet<SalesOrderMaster>(query);
(I avoided using var
so the returned types are clear in the examples.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With