Nhibernate 集合。子实体未保存

Nhibernate collections .Child entities not saved

使用

.NET Core SDK (reflecting any global.json): Version: 2.2.300 Commit: 73efd5bd87

Nhibernate 5.2.5

有以下实体

public class Customer : Entity {
    public Customer() {
        Initialize();
    }
    public virtual string Name { get; set; }
    public virtual string LegalName { get; set; }
    public virtual string VATCode { get; set; }
    public virtual ICollection<Site> Sites { get; set; }
    public virtual DateTime Created { get; set; } = DateTime.UtcNow;

    private void Initialize() {
        Sites = new List<Site>();
    }
}

public class Site : Entity {
    public virtual string Address { get; set; }
    public virtual string City { get; set; }
    public virtual string Country { get; set; }
    public virtual Customer Customer { get; set; }
}

使用以下映射器

internal class CustomerConfiguration : ClassMapping<Customer> {
    public CustomerConfiguration() {
        Table( TableNames.CustomersTable );

        Id( x => x.EntityID, im => {
            im.Column( "CustomerID" );
            im.Generator( Generators.Identity );
        } );
        [... code omitted for brevity ...]
        Set<Site>( property => property.Sites,
            collection => {
                collection.Fetch( CollectionFetchMode.Join );
                collection.Lazy( CollectionLazy.Lazy );
                collection.Cascade( Cascade.Persist.Include( Cascade.DeleteOrphans ) );
                collection.Inverse( true );
                collection.Key( keyMapping => {
                    keyMapping.Column( "CustomerID" );
                } );
            },
            mapping => {
                mapping.OneToMany( relationalMapping => {
                    relationalMapping.Class( typeof( Site ) );
                } );
            } );
    }
}

internal class SiteConfiguration : ClassMapping<Site> {
    public SiteConfiguration() {
        Table( TableNames.SitesTable );

        Id( x => x.EntityID, im => {
            im.Column( "SiteID" );
            im.Generator( Generators.Identity );
        } );
        [... code omitted for brevity ...]
        ManyToOne( x => x.Customer, mm => {
            mm.Column( "CustomerID" );
            mm.NotNullable( true );
        } );
    }
}

我猜这个映射不正确,因为如果我做类似

using ( var session = sessionFactory.OpenSession() ) {
    var customer = new Customer() {
        Name = $"Customer 1",
        LegalName = $"Customer 1 LLC",
        VATCode = "xxxxxxxxx",
        Created = DateTime.UtcNow
    };
    customer.Sites.Add( new Site() {
        Address = $"Address Customer 1",
        City = $"City Customer 1",
        Country = $"Country Customer 1",
        Customer = customer
    } );
    session.Save( customer );
}

我得到以下异常

Unhandled Exception: NHibernate.TransientObjectException: 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: Nhibernate.ConsoleApp.Entities.Site, Entity: [Site 0]

有什么建议吗?

编辑

实际上问题出在不同的区域。这是因为存在两个已注册的侦听器(IDeleteEventListenerIUpdateEventListener)。每当我添加那些更新和删除时都不起作用。但是,由于 Roman Artiukhin 评论

,我能够发现问题

例外是 telling 根实体 Customer 持有对其他 (Site) 实体的引用,该实体是临时实体。这就是为什么不能持久化根实体的原因。

Scenario 1: No cascade options were configured.
In this case the child or referenced objects must be saved first.

using ( var session = sessionFactory.OpenSession() ) {
    var customer = new Customer() {
        Name = $"Customer 1",
        LegalName = $"Customer 1 LLC",
        VATCode = "xxxxxxxxx",
        Created = DateTime.UtcNow
    };
    Site site = new Site() {
        Address = $"Address Customer 1",
        City = $"City Customer 1",
        Country = $"Country Customer 1",
        Customer = customer
    }
    customer.Sites.Add(site);
    session.Save( customer );//<--Save the master
    session.Save( site );//<--Save the site
    ...
    ...
    ...
    session.Flush();//<--You are calling this somewhere in your code
}

这应该有效。

Scenario 2: Cascade options were not configured for all INPUT; UPDATE or DELETE operations.
In this case the configuration must be changed or child or referenced objects must be saved first.

您已经:

  • 设置collection.Inverse( true );
  • 通过扩展方法 Include 添加了两个 DeleteOrphans | Persist values for Cascade

所以这不是问题。

Scenario3: Related transient objects where instantiated and associated to the persitent object but no save operation is to be performed on those objects.
In this case the trainsient objects must be detached from the current session through the command: ISession.Evict(obj)

而不是 Evict,您可以调用 Save 来显式附加那些对象,如上所述。

要获得更多见解,请查看 documentation,其中解释了各种策略。

我猜 collection 不是你的问题。看起来你在其他地方暴露了 Site 实体(例如 many-to-one customer.LastSite 属性 或类似的东西)。级联逻辑在 collection 保存之前偶然发现了这个尚未保存的 Site 属性。所以你必须确保其他 Site 属性也有 Cascade.Persist 映射