帮助使用NHibernate映射类对象列表

本文关键字:对象 列表 映射 NHibernate 帮助 | 更新日期: 2023-09-27 18:08:30

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

我之前把它作为illist的类型,它映射得很好。然而,我想通过PropertyGrid控件在这个列表中添加/删除/编辑项目的能力。

因此,我需要将List设置为从CollectionBase派生的Collection类型,并且包含一个ICustomTypeDescriptor,以便使其工作,而不是原始的illist。

我在徘徊,如果有人能告诉我如何,或者如果它将有可能映射这个列表使用这种方法,或者我如何可以修改我目前的方法来获得列表中的项目是可编辑的,通过PropertyGrid。

这是我的CollectionClass,我也想弄清楚如何使通用,所以我可以重用这个,但尽管如此,这里有两个类,我必须定义为了使对象列表可编辑通过PropertyGrid:

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我使用的是一个illist,但我不确定什么或如何我会这样做,现在区域列表不是一个illist,而是一个从CollectionBase派生的ZoneCollection类。

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

有什么建议吗?

帮助使用NHibernate映射类对象列表

回到这里,以防别人看到这个。

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

我偏离了使用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来说可不是一件小事。因此,我决定通过实现一个illist来尝试解决这个问题,其中Zone将是您在Collection中存储的对象类型。

事实证明,PropertyGrid控件不喜欢这样。它需要非泛型的IList显式实现,而不是泛型的。我以为我的实现没问题因为我在这里指定了一个特定的类型;然而,我错了。

所以我可以同时实现泛型和非泛型IList这就解决了我所有的问题。现在我可以用NHibernate映射集合,在PropertyGrid控件中显示属性,并通过CollectionEditor编辑集合,当点击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;
    }
}

特别感谢费罗让我走上了正确的道路,西蒙·莫里尔帮助我澄清了剩下的事情。希望将来有人能从中受益。

有一些选项

1:实现NH自定义集合

2:在自己的集合

中使用内部集合例如

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>