2014-02-17 47 views
0

我已經閱讀了inversecascade映射屬性,並想知道:使用NHibernate進行級聯追蹤?

  • 無論是否有可能在我的情況下使用它們?如果是這樣,
  • 如何相應地參數化它們?

比方說,我有兩個班,CustomerInvoice,都需要可追溯性,TraceableEntity

我對我的所有實體都使用Repository模式,因此在這裏存儲庫注入了一個NHibernate.ISession這裏的構造函數。事實上,我有一個每個實體CustomerInvoice的存儲庫。

因爲我需要用戶登錄,所以我認爲這不是業務模型的問題,所以我將它設置在存儲庫的Save方法中,因爲只有ISession知道用戶用於連接到底層數據庫和存儲庫取決於它。這樣,商業模式就不會被無用的信息所污染。

另外,由於這種追溯性的需要,我失去了映射屬性的功能和易用性,否則,我不知道如何將它們用於我的特定需求。

讓我們來看看BaseRepository.Save()方法。

public abstract class BaseRepository<T> where T : TraceableEntity { 
    public BaseRepository(ISession session) { Session = session; } 

    public ISession Session { get; private set; } 

    public T Save(T instance) { 
     if (instance.IsNew && instance.IsDirty) 
      instance.Creator = readLoginFromConnectionString(); 
     else if (!instance.IsNew && (instance.IsDirty || instance.IsDeleted)) 
      instance.Updater = readLoginFromConnectionString(); 
     Session.SaveOrUpdate(instance); 
     return instance; 
    } 
} 

TraceableEntity

public abstract class TraceableEntity { 
    public TraceableEntity() { 
     Created = DateTime.Today; 
     IsNew = true; 
    } 

    public virtual DateTime Created { get; set; } 
    public virtual string Creator { get; set; } 
    public virtual DateTime? Deleted { get; set; } 
    public virtual int Id { get; protected set; } 
    public virtual bool IsDeleted { get; set; } 
    public virtual bool IsDirty { get; set; } 
    public virtual bool IsNew { get; set; } 
    public virtual DateTime? Updated { get; set; } 
    public virtual string Updater { get; set; } 
} 

Customer

public class Customer : TraceableEntity { 
    public Customer() : base() { Invoices = new List<Invoice>(); } 

    public virtual Name { get; set; } 
    public virtual Number { get; set; } 
    public virtual IList<Invoice> Invoices { get; private set; } 
} 

​​

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"  
        namespace="MyProject.Model" 
        assembly="MyProject"> 
    <class name="Customer" table="CUSTOMERS"> 
     <id name="Id" column="CUST_ID" type="Int32" unsaved-value="0"> 
      <generator class="sequence-identity"> 
       <param name="sequence">CUST_ID_SEQ</param> 
      </generator> 
     </id> 
     <property name="Name" column="CUST_NAME" type="String" length="128" not-null="true" /> 
     <property name="Number" column="CUST_NUMBER" type="String" length="12" not-null="true" /> 
     <property name="Creator" column="CUST_CREATOR_USR_ID" type="String" length="15" not-null="true" /> 
     <property name="Created" column="CUST_CREATED_DT" type="DateTime" not-null="true" /> 
     <property name="Updater" column="CUST_UPDATER_USR_ID" type="String" length="15" /> 
     <property name="Updated" column="CUST_UPDATED_DT" type="DateTime" not-null="false" /> 
     <property name="Deleted" column="CUST_DELETED_DT" type="DateTime" not-null="false" /> 
     <bag name="Invoices" table="INVOICES" fetch="join" lazy="true" inverse="true"> 
      <key column="CUST_ID" foreign-key="INV_CUST_ID_FK" /> 
      <one-to-many class="Invoice" /> 
     </bag> 
    </class> 
</hibernate-mapping> 

Invoice

public class Invoice : TraceableEntity { 
    public Invoice() : base() { } 

    public virtual Customer Customer { get; set; } 
    public virtual DateTime InvoiceDate { get; set; } 
    public virtual string Number { get; set; } 
    public virtual float Total { get; set; } 
} 

Invoice.hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"  
        namespace="MyProject.Model" 
        assembly="MyProject"> 
    <class name="Invoice" table="INVOICES"> 
     <id name="Id" column="INV_ID" type="Int32" unsaved-value="0"> 
      <generator class="sequence-identity"> 
       <param name="sequence">INV_ID_SEQ</param> 
      </generator> 
     </id> 
     <property name="InvoiceDate" column="INV_DT" type="DateTime" not-null="true" /> 
     <property name="Number" column="INV_NUMBER" type="String" length="12" not-null="true" /> 
     <property name="Total" column="INV_TOTAL" type="decimal" not-null="true" /> 
     <property name="Creator" column="INV_CREATOR_USR_ID" type="String" length="15" not-null="true" /> 
     <property name="Created" column="INV_CREATED_DT" type="DateTime" not-null="true" /> 
     <property name="Updater" column="INV_UPDATER_USR_ID" type="String" length="15" /> 
     <property name="Updated" column="INV_UPDATED_DT" type="DateTime" not-null="false" /> 
     <property name="Deleted" column="INV_DELETED_DT" type="DateTime" not-null="false" /> 
     <many-to-one name="Customer" class="Customer" column="CUST_ID" /> 
    </class> 
</hibernate-mapping> 

這是說,我想知道是否有這樣做的另一個可能更好的辦法,因爲實際上,我需要在CustomerRepository,只覆蓋BaseRepository.Save()方法,使到InvoiceRepository.Save()方法的調用如下:

public class CustomerRepository : BaseRepository<Customer> { 
    public CustomerRepository(ISession session) : base(session) { } 

    public override Customer Save(Customer instance) { 
     instance = base.Save(instance); 
     var invoices = new InvoiceRepository(session); 
     instance.Invoices.ToList().ForEach(inv => { 
      inv.Customer = instance; 
      invoices.Save(inv) 
     }); 
    } 
} 

public class InvoiceRepository : BaseRepository<Invoice> { 
    public InvoiceRepository(ISession session) : base(session) { }   
} 

另外,我不知道是否有可能爲發票「知道」誰是客戶,而無需分配上節省了客戶的財產,和樂t NHibernate魔術對我有用嗎?

回答

1

在事件前添加一個偵聽器,並執行您的定製邏輯,實現名稱空間內的IPreDeleteEventListener,IPreInsertEventListener,IPreUpdateEventListener中的任一個。

Ayende Rahien的一個簡單例子:NHibernate IPreUpdateEventListener & IPreInsertEventListener

public class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener { 
    public bool OnPreInsert(OnPreInsert @event) { 
     var audit = @event.Entity as IHaveAuditInformation; 
     if (audit == null) return false; 

     var time = DateTime.Now; 
     var name = WindowsIdentity.GetCurrent().Name; 

     Set(@event.Persister, @event.State, "CreatedAt", time); 
     Set(@event.Persister, @event.State, "CreatedBy", name); 

     audit.CreatedAt = time; 
     audit.CreatedBy = name; 

     return false; 
    } 

    public bool OnPreUpdate(OnPreUpdate @event) { 
     var audit = @event.Entity as IHaveAuditInformation; 
     if (audit == null) return false; 

     var time = DateTime.Now; 
     var name = WindowsIndentity.GetCurrent().Name; 

     Set(@event.Persister, @event.State, "UpdatedAt", time); 
     Set(@event.Persister, @event.State, "UpdatedBy", name); 

     audit.UpdatedAt= time; 
     audit.UpdatedBy = name; 

     return false; 
    } 
} 

同樣可以用IPreDeleteEventListener完成。

注意返回值false。這實際上應該是兩個OnPreEventResult枚舉值之一。

  • OnPreEventResult.Continue(假)
  • OnPreEventResult.Break(真)

按答案從@Radim Köhler這個問題:

因此,由於枚舉不不存在,而不是返回truefalse,我首選通過另一個方法調用返回布爾值,它實際上說明了它明確地做了什麼。

private bool AbortOperation() { return true; } 
private bool ContinueOperation() { return false; } 

而且通過return ContinueOperation()更換return false。這使得代碼更加清晰,並揭示了事件前方法的準確意圖和行爲。

接口實現後,只需將偵聽器添加到配置。

var listener = new AuditEventListener(); 
Configuration cfg = new Configuration(); 
c.SetListener(ListenerType.PreDelete, listener); 
c.SetListener(ListenerType.PreInsert, listener); 
c.SetListener(ListenerType.PreUpdate, listener); 

現在,都仍然做的是一個乾淨的呼叫ISession.SaveOrUpdate(),它們使用了cascade="all"映射屬性,你就大功告成了!