Reputation: 30922
I have written a GroupBy statement like so:
var aggregated = sitesWithLive
.GroupBy(s => new {s.SiteRefNum, s.SiteRefName, s.Address})
.Select(g =>
new Site
{
SiteRefNum = g.Key.SiteRefNum,
SiteRefName = g.Key.SiteRefName,
Address = g.Key.Address,
ContractLive = g.Max(x => x.ContractLive)
});
In the grouping is Address
which is a complex type:
public class Address
{
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Line4 { get; set; }
public string PostCode { get; set; }
public bool IsEmpty()
{
return GetType().GetProperties()
.Where(a => a.GetValue(this) is string)
.Select(a => (string)a.GetValue(this))
.All(string.IsNullOrEmpty);
}
public override string ToString()
{
var addr = Line1 + "," + Line2 + "," + Line3 + "," + Line4 + "," + PostCode;
var address = Regex.Replace(addr, @"^,+|,{2,}|,(?=[\w.])", ", ");
return address;
}
}
However this does not correctly group by address, instead returning a separate group for each element.
It was my understanding that to group by the complex type you must provide an IEqualityComparer
so I created the following:
public class AddressComparer : IEqualityComparer<Address>
{
public bool Equals(Address x, Address y)
{
return x.ToString() == y.ToString();
}
public int GetHashCode(Address obj)
{
return 1;
}
}
and supplied it like so (subset of above):
var aggregated = sitesWithLive.GroupBy(s => new {s.SiteRefNum, s.SiteRefName, s.Address}, new AddressComparer())
...
This however gives me
The type arguments cannot be inferred from the usage. Try specifying the type arguments explicitly.
I'm at a loss as to my next step, surely this sort of grouping shouldn't be too difficult?
Upvotes: 2
Views: 1061
Reputation: 2574
You can try to override Equals and GetHashCode in your Address class (like in your comparer):
public override bool Equals(object obj)
{
Address adr = obj as Address;
if (adr != null)
return adr.ToString() == this.ToString();
return false;
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
Upvotes: 1
Reputation: 236278
Enumerable.GroupBy
allows you to pass custom equality comparer for keys. But in your case key is not an Address
object - it's an anonymous object containing three properties - SiteRefNum
, SiteRefName
and Address
. Of course passing AddressComparer
to compare such keys will cause an error.
And your first problem was using complex object as key property. If you don't override Equals
and GetHashCode
methods for Address
objects, then all addresses will be compared by reference. Which is of course different for each address instance. You can provide Equals
and GetHashCode
implementations to compare addresses.
Or you can modify your query to use address string for grouping:
var aggregated =
from s in sitesWithLive
group s by new {
s.SiteRefNum,
s.SiteRefName,
Address = s.Address.ToString() // here we group by string
} into g
select new Site
{
SiteRefNum = g.Key.SiteRefNum,
SiteRefName = g.Key.SiteRefName,
Address = g.First().Address, // here we just get first address object
ContractLive = g.Max(x => x.ContractLive)
};
You can use method syntax for query, but I find declarative query syntax more readable :)
Upvotes: 5