【问题标题】:Help mapping list of class objects using NHibernate帮助使用 NHibernate 映射类对象列表
【发布时间】:2011-09-16 05:58:46
【问题描述】:

我有一个包含对象列表的类。

我以前将此作为 IList 类型,它映射得很好。但是,我想通过 PropertyGrid 控件添加在此列表中添加/删除/编辑项目的功能。

因此,我需要将 List 设置为从 CollectionBase 派生的 Collection 类型,并且它包含 ICustomTypeDescriptor 才能使其正常工作,而不是原来的 IList。

如果有人能告诉我如何,或者是否可以使用此方法映射此列表,或者我如何修改我当前的方法以使列表中的项目可通过 PropertyGrid 编辑,我正在徘徊。

这是我的 CollectionClass,我还想弄清楚如何使其成为通用类,以便我可以重用它,但这里是我必须定义的两个类,以便通过属性网格:

public class ZoneCollection : CollectionBase, ICustomTypeDescriptor
{
    #region Collection Implementation

    /// <summary>
    /// Adds an zone object to the collection
    /// </summary>
    /// <param name="emp"></param>
    public void Add(Zone zone)
    {
        this.List.Add(zone);
    }

    /// <summary>
    /// Removes an zone object from the collection
    /// </summary>
    /// <param name="emp"></param>
    public void Remove(Zone zone)
    {
        this.List.Remove(zone);
    }

    /// <summary>
    /// Returns an employee object at index position.
    /// </summary>
    public Zone this[int index]
    {
        get
        {
            return (Zone)this.List[index];
        }
    }

    #endregion

    // Implementation of interface ICustomTypeDescriptor 
    #region ICustomTypeDescriptor impl

    public String GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public String GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }


    /// <summary>
    /// Called to get the properties of this type. Returns properties with certain
    /// attributes. this restriction is not implemented here.
    /// </summary>
    /// <param name="attributes"></param>
    /// <returns></returns>
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    /// <summary>
    /// Called to get the properties of this type.
    /// </summary>
    /// <returns></returns>
    public PropertyDescriptorCollection GetProperties()
    {
        // Create a collection object to hold property descriptors
        PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

        // Iterate the list of employees
        for (int i = 0; i < this.List.Count; i++)
        {
            // Create a property descriptor for the employee item and add to the property descriptor collection
            ZoneCollectionPropertyDescriptor pd = new ZoneCollectionPropertyDescriptor(this, i);
            pds.Add(pd);
        }
        // return the property descriptor collection
        return pds;
    }

    #endregion
}

/// <summary>
/// Summary description for CollectionPropertyDescriptor.
/// </summary>
public class ZoneCollectionPropertyDescriptor : PropertyDescriptor
{
    private ZoneCollection collection = null;
    private int index = -1;

    public ZoneCollectionPropertyDescriptor(ZoneCollection coll, int idx) :
        base("#" + idx.ToString(), null)
    {
        this.collection = coll;
        this.index = idx;
    }

    public override AttributeCollection Attributes
    {
        get
        {
            return new AttributeCollection(null);
        }
    }

    public override bool CanResetValue(object component)
    {
        return true;
    }

    public override Type ComponentType
    {
        get
        {
            return this.collection.GetType();
        }
    }

    public override string DisplayName
    {
        get
        {
            Zone zone = this.collection[index];
            return zone.ID.ToString();
        }
    }

    public override string Description
    {
        get
        {
            Zone zone = this.collection[index];
            StringBuilder sb = new StringBuilder();
            sb.Append(zone.ID.ToString());

            if ( zone.Streets.Route != String.Empty || zone.Streets.Crossing != String.Empty)
                sb.Append("::");
            if (zone.Streets.Route != String.Empty)
                sb.Append(zone.Streets.Route);
            if ( zone.Streets.Crossing != String.Empty)
            {
                sb.Append(" and ");
                sb.Append(zone.Streets.Crossing);
            }

            return sb.ToString();
        }
    }

    public override object GetValue(object component)
    {
        return this.collection[index];
    }

    public override bool IsReadOnly
    {
        get { return false; }
    }

    public override string Name
    {
        get { return "#" + index.ToString(); }
    }

    public override Type PropertyType
    {
        get { return this.collection[index].GetType(); }
    }

    public override void ResetValue(object component)
    {
    }

    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }

    public override void SetValue(object component, object value)
    {
        // this.collection[index] = value;
    }
}

这是我的 Zone 类:

[ComVisible(true)]
[TypeConverter(typeof(ExpandableObjectConverter))]
[CategoryAttribute("Configuration")]
[Serializable]
public class Zone
{
    #region Private Fields

    private bool active;
    private string dir;
    private Heading heading = new Heading();
    private int id;
    private int intID;
    private Position start = new Position();
    private Position finish = new Position();
    private int width;
    private Position[] corners = new Position[4];
    private Streets streets = new Streets();

    #endregion        

    #region Constructors

    public Zone() 
    {
        if (Program.main != null)
        {
            IntID = Program.main.intID;

            Intersection intersection = Program.data.Intersections.list.Find(
                delegate(Intersection tInt)
                {
                    return tInt.ID == IntID;
                }
            );

            if (intersection != null)
            {
                Streets.Crossing = intersection.Streets.Crossing;
                Streets.Route = intersection.Streets.Route;
            }
        }
    }

    #endregion

    #region Properties

    public virtual long PK { get; set; }

    [Browsable(false)]
    public virtual bool Active
    {
        get { return active; }
        set { active = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The direction for the Zone.")]
    public virtual string Dir
    {
        get { return dir; }
        set { dir = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The heading for the Zone.")]
    public virtual Heading Heading
    {
        get { return heading; }
        set { heading = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The Zone Identification Number.")]
    public virtual int ID
    {
        get { return id; }
        set { id = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The Identification Number associated with the Priority Detector of the Zone.")]
    public virtual int IntID
    {
        get { return intID; }
        set { intID = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The location of the Zone's Start.")]
    public virtual Position Start
    {
        get { return start; }
        set { start = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The location of the Zone's Finish.")]
    public virtual Position Finish
    {
        get { return finish; }
        set { finish = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The width of the Zone.")]
    public virtual int Width
    {
        get { return width; }
        set { width = value; }
    }

    [Browsable(false)]
    public virtual Position[] Corners
    {
        get { return corners; }
        set { corners = value; }
    }

    [CategoryAttribute("Configuration"),
        DescriptionAttribute("The streets associated with the Zone."),
        DisplayName("Zone Streets")]
    public virtual Streets Streets
    {
        get { return streets; }
        set { streets = value; }
    }

    #endregion
}

这是我对包含在我的 Intersection 类中的 Zone 类对象列表的原始映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`" lazy="false">
  <id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <column name="PK" />
    <generator class="identity" />
  </id>
  <many-to-one class="EMTRAC.Connections.Connection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="LocalConnection" lazy="false" cascade="all">
    <column name="LocalConnection_id" />
  </many-to-one>
  <many-to-one class="EMTRAC.Connections.Connection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Connection" lazy="false" cascade="all">
    <column name="Connection_id" />
  </many-to-one>
  <many-to-one class="EMTRAC.Packets.Packet, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Configuration" lazy="false" cascade="all">
    <column name="Configuration_id" />
  </many-to-one>
  <joined-subclass name="EMTRAC.Intersections.Intersection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" lazy="false">
    <key>
      <column name="Device_id" />
    </key>
    <bag name="Zones" cascade="all-delete-orphan">
      <key>
        <column name="Intersection_id" />
      </key>
      <one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </bag>
    <many-to-one class="EMTRAC.Intersections.Streets, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Streets" lazy="false" cascade="all">
      <column name="Streets_id" />
    </many-to-one>
    <many-to-one class="EMTRAC.Positions.Position, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Position" lazy="false" cascade="all">
      <column name="Position" />
    </many-to-one>
  </joined-subclass>
</class>

我最初带着一个简单的袋子 b/c 我使用的是 IList,但我不确定现在我会做什么或如何做到这一点,因为 Zones 列表不是 IList 而是派生自 CollectionBase 的 ZoneCollection 类.

我假设它正在呕吐 b/c 我没有映射出 ZoneCollection 类,但我不知道如何开始映射这个 b/c 所有类都有一个区域对象列表。我只是映射班级并在班级里面放一个包吗?

有什么建议吗?

【问题讨论】:

    标签: c# .net nhibernate nhibernate-mapping propertygrid


    【解决方案1】:

    回到这个以防其他人遇到这个。

    我实际上能够实现我正在寻找的功能,但需要进行一些调整。

    我偏离了使用 CustomCollection 实现的路线,转而使用 IList 的通用和非通用实现,以便能够将集合与 NHibernate 完全映射,并将集合显示为基础的属性可通过集合编辑器编辑的类。

    映射本身非常简单。我们简单地定义一个组件,将访问指定为一个属性。在组件中,我们定义了包名称,并将包的访问权限设置为一个字段。我还建议将级联选项设置为“all-delete-orphan”。最后,我们像往常一样用一个键声明这个包,然后是它包含的类。这是我的映射:

         <component name="Zones" access="property">
        <bag name="_list" cascade="all-delete-orphan" access="field" lazy="false">
          <key>
            <column name="Intersection_PK" />
          </key> 
          <one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
    

    现在,棘手的部分。属性网格实际上对它的期望非常挑剔。

    您可能知道也可能不知道,映射一个实现 CollectionBase 的类对于 NHibernate 来说不是一件小事。因此,我决定尝试通过实现 IList 来解决这种方法,其中 Zone 将是您存储在 Collection 中的对象类型。

    事实证明,PropertyGrid 控件不喜欢这样。它需要非通用的 IList 显式实现,而不是通用的。我认为我的实现会没问题,因为在这种情况下我指定了一个特定的类型;但是,我错了。

    事实证明,我可以同时实现通用和非通用 IList,这解决了我的所有问题。我现在可以使用 NHibernate 映射集合,在 PropertyGrid 控件中显示属性,并通过单击 PropertyGrid 控件中的集合时自动打开的 CollectionEditor 编辑集合。这是我对 Collection 类的实现:

        public class ZoneCollection : IList<Zone>, IList, ICustomTypeDescriptor
    {
        private IList<Zone> _list = new List<Zone>();
    
        //private IList _list = new ArrayList();
    
        public ZoneCollection()
        {
            //_list = new ArrayList();
        }
    
        public int IndexOf(Zone item)
        {
            return _list.IndexOf(item);
        }
    
        public void Insert(int index, Zone item)
        {
            _list.Insert(index, item);
        }
    
        public void RemoveAt(int index)
        {
            _list.RemoveAt(index);
        }
    
        public Zone this[int index]
        {
            get
            {
                return _list[index];
            }
            set
            {
                _list[index] = value;
            }
        }
    
        public void Add(Zone item)
        {
            _list.Add(item);
        }
    
        public void Clear()
        {
            _list.Clear();
        }
    
        public bool Contains(Zone item)
        {
            return _list.Contains(item);
        }
    
        public void CopyTo(Zone[] array, int arrayIndex)
        {
            _list.CopyTo(array, arrayIndex);
        }
    
        public int Count
        {
            get { return _list.Count; }
        }
    
        public bool IsReadOnly
        {
            get { return ((IList)_list).IsReadOnly; }
        }
    
        public bool Remove(Zone item)
        {
            return _list.Remove(item);
        }
    
        public IEnumerator<Zone> GetEnumerator()
        {
            return _list.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        int IList.Add(object value)
        {
            int index = Count;
            Add((Zone)value);
            return index;
        }
    
        bool IList.Contains(object value)
        {
            return Contains((Zone)value);
        }
    
        int IList.IndexOf(object value)
        {
            return IndexOf((Zone)value);
        }
    
        void IList.Insert(int index, object value)
        {
            Insert(index, (Zone)value);
        }
    
        bool IList.IsFixedSize
        {
            get { return ((IList)_list).IsFixedSize; }
        }
    
        bool IList.IsReadOnly
        {
            get { return ((IList)_list).IsReadOnly; }
        }
    
        void IList.Remove(object value)
        {
            Remove((Zone)value);
        }
    
        object IList.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                this[index] = (Zone)value;
            }
        }
    
        void ICollection.CopyTo(Array array, int index)
        {
            CopyTo((Zone[])array, index);
        }
    
        bool ICollection.IsSynchronized
        {
            get { return ((ICollection)_list).IsSynchronized; }
        }
    
        object ICollection.SyncRoot
        {
            get { return ((ICollection)_list).SyncRoot; }
        }
    
    
        // Implementation of interface ICustomTypeDescriptor 
        #region ICustomTypeDescriptor impl
    
        public String GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }
    
        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }
    
        public String GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }
    
        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }
    
        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }
    
        public PropertyDescriptor GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }
    
        public object GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }
    
        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }
    
        public EventDescriptorCollection GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }
    
        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }
    
    
        /// <summary>
        /// Called to get the properties of this type. Returns properties with certain
        /// attributes. this restriction is not implemented here.
        /// </summary>
        /// <param name="attributes"></param>
        /// <returns></returns>
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return GetProperties();
        }
    
        /// <summary>
        /// Called to get the properties of this type.
        /// </summary>
        /// <returns></returns>
        public PropertyDescriptorCollection GetProperties()
        {
            // Create a collection object to hold property descriptors
            PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
    
            // Iterate the list of zones
            for (int i = 0; i < this._list.Count; i++)
            {
                // Create a property descriptor for the zone item and add to the property descriptor collection
                ZoneCollectionPropertyDescriptor pd = new ZoneCollectionPropertyDescriptor(this, i);
                pds.Add(pd);
            }
            // return the property descriptor collection
            return pds;
        }
    
        #endregion
    }
    
    /// <summary>
    /// Summary description for CollectionPropertyDescriptor.
    /// </summary>
    public class ZoneCollectionPropertyDescriptor : PropertyDescriptor
    {
        private ZoneCollection collection = null;
        private int index = -1;
    
        public ZoneCollectionPropertyDescriptor(ZoneCollection coll, int idx) :
            base("#" + idx.ToString(), null)
        {
            this.collection = coll;
            this.index = idx;
    
        }
    
        public override AttributeCollection Attributes
        {
            get
            {
                return new AttributeCollection(null);
            }
        }
    
        public override bool CanResetValue(object component)
        {
            return true;
        }
    
        public override Type ComponentType
        {
            get
            {
                return this.collection.GetType();
            }
        }
    
        public override string DisplayName
        {
            get
            {
                Zone zone = (Zone)this.collection[index];
                return zone.ID.ToString();
            }
        }
    
        public override string Description
        {
            get
            {
                Zone zone = (Zone)this.collection[index];
                StringBuilder sb = new StringBuilder();
                sb.Append(zone.ID.ToString());
    
                if (zone.Streets.Route != String.Empty || zone.Streets.Crossing != String.Empty)
                    sb.Append("::");
                if (zone.Streets.Route != String.Empty)
                    sb.Append(zone.Streets.Route);
                if (zone.Streets.Crossing != String.Empty)
                {
                    sb.Append(" and ");
                    sb.Append(zone.Streets.Crossing);
                }
    
                return sb.ToString();
            }
        }
    
        public override object GetValue(object component)
        {
            return this.collection[index];
        }
    
        public override bool IsReadOnly
        {
            get { return false; }
        }
    
        public override string Name
        {
            get { return "#" + index.ToString(); }
        }
    
        public override Type PropertyType
        {
            get { return this.collection[index].GetType(); }
        }
    
        public override void ResetValue(object component)
        {
        }
    
        public override bool ShouldSerializeValue(object component)
        {
            return true;
        }
    
        public override void SetValue(object component, object value)
        {
            // this.collection[index] = value;
        }
    }
    

    特别感谢 Firo 让我走上了正确的道路,感谢 Simon Mourier 帮助我澄清了其余的问题。希望其他人将来可以从中受益。

    【讨论】:

      【解决方案2】:

      有一些选择

      1:implementing a NH custom collection

      2:use an inner collection in your own collection

      例如

      class ZoneCollection : IList<Zone>, ICustomTypeDescriptor
      {
          //  Must be defined as an IList and not a List for NHibernate to save correctly
          private IList<Zone> _inner;
      
          public ZoneCollection()
          {
              _inner = new List<Zone>();
          }
      
          public int IndexOf(Zone item)
          {
              return _inner.IndexOf(item);
          }
      
          // ...
      }
      
      <component name="Zones" access="nosetter.camelcase-underscore">
        <bag name="_inner" access="field" cascade="all-delete-orphan">
          <key>
            <column name="Intersection_id" />
          </key>
          <one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      

      【讨论】:

      • 这实际上很有帮助。我只有一个问题。在我的情况下,映射中组件的访问属性将使用什么?包名称是什么?
      • 我不知道如何使用您提到的映射自定义集合参考中提到的组件声明来映射它。我的 List 错误没有 getter/setter。由于我从 CollectionsBase 派生,因此属性 Zones 就是列表本身。如果我不声明组件而只声明包,当我尝试保存对象时,我会得到一个 {"Unable to cast object of type 'NHibernate.Collection.PersistentBag' to type 'EMTRAC.Collections.ZoneCollection'。"}例外。
      • 只有当你有一个你现在没有的内部 IList 时,组件才是可行的,请编辑我的答案
      • 好吧,这是有道理的。不要认为有一种方法可以从 CollectionsBase 派生吗?在这种情况下,我不确定如何从 CollectionsBase 获取列表。如果不是什么大不了的事,那只会让我不必为 IList 开发整个界面。
      • 有一种从 CollectionsBase 派生的方法(参见链接 1),但它也有额外的工作。此外,将IList&lt;Zone&gt; 作为属性也更好,因为您可以用其他东西来装饰您的列表,而 ZoneCollection 并不那么容易
      猜你喜欢
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 2011-10-26
      • 2021-10-28
      相关资源
      最近更新 更多