views:

57

answers:

2

Problem: If I pass a class with value semantics (Equals method overridden) to NHibernate, NHibernate tries to save it to db even though it just saved an entity equal by value (but not by reference) to the database. What am I doing wrong?

Here is a simplified example model for my problem:

Let's say I have a Person entity and a City entity. One thread (producer) is creating new Person objects which belong to a specific existing City, and another thread (consumer) is saving them to a repository (using NHibernate as DAL).

Since there is lot of objects being flushed at a time, I am using Guid.Comb id's to ensure that each insert is made using a single SQL command. City is an object with value-type semantics (equal by name only -- for this example purposes only):

public class City : IEquatable<City>
{
    public virtual Guid Id { get; private set; }
    public virtual string Name { get; set; }

    public virtual bool Equals(City other)
    {
        if (other == null)
            return false;

        return this.Name == other.Name;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as City);
    }

    public override int GetHashCode()
    {
        return this.Name.GetHashCode();
    }
}

Fluent NH mapping is something like:

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Id(x => x.Id)
            .GeneratedBy.GuidComb();
        References(x => x.City)
            .Cascade.SaveUpdate();
    }
}

public class CityMap : ClassMap<City>
{
    public CityMap()
    {
        Id(x => x.Id)
            .GeneratedBy.GuidComb();
        Map(x => x.Name);
    }
}

Right now (with my current NHibernate mapping config), my consumer thread maintains a dictionary of cities and replaces their references in incoming person objects (otherwise NHibernate will see a new, non-cached City object and try to save it as well), and I need to do it for every produced Person object.

Since I have implemented City class to behave like a value type, I hoped that NHibernate would compare Cities by value and not try to save them each time -- i.e. I would only need to do a lookup once per session and not care about them anymore.

Is this possible, and if yes, what am I doing wrong here?

+2  A: 

Overriding Equals on an entity does not cause NHibernate to use "value semantics" on it.

If you call session.Save on two different objects, NHibernate will try to insert them both.

Diego Mijelshon
+1  A: 

When City is a value-object, it should not have an Identifier, since it's identity is determined based on its value.

Typically, value objects are declared as 'components' within NHibernate.

Frederik Gheysels