For a NetCore Web API GET
method I need to caluclate the ETag for a returned List<T>
. T is the DTO in the form of a record
that holds only primitive types.
I wanted to calculate a hash of the list. I was searching for information about how GetHashCode()
is implemented, but couldn't find any information. The documentation of object.GetHashCode()
doesn't state any information about lists or collections. By the results of the code I observed that on each run the same list data creates a different hash code. I concluded that GetHashCode()
uses the pointer values for reference type items.
GetHashCode()
of record
calculates the hash code per member value. Therefore I created the list hash code by looping over the list items:
List<GetGroupsDTO> dtoList = commandResult.Value;
int hash = 17;
foreach(GetGroupsDTO dto in dtoList)
{
hash = hash * 23 dto.GetHashCode();
}
string eTagPayload = hash.ToString().SurroundWithDoubleQuotes();
I don't want to do this for every List<T>
, of course. I thought to override GetHashCode()
, but I'm struggling with it. I don't know how to override it for the generic List. I could derive a new class DTOList where I can override GetHashCode()
. But this leads to more complexity in other places. Since the result of an EFCore Set query fills the List I would need a custom converter and then a custom serializer to return the List in Web API.
Therefore I wonder if I rather should create an extension method for List or just a function that takes List as an argument. Is there any other option to calculate the ETag? How can I calculate the ETag for a list of DTO objects efficently?
CodePudding user response:
A little extension method and HashCode
could help with this:
internal static class EnumerableExtensions {
public static int GetCombinedHashCode<T>(this IEnumerable<T> source) =>
source.Aggregate(typeof(T).GetHashCode(), (hash, t) => HashCode.Combine(hash, t));
}
Seeding the hash with typeof(T).GetHashCode
is a rather arbitrary, but ensures that empty collections of different types do not all "look equal", since they would not normally compare equal either. Whether this matters or is even desirable will depend on your scenario.
Of course the result of this is only usable if T
has a meaningful GetHashCode
implementation, but that's true of hashes in general. For extra peace of mind a where T : IEquatable<T>
constraint could be added, although that's not the standard approach for methods involving hashes. Adding the ability to use a custom IEqualityComparer<T>
for the hash is left as an exercise.