Search code examples
c#nhibernate

Configure OneToOne mapping with non-key column in NHibernate


I would like to create a OneToOne mapping between two of my entities, using non-primary columns. How do I configure this in NHibernate ?

Entities:

public class DocumentLock
{
    public virtual long Id { get; set; }
    public virtual string LockedBy { get; set; }
    public virtual string DocumentCode { get; set; }
    public virtual LegalDocument LegalDocument { get; set; }
}
public class LegalDocument
{
    public virtual long Id { get; set; }
    public virtual string Title { get; set; }
    public virtual string DocumentCode { get; set; }
    public virtual DocumentLock DocumentLock { get; set; }
}

Note: DocumentCode is a string generated in the application, it's unique for each DocumentLock and LegalDocument

Mappings:

public class DocumentLockMap : ClassMapping<DocumentLock>
{
    public DocumentLockMap()
    {
        ManyToOne(x => x.LegalDocument, map =>
        {
            map.Column("DocumentCode");
            map.Insert(false);
            map.Update(false);
            map.Unique(true);
            map.Cascade(Cascade.None);
        });
    }
}
public class LegalDocumentMap : ClassMapping<LegalDocument>
{
    public LegalDocumentMap()
    {
        OneToOne(x => x.DocumentLock, map =>
        {
            map.PropertyReference(x => x.LegalDocument);
            map.Cascade(Cascade.All.Include(Cascade.DeleteOrphans));
            map.Fetch(FetchKind.Join);
            map.Constrained(true);
        });
    }
}

Query code:

using (var session = _sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
    var data = session.QueryOver<LegalDocument>().List();
};

Generated query:

SELECT
    this_.Id as id1_1_1_,
    this_.Title as title2_1_1_,
    this_.DocumentCode as documentcode3_1_1_,
    documentlo2_.Id as id1_0_0_,
    documentlo2_.LockedBy as lockedby2_0_0_,
    documentlo2_.DocumentCode as documentcode3_0_0_ 
FROM
    LegalDocument this_ 
inner join
    DocumentLock documentlo2_ 
        on this_.Id = documentlo2_.DocumentCode

My expectation for the generated query is: it should join using this_.DocumentCode, not this_.Id

on this_.DocumentCode = documentlo2_.DocumentCode

Solution

  • Thanks @Firo and @Roman Artiukhin.

    Solution: use ManyToMany on both mappings So in my case, I'll use:

    public class DocumentLockMap : ClassMapping<DocumentLock>
    {
        public DocumentLockMap()
        {
            ManyToOne(x => x.LegalDocument, map =>
            {
                map.PropertyRef("DocumentCode");
                map.Column("DocumentCode");
                map.Insert(false);
                map.Update(false);
                map.Unique(true);
                map.Cascade(Cascade.None);
            });
        }
    }
    
    
    public class LegalDocumentMap : ClassMapping<LegalDocument>
    {
        public LegalDocumentMap()
        {
            ManyToOne(x => x.DocumentLock, map =>
            {
                map.PropertyRef("DocumentCode");
                map.Column("DocumentCode");
                map.Insert(false);
                map.Update(false);
                map.Unique(true);
                map.Cascade(Cascade.All.Include(Cascade.DeleteOrphans));
                map.Fetch(FetchKind.Join);
            });
        }
    }
    
    

    Generated query:

    SELECT
        this_.Id as id1_1_1_,
        this_.Title as title2_1_1_,
        this_.DocumentCode as documentcode3_1_1_,
        documentlo2_.Id as id1_0_0_,
        documentlo2_.LockedBy as lockedby2_0_0_,
        documentlo2_.DocumentCode as documentcode3_0_0_ 
    FROM
        LegalDocument this_ 
    left outer join
        DocumentLock documentlo2_ 
            on this_.DocumentCode = documentlo2_.DocumentCode