简体   繁体   中英

Fluent NHibernate, multiple abstract base class inheritance not loading middle base class

General example:

Abstract mapped class A. Abstract mapped class B : A. B has a property of type int (or whatever you want.) Mapped class C : A.

Abstract mapped class D has a property of type A. Mapped class E : D. Has a property of type B.

Save E to the database. Retrieve it and attempt to access B.int from E. throws "InvalidCastException: Unable to cast object of type 'AProxy' to type 'B'.

Using FluentNhibernate: 1.3.0 Nhibernate: 3.2.0.4

Actual stripped down example:

BaseTemplate Table:

USE [DB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [temp].[BaseTemplate](
    [ID] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_BaseTemplate] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

MiddleTemplate Table:

USE [DB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [temp].[MiddleTemplate](
    [ID] [int] NOT NULL,
    [MiddleTemplateProperty] [int] NULL,
 CONSTRAINT [PK_MiddleTemplate] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [temp].[MiddleTemplate]  WITH CHECK ADD  CONSTRAINT [FK_MiddleTemplate_BaseTemplate] FOREIGN KEY([ID])
REFERENCES [temp].[BaseTemplate] ([ID])
GO

ALTER TABLE [temp].[MiddleTemplate] CHECK CONSTRAINT [FK_MiddleTemplate_BaseTemplate]
GO

LastTemplate Table:

USE [DB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [temp].[LastTemplate](
    [ID] [int] NOT NULL,
    [LastTemplateProperty] [int] NULL,
 CONSTRAINT [PK_LastTemplate] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [temp].[LastTemplate]  WITH CHECK ADD  CONSTRAINT [FK_LastTemplate_BaseTemplate] FOREIGN KEY([ID])
REFERENCES [temp].[BaseTemplate] ([ID])
GO

ALTER TABLE [temp].[LastTemplate] CHECK CONSTRAINT [FK_LastTemplate_BaseTemplate]
GO

BaseTemplateInstance table:

USE [DB]
GO


SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [temp].[BaseTemplateInstance](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [TemplateID] [int] NOT NULL,
 CONSTRAINT [PK_BaseTemplateInstance] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [temp].[BaseTemplateInstance]  WITH CHECK ADD  CONSTRAINT [FK_BaseTemplateInstance_BaseTemplate] FOREIGN KEY([TemplateID])
REFERENCES [temp].[BaseTemplate] ([ID])
GO

ALTER TABLE [temp].[BaseTemplateInstance] CHECK CONSTRAINT [FK_BaseTemplateInstance_BaseTemplate]
GO

LastTemplateInstance table:

USE [DB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [temp].[LastTemplateInstance](
    [ID] [int] NOT NULL,
 CONSTRAINT [PK_LastTemplateInstance] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [temp].[LastTemplateInstance]  WITH CHECK ADD  CONSTRAINT [FK_LastTemplateInstance_BaseTemplateInstance] FOREIGN KEY([ID])
REFERENCES [temp].[BaseTemplateInstance] ([ID])
GO

ALTER TABLE [temp].[LastTemplateInstance] CHECK CONSTRAINT [FK_LastTemplateInstance_BaseTemplateInstance]
GO

Classes:

public abstract class BaseTemplate

public abstract class MiddleTemplate : BaseTemplate
    {
        public virtual int MiddleTemplateProperty { get; set; }
    }

public class LastTemplate : MiddleTemplate
    {
        public virtual int LastTemplateProperty { get; set; }
    }

public abstract class BaseTemplateInstance
    {
        public virtual BaseTemplate Template { get; set; }

        protected BaseTemplateInstance() {}
        protected BaseTemplateInstance(BaseTemplate template) : this()
        {
            Template = template;
        }
    }

public class LastTemplateInstance : BaseTemplateInstance
    {
        protected LastTemplateInstance() {}

        public LastTemplateInstance(LastTemplate template)
            :base(template) {}

        public virtual int MiddleTemplateProperty { get { return ((MiddleTemplate) Template).MiddleTemplateProperty; } }
    }

Mappings:

public class BaseTemplateMap : ClassMap<BaseTemplate>
    {
        public BaseTemplateMap()
        {
            Table("temp.BaseTemplate");

            // Unique Identifier
            Id(x => x.Id, "ID")
                .GeneratedBy.Identity();
        }
    }

 public class MiddleTemplateMap : SubclassMap<MiddleTemplate>
    {
        public MiddleTemplateMap()
        {
            Table("temp.MiddleTemplate");

            KeyColumn("ID");

            Map(x => x.MiddleTemplateProperty)
                .Nullable();
        }
    }


public class LastTemplateMap : SubclassMap<LastTemplate>
    {
        public LastTemplateMap()
        {
            Table("temp.LastTemplate");

            KeyColumn("ID");

            Map(x => x.LastTemplateProperty)
                .Nullable();
        }
    }

public class BaseTemplateInstanceMap : ClassMap<BaseTemplateInstance>
    {
        public BaseTemplateInstanceMap()
        {
            Table("temp.BaseTemplateInstance");

            // Unique Identifier
            Id(x => x.Id, "Id")
                .GeneratedBy.Identity();

            References(x => x.Template, "TemplateID")
                .Not.Nullable();
        }
    }

public class LastTemplateInstanceMap : SubclassMap<LastTemplateInstance>
    {
        public LastTemplateInstanceMap()
        {
            Table("temp.LastTemplateInstance");

            // Unique Identifier
            KeyColumn("ID");
        }
    }

Test Example:

[TestFixture]
    internal class TempFileTests
    {
        #region Members

        private LastTemplate _entity;
        private MappingsRepository _repository;

            #endregion // Members

        #region SetUp

        [TestFixtureSetUp]
        public void SetUpFixture()
        {
            _repository = MappingsRepository.GetInstance();
        }

        [SetUp]
        public void SetUp()
        {
            _entity = new LastTemplate();
        }

        #endregion // SetUp

        #region Tests

        [Test]
        public void LastTemplateInstanceMappingsTest()
        {
            var lastTemplateInstance = new LastTemplateInstance(_entity);
            _repository.Save(_entity);

            lastTemplateInstance = new PersistenceSpecification<LastTemplateInstance>(_repository.CurrentSession)
                .VerifyTheMappings(lastTemplateInstance);

            var middleProperty = lastTemplateInstance.MiddleTemplateProperty;
        }

        #endregion // Tests

        #region TearDown

        [TearDown]
        public void TearDown()
        {
            if (_entity != null && !_entity.IsNew())
                _repository.Delete(_entity);
        }

        [TestFixtureTearDown]
        public void TearDownFixture()
        {
            _repository.Dispose();
            _repository = null;
        }

        #endregion // TearDown
    }

I see your missing the DiscriminateSubClassesOnColumn map on BaseTemplateMap, and DiscriminatorValue map on the MiddleTemplateMap and LastTemplateMap.

Once you let NHibernate know how to identify the type based on descriminator, it should load the right object (ie in your case it's B type), your casting should work.

Look at this sample

The problem arises when trying to access BaseTemplateInstance.Template. If you set this reference in the mapping to NOT lazyLoad, works fine!

The current theory is that trying to access properties from it before it is loaded causes the exception. Making sure it is not lazy loaded, means that it will be loaded, proper casting will be allowed, and properties can be accessed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM