我正在使用Fluent NHibernate版本1.0.0.579(此日期的最新版本)。我有一个抽象的Activity类和几个继承类,例如。 DummyActivity。所有这些都使用相同的表活动,并且它们都具有基于积分类型的鉴别器值,该积分类型指向项目中的映射(而不是数据库中的FK)。
我们构建了这样的映射:
public class ActivityMap : ClassMap<Activity>
{
public ActivityMap()
{
Table("Activities");
Id(x => x.Id).Column("ID").GeneratedBy.Guid();
Map(x => x.ActivityName).Not.Nullable().Length(50);
HasMany(x => x.ActivityParameters)
.KeyColumn("ActivityID")
.AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue"))
.Not.LazyLoad()
.Cascade.Delete()
.Table("ActivityParameters");
DiscriminateSubClassesOnColumn<int>("ActivityType")
.SubClass<DummyActivity>(1, c => { });
}
}
生成的hbm.xml文件为:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="***.Activity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Activities">
<id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
<generator class="guid" />
</id>
<discriminator column="ActivityType" type="Int32" insert="true" not-null="true" />
<property name="ActivityName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ActivityName" length="50" not-null="true" />
</property>
<map cascade="delete" lazy="false" name="ActivityParameters" table="ActivityParameters">
<key>
<column name="ActivityID" />
</key>
<index type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ParameterName" />
</index>
<element type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ParameterValue" />
</element>
</map>
<subclass name="***.DummyActivity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1" />
</class>
</hibernate-mapping>
根据我的观点,这看起来像一个有效的hbm.xml文件,结构与官方NHibernate参考文档中给出的示例相同,即
<class name="IPayment" table="PAYMENT">
<id name="Id" type="Int64" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="String"/>
<property name="Amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>
我们在制图中犯了一些错误吗?此外,有人可以指出Fluent推荐的新实现(使用具有鉴别器列的SubClass,类似
public class ActivityMap : ClassMap<Activity>
{
public ActivityMap()
{
Table("Activities");
Id(x => x.Id).Column("ID").GeneratedBy.Guid();
Map(x => x.ActivityName).Not.Nullable().Length(50);
HasMany(x => x.ActivityParameters)
.KeyColumn("ActivityID")
.AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue"))
.Not.LazyLoad()
.Cascade.Delete()
.Table("ActivityParameters");
DiscriminateSubClassesOnColumn<int>("ActivityType");
}
}
public class DummyActivityMap : SubClass<DummyActivity>
{
///discriminator value here how???
}
?)
堆栈跟踪
[FormatException: Input string was not in a correct format.]
System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) +7469351
System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) +119
NHibernate.Type.Int32Type.FromStringValue(String xml) +36
NHibernate.Type.Int32Type.StringToObject(String xml) +10
NHibernate.Persister.Entity.SingleTableEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) +7824
[MappingException: Could not format discriminator value to SQL string of entity ***.Activity]
NHibernate.Persister.Entity.SingleTableEntityPersister..ctor(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) +8183
NHibernate.Persister.PersisterFactory.CreateClassPersister(PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping cfg) +68
NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners) +1468
NHibernate.Cfg.Configuration.BuildSessionFactory() +87
FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs:93
[FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
]
***.Container.ConfigureNHibernate() in ***.Unity\Container.cs:92
***.Container.ConfigureContainer() in ***.Unity\Container.cs:60
***.Container.GetInstance() in ***.Unity\Container.cs:45
***.Global.CreateContainer() in ***\Global.asax.cs:72
***.Global.Application_Start(Object sender, EventArgs e) in ***\Global.asax.cs:44
答案 0 :(得分:2)
我在枚举时想出来了。
考虑这个枚举类型:
public enum ActivityType
{
[EnumKey("1")]
[EnumDescription("ImportFromFile")]
ImportFromFile,
}
其中EnumKey和EnumDescription是(流行的)扩展方法,我重新定义了活动,如
public abstract class Activity
{
public virtual Guid Id { get; set; }
public virtual ActivityExecutionResult ExecutionResult { get; private set; }
public virtual ActivityExecutionStatus ExecutionStatus {get;private set;}
public abstract ActivityExecutionStatus Execute();
public virtual string ActivityName { get; private set; }
public virtual IDictionary<string, string> ActivityParameters { get; private set; }
public virtual ActivityType ActivityType { get; private set; }
}
映射文件如下所示:
public class ActivityMap : ClassMap<Activity>
{
public ActivityMap()
{
Table("Activities");
Id(x => x.Id).Column("ID").GeneratedBy.Guid();
Map(x => x.ActivityName).Not.Nullable().Length(50);
Map(x => x.ActivityType).CustomType<int>().Column("ActivityType").Not.Nullable();
HasMany(x => x.ActivityParameters)
.KeyColumn("ActivityID")
.AsMap<string>(idx => idx.Column("ParameterName"), elem => elem.Column("ParameterValue"))
.Not.LazyLoad()
.Cascade.Delete()
.Table("ActivityParameters");
DiscriminateSubClassesOnColumn("ActivityType");
}
}
public class ImportActivityFromFileMap : SubclassMap<ImportActivityFromFile>
{
public ImportActivityFromFileMap()
{
DiscriminatorValue(ActivityType.ImportFromFile.GetKey());
}
}
生成的hbm文件如下所示:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="***.Activity, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Activities">
<id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
<generator class="guid" />
</id>
<discriminator column="ActivityType" type="String" insert="true" not-null="true" />
<property name="ActivityName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ActivityName" length="50" not-null="true" />
</property>
<property name="ActivityType" type="Int32">
<column name="ActivityType" not-null="true" />
</property>
<map cascade="delete" lazy="false" name="ActivityParameters" table="ActivityParameters">
<key>
<column name="ActivityID" />
</key>
<index type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ParameterName" />
</index>
<element type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ParameterValue" />
</element>
</map>
<subclass name="***.ImportActivityFromFile, ***, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1" />
</class>
</hibernate-mapping>
它就像一个魅力!
答案 1 :(得分:0)
你有没有弄清楚如何使用枚举而不是整数?对于整数,它的工作方式与您所描述的相同,但使用枚举则不然。即使我将它们转换为int
也没有E.g。 DiscriminatorValue((int)的SomeEnum.SomeVaue))
答案 2 :(得分:0)
我将展示我想出的另一个实现。
public class SmartEnumMapping<T> : IUserType
{
#region IUserType Members
public object Assemble(object cached, object owner)
{
return cached;
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return value;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public bool IsMutable
{
get { return false; }
}
public new bool Equals(object x, object y)
{
return object.Equals(x, y);
}
public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
int index0 = rs.GetOrdinal(names[0]);
if (rs.IsDBNull(index0))
{
return null;
}
string key = rs.GetString(index0);
return EnumExtensions.EnumParseKey<T>(key, false, true);
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDbDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
T enumValue = (T)Enum.Parse(typeof(T), value.ToString());
((IDbDataParameter)cmd.Parameters[index]).Value = enumValue.GetKey();
}
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType
{
get { return typeof(T); }
}
public global::NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return new SqlType[] { SqlTypeFactory.GetString(4096) }; }
}
#endregion
}
这样,映射变为
Map(x => x.ActivityType).CustomType<SmartEnumMapping<ActivityType>>().Column("ActivityType").Not.Nullable();
ActivityType看起来像
public enum ActivityType
{
[EnumKey("1")]
[EnumDescription("dada")]
dad,
[EnumKey("2")]
[EnumDescription("da")]
ImportCalculAtasateSfarsitLuna,
[EnumKey("3")]
[EnumDescription("da")]
das,
}
有了这个,在代码中我可以使用“ActivityType.das”,但是当持久化时,它会持久化为“3”。再次,当从数据库读取时,我读取“3”,但我将其转换为“ActivityType.das”。
我想这是一个更恰当的答案,可以解释前面答案中提到的行为。
再次,EnumKey,EnumDescription,EnumParseKey等是可以在互联网上轻松找到的东西。