C# 每个层次结构的表和未来的证明
我正在开发一个应用程序,它将使用NHibernate在数据库中存储“命令”。每个命令都有一组参数。例如,“sleep”命令有一个持续时间,“set text”有一个字符串值。所有这些命令都源自相同的C# 每个层次结构的表和未来的证明,c#,nhibernate,database-design,class-table-inheritance,C#,Nhibernate,Database Design,Class Table Inheritance,我正在开发一个应用程序,它将使用NHibernate在数据库中存储“命令”。每个命令都有一组参数。例如,“sleep”命令有一个持续时间,“set text”有一个字符串值。所有这些命令都源自相同的命令基本类型 我希望将来可以添加额外的命令,对数据库的影响尽可能小。我最初的反应是使用该模式,因为唯一需要修改的模式是向命令表中添加一列 我还考虑使用TPH模式,但映射泛型列而不是特定列,然后将它们转换为类本身中的特定(强类型)属性值(即使用强类型公共属性对映射的泛型字符串属性进行阴影处理)。这样,如
命令
基本类型
我希望将来可以添加额外的命令,对数据库的影响尽可能小。我最初的反应是使用该模式,因为唯一需要修改的模式是向命令表中添加一列
我还考虑使用TPH模式,但映射泛型列而不是特定列,然后将它们转换为类本身中的特定(强类型)属性值(即使用强类型公共属性对映射的泛型字符串属性进行阴影处理)。这样,如果我的列数等于任何命令可能需要的最大参数,我就根本不需要更改表。这些在我的脑海里似乎有点烦躁
作为数据库设计和NHibernate使用的新手,有人能指出这些方法中的漏洞,或者提出更好的建议吗?我试图避免(尽可能多地)更改模式,同时允许将来的扩展和简单的C#API。查看
BaseImmutableUserType:IUserType
实现,这将允许您使用泛型列 查看BaseImmutableUserType:IUserType
实现,这将允许您使用泛型列 IMO,您最好使用一个包含单个XML类型列的表,而不是在继承层次结构中烘焙,因为继承层次结构可能会或可能不会模拟实际的域问题。众所周知,XML列是发展数据模型的一种无摩擦的方式,尤其是您预期随着时间的推移可能会发生重大变化的数据模型
XML列可以存储将命令对象表示为由.NET BCL类序列化或使用您自己的自定义XML序列化程序(请参见IXmlSerializable
)所需的整个对象图
NHibernate本机支持XML SQL Server列类型。谷歌搜索应该会带来一些如何进行映射的例子。在我看来,最好的办法是使用一个包含单个XML类型列的表,而不是在继承层次结构中烘焙,因为继承层次结构可能会或可能不会模拟实际的域问题。众所周知,XML列是发展数据模型的一种无摩擦的方式,尤其是您预期随着时间的推移可能会发生重大变化的数据模型 XML列可以存储将命令对象表示为由.NET BCL类序列化或使用您自己的自定义XML序列化程序(请参见
IXmlSerializable
)所需的整个对象图
NHibernate本机支持XML SQL Server列类型。谷歌搜索应该会带回一些如何进行映射等的例子。我最终从@mxmissile那里得到了答案,下面是实现中有趣部分的细节,希望能帮助其他人。结果是总体上非常干净,所有的逻辑都在映射中处理
/// <summary>NHibernate class mapping file for <see cref="Action"/>.</summary>
internal sealed class ActionMapper : ClassMap<Action>
{
/// <summary>Constructor.</summary>
public ActionMapper()
{
DiscriminateSubClassesOnColumn("ClassType").Not.Nullable();
Id(x => x.Id);
}
}
/// <summary>NHibernate class mapping file for <see cref="SetText"/>.</summary>
internal sealed class SetTextMapper : SubclassMap<SetText>
{
public SetTextMapper()
{
DiscriminatorValue(typeof(SetText).Name);
Map(x => x.Text).Column("Arg1").CustomType<StringArgType>();
}
}
/// <summary>NHibernate class mapping file for <see cref="Sleep"/>.</summary>
internal sealed class SleepMapper : SubclassMap<Sleep>
{
public SleepMapper()
{
DiscriminatorValue(typeof(Sleep).Name);
Map(x => x.Duration).Column("Arg1").CustomType<TimeSpanArgType>();
}
}
internal class StringArgType : BaseImmutableUserType<String>
{
public override SqlType[] SqlTypes
{
// All arguments map to strings in the database
get { return new[] {new SqlType(DbType.String)}; }
}
public override object NullSafeGet(IDataReader Reader, string[] Names, object Owner)
{
return NHibernateUtil.String.NullSafeGet(Reader, Names[0]).As<String>();
}
public override void NullSafeSet(IDbCommand Command, object Value, int Index)
{
NHibernateUtil.String.NullSafeSet(Command, Value, Index);
}
}
internal class TimeSpanArgType : BaseImmutableUserType<TimeSpan>
{
public override SqlType[] SqlTypes
{
// All arguments map to strings in the database
get { return new[] {new SqlType(DbType.String)}; }
}
public override object NullSafeGet(IDataReader Reader, string[] Names, object Owner)
{
return NHibernateUtil.TimeSpan.NullSafeGet(Reader, Names[0]).As<TimeSpan?>();
}
public override void NullSafeSet(IDbCommand Command, object Value, int Index)
{
object val = DBNull.Value;
if (Value != null)
{
TimeSpan timespan = (TimeSpan)Value;
val = timespan.Ticks;
}
NHibernateUtil.String.NullSafeSet(Command, val, Index);
}
}
的NHibernate类映射文件。
内部密封类ActionMapper:ClassMap
{
///构造器。
公共ActionMapper()
{
DiscriminateSubClassesOnColumn(“类类型”).Not.Nullable();
Id(x=>x.Id);
}
}
///的NHibernate类映射文件。
内部密封类SetTextMapper:子类映射
{
公共SetTextMapper()
{
鉴别器值(typeof(SetText).Name);
Map(x=>x.Text).Column(“Arg1”).CustomType();
}
}
///的NHibernate类映射文件。
内部密封类SleepMapper:子类映射
{
公共睡眠映射器()
{
鉴别器值(typeof(Sleep).Name);
Map(x=>x.Duration).Column(“Arg1”).CustomType();
}
}
内部类StringArgType:BaseImmutableUserType
{
公共重写SqlType[]SqlTypes
{
//所有参数都映射到数据库中的字符串
获取{returnnew[]{newsqltype(DbType.String)};}
}
公共重写对象NullSafeGet(IDataReader读取器,字符串[]名称,对象所有者)
{
返回NHibernateUtil.String.NullSafeGet(读取器,名称[0]).As();
}
公共覆盖无效NullSafeSet(IDbCommand命令、对象值、int索引)
{
NHibernateUtil.String.NullSafeSet(命令、值、索引);
}
}
内部类TimeSpanArgType:BaseImmutableUserType
{
公共重写SqlType[]SqlTypes
{
//所有参数都映射到数据库中的字符串
获取{returnnew[]{newsqltype(DbType.String)};}
}
公共重写对象NullSafeGet(IDataReader读取器,字符串[]名称,对象所有者)
{
返回NHibernateUtil.TimeSpan.NullSafeGet(读取器,名称[0]).As();
}
公共覆盖无效NullSafeSet(IDbCommand命令、对象值、int索引)
{
对象val=DBNull.Value;
if(值!=null)
{
TimeSpan TimeSpan=(TimeSpan)值;
val=timespan.Ticks;
}
NHibernateUtil.String.NullSafeSet(命令、val、索引);
}
}
我最终从@mxmissile那里得到了答案,下面是实现中有趣部分的细节,希望能帮助其他人。结果是总体上非常干净,所有的逻辑都在映射中处理
/// <summary>NHibernate class mapping file for <see cref="Action"/>.</summary>
internal sealed class ActionMapper : ClassMap<Action>
{
/// <summary>Constructor.</summary>
public ActionMapper()
{
DiscriminateSubClassesOnColumn("ClassType").Not.Nullable();
Id(x => x.Id);
}
}
/// <summary>NHibernate class mapping file for <see cref="SetText"/>.</summary>
internal sealed class SetTextMapper : SubclassMap<SetText>
{
public SetTextMapper()
{
DiscriminatorValue(typeof(SetText).Name);
Map(x => x.Text).Column("Arg1").CustomType<StringArgType>();
}
}
/// <summary>NHibernate class mapping file for <see cref="Sleep"/>.</summary>
internal sealed class SleepMapper : SubclassMap<Sleep>
{
public SleepMapper()
{
DiscriminatorValue(typeof(Sleep).Name);
Map(x => x.Duration).Column("Arg1").CustomType<TimeSpanArgType>();
}
}
internal class StringArgType : BaseImmutableUserType<String>
{
public override SqlType[] SqlTypes
{
// All arguments map to strings in the database
get { return new[] {new SqlType(DbType.String)}; }
}
public override object NullSafeGet(IDataReader Reader, string[] Names, object Owner)
{
return NHibernateUtil.String.NullSafeGet(Reader, Names[0]).As<String>();
}
public override void NullSafeSet(IDbCommand Command, object Value, int Index)
{
NHibernateUtil.String.NullSafeSet(Command, Value, Index);
}
}
internal class TimeSpanArgType : BaseImmutableUserType<TimeSpan>
{
public override SqlType[] SqlTypes
{
// All arguments map to strings in the database
get { return new[] {new SqlType(DbType.String)}; }
}
public override object NullSafeGet(IDataReader Reader, string[] Names, object Owner)
{
return NHibernateUtil.TimeSpan.NullSafeGet(Reader, Names[0]).As<TimeSpan?>();
}
public override void NullSafeSet(IDbCommand Command, object Value, int Index)
{
object val = DBNull.Value;
if (Value != null)
{
TimeSpan timespan = (TimeSpan)Value;
val = timespan.Ticks;
}
NHibernateUtil.String.NullSafeSet(Command, val, Index);
}
}
的NHibernate类映射文件。
内部密封类ActionMapper:ClassMap
{
///构造器。
公共ActionMapper()
{
DiscriminateSubClassesOnColumn(“类类型”).Not.Nullable();
Id(x=>x.Id);
}
}
///的NHibernate类映射文件。
内部密封类SetTextMapper:子类映射
{
公共SetTextMapper()
{
鉴别器值(typeof(SetText).Name);
Map(x=>x.Text).Column(“Arg1”).CustomType();
}
}
///的NHibernate类映射文件。
内密封c