如何在同一列中使用NHibernate DiscriminateSubClassesOnColumn和References

时间:2014-09-19 16:09:49

标签: nhibernate orm fluent-nhibernate

设置

我在.NET4.0库中使用FluentNHibernate 1.4.0进行NHibernate 3.3.3-SP1映射。我正在为我的类型层次结构使用“table-per-inheritance”方法,如下所示:

-- Different process types potentially use 
-- different types of reference values
CREATE TABLE ProcessTypes
(Id INT PRIMARY KEY)

-- Contains reference values for value comparisons
CREATE TABLE ProcessReferenceValues
(Id INT PRIMARY KEY IDENTITY(1,1),
 ProcessTypeId INT FOREIGN KEY REFERENCES ProcessTypes(Id),
 FloatReferencesValue FLOAT NULL,
 IntReferenceValue INT NULL)
// POCOs

class ProcessReferenceValues
{
    public virtual int Id { get; set; }
    public virtual ProcessTypes ProcessType { get; set; }
    public virtual float? FloatReferenceValue { get; set; }
    public virtual int? IntReferenceValue { get; set; }
}

class IntProcessReferenceValues : ProcessReferenceValues { }

class FloatProcessReferenceValues : ProcessReferenceValues { }

enum ProcessTypeName : int
{
    IntProcess = 1,
    FloatProcess = 2
}

class ProcessTypes
{
    public virtual int Id { get; set; }
    public virtual ProcessTypeName Name { get; set; }
}

// FluentNHibernate Mappings

class ProcessReferenceValuesMap 
    : FluentNHibernate.Mapping.ClassMap<ProcessReferenceValues>
{
    public ProcessReferenceValuesMap()
    {
        string processTypeId = "ProcessTypeId";

        this.Id(x => x.Id);
        this.Map(x => x.FloatReferenceValue).Nullable();
        this.Map(x => x.IntReferenceValue).Nullable();

        // Here is the tricky bit
        this.References(x => x.ProcessType, processTypeId);
        this.DiscriminateSubClassesOnColumn(processTypeId);
    }
}

class IntProcessReferenceValuesMap 
    : FluentNHibernate.Mapping.SubclassMap<IntProcessReferenceValues>
{
    public IntProcessReferenceValuesMap()
    {
        this.DiscriminatorValue((int)ProcessTypeName.IntProcess);
    }
}

class FloatProcessReferenceValuesMap 
    : FluentNHibernate.Mapping.SubclassMap<FloatProcessReferenceValues>
{
    public FloatProcessReferenceValuesMap()
    {
        this.DiscriminatorValue((int)ProcessTypeName.FloatProcess);
    }
}

class ProcessPeriodTypesMap : FluentNHibernate.Mapping.ClassMap<ProcessPeriodTypes>
{
    public ProcessPeriodTypesMap()
    {
        this.ReadOnly();
        this.Id(x => x.Id, "id");
        this.Map(x => x.Name, "id").ReadOnly().CustomType<PeriodTypeName>();
    }
}

问题

虽然从数据库中读取就像魅力一样 - 正确选择了适当的子类 - 保存新的流程参考值会给我一个例外:

// Reading

var processType =
   (from type in session.Query<ProcessTypes>()
    where type.Name == ProcessTypeName.IntProcess
    select type).FirstOrDefault(); // OK, finds the IntProcess

var referenceValues =
   (from val in session.Query<ProcessReferenceValues>()
    select val).ToList(); // OK, finds the appropriate subclasses
// Inserting

var processType = new ProcessTypes
{
    Id = (int)ProcessTypeName.IntProcess
};

var referenceValue = new ProcessReferenceValues
{
    FloatReferenceValue = 0.7f,
    IntReferenceValue = null,
    ProcessType = processType // Needs the appropriate ProcessType
};
session.Save(referenceValue); // <- BOOM!
Error dehydrating property value for ProcessReferenceValues.ProcessType

Invalid Index 2 for OleDbParameterCollection with Count=2.
    bei System.Data.OleDb.OleDbParameterCollection.RangeCheck(Int32 index)
    bei System.Data.OleDb.OleDbParameterCollection.GetParameter(Int32 index)
    bei System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index)
    bei NHibernate.Type.Int32Type.Set(IDbCommand rs, Object value, Int32 index) in p:\nhibernate-core\src\NHibernate\Type\Int32Type.cs:Zeile 60.
    bei NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int32 index) in p:\nhibernate-core\src\NHibernate\Type\NullableType.cs:Zeile 182.
    bei NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session) in p:\nhibernate-core\src\NHibernate\Type\NullableType.cs:Zeile 122.
    bei NHibernate.Type.ManyToOneType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session) in p:\nhibernate-core\src\NHibernate\Type\ManyToOneType.cs:Zeile 50.
    bei NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index) in p:\nhibernate-core\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:Zeile 2410.

在困难时期,我总是召唤gooragle,问题似乎是this.DiscriminateSubClassesOnColumn(processTypeId)this.References(x => x.ProcessType, processTypeId)映射发生冲突。当我删除前者时,插入成功,但我想要子类映射。我还需要在添加ProcessReferenceValues.ProcessType的新实例时设置ProcessReferenceValues以区分子类。

问题

是否可以区分列上的子类,同时引用相同类型的同一列?

非常感谢帮助,必须有办法做到这一点......

提前

thx!

1 个答案:

答案 0 :(得分:5)

没有问题的类和映射

// POCOs

class ProcessValue
{
    public virtual int Id { get; set; }
    public abstract ProcessValueType Type { get; }
}

class IntProcessValue : ProcessValue
{
    public virtual int? Value { get; set; }
    public override ProcessValueType Type { get { return ProcessValueType.Int; } }
}

class FloatProcessValue : ProcessValue
{
    public virtual float? Value { get; set; }
    public override ProcessValueType Type { get { return ProcessValueType.Float; } }
}

enum ProcessValueType : int
{
    Int = 1,
    Float = 2
}

// FluentNHibernate Mappings

class ProcessValueMap  : ClassMap<ProcessValue>
{
    public ProcessValueMap()
    {
        string processTypeId = "ProcessValueTypeId";

        Id(x => x.Id);

        // just so you can query by type enum also. Querying by clr Type is already implemented
        Map(x => x.Type, processTypeId).CustomType<ProcessValueType>().ReadOnly().Access.None();
        DiscriminateSubClassesOnColumn(processTypeId);
    }
}

class IntProcessValueMap  : SubclassMap<IntProcessValue>
{
    public IntProcessValueMap()
    {
        Map(x => x.Value).Nullable();

        DiscriminatorValue((int)ProcessValueType.Int);
    }
}

class FloatProcessValueMap  : SubclassMap<FloatProcessValue>
{
    public FloatProcessValueMap()
    {
        Map(x => x.Value).Nullable();

        DiscriminatorValue((int)ProcessValueType.Float);
    }
}