代码之家  ›  专栏  ›  技术社区  ›  John Allers

FLUENT NHiBiNATE:一对多映射到抽象类中存在外键的子类

  •  1
  • John Allers  · 技术社区  · 14 年前

    我试图映射的数据库结构大致类似于:

    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);
        }
    }
    

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

    看起来底层SQL查询试图在EnvialRealStand表上查询“FuffID”列,但该列不存在。我花了很多时间用我的map类尝试不同的配置变体,但没有成功。

    所以我的问题是:在检索教育记录时,如何告诉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>
    

    模型的类型为Form。

    [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=?
    
    5 回复  |  直到 7 年前
        1
  •  1
  •   John Allers    13 年前

    我花了一段时间才重温这件事。我发布这个问题后不久就不再和流利的NHibernate玩了。

    EducationRecords 具有 Records . 如果需要,可以从中检索特定类型的记录。

    教育记录 在里面 Forms . 我怀疑这可能是因为我对ORMs缺乏经验,或者数据库设计中可能存在一个未正确表示的约束。

        2
  •  1
  •   FreeClimb    10 年前

    解决办法真的很简单。您需要为所有子类映射指定,这是一个抽象 基类

    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);
      }
     }
    
        3
  •  0
  •   Variant    14 年前

    HasMany(x => x.EducationRecords);
    

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

    您可以在此处阅读有关多余更新的更多信息: http://nhprof.com/Learn/Alerts/SuperfluousManyToOneUpdate

        4
  •  0
  •   Sly    14 年前

    您应该检查得到的XML映射。可能是它生成的子类映射而不是连接的子类。 http://knol.google.com/k/fabio-maulo/nhibernate-chapter-8-inheritance-mapping/1nr4enxv3dpeq/11

        5
  •  0
  •   Serhiy    14 年前

    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_u'不是正确的列名。您可以在生成的SQL中找到正确的一个。