2

下午好。流利Nhibernate:映射與接口要求的單個實體

在開始我的解釋之前,我看了一下其他類似的問題,但細微差別(主要是設計目的)意味着這些答案中提供的解決方案不適用於我。

我正在嘗試創建一個「基礎數據訪問庫」以供將來的項目使用,這樣我就不必花時間在多個項目中編寫相同的邏輯。其目的是簡單地導入這個庫,並立即擁有從IBaseDao繼承的能力,並立即擁有標準的「CRUD」能力。我無法確切知道我的業務對象會是什麼(Customer,FileStructure,Cat,Dog,Order等)我已經在我的基礎庫中定義了一個接口,其中所有的業務對象必須在它們可以與這個庫一起使用之前執行。在「真正的」應用程序,我打算有類似下面的東西所以現在

public interface IEntity 
{ 
    /// Indicates weather this entity is used as test 
    /// data or if it is a real-world entity. 
    bool IsMockObject { get; set; } 

    /// This is not intended for use in a 'real' application 
    /// and is only used in testing. 
    string ReadableName { get; set; } 

    /// The unique identifier for this object. 
    Guid Id { get; set; } 
} 

public class Customer : IEntity 
{ 
    public Customer() 
    { 

    } 

    string name = ""; 
    public virtual String Name 
    { 
     get 
     { 
      return name; 
     } 
     set 
     { 
      name = value; 
     } 
    } 

    private DateTime birthday = DateTime.Now; 
    public virtual DateTime Birthday 
    { 
     get; 
     set; 
    } 

    private List<Order> orders = new List<Order>(); 
    public virtual List<Order> Orders 
    { 
     get 
     { 
      return orders; 
     } 
     set 
     { 
      orders = value; 
     } 
    } 

    #region IEntity Members 

    private bool isMockObject; 
    public virtual bool IsMockObject 
    { 
     get 
     { 
      return true; 
     } 
     set 
     { 
      isMockObject = value; 
     } 
    } 

    private string readableName; 
    public virtual string ReadableName 
    { 
     get 
     { 
      return readableName; 
     } 
     set 
     { 
      readableName = value; 
     } 
    } 

    private Guid id; 
    public virtual Guid Id 
    { 
     get 
     { 
      return id; 
     } 
     set 
     { 
      id = value; 
     } 
    } 
    #endregion 
} 

現在這裏是哪裏的問題出現這樣的接口定義如下。在我的單元測試期間,當我嘗試並保存「客戶」時,出現參數計數不匹配異常。我懷疑這個問題的原因是我的數據庫中的Customer表不包含所有IEntity屬性的列。但它確實包含一個Id列。到目前爲止,我CustomerMap看起來是這樣的:

public class CustomerMap : ClassMap<Customer> 
{ 
    public CustomerMap() 
    { 
     Table("Customer"); 

     // From IEntity 
     Id(x => x.Id); 

     Map(x => x.Name); 
     Map(x => x.Birthday); 

     // From IEntity 
     Map(x => x.IsMockObject); 

     // From IEntity 
     Map(x => x.ReadableName); 
     HasMany(x => x.Orders); 
    } 
} 

我真正想要的NHibernate辦時節省保存客戶表中,所有獨特的Customer還有IEntity.Id財產的屬性,然後保存在一個單獨的表稱爲(如果IsMockEntity爲true)MockEntitiy模擬對象的Id及其可讀的名稱。

我發現ClassMap很難理解,因爲我對NHibernate的基本持久性做了很少的工作。任何幫助或鏈接相關材料大大讚賞。

謝謝你的時間。

回答

0

好的我修改了代碼以反映您的建議更改,並且還嘗試了一些「挖掘」來嘗試解決此問題。我現在有以下數據庫結構:

表客戶
標識唯一標識符
名稱 VARCHAR(50)
生日日期時間

表順序
標識唯一標識符
CustomerId唯一標識符
價格浮子

TABLE MockEntities
標識唯一標識符
ENTITYID唯一標識符
ReadableName爲nvarchar(MAX)

,因此,下面的類映射的。從我從其他來源收集的信息來看,我試圖在這裏實現的是某種聯合。至於天氣,它是一個外部或內部...我不知道。

public class CustomerMap : ClassMap<Customer> 
{ 
    public CustomerMap() 
    { 
     Table("Customer"); 

     Id(x => x.Id); 
     Map(x => x.Name); 
     Map(x => x.Birthday); 
     Join("MockEntities", me => 
      { 
       Id<Guid>("Id"); 
       me.Map(xx => xx.ReadableName); 
       me.Map(xx => xx.Id, "EntityId"); 
      }); 
     HasMany(x => x.Orders).Cascade.All().Not.LazyLoad(); 

    } 
} 

訂單的ClassMap看起來幾乎完全相同,但爲了完整起見,我將它包含在這篇文章中。

public class OrderMap : ClassMap<Order> 
{ 
    public OrderMap() 
    { 
     Table("Order"); 
     Id(x => x.Id); 
     Map(x => x.Price); 
     Map(x => x.CustomerId, "CustomerId"); 
     Join("MockEntities", me => 
      { 
       Id<Guid>("Id"); 
       me.Map(xx => xx.ReadableName); 
       me.Map(xx => xx.Id, "EntityId"); 
      }); 
    } 
} 

不過,現在,當我運行我的測試中,我給出以下錯誤:

`System.Data.SqlServerCeException:列名無效。 [節點名(如果有的話)=,列名=客戶ID]

萬一它是對任何人,從NUnit的GUI堆棧跟蹤有用:

at System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan() 
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options) 
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery() 
at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd) 
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session) 
at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session) 
at NHibernate.Action.EntityInsertAction.Execute() 
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) 
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) 
at NHibernate.Engine.ActionQueue.ExecuteActions() 
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) 
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) 
at NHibernate.Impl.SessionImpl.Flush() 
at NHibernate.Transaction.AdoTransaction.Commit() 
at MySolution.DataAccess.ConnectionProviders.NHibernateConnection.Create[TEntity](TEntity objectToCreate) in MySolution.DataAccess\ConnectionProviders\NHibernateConnection.cs:line 154 
at MySolution.Testing.DataAccess.Connection.Testing_Connections.Function_NHibernateCreateNestedEntity() 

最後,SQL該NHibernate的正在使用:

NHibernate: INSERT INTO Customer (Name, Birthday, Id) VALUES (@p0, @p1, @p2);@p0 = 'David', @p1 = 03/09/2010 07:28:22, @p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56 
NHibernate: INSERT INTO MockEntities (ReadableName, EntityId, Customer_id) VALUES (@p0, @p1, @p2);@p0 = 'Function_NHibernateCreateNestedEntity1', @p1 = 00000000-0000-0000-0000-000000000000, @p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56 
07:28:28,851 ERROR [TestRunnerThread] AbstractBatcher [(null)]- Could not execute command: INSERT INTO MockEntities (ReadableName, EntityId, Customer_id) VALUES (@p0, @p1, @p2) 
The column name is not valid. [ Node name (if any) = ,Column name = Customer_id ] 

當我們遇到這個問題時,我有點害怕我的深度。在我之前試圖在映射工作中加入這個連接時,系統保存了一個Customer對象和它包含的Order集合。然而,現在看起來好像它正在等待我沒有定義的列,即「客戶ID」。

任何輸入非常感謝,謝謝。

0

我會做的第一件事是

  1. 列表設置爲一個IList; NHibernate需要能夠將自己的枚舉類型注入到對象中,而不是C#的已定義列表。
  2. 確保NHibernate能夠讀取和寫入所有屬性,即不要在映射中設置默認值。如果你需要,你可以在構造函數(或其他地方)中完成。

_

public class Customer 
{ 
    public Customer() 
    { 
     IsMockObject = true; 
     Orders = new List<Order>(); 
    } 

    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual DateTime Birthday { get; set; } 

    // Protected: the user can't modify this! 
    public virtual bool IsMockObject { get; protected set; } 

    public virtual string ReadableName { get; set; } 

    // Needs to be an IList<T>, NOT a regular List<T>. 
    public virtual IList<Order> Orders { get; set; } 
} 

_

我不認爲有一個與你的類映射任何問題,但我會先改變這些東西,然後看看這是怎麼回事。如果它仍然不起作用,請通知我們拋出的新異常。

+0

我相當肯定有一個與類映射,因爲當我看看什麼NHibernate的實際上是試圖做的,它試圖在「IsMockObject」和「ReadableName」值添加到客戶表中的問題而不是從Customer對象中取出'Id',並將其放在'MockEntities'表中的'ReadableName'旁邊。我可以看到它爲什麼試圖這樣做,因爲CustomerMap的ClassMap沒有提到MockEntites表。 – user407356 2010-08-30 11:58:36

0
public class CustomerMap : ClassMap<Customer> 
{ 
    public CustomerMap() 
    { 
     Table("Customer"); 

     Id(x => x.Id); 

     ... 

此時,您正在定義正在炸燬的Customer.Id字段,因此您需要檢查幾件事情。

首先,您需要確保它存在。(在你的情況我想大概不會)

其次,看你的SQL,我們有一個問題...

@p2 = 68fb7dd7-5379-430f-aa59-9de6007b2d56 

看起來這是一個字符串,但是它不裹單引號。

@p2 = '68fb7dd7-5379-430f-aa59-9de6007b2d56' 

這可能是因爲NHibernate的是,我們正在處理一個整數,我想你可以通過調整CustomerMap這樣解決錯誤的印象。如果我不指出,數字ID幾乎肯定是比什麼都好,因爲它們導致更快的查詢,更好的索引和是一個公認的慣例

public class CustomerMap : ClassMap<Customer> 
{ 
    public CustomerMap() 
    { 
     Table("Customer"); 

     Id<Guid>(x => x.Id); 

     ... 

這個答案將是不完整。但是,這應該讓你的事情再次發生。

+0

從Id(x => x.Id)到'Id (「Id」)的更改對提到的錯誤沒有任何影響。我明白你的意思是插入沒有被封裝爲apostrophies,但表中的Id列被定義爲'uniqueidentifier',因此,如果Nhibernate試圖將該值作爲字符串添加,則類型將不匹配,並且錯誤會發生。這裏的主要問題是NHibernate爲什麼覺得應該在MockEntities表上有'Customer_id'列。 – user407356 2010-09-03 10:20:03

+0

是的,這聽起來像堅持數字ID的另一個重要原因! – Fenton 2010-09-06 07:12:25

0

創建一個抽象實現的IEntityBaseEntityBase)則有Customer繼承EntityBase(並因此實現IEntityBase)。然後創建EntityBase的地圖和Customer的子類地圖。然後,您應該擁有一個EntityBase表和一個Customer表,其中有一個與EntityBase表相關的EntityBase_Id列。你會發現EntityBase將需要另一個屬性(因此可以通過插入一個新行並設置另一個屬性來在數據庫中生成Id),所以我通常會添加Created時間戳或類似信息。

public interface IEntityBase 
{ 
    int Id { get; set; } 
    DateTime? Created { get; set; } 
} 

public abstract class EntityBase : IEntityBase 
{ 
    public int Id { get; set; } 
    public DateTime? Created { get; set ;} 
} 

public class Customer : EntityBase 
{ 
    public string Name { get; set; } 
} 

public class EntityBaseMap : ClassMap<EntityBase> 
{ 
    Id(x => x.Id); 
    Map(x => x.Created); 
} 

public class CustomerMap : SubclassMap<Customer> 
{ 
    Map(x => x.Name); 
}