Search code examples
c#nhibernatemappingfluent

How do I map a table were primary key and identifier is not the same column?


I have to access a legacy database in which I'm faced with the following: A column 'unit_name' is defined as primary key and is a string, and another column 'id' is defined as a classic int identifier.

My question is - how do I map this correct in Fluent NHibernate?

Currently my mapping looks like this, but I can't find any proper documentation for my scenario, so I'm not sure if it is correct.

    public InputMap()
    {
        Table("input");
        Map(x => x.unit_name).Not.Nullable().Unique();
        //other mappings ...
        Id(x => x.id).Column("id").Not.Nullable();
        //Maybe use this instead? NaturalId().Property(x => x.unit_name);            
    }

Full context:

In my hunt for documentation I've created an id class that implements Equals and GetHashCode, but that might be overkill after all.

[Serializable]
public class GenericEntityId : EntityId, IEquatable<GenericEntityId>
{
    public GenericEntityId(string idString)
    {
        IdString = idString;
    }

    public string IdString { get; set; }

    private Guid _internalId { get; set; }

    protected GenericInoEntityId()
    {
        _internalId = Guid.NewGuid();
    }

    public virtual bool Equals(GenericEntityId obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (GetType() != obj.GetType()) return false;
        if (!string.IsNullOrEmpty(IdString) )
            return obj.IdString == IdString;
        return false;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (GetType() != obj.GetType()) return false;
        return Equals((GenericEntityId)obj);
    }

    public override int GetHashCode()
    {
        if (!string.IsNullOrEmpty(IdString))
        {
            return (IdString.GetHashCode() * 397) ^ GetType().GetHashCode();
        }
        else
        {
            return (_internalId.GetHashCode() * 397) ^ GetType().GetHashCode();
        }
    }

    public static bool operator ==(GenericEntityId left, GenericEntityId right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(GenericEntityId left, GenericEntityId right)
    {
        return !Equals(left, right);
    }
}

and EntityId is just an abstract class:

public abstract class EntityId
{
    public abstract override int GetHashCode();
}

The reason for creating an abstract base is that I want to have a generic base I can use for all my repositories no matter if they have a string primary key or a composite key (or maybe the identifier)

[Serializable]
public abstract class EntityBase
{
    public abstract EntityId entityId { get; }

    protected EntityBase()
    {
    }
}

public class GenericRepository<TEntity> : SessionManagerBase, IEntityRepository<TEntity> where TEntity : EntityBase
{
    public TEntity GetById(EntityId id)
    {
        ISession currentSession = OpenSession;

        var returnObject = currentSession.Get<TEntity>(id);
        return returnObject;
    }
}

Solution

  • This is Nhibernate mapping-by-code, but fluent should be similar

    domain object class

    public class TestEntity
    {
        public String unit_name { get; set; }
        public Int32 id { get; protected set; }
    }
    

    mapping class

    public class TestEntityMap : ClassMapping<TestEntity>
    {
        public TestEntityMap()
        {
            Id( x => x.unit_name, map =>
            {
                map.Column("user_name");
                map.Generator(Generators.Assigned);
            });
    
            Property(x => x.id, map =>
            {
                map.Generated(PropertyGeneration.Always);
                map.Unique(true);
                map.NotNullable(true);
            });
        }
    }