更新实体,使用Fluent NHibernate删除多对多关系中的关联

时间:2012-06-26 11:21:34

标签: nhibernate fluent-nhibernate mapping many-to-many

我有两个这样的实体:

public class Service
{
    public virtual int ServiceId { get; set; }
    public virtual char LayoutCode { get; set; }
    public virtual string ServiceNameEn { get; set; }
    public virtual string ServiceNameTh { get; set; }
    public virtual string ServiceDescEn { get; set; }
    public virtual string ServiceDescTh { get; set; }
    public virtual string ServicePhone { get; set; }
    public virtual byte[] ServiceImage { get; set; }
    public virtual bool Permanent { get; set; }
    public virtual bool Active { get; set; }
    public virtual bool IsAutoAssign { get; set; }
    public virtual DateTime CreatedDatetime { get; set; }
    public virtual DateTime LastUpdatedDatetime { get; set; }
    public virtual string RedirectUrl { get; set; }
    public virtual IList<Reply> Replies { get; set; }
    public virtual IList<Employee> Employees { get; set; }
    public virtual IList<ServicesOfUser> ServicesOfUser { get; set; }
    private IList<Category> _categories = new List<Category>();
    public virtual IEnumerable<Category> Categories
    {
        get
        {
            return _categories;
        }
    }
    public virtual void Add(Category category)
    {
        if (!_categories.Any(x => x.CategoryId == category.CategoryId))
        {
            _categories.Add(category);
        }
    }
    public virtual void Clear()
    {
        _categories.Clear();
    }
}

public class Category
{
    public virtual int CategoryId { get; set; }
    public virtual string CategoryNameEn { get; set; }
    public virtual string CategoryNameTh { get; set; }
    public virtual string CategoryDescEn { get; set; }
    public virtual string CategoryDescTh { get; set; }
    public virtual byte[] CategoryImage { get; set; }
    public virtual bool Active { get; set; }
    public virtual DateTime CreatedDatetime { get; set; }
    public virtual DateTime LastUpdatedDatetime { get; set; }
    public virtual IList<Service> Services { get; set; }
}

这两个实体的关系是多对多的映射:

public class ServiceMap : ClassMap<Service>
{
    public ServiceMap()
    {
        Table("[SERVICES]");

        Id(x => x.ServiceId)
            .GeneratedBy.Identity()
            .Column("service_id")
            .CustomType("int")
            .Access.Property()
            .CustomSqlType("int")
            .Not.Nullable();
        Map(x => x.LayoutCode)
            .Column("layout_code")
            .CustomType("char")
            .Access.Property()
            .CustomSqlType("char")
            .Length(1)
            .Not.Nullable();
        Map(x => x.ServiceNameEn)
            .Column("service_name_en")
            .Access.Property()
            .CustomType("string")
            .CustomSqlType("varchar")
            .Length(128)
            .Not.Nullable();
        Map(x => x.ServiceNameTh)
            .Column("service_name_th")
            .Access.Property()
            .CustomType("string")
            .CustomSqlType("nvarchar")
            .Length(128)
            .Not.Nullable();
        Map(x => x.ServiceDescEn)
            .Column("service_desc_en")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("varchar")
            .Length(4000)
            .Nullable();
        Map(x => x.ServiceDescTh)
            .Column("service_desc_th")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("nvarchar")
            .Length(4000)
            .Nullable();
        Map(x => x.ServicePhone)
            .Column("service_phone")
            .Access.Property()
            .CustomType("string")
            .CustomSqlType("nvarchar")
            .Length(50)
            .Nullable();
        Map(x => x.ServiceImage)
            .Column("service_image")
            .CustomType("BinaryBlob")
            .Access.Property()
            .Generated.Never()
            .CustomSqlType("image")
            .Length(131231)
            .Nullable();
        Map(x => x.Permanent)
            .Column("permanent")
            .CustomType("bool")
            .Access.Property()
            .CustomSqlType("bit")
            .Not.Nullable();
        Map(x => x.Active)
            .Column("active")
            .CustomType("bool")
            .Access.Property()
            .CustomSqlType("bit")
            .Not.Nullable();
        Map(x => x.IsAutoAssign)
            .Column("is_auto_assign")
            .CustomType("bool")
            .Access.Property()
            .CustomSqlType("bit")
            .Not.Nullable();
        Map(x => x.CreatedDatetime)
            .Column("created_datetime")
            .Access.Property()
            .CustomType("DateTime")
            .CustomSqlType("datetime")
            .Not.Nullable();
        Map(x => x.LastUpdatedDatetime)
            .Column("last_updated_datetime")
            .Access.Property()
            .CustomType("DateTime")
            .CustomSqlType("datetime")
            .Not.Nullable();
        Map(x => x.RedirectUrl)
            .Column("redirect_url")
            .Access.Property()
            .CustomType("string")
            .CustomSqlType("nvarchar")
            .Length(213123)
            .Nullable();
        HasMany<ServicesOfUser>(x => x.ServicesOfUser)
            .AsBag()
            .Cascade.None()
            .LazyLoad()
            .Inverse()
            .KeyColumns.Add("service_id", mapping => mapping.Name("service_id")
                .SqlType("int")
                .Nullable());
        HasMany<Reply>(x => x.Replies)
            .AsBag()
            .Cascade.None()
            .LazyLoad()
            .Inverse()
            .KeyColumns.Add("send_to_service_id", mapping => mapping.Name("send_to_service_id")
                .SqlType("int")
                .Nullable());
        HasManyToMany<Employee>(x => x.Employees)
            .AsBag()
            .Cascade.None()
            .LazyLoad()
            .Table("SERVICE_MONITORS")
            .Inverse()
            .ChildKeyColumns.Add("employee_id", mapping => mapping.Name("employee_id")
                .SqlType("uniqueidentifier")
                .Not.Nullable())
            .ParentKeyColumns.Add("service_id", mapping => mapping.Name("service_id")
                .SqlType("int")
                .Not.Nullable());
        HasManyToMany<Category>(x => x.Categories)
            .AsBag()
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.AllDeleteOrphan()
            .Table("SERVICES_IN_CATEGORIES")
            .ChildKeyColumns.Add("category_id", mapping => mapping.Name("category_id")
                .SqlType("int")
                .Not.Nullable())
            .ParentKeyColumns.Add("service_id", mapping => mapping.Name("service_id")
                .SqlType("int")
                .Not.Nullable());
    }
}

public class CategoryMap : ClassMap<Category>
{
    public CategoryMap()
    {
        Table("[SERVICE_CATEGORIES]");

        Id(x => x.CategoryId)
            .GeneratedBy.Identity()
            .Column("category_id")
            .CustomType("int")
            .Access.Property()
            .CustomSqlType("int")
            .Not.Nullable();
        Map(x => x.CategoryNameEn)
            .Column("category_name_en")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("varchar")
            .Length(128)
            .Not.Nullable();
        Map(x => x.CategoryNameTh)
            .Column("category_name_th")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("nvarchar")
            .Length(128)
            .Not.Nullable();
        Map(x => x.CategoryDescEn)
            .Column("category_desc_en")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("varchar")
            .Length(3423423)
            .Not.Nullable();
        Map(x => x.CategoryDescTh)
            .Column("category_desc_th")
            .CustomType("string")
            .Access.Property()
            .CustomSqlType("nvarchar")
            .Length(3243234)
            .Not.Nullable();
        Map(x => x.CategoryImage)
            .Column("category_image")
            .CustomType("BinaryBlob")
            .Access.Property()
            .Generated.Never()
            .CustomSqlType("image")
            .Length(131231)
            .Nullable();
        Map(x => x.Active)
            .Column("active")
            .CustomType("bool")
            .Access.Property()
            .CustomSqlType("bit")
            .Not.Nullable();
        Map(x => x.CreatedDatetime)
            .Column("created_datetime")
            .Access.Property()
            .CustomType("DateTime")
            .CustomSqlType("datetime")
            .Not.Nullable();
        Map(x => x.LastUpdatedDatetime)
            .Column("last_updated_datetime")
            .Access.Property()
            .CustomType("DateTime")
            .CustomSqlType("datetime")
            .Not.Nullable();
        HasManyToMany<Service>(x => x.Services)
            .AsBag()
            .Cascade.AllDeleteOrphan()
            .Inverse()
            .LazyLoad()
            .Table("SERVICES_IN_CATEGORIES")
            .ChildKeyColumns.Add("service_id", mapping => mapping.Name("service_id")
                .SqlType("int")
                .Not.Nullable())
            .ParentKeyColumns.Add("category_id", mapping => mapping.Name("category_id")
                .SqlType("int")
                .Not.Nullable());
    }
}

编码:

using (unitOfWork = new UnitOfWork(SessionFactory))
{
    serviceRepo = new ServiceRepo(unitOfWork.Session);
    try
    {
        Service service = new Service();
        if (id != null)
            service = serviceRepo.GetById((int)id);

        // set values for service here

        IList<Category> categories = new List<Category>();
        // add some categories to service

        serviceRepo.Save(service);
        unitOfWork.Commit();

        return service;
    }
    catch (Exception ex)
    {
        unitOfWork.Rollback();
        return null;
    }
}

服务已更新,但联系表中的关联记录不会被删除。

如果我在向服务添加类别之前添加以下代码:

service.Clear();

提交时,我收到以下错误消息:

not-null property references a null or transient value ilu.src.Entities.Category.CategoryDescEn

完整堆栈跟踪:

   at NHibernate.Engine.Nullability.CheckNullability(Object[] values, IEntityPersister persister, Boolean isUpdate)
   at NHibernate.Event.Default.DefaultDeleteEventListener.DeleteEntity(IEventSource session, Object entity, EntityEntry entityEntry, Boolean isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities)
   at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.Delete(String entityName, Object child, Boolean isCascadeDeleteEnabled, ISet transientEntities)
   at NHibernate.Engine.CascadingAction.DeleteCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.DefaultDeleteEventListener.CascadeBeforeDelete(IEventSource session, IEntityPersister persister, Object entity, EntityEntry entityEntry, ISet transientEntities)
   at NHibernate.Event.Default.DefaultDeleteEventListener.DeleteEntity(IEventSource session, Object entity, EntityEntry entityEntry, Boolean isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities)
   at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.Delete(String entityName, Object child, Boolean isCascadeDeleteEnabled, ISet transientEntities)
   at NHibernate.Engine.CascadingAction.DeleteCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.DefaultDeleteEventListener.CascadeBeforeDelete(IEventSource session, IEntityPersister persister, Object entity, EntityEntry entityEntry, ISet transientEntities)
   at NHibernate.Event.Default.DefaultDeleteEventListener.DeleteEntity(IEventSource session, Object entity, EntityEntry entityEntry, Boolean isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities)
   at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event, ISet transientEntities)
   at NHibernate.Impl.SessionImpl.Delete(String entityName, Object child, Boolean isCascadeDeleteEnabled, ISet transientEntities)
   at NHibernate.Engine.Cascade.DeleteOrphans(String entityName, IPersistentCollection pc)
   at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything)
   at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
   at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
   at NHibernate.Impl.SessionImpl.Flush()
   at NHibernate.Transaction.AdoTransaction.Commit()
   at ilu.src.Common.UnitOfWork.Commit() in D:\Arunsawad\iLertU\ilu\ilu\src\Common\UnitOfWork.cs:line 41
   at ilu.Controllers.AdminController.SaveService(Nullable`1 id, String name, String nameth, String desc, String descth, String phone, String catids, String url, Char layoutcode, Boolean permanent, Boolean active, Boolean autoassign) in D:\Arunsawad\iLertU\ilu\ilu\Controllers\AdminController.cs:line 107

这里有什么我想念的吗?

谢谢!

1 个答案:

答案 0 :(得分:2)

将级联声明为Cascade.AllDeleteOrphan()。另外,尝试将.Inverse()添加到多对多映射之一。

更多信息:

来自NHibernate文档的

Bidirectional associations
NHibernate Pitfalls: Many to Many and Inverse
NHibernate Cascades: the different between all, all-delete-orphans and save-update


修改

根据您获得的错误判断,您只需要在尝试保存之前初始化该类别的CategoryDescEn属性。您尝试保存时似乎是null

  

not-null属性引用 null 或瞬态值   ilu.src.Entities.Category。的 CategoryDe​​scEn