views:

22

answers:

1

Hi all,

I have collection of custom objects(assets) which I want to group with LINQ. Custom object has standard properties like id, name and cost property. When grouping I want to calculate cost for each group, so I'm using little trick like this:

from a in assets
group a by a.AssetId into ga
select new Asset()
                 {
                    AssetId = ga.Key,
                    Cost = ga.Select(gg=>gg.Cost).Sum()
                 }

Ok, everything is fine here. But...in order to initialize order properties as well, I'm using copy contructor and cost calculations together...

from a in assets
group a by a.AssetId into ga
select new Asset(ga.FirstOrDefault())
                 {
                    AssetId = ga.Key,
                    Cost = ga.Select(gg=>gg.Cost).Sum()
                 }

So now, I get collection of grouped assets by id, with all properties copied from a first asset in a group with calculated grouped cost. But...in order to do that I need to write for every object that using this kind of grouping, a copy constructor with 'all properties initialization' which in my case is overhead because there are objects with 20+ properties.

I've tried to use clone trick from a link:

http://stackoverflow.com/questions/78536/cloning-objects-in-c

in linq group query but with no success.

My question: is there a better/more elegant way to accomplish this?

Thank you

+1  A: 

If you don't need deep copy them you can use MemberwiseClone method. The implementation (comes from Object class) & gives you a shallow copy. So line new Asset(ga.FirstOrDefault()) will become ((Asset)(ga.FirstOrDefault().MemberwiseClone())). However, being shallow copy, [First Asset].SomeObject would point to [Grouped Asset].SomeObject and changes from one would reflect at other.

Edited to include helper method described in comments:

static T CloneAndUpdate<T>(T t, Action<T> updater) where T: class
{
T clone = null; // Use reflection/serialization to create shallow/deep clone
updater(clone);
return clone;
}

Now, you may use the method as follows:

select Utility.CloneAndUpdate(ga.FirstOrDefault(), a =>
             {
                AssetId = ga.Key,
                Cost = ga.Select(gg=>gg.Cost).Sum()
             })
VinayC
Oops! I notice that you want to use object initializers - not sure if they would work on Asset instance w/o using constructor.
VinayC
Only way that I can think of is to add copy constructor that uses MemberwiseClone() - that will reduce at least time to write copy constructor.Another way would be to write a static method such as --static T CloneAndUpdate<T>(T t, Action<T> updater) where T: class{ T clone = ...; /* Use reflection/serialization to create clone */ updater(clone); return clone;} then use it as ----select Utility.CloneAndUpdate(ga.FirstOrDefault(), a => { AssetId = ga.Key, Cost = ga.Select(gg=>gg.Cost).Sum()})
VinayC
yes, thats my problem.. thanks anyway, nice answer
Marko
hmm, last comment is a nice solution, could you post it as a answer please, it is little unreadable like this...thanx :)..oh, you did it already...ok thats it, thank you very much, I like your solution...+1 ;) and marked as answer :)
Marko