Implementing GetHashCode on a value class - gethashcode

I have a class Money and I want to know what the best way of implementing GetHashCode on this value class would be give that $1 != €1. Having a weighted value against the currency * value is not going to work.
public class Money : System.IEquatable<Money>
{
public Money(Currency c, decimal val)
{
this.Currency = c;
this.Value = val;
}
public Currency Currency
{
get;
protected set;
}
public decimal Value
{
get;
protected set;
}
public override bool Equals(object obj)
{
Money m = obj as Money;
if (m == null){throw new System.ArgumentNullException("m");}
if(m.Currency.Id == this.Currency.Id)
{
if(m.Value == this.Value)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public override int GetHashCode()
{
// What would be the best way of implementing this as €1 != $1
// Currency object contains 2 members, (int) Id and (string) symbol
}
}

As the Currency.Id seems unique, provided it's a non-zero integer I'd go with
public override int GetHashCode()
{
unchecked
{
return (Currency.Id*397) ^ Value.GetHashCode();
}
}
Would Currency.Id be a not-empty string or a Guid, the following would do the trick
public override int GetHashCode()
{
unchecked
{
return (Currency.Id.GetHashCode()*397) ^ Value.GetHashCode();
}
}

Related

Is it safe to implement GetHashCode by returning an unique ID from the object?

Let's say I have a POCO that represents a database row. I have access to the ID of that row.
Is it safe to implement GetHashCode and Equals by leveraging that unique ID ?
public class Project
{
public string ID { get; }
public string Name { get; set; }
public override bool Equals(object obj)
{
var pr = obj as Project;
if (pr == null) return false;
if (pr.ID == null) throw new InvalidOperationException("Attempt to .Equals an entity with null ID");
return pr.ID == ID;
}
public override int GetHashCode()
{
if (ID == null) throw new InvalidOperationException("Attempt to .GetHashCode on an entity with null ID");
return ID.GetHashCode();
}
}
I believe this could work because
ID is not mutable
I handle the case where ID is null
Any thoughts on that ?

MongoDB class has argument but none are configured

I have a class with some read only properties that I want to store using mongodb.
User.cs
public class User: ValueObject<User>
{
public UserId Id { get; }
public string Firstname { get; }
public string Lastname { get; }
public User(UserId id, string firstname, string lastname)
{
Id = new UserId(id.Id);
Firstname = firstname;
Lastname = lastname;
}
public User(string id, string firstname, string lastname)
{
Id = new UserId(id);
Firstname = firstname;
Lastname = lastname;
}
protected override bool MembersEquals(User other)
{
return Id == other.Id && Firstname == other.Firstname && Lastname == other.Lastname;
}
protected override int MembersHashCode()
{
return Id.GetHashCode() ^ Firstname.GetHashCode() ^ Lastname.GetHashCode();
}
}
Class map registration:
BsonClassMap.RegisterClassMap<User>(cm =>
{
cm.AutoMap();
cm.MapProperty(user => user.Id);
cm.MapProperty(user => user.Firstname);
cm.MapProperty(user => user.Lastname);
cm.MapCreator(user => new User(user.Id, user.Firstname, user.Lastname));
});
this class is stored as a subdocument. when i try to store the whole document mongodb driver tell me that the MongoDB.Bson.BsonSerializationException: Creator map for class Box.Domain.User has 3 arguments, but none are configured.. I didn't really understand what does it mean.
NB: the UserId class has a registred serializer
public class UserIdBsonSerializer : SerializerBase<UserId>
{
public override UserId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var currentBsonType = context.Reader.GetCurrentBsonType();
return currentBsonType switch
{
BsonType.String => new UserId(context.Reader.ReadString()),
_ => throw new NotSupportedException($"Cannot deserialize {currentBsonType} to an UserId")
};
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, UserId value)
{
context.Writer.WriteString(value.Id);
}
}
EDIT
ValueObject.cs
public abstract class ValueObject<T> : IEquatable<T>
where T : ValueObject<T>
{
// Verify the value object members equality.
protected abstract bool MembersEquals(T other);
// Generate a hash code depending on the value object members values.
protected abstract int MembersHashCode();
#region Equality
public bool Equals([AllowNull] T other)
{
if (ReferenceEquals(other, null))
return false;
if (ReferenceEquals(this, other))
return true;
return MembersEquals(other);
}
public override bool Equals(object obj)
{
var other = obj as T;
return Equals(other);
}
public static bool operator ==(ValueObject<T> lhs, ValueObject<T> rhs)
{
if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null))
return true;
if (ReferenceEquals(lhs, null))
return false;
return lhs.Equals(rhs);
}
public static bool operator !=(ValueObject<T> lhs, ValueObject<T> rhs) => !(lhs == rhs);
#endregion
public override int GetHashCode()
{
return MembersHashCode();
}
}
UserId.cs
public class UserId: ValueObject<UserId>
{
public string Id { get; }
public UserId(string id)
{
if (id.Length < 5)
throw new ArgumentException($"user id must be 5 characters length. {id}");
// TODO: Add more validation rules for user id
Id = id;
}
protected override bool MembersEquals(UserId other)
{
return Id == other.Id;
}
protected override int MembersHashCode()
{
return Id.GetHashCode();
}
}
Looks like the cm.AutoMap(); in your register class map is creating 2 creators before adding your own.
If you remove that it'll start working.
BsonClassMap.RegisterClassMap<User>(cm =>
{
cm.MapProperty(user => user.Id);
cm.MapProperty(user => user.Firstname);
cm.MapProperty(user => user.Lastname);
cm.MapCreator(user => new User(user.Id, user.Firstname, user.Lastname));
});
An alternative would be to remove the ImmutableTypeClassMapConvention from the default convention pack.
ConventionRegistry.Remove("__defaults__");
var pack = new ConventionPack();
var defaultConventions = DefaultConventionPack.Instance.Conventions;
pack.AddRange(defaultConventions.Except(
defaultConventions.OfType<ImmutableTypeClassMapConvention>()
));
ConventionRegistry.Register(
"__defaults__",
pack,
t => true);
It is a bit late now but still in case anybody would be interested in the root cause of this issue then please see the following expression && GetMemberType(memberInfos[0]) == parameter.ParameterType.
To summarize:
it is required that both -
constructor parameters be matched to
properties using case insensitive name matching and to be of the exact
same type.
For even more details please see CSHARP-3526

How to implement code first with existing database

I am using entityframework 5 with code first model and existing database.I am implementing repository pattern.I have a BaseEntity class like below:
public abstract partial class BaseEntity
{
/// <summary>
/// Gets or sets the entity identifier
/// </summary>
public virtual int Id { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as BaseEntity);
}
private static bool IsTransient(BaseEntity obj)
{
return obj != null && Equals(obj.Id, default(int));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual bool Equals(BaseEntity other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
public override int GetHashCode()
{
if (Equals(Id, default(int)))
return base.GetHashCode();
return Id.GetHashCode();
}
public static bool operator ==(BaseEntity x, BaseEntity y)
{
return Equals(x, y);
}
public static bool operator !=(BaseEntity x, BaseEntity y)
{
return !(x == y);
}
protected virtual void SetParent(dynamic child)
{
}
protected virtual void SetParentToNull(dynamic child)
{
}
protected void ChildCollectionSetter<T>(ICollection<T> collection, ICollection<T> newCollection) where T : class
{
if (CommonHelper.OneToManyCollectionWrapperEnabled)
{
collection.Clear();
if (newCollection != null)
newCollection.ToList().ForEach(x => collection.Add(x));
}
else
{
collection = newCollection;
}
}
protected ICollection<T> ChildCollectionGetter<T>(ref ICollection<T> collection, ref ICollection<T> wrappedCollection) where T : class
{
return ChildCollectionGetter(ref collection, ref wrappedCollection, SetParent, SetParentToNull);
}
protected ICollection<T> ChildCollectionGetter<T>(ref ICollection<T> collection, ref ICollection<T> wrappedCollection, Action<dynamic> setParent, Action<dynamic> setParentToNull) where T : class
{
if (CommonHelper.OneToManyCollectionWrapperEnabled)
return wrappedCollection ?? (wrappedCollection = (collection ?? (collection = new List<T>())).SetupBeforeAndAfterActions(setParent, SetParentToNull));
return collection ?? (collection = new List<T>());
}
}
Now I have a table Customer which has CustomerId as primary key .How should I map such like fields with Id of BaseEntity.If I have a composite key how should I map this to the BaseEntity ID.
Please Help me.
Make sure you set the initialize for your DBContext to false:
context.Database.Initialize(false);
Here is a good article on database initializers for code first:
http://www.codeguru.com/csharp/article.php/c19999/Understanding-Database-Initializers-in-Entity-Framework-Code-First.htm

Entity Framework: Entity with composite key as PK/FK throws exception

On escalado, throws the exception. It throws with or wihtout Include.
static void Main(string[] args)
{
try
{
using (var context = new CKContext())
{
var servReprosWithIncludes = context.ServicioRepro
.Include(p => p.Categoria)
.ToList();
var escalado = context.EscaladoPrecio
//.Include(p => p.Servicio)
.ToList();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
InvalidOperationException: The value of a property that is part of an object's key does not match the corresponding property value stored in the ObjectContext. This can occur if properties that are part of the key return inconsistent or incorrect values or if DetectChanges is not called after changes are made to a property that is part of the key.
The mapping of EscaladoPrecio:
public class EscaladoPrecioMapping : EntityTypeConfiguration<EscaladoPrecio>
{
public EscaladoPrecioMapping()
{
base.HasKey(p => new { p.Desde, p.Hasta, p.ServicioReproId });
base.HasRequired(p => p.Servicio)
.WithMany()
.HasForeignKey(p => p.ServicioReproId);
base.ToTable("PreciosServicioReprografia");
}
}
The entity ServicioRepro is a part from TPT hierarchy. Looks like:
public class ServicioRepro : Producto
{
public bool IncluirPrecioClick { get; set; }
public bool IncluirPrecioPapel { get; set; }
public bool HayPapel { get; set; }
public bool HayImpresion { get; set; }
public bool PrecioPorVolumen { get; set; }
//public virtual ICollection<EscaladoPrecio> EscaladoPrecio { get; set; }
public virtual CategoriaServicioRepro Categoria { get; set; }
public virtual ServicioReproFacturacionType ServicioReproFacturacionType { get; set; }
}
On this entity you can't see the key, because the base entity Producto have it.
The entity EscaladoPrecio have 3 PK: desde, hasta and Servicio. Servicio is PK and FK.
The entity looks like (methods, overrides and members have been removed to reduce the code):
public class EscaladoPrecio : IComparable<EscaladoPrecio>, IComparable<int>, IComparable, IEntity
{
#region Declarations
private int _desde;
private int _hasta;
private double _precio;
private int _cada;
#endregion Declarations
#region Constructor
public EscaladoPrecio()
: this(1, 1, 0, 0)
{ }
public EscaladoPrecio(int desde, int hasta, double precio)
: this(desde, hasta, precio, 0)
{ }
public EscaladoPrecio(int desde, int hasta, double precio, int cada)
{
_desde = desde;
_hasta = hasta;
_precio = precio;
_cada = cada;
}
#endregion Constructor
#region Properties
public int Desde
{
get
{
return _desde;
}
set
{
_desde = value;
}
}
public int Hasta
{
get
{
return _hasta;
}
set
{
_hasta = value;
}
}
public double Precio
{
get
{
return _precio;
}
set
{
_precio = value;
}
}
public int Cada
{
get
{
return _cada;
}
set
{
_cada = value;
}
}
#endregion Properties
private int _ServicioReproId;
public int ServicioReproId
{
get
{
if (Servicio != null)
{
_ServicioReproId = Servicio.Id;
return Servicio.Id;
}
else
return 0;
}
set
{
_ServicioReproId = value;
}
}
public virtual ServicioRepro Servicio { get; set; }
}
Why throws the exception?
Why are you doing this:
public int ServicioReproId
{
get
{
if (Servicio != null)
{
_ServicioReproId = Servicio.Id;
return Servicio.Id;
}
else
return 0;
}
set
{
_ServicioReproId = value;
}
}
Your part of the key property ServicioReproId is returning 0 here potentially although it has been loaded (and stored in the context) with a value != 0 (probably). I think this part of the exception is refering to this problem: "This can occur if properties that are part of the key return inconsistent or incorrect values."
Better leave it an automatic property:
public int ServicioReproId { get; set; }
try to initialice his virtual property in the constructor of the class EscaladoPrecio()

Equals and GetHashCode

What do you think about this Person class? Is it a bad idea or best practise to override Equals and GetHashCode like that?
public class Person {
public int PersonId { get; set; }
public string Name { get; set; }
public override bool Equals(object obj) {
var person = obj as Person;
return PersonId == person.PersonId;
}
public override int GetHashCode() {
return PersonId;
}
}
Usage :
static void Main(string[] args) {
var list = new List<Person>();
list.Add(new Person(){ PersonId = 1, Name = "Mike"});
list.Add(new Person() { PersonId = 2, Name = "Michael Sync" });
list.Add(new Person(){ PersonId = 1, Name = "Mike"});
var list1 = new List<Person>();
list1.Add(new Person() { PersonId = 1, Name = "Mike" });
list1.Add(new Person() { PersonId = 3, Name = "Julia" });
var except = list.Except(list1);
foreach (var item in except) {
Console.WriteLine(item.Name);
}
Console.ReadKey();
}
A few points:
It's not null safe or "different type" safe. Try this:
new Person().Equals(new Object());
or
new Person().Equals(null);
Bang.
Classes defining equality operations should usually be immutable IMO. Changing the contents of an object after using it as a dictionary key is a Bad Thing, for example.
Consider implementing IEquatable<Person>
A quick reimplementation, which still assumes you want equality based solely on ID.
public sealed class Person : IEquatable<Person> {
private readonly int personId;
public int PersonId { get { return personId; }
private readonly string name;
public string Name { get { return name; } }
public Person(int personId, string name) {
// Is a null name valid? If not, throw here.
this.personId = personId;
this.name = name;
}
public override bool Equals(object obj) {
return Equals(obj as Person);
}
public Equals(Person other) {
return other != null && other.personId == personId;
}
public override int GetHashCode() {
return personId;
}
}
Yes this is wrong. You should never use a mutable property as part of the calculation for GetHashCode. Doing so opens you up to numerous hard to track down bugs.
One problem I can see is that you'll get lots of collisions for new (unsaved) records (lots of zeros) - unless you do something like have consecutive -ve ids for those... But do you really need to use Person as a key? Personally I don't think I'd bother...