{ public LensMapping() { Id((Lens x) => x.ID).GeneratedBy.GuidComb(); References((Lens x) => x.Monocle).Cascade.All().WithForeignKey("LensMonocle"); } } [TestFixture()] public class GlassesMappingTests { [Test()] public void CanCascadeSaveFromGlassesToLense() { Guid ID; Monocle Monocle; Lens Lens; using (SQLiteDatabaseScope<MonocleMapping> Scope = new SQLiteDatabaseScope<MonocleMapping>()) { using (Session == Scope.OpenSession()) { using (Tran == Session.BeginTransaction()) { Monocle = new Monocle(); Lens = new Lens { Monocle = Monocle }; Monocle.Lens = Lens; //These are just idiot checks... Monocle.Should.Not.Be.Null(); Lens.Should.Not.Be.Null(); Monocle.Lens.Should.Be.SameInstanceAs(Lens); Lens.Monocle.Should.Be.SameInstanceAs(Monocle); ID = Session.Save(Monocle); Tran.Commit(); } } using (Session == Scope.OpenSession) { using (Tran == Session.BeginTransaction) { Monocle = Session.Get<Monocle>(ID); Monocle.Should.Not.Be.Null(); Monocle.ID.Should.Be.EqualTo(ID); Monocle.Lens.Should.Not.Be.Null(); Monocle.Lens.Should.Not.Be.SameInstanceAs(Lens); Monocle.Lens.Should.Be.EqualTo(Lens); Monocle.Lens.Monocle.Should.Be.SameInstanceAs(Monocle); Tran.Commit(); } } } } } Here’s the resulting SQL. drop table if exists "Monocle" drop table if exists "Lens" create table "Monocle" (ID UNIQUEIDENTIFIER not null, Lens_id UNIQUEIDENTIFIER, primary key (ID)) create table "Lens" (ID UNIQUEIDENTIFIER not null, Monocle_id UNIQUEIDENTIFIER, primary key (ID)) INSERT INTO "Lens" (Monocle_id, ID) VALUES (@p0, @p1); @p0 = '', @p1 = 'f6f37089-a66b-4007-b732-9c6b008d1448' INSERT INTO "Monocle" (Lens_id, ID) VALUES (@p0, @p1); @p0 = 'f6f37089-a66b-4007-b732-9c6b008d1448', @p1 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b' UPDATE "Lens" SET Monocle_id = @p0 WHERE ID = @p1; @p0 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b', @p1 = 'f6f37089-a66b-4007-b732-9c6b008d1448' SELECT monocle0_.ID as ID0_0_, monocle0_.Lens_id as Lens2_0_0_ FROM "Monocle" monocle0_ WHERE monocle0_.ID=@p0; @p0 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b' SELECT lens0_.ID as ID1_0_, lens0_.Monocle_id as Monocle2_1_0_ FROM "Lens" lens0_ WHERE lens0_.ID=@p0; @p0 = 'f6f37089-a66b-4007-b732-9c6b008d1448' Of course, I think that making this a double many-to-one relationship is probably wrong, but it gets me close to a working solution. This works, except for the unnecessary UPDATE “Lens” statement. In my mind, since I’m using the GuidComb generator, NHibernate should specify the Monicle_id in the INSERT “Lens” statement. Instead, it’s inserting NULL, then updating it later. This prevents me from putting a NOT NULL constraint on Lens.Monicle_id. It’s been suggested that I have superfluous update, and I agree. However, the prescribed inverse=”true” won’t work. I don’t have a one-to-many relationship to put it on. I’ve tried using HasOne instead of References to create a bidirectional one-to-one relationship. It didn’t work at all. My database schema didn’t even have FK fields. Am I missing something or is this double many-to-one as good as it’s going to get? " /> Double many-to-one?

Double many-to-one?

I’m working on a mapping for a project at work. The entity names have been changed to protect the innocent.

ClassDiagram1

In the application I’m working on, the model is more like glasses with a left and right lens, but I’ve simplified it.

Public Class Monocle
    Inherits Entity

    Private m_LeftLens As Lens

    Public Overridable Property Lens() As Lens
        Get
            Return m_LeftLens
        End Get
        Set(ByVal value As Lens)
            m_LeftLens = value
        End Set
    End Property

End Class

Public Class Lens
    Inherits Entity

    Private m_Glasses As Monocle

    Public Overridable Property Monocle() As Monocle
        Get
            Return m_Glasses
        End Get
        Set(ByVal value As Monocle)
            m_Glasses = value
        End Set
    End Property

End Class

Public Class MonocleMapping
    Inherits ClassMap(Of Monocle)
    Public Sub New()
        Id(Function(x As Monocle) x.ID).GeneratedBy.GuidComb()
        References(Function(x As Monocle) x.Lens) _
            .Cascade.All() _
            .WithForeignKey("MonocleLens")

    End Sub
End Class

Public Class LensMapping
    Inherits ClassMap(Of Lens)
    Public Sub New()
        Id(Function(x As Lens) x.ID).GeneratedBy.GuidComb()
        References(Function(x As Lens) x.Monocle) _
            .Cascade.All() _
            .WithForeignKey("LensMonocle")
    End Sub
End Class

<TestFixture()> _
Public Class GlassesMappingTests

    <Test()> _
    Public Sub CanCascadeSaveFromMonocleToLens()
        Dim ID As Guid
        Dim Monocle As Monocle
        Dim Lens As Lens
        Using Scope As New SQLiteDatabaseScope(Of MonocleMapping)
            Using Session = Scope.OpenSession
                Using Tran = Session.BeginTransaction
                    Monocle = New Monocle

                    Lens = New Lens With {.Monocle = Monocle}
                    Monocle.Lens = Lens

                    'These are just idiot checks...
                    Monocle.Should.Not.Be.Null()
                    Lens.Should.Not.Be.Null()
                    Monocle.Lens.Should.Be.SameInstanceAs(Lens)
                    Lens.Monocle.Should.Be.SameInstanceAs(Monocle)

                    ID = Session.Save(Monocle)
                    Tran.Commit()
                End Using
            End Using

            Using Session = Scope.OpenSession
                Using Tran = Session.BeginTransaction

                    Monocle = Session.Get(Of Monocle)(ID)

                    Monocle.Should.Not.Be.Null()
                    Monocle.ID.Should.Be.EqualTo(ID)

                    Monocle.Lens.Should.Not.Be.Null()
                    Monocle.Lens.Should.Not.Be.SameInstanceAs(Lens)
                    Monocle.Lens.Should.Be.EqualTo(Lens)
                    Monocle.Lens.Monocle.Should.Be.SameInstanceAs(Monocle)

                    Tran.Commit()
                End Using

            End Using
        End Using

    End Sub

End Class
using NUnit.Framework;
using FluentNHibernate.Mapping;

public class Monocle : Entity
{
    
    private Lens m_Lens;
    
    public virtual Lens Lens {
        get { return m_Lens; }
        set { m_Lens = value; }
    }
    
}

public class Lens : Entity
{
    
    private Monocle m_Glasses;
    
    public virtual Monocle Monocle {
        get { return m_Glasses; }
        set { m_Glasses = value; }
    }
    
}

public class MonocleMapping : ClassMap<Monocle>
{
    public MonocleMapping()
    {
        Id((Monocle x) => x.ID).GeneratedBy.GuidComb();
            
        References((Monocle x) => x.Lens).Cascade.All().WithForeignKey("MonocleLens");
    }
}

public class LensMapping : ClassMap<Lens>
{
    public LensMapping()
    {
        Id((Lens x) => x.ID).GeneratedBy.GuidComb();
        References((Lens x) => x.Monocle).Cascade.All().WithForeignKey("LensMonocle");
    }
}

[TestFixture()]
public class GlassesMappingTests
{
    
    [Test()]
    public void CanCascadeSaveFromGlassesToLense()
    {
        Guid ID;
        Monocle Monocle;
        Lens Lens;
        using (SQLiteDatabaseScope<MonocleMapping> Scope = new SQLiteDatabaseScope<MonocleMapping>()) {
            using (Session == Scope.OpenSession()) {
                using (Tran == Session.BeginTransaction()) {
                    Monocle = new Monocle();
                    
                    Lens = new Lens { Monocle = Monocle };
                    Monocle.Lens = Lens;
                    
                    //These are just idiot checks...
                    Monocle.Should.Not.Be.Null();
                    Lens.Should.Not.Be.Null();
                    Monocle.Lens.Should.Be.SameInstanceAs(Lens);
                    Lens.Monocle.Should.Be.SameInstanceAs(Monocle);
                    
                    ID = Session.Save(Monocle);
                    Tran.Commit();
                }
            }
            
            using (Session == Scope.OpenSession) {
                using (Tran == Session.BeginTransaction) {
                    
                    Monocle = Session.Get<Monocle>(ID);
                    
                    Monocle.Should.Not.Be.Null();
                    Monocle.ID.Should.Be.EqualTo(ID);
                    
                    Monocle.Lens.Should.Not.Be.Null();
                    Monocle.Lens.Should.Not.Be.SameInstanceAs(Lens);
                    Monocle.Lens.Should.Be.EqualTo(Lens);
                    Monocle.Lens.Monocle.Should.Be.SameInstanceAs(Monocle);
                    
                    Tran.Commit();
                    
                }
            }
            
        }
    }
    
}

Here’s the resulting SQL.

drop table if exists "Monocle"
drop table if exists "Lens"
create table "Monocle" (ID UNIQUEIDENTIFIER not null, Lens_id UNIQUEIDENTIFIER, primary key (ID))
create table "Lens" (ID UNIQUEIDENTIFIER not null, Monocle_id UNIQUEIDENTIFIER, primary key (ID))
INSERT INTO "Lens" (Monocle_id, ID) VALUES (@p0, @p1); @p0 = '', @p1 = 'f6f37089-a66b-4007-b732-9c6b008d1448'
INSERT INTO "Monocle" (Lens_id, ID) VALUES (@p0, @p1); @p0 = 'f6f37089-a66b-4007-b732-9c6b008d1448', @p1 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b'
UPDATE "Lens" SET Monocle_id = @p0 WHERE ID = @p1; @p0 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b', @p1 = 'f6f37089-a66b-4007-b732-9c6b008d1448'
SELECT monocle0_.ID as ID0_0_, monocle0_.Lens_id as Lens2_0_0_ FROM "Monocle" monocle0_ WHERE monocle0_.ID=@p0; @p0 = 'bb2879f6-d27a-4eb0-8bf0-9c6b008d143b'
SELECT lens0_.ID as ID1_0_, lens0_.Monocle_id as Monocle2_1_0_ FROM "Lens" lens0_ WHERE lens0_.ID=@p0; @p0 = 'f6f37089-a66b-4007-b732-9c6b008d1448'

Of course, I think that making this a double many-to-one relationship is probably wrong, but it gets me close to a working solution.

This works, except for the unnecessary UPDATE “Lens” statement. In my mind, since I’m using the GuidComb generator, NHibernate should specify the Monicle_id in the INSERT “Lens” statement. Instead, it’s inserting NULL, then updating it later. This prevents me from putting a NOT NULL constraint on Lens.Monicle_id. It’s been suggested that I have superfluous update, and I agree. However, the prescribed inverse=”true” won’t work. I don’t have a one-to-many relationship to put it on.

I’ve tried using HasOne instead of References to create a bidirectional one-to-one relationship. It didn’t work at all. My database schema didn’t even have FK fields.

Am I missing something or is this double many-to-one as good as it’s going to get?

blog comments powered by Disqus