2010-11-03 36 views
1

我是使用NHibernate/Fluent NHibernate的新手,想弄清楚如何將它與現有的數據庫結構一起使用。如果可能的話,我希望能夠在不改變數據庫結構的情況下工作。流利的NHibernate:一對多映射到抽象類中存在外鍵的子類

我試圖鬆散地圖數據庫結構類似於:

Forms 
---- 
FormId 
CompletedBy 

Records 
------- 
RecordId 
RecordTypeId 
FormId 

EducationRecords 
---------------- 
RecordId 
SchoolName 
DateAttendedFrom 
DateAttendedTo 

我的實體:

public class Form 
{ 
    public virtual int Id { get; private set; } 
    public virtual string CompletedBy { get; set; } 

    public virtual IList<Entities.EducationRecord> EducationRecords { get; set; } 

    public Form() 
    { 
     this.EducationRecords = new List<EducationRecord>(); 
    } 
} 

public abstract class Record 
{ 
    public virtual int Id { get; set; } 
    public virtual int RecordTypeId { get; set; } 
    public virtual Form Parent { get; set; } 
} 

public class EducationRecord : Record 
{ 
    public virtual string SchoolName { get; set; } 
    public virtual DateTime DateAttendedFrom { get; set; } 
    public virtual DateTime DateAttendedTo { get; set; } 
} 

我的映射:

public class FormMap : ClassMap<Entities.Form> 
{ 
    public FormMap() 
    { 
     Table("Forms"); 
     Id(x => x.Id, "FormId"); 
     Map(x => x.CompletedBy); 

     HasMany(x => x.EducationRecords); 
    } 
} 

public class RecordMap : ClassMap<Entities.Record> 
{ 
    public RecordMap() 
    { 

     Table("Records"); 
     Id(x => x.Id, "RecordId"); 
     Map(x => x.RecordTypeId); 
     References(x => x.Parent, "FormId"); 
    } 
} 

public class EducationRecordMap : SubclassMap<Entities.EducationRecord> 
{ 
    public EducationRecordMap() 
    { 
     Table("EducationRecords"); 
     KeyColumn("RecordId"); 
     Map(x => x.SchoolName); 
     Map(x => x.DateAttendedFrom); 
     Map(x => x.DateAttendedTo); 
    } 
} 

隨着方式,目前正在安裝,嘗試訪問Form的EducationRecords屬性時出現以下異常:

[SqlException (0x80131904): Invalid column name 'FormId'. 
Invalid column name 'FormId'.] 

它看起來像底層SQL查詢試圖查詢EducationRecords表上的'FormId'列,但該列在那裏不存在。我花了很多時間嘗試使用我的地圖類的不同配置變體,並沒有運氣。

所以我的問題是:如何告訴Fluent NHibernate在檢索教育記錄時使用記錄表中的'FormId'列,或者這甚至可能?


更新:
我的問題似乎基本是一樣的這裏所說的一個(其中,不幸的是,這個問題一直沒有解決):
Fluent NHibernate inheritance mapping problem


更新2:

如上所示,我對FormMap做了如下修改:

HasMany(x => x.EducationRecords).Inverse(); 

但是同樣的問題仍然存在。

這裏是源錯誤:

Line 14: 
Line 15:  <div> 
Line 16:   <% if (Model.EducationRecords.Any()) { %> 
Line 17: 
Line 18:    <table> 

該模型類型的表格。

這裏是堆棧跟蹤:

[SqlException (0x80131904): Invalid column name 'FormId'. 
Invalid column name 'FormId'.] 
    System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +2030802 
    System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +5009584 
    System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() +234 
    System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +2275 
    System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +33 
    System.Data.SqlClient.SqlDataReader.get_MetaData() +86 
    System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +311 
    System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +987 
    System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162 
    System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32 
    System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141 
    System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12 
    System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader() +12 
    NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) +278 
    NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) +264 
    NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +186 
    NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +70 
    NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +226 

[GenericADOException: could not initialize a collection: [Sample.NHibernate.Entities.Form.EducationRecords#1][SQL: SELECT educationr0_.FormId as FormId1_, educationr0_.RecordId as RecordId1_, educationr0_.RecordId as RecordId1_0_, educationr0_1_.RecordTypeId as RecordTy2_1_0_, educationr0_1_.FormId as FormId1_0_, educationr0_.SchoolName as SchoolName2_0_, educationr0_.DateAttendedFrom as DateAtte3_2_0_, educationr0_.DateAttendedTo as DateAtte4_2_0_, educationr0_.Degree as Degree2_0_, educationr0_.DateDegreeAwarded as DateDegr6_2_0_ FROM dbo.EducationRecords educationr0_ inner join dbo.Records educationr0_1_ on educationr0_.RecordId=educationr0_1_.RecordId WHERE educationr0_.FormId=?]] 
    NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +345 
    NHibernate.Loader.Collection.CollectionLoader.Initialize(Object id, ISessionImplementor session) +27 
    NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(Object key, ISessionImplementor session) +29 
    NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(InitializeCollectionEvent event) +349 
    NHibernate.Impl.SessionImpl.InitializeCollection(IPersistentCollection collection, Boolean writing) +431 
    NHibernate.Collection.AbstractPersistentCollection.Initialize(Boolean writing) +47 
    NHibernate.Collection.Generic.PersistentGenericBag`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +16 
    System.Linq.Enumerable.Any(IEnumerable`1 source) +71 
    ASP.views_form_index_aspx.__RenderContent2(HtmlTextWriter __w, Control parameterContainer) in c:\Dev\Sandbox\NHibernateSample\Sample.Web\Views\Form\Index.aspx:16 
    System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109 
    System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 
    System.Web.UI.Control.Render(HtmlTextWriter writer) +10 
    System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 
    System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208 
    System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 
    System.Web.UI.Control.Render(HtmlTextWriter writer) +10 
    System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 
    System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208 
    System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 
    System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +55 
    System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 
    System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3060 

在GenericADOException所示的生成的SQL查詢中,WHERE子句指定:

WHERE educationr0_.FormId=? 

但它需要是:

WHERE educationr0_1_.FormId=? 
+0

盲目猜測,但嘗試刪除'擴展()'。我從來沒有回憶過必須使用它,因爲它被SubclassMap所隱含。 – 2010-11-03 01:12:30

+0

這不應該在那裏。這是從我做的一些修補中剩下的。我有或沒有相同的問題。 – 2010-11-03 01:24:47

+0

你有沒有找到解決這個問題的解決方案?我有確切的一個:( – Serhiy 2011-02-28 07:57:43

回答

1

它花了我一段時間來重溫這一點。在發佈這個問題後我不再玩Fluent NHibernate。

回想起來,我認爲我想要完成的事情可能存在根本性錯誤。我的問題可以簡單地通過將EducationRecords的列表替換爲Records來解決。如果需要,可以從中檢索特定類型的記錄。

我不記得爲什麼我原本以爲我需要在FormsEducationRecords清單。我懷疑它對我來說或者是ORM經驗不足,或者可能存在一個在數據庫設計中沒有正確表示的約束。

0

試着改變:

HasMany(x => x.EducationRecords); 

HasMany(x => x.EducationRecords).Inverse(); 

這樣你告訴NH,只有在這種關係中的孩子負責保存的。

您可以在此處詳細瞭解多餘的更新: http://nhprof.com/Learn/Alerts/SuperfluousManyToOneUpdate

+0

它似乎沒有任何效果。不知道它是否有所不同,但現在我只是試圖檢索記錄,我還沒有足夠的努力來保存任何東西,但感謝您的建議,但! – 2010-11-17 13:58:43

0

試試這個映射:

public class FormMap : ClassMap<Entities.Form> 
{ 
    public FormMap() 
    { 
     Table("Forms"); 
     Id(x => x.Id, "FormId"); 
     Map(x => x.CompletedBy); 

     HasMany<Record>(x => x.EducationRecords).Where("form0_1_.EducationRecordId is not null"); 
    } 
} 

並做好記錄類不是抽象的。這對我有用。也許'form0_1_'不是正確的列名稱。你可以在生成的SQL中找到正確的一個。

+0

我挖了我的舊樣本項目嘗試你的建議,但現在我得到了以下錯誤:「無法實例化抽象類或接口:Sample.NHibernate.Entities.Record」在同一行中,像以前一樣。 – 2011-03-01 03:50:41

+0

請嘗試使用更新的映射。 – Serhiy 2011-03-01 20:42:09

1

該解決方案確實非常簡單。你需要指定所有subclassmap,這是一個抽象 基類

public class EducationRecordMap : SubclassMap<Entities.EducationRecord> 
{ 
    public EducationRecordMap() 
    { 
    Table("EducationRecords"); 

    Abstract(); // because Record base class is abstract 

    KeyColumn("RecordId"); 
    Map(x => x.SchoolName); 
    Map(x => x.DateAttendedFrom); 
    Map(x => x.DateAttendedTo); 
    } 
}