Nhibernate 建模一对零或一关系(Z基数)
我正在努力寻找建立1:0,1关系模型的最佳方法(“可能有一个”或“最多有一个”)。我相信这就是所谓的Z基数 例如,假设我有两个类Nhibernate 建模一对零或一关系(Z基数),nhibernate,oop,nhibernate-mapping,Nhibernate,Oop,Nhibernate Mapping,我正在努力寻找建立1:0,1关系模型的最佳方法(“可能有一个”或“最多有一个”)。我相信这就是所谓的Z基数 例如,假设我有两个类Widget和widgetest。并非所有小部件都经过测试,而且测试是破坏性的,因此每个小部件最多只能有一个WidgetTest。还假设向小部件添加WidgetTest字段是不合适的 我希望我的公共界面是: Widget WidgetTest { get; set; } WidgetTest Widget { get; } 模型1:小部件具有Widg
Widget
和widgetest
。并非所有小部件都经过测试,而且测试是破坏性的,因此每个小部件最多只能有一个WidgetTest。还假设向小部件添加WidgetTest字段是不合适的
我希望我的公共界面是:
Widget
WidgetTest { get; set; }
WidgetTest
Widget { get; }
模型1:小部件具有WidgetTest属性,并且在数据库中,小部件表具有WidgetTest的唯一约束外键。我的DBA认为这将允许WidgetTest记录在没有小部件的情况下存在
WidgetTable
WidgetTestId (FK, UQ)
模型2:Widget有一个WidgetTest的私有集合,并通过从由公共WidgetTest属性控制的集合中添加或删除单个对象来强制0,1关系。数据库将其建模为1:m,WidgetTest对小部件具有唯一约束的外键。我认为这意味着采用模型来适应数据库模式(即,我需要做更多的工作)
哪种型号更好?使用NHibernate哪个更容易实现?还是有第三条路
编辑。。。以下是我最终得到的结果:
public class Widget
{
// This is mapped in NH using a access strategy
private IList<WidgetTest> _widgetTests = new List<WidgetTest>(1);
public WidgetTest
{
get { return _widgetTests.FirstOrDefault(); }
set
{
_widgetTests.Clear();
if (value != null)
{
_widgetTests.Add(value);
}
}
}
}
公共类小部件
{
//这是使用访问策略在NH中映射的
私有IList_widgetests=新列表(1);
公共寡妇
{
获取{return _widgetests.FirstOrDefault();}
设置
{
_widgetests.Clear();
if(值!=null)
{
_widgetests.Add(值);
}
}
}
}
当您说“假设将WidgetTest字段添加到Widget是不合适的”时,您的意思是在域对象中还是在数据库中。如果您希望字段位于数据库中的同一个表中,那么将WidgetTest映射为Widget的一个组件如何?使NHibernate映射文件如下所示:
<class name="Widget" table="Widget">
...
<property name="WidgetProperty"/>
...
<component name="WidgetTest" class="WidgetTest">
<property name="WidgetTestProperty"/>
</component>
</class>
这仍然可以让您拥有指定的公共接口,但是,WidgetTest将成为您可能想要或不想要的值对象。我的方法是在映射中建模一对多关系,但将“多”约束到单个项。这允许可选的一对一,并且还保证在保存小部件时保存WidgetTest实例。例如:
public class Widget
{
/// <summary>
/// This property is ignored by the NHibernate mappings.
/// </summary>
public virtual WidgetTest WidgetTest { get; set; }
/// <summary>
/// For easier persistence with NHibernate, this property repackages the
/// WidgetTest property as a list containing a single item. If an
/// attempt is made to set this property to a list containing more than
/// one item, an exception will be thrown. But why bother? Just use the
/// WidgetTest property.
/// </summary>
public virtual IList<WidgetTest> WidgetTests
{
get
{
IList<WidgetTest> widgetTests = new List<WidgetTest>();
if (this.WidgetTest != null)
{
widgetTests.Add(this.WidgetTest);
}
return widgetTests;
}
set
{
if (value != null && value.Count > 1)
{
throw new Exception("The WidgetTests collection may not contain more than one item.");
}
else if (value != null && value.Count == 1)
{
this.WidgetTest = value[0];
}
else
{
this.WidgetTest = null;
}
}
}
}
公共类小部件
{
///
///NHibernate映射将忽略此属性。
///
公共虚拟widgetest widgetest{get;set;}
///
///为了使NHibernate更容易持久化,此属性重新打包
///WidgetTest属性作为包含单个项的列表。如果
///试图将此属性设置为包含多个属性的列表
///一个项目,将抛出一个异常。但是为什么要麻烦呢?只需使用
///Widgetest属性。
///
公共虚拟IList Widgetests
{
得到
{
IList widgetTests=新列表();
if(this.widgetest!=null)
{
添加(this.widgetest);
}
寡妇归来;
}
设置
{
if(value!=null&&value.Count>1)
{
抛出新异常(“WidgetTests集合不能包含多个项”);
}
else if(value!=null&&value.Count==1)
{
this.widgetest=值[0];
}
其他的
{
this.widgetest=null;
}
}
}
}
我还有两个想法
- 将表和映射作为组件连接
- 忽略从属类的Id
拥有实体实例不再引用带有cascade=“all delete orphan”的集合”
如果在映射文件中使用inverse=“true”
和cascade=“all delete orphan”
,您会发现这种情况
这是因为nw的答案在每次调用get
访问器时都会创建一个新列表,并且不会对通过set
访问器传入的列表执行任何操作。因此,NHibernate没有创建对象时最初传入的IList
引用,因此无法继续级联
因此,为了解决这个问题,我们需要对IList
引用进行一些处理,注意不要取消引用它
public class Widget
{
public Widget()
{
_widgetTests = new List<WidgetTest>();
}
/// <summary>
/// This property is ignored by the NHibernate mappings.
/// </summary>
public WidgetTest WidgetTest { get; set; }
/// <summary>
/// For easier persistence with NHibernate, this property repackages the
/// WidgetTest property as a list containing a single item. If an
/// attempt is made to set this property to a list containing more than
/// one item, an exception will be thrown. But why bother? Just use the
/// WidgetTest property.
/// </summary>
private IList<WidgetTest> _widgetTests;
protected virtual IList<WidgetTest> WidgetTests
{
get
{
if (_widgetTests.Count == 0 && WidgetTest != null)
{
_widgetTests.Add(WidgetTest);
}
else if (_widgetTests.Count > 0 && WidgetTest == null)
{
_widgetTests.Clear();
}
else if (_widgetTests.Count > 0 && WidgetTest != _widgetTests[0])
{
_widgetTests.Clear();
_widgetTests.Add(WidgetTest);
}
return _widgetTests;
}
set
{
if (value != null && value.Count > 1)
{
throw new Exception("The WidgetTest collection may not contain more than one item.");
}
if (value != null && value.Count == 1)
{
WidgetTest = value[0];
}
else
{
WidgetTest = null;
}
//Store the reference
_widgetTests = value;
}
}
}
公共类小部件
{
公共小部件()
{
_widgetTests=新列表();
}
///
///NHibernate映射将忽略此属性。
///
公共widgetest widgetest{get;set;}
///
///为了使NHibernate更容易持久化,此属性重新打包
///WidgetTest属性作为包含单个项的列表。如果
///试图将此属性设置为包含多个属性的列表
///一个项目,将抛出一个异常。但是为什么要麻烦呢?只需使用
///Widgetest属性。
///
私人寡妇;
受保护的虚拟IList Widgetests
{
得到
{
if(_widgetests.Count==0&&widgetest!=null)
{
_添加(widgetest);
}
else如果(_widgetests.Count>0&&widgetest==null)
{
_widgetests.Clear();
}
else if(\u widgetests.Count>0&&widgetest!=\u widgetests[0])
{
_widgetests.Clear();
_添加(widgetest);
}
返回寡妇;
}
设置
{
if(value!=null&&value.Count>1)
{
抛出新异常(“WidgetTest集合不能包含多个项”);
}
if(value!=null&&value.Count==1)
{
public class Widget
{
/// <summary>
/// This property is ignored by the NHibernate mappings.
/// </summary>
public virtual WidgetTest WidgetTest { get; set; }
/// <summary>
/// For easier persistence with NHibernate, this property repackages the
/// WidgetTest property as a list containing a single item. If an
/// attempt is made to set this property to a list containing more than
/// one item, an exception will be thrown. But why bother? Just use the
/// WidgetTest property.
/// </summary>
public virtual IList<WidgetTest> WidgetTests
{
get
{
IList<WidgetTest> widgetTests = new List<WidgetTest>();
if (this.WidgetTest != null)
{
widgetTests.Add(this.WidgetTest);
}
return widgetTests;
}
set
{
if (value != null && value.Count > 1)
{
throw new Exception("The WidgetTests collection may not contain more than one item.");
}
else if (value != null && value.Count == 1)
{
this.WidgetTest = value[0];
}
else
{
this.WidgetTest = null;
}
}
}
}
public class Widget
{
public Widget()
{
_widgetTests = new List<WidgetTest>();
}
/// <summary>
/// This property is ignored by the NHibernate mappings.
/// </summary>
public WidgetTest WidgetTest { get; set; }
/// <summary>
/// For easier persistence with NHibernate, this property repackages the
/// WidgetTest property as a list containing a single item. If an
/// attempt is made to set this property to a list containing more than
/// one item, an exception will be thrown. But why bother? Just use the
/// WidgetTest property.
/// </summary>
private IList<WidgetTest> _widgetTests;
protected virtual IList<WidgetTest> WidgetTests
{
get
{
if (_widgetTests.Count == 0 && WidgetTest != null)
{
_widgetTests.Add(WidgetTest);
}
else if (_widgetTests.Count > 0 && WidgetTest == null)
{
_widgetTests.Clear();
}
else if (_widgetTests.Count > 0 && WidgetTest != _widgetTests[0])
{
_widgetTests.Clear();
_widgetTests.Add(WidgetTest);
}
return _widgetTests;
}
set
{
if (value != null && value.Count > 1)
{
throw new Exception("The WidgetTest collection may not contain more than one item.");
}
if (value != null && value.Count == 1)
{
WidgetTest = value[0];
}
else
{
WidgetTest = null;
}
//Store the reference
_widgetTests = value;
}
}
}
<class name="Widget" table="widgets">
...
<id name="Id" type="Guid" column="widgetId">
...
</id>
...
<bag name="WidgetTests" inverse="true" cascade="all-delete-orphan" access="property">
...
<key column="widgetId" />
<one-to-many class="WidgetTest" />
</bag>
</class>