简体   繁体   中英

Fluent NHibernate One-to-many with 3 classes

I have 3 classes and their mappings and each of them has reference to the previous like:

A -> B -> C

A can have C but only if B got C(due to its impossible A -> C)

class A
{
    public virtual int Id {get;set;}
    public virtual string messageA {get;set;}
    public virtual IList<B> Bs {get;set;}
}


class B
{
    public virtual int Id {get;set;}
    public virtual string messageB {get;set;}
    public virtual A A {get; set;}
    public virtual IList<C> Cs {get;set;}
}

class C
{
    public virtual int Id {get;set;}
    public virtual string messageC {get;set;}
    public virtual B B{get;set;}
}

//Mappings

class AMap : ClassMap<A>
{
    public AMap()
    {
        Id(x => x.Id);
        Map(x => x.messageA);
        HasMany(x => x.Bs);
    }   
}

class BMap : ClassMap<B>
{
    public BMap()
    {
        Id(x => x.Id);
        Map(x => x.messageB);
        References(x => x.A).Cascade.All();
        HasMany(x => x.Cs);
    }   
}

class CMap : ClassMap<C>
{
    public CMap()
    {
        Id(x => x.Id);
        Map(x => x.messageC);
        References(x => x.B).Cascade.All();
    }   
}

I got such an error while trying to commit:

object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave. Type: NHibernateTest.C, Entity: NHibernateTest.C.

I was looking for the solution, someone said I have to modify foreign key update rule into cascade, I did it and still not working.

I just want to mention that it was working with A -> B relation, when I added C the error occured.

To avoid such problems in future you should make sure that:

  • All your classes are public
  • Don't have circular references as in your example B->A, C->B

The following test case works as you expect (A->B->C).

Model definitions:

public class A
{
    public virtual int Id { get; set; }
    public virtual string messageA { get; set; }
    public virtual IList<B> Bs { get; set; }
}


public class B
{
    public virtual int Id { get; set; }
    public virtual string messageB { get; set; }
    public virtual IList<C> Cs { get; set; }
}

public class C
{
    public virtual int Id { get; set; }
    public virtual string messageC { get; set; }
}

Map definitions:

public class AMap : ClassMap<A>
{
    public AMap()
    {
        Table("A");
        Id(x => x.Id).Column("Id");
        Map(x => x.messageA);
        HasMany(x => x.Bs).Cascade.All();
    }
}

public class BMap : ClassMap<B>
{
    public BMap()
    {
        Table("B");
        Id(x => x.Id).Column("Id"); ;
        Map(x => x.messageB);
        HasMany(x => x.Cs).Cascade.All();
    }
}

public class CMap : ClassMap<C>
{
    public CMap()
    {
        Table("C");
        Id(x => x.Id).Column("Id"); ;
        Map(x => x.messageC);
    }
}

NUnit test method:

[Test]
public void ShouldCorrectlyMapA()
{
    var objectA = new A
    {
        Id = 1,
        messageA = "Message A",
        Bs = new List<B> { 
            new B
            {
                messageB = "Message B",
                Cs = new List<C>
                {
                    new C
                    {
                        messageC = "Message C"
                    }
                }
            }
        }
    };

    new PersistenceSpecification<A>(_session)
        .VerifyTheMappings(objectA);

}

Using in-memory SQLite unit test, the test runners output of

Schema creation:

 create table A ( Id integer primary key autoincrement, messageA TEXT ) create table B ( Id integer primary key autoincrement, messageB TEXT, A_id INT, constraint FKCDCAB7DE9EFDCD1D foreign key (A_id) references A ) create table C ( Id integer primary key autoincrement, messageC TEXT, B_id INT, constraint FKCDCAB7DD3844B1E1 foreign key (B_id) references B ) 

DML operations on the just created schema:

 NHibernate: INSERT INTO A (messageA) VALUES (@p0); select last_insert_rowid();@p0 = 'Message A' [Type: String (0)] NHibernate: INSERT INTO B (messageB) VALUES (@p0); select last_insert_rowid();@p0 = 'Message B' [Type: String (0)] NHibernate: INSERT INTO C (messageC) VALUES (@p0); select last_insert_rowid();@p0 = 'Message C' [Type: String (0)] NHibernate: UPDATE B SET A_id = @p0 WHERE Id = @p1;@p0 = 1 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)] NHibernate: UPDATE C SET B_id = @p0 WHERE Id = @p1;@p0 = 1 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)] 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM