代码之家  ›  专栏  ›  技术社区  ›  Jamie Ide

建模一对零或一关系(Z基数)

  •  8
  • Jamie Ide  · 技术社区  · 15 年前

    例如,假设我有两个类 Widget WidgetTest . 并非所有小部件都经过测试,而且测试是破坏性的,因此每个小部件最多只能有一个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对小部件具有唯一约束的外键。我认为这意味着采用模型来适应数据库模式(即,我需要做更多的工作)。

    WidgetTestTable
        WidgetId (FK, UQ)
    

    哪种型号更好?使用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);
                }
             }
         }
    }
    
    4 回复  |  直到 15 年前
        1
  •  4
  •   nw.    15 年前

    我的方法是在映射中建模一对多关系,但将“多”约束到单个项。这允许可选的一对一,并且还保证在保存小部件时保存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;
                }
            }
        }
    }
    
        2
  •  1
  •   Graham    15 年前

    当您说“假设将WidgetTest字段添加到Widget是不合适的”时,您的意思是在域对象中还是在数据库中。如果您希望字段位于数据库中的同一个表中,那么将WidgetTest映射为Widget的一个组件如何?使NHibernate映射文件如下所示:

    <class name="Widget" table="Widget">
        ...
        <property name="WidgetProperty"/>
        ...
        <component name="WidgetTest" class="WidgetTest">
            <property name="WidgetTestProperty"/>
        </component>
    </class>
    

    给出表格结构:

    WidgetTable
        WidgetProperty
        WidgetTestProperty
    

        3
  •  0
  •   Community CDub    7 年前

    我还有两个想法 here

    • 将表和映射作为组件连接
        4
  •  0
  •   Community CDub    7 年前

    这个 answer given by nw. A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance ".

    如果您使用 inverse="true" cascade="all-delete-orphan" 在映射文件中。

    get 访问器被调用,并且不会对通过 set IList<WidgetTest> 创建对象时最初传入的引用,无法继续级联。

    所以为了解决这个问题,我们需要做点什么 IList<Widgetest> 引用,注意不要取消引用。

    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>
    

    增强的灵感:

    http://www.onkarjoshi.com/blog/188/hibernateexception-a-collection-with-cascade-all-delete-orphan-was-no-longer-referenced-by-the-owning-entity-instance/comment-page-1/