Справка по отображению списка объектов класса с использованием NHibernate - PullRequest
2 голосов
/ 14 сентября 2011

У меня есть класс, который содержит список объектов.

Ранее у меня был этот тип IList, который отображался нормально.Однако я хотел добавить возможность добавлять / удалять / редактировать элементы в этом списке с помощью элемента управления PropertyGrid.

Поэтому мне потребовалось установить список в качестве типа коллекции, производной от CollectionBase и содержащейICustomTypeDescriptor для того, чтобы это работало, а не исходный IList.

Я бродил, если кто-то может сказать мне, как, или если будет возможно сопоставить этот список, используя этот метод, или как я могу изменить свой текущийподход, чтобы элементы в списке были доступны для редактирования через 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
}

А вот мое оригинальное отображениесписка объектов класса 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>

I originсоюзник пошел с простой сумкой, потому что я использовал IList, но я не уверен, что и как я бы сделал это сейчас, поскольку список Zones - это не IList, а скорее класс ZoneCollection, производный от CollectionBase.

Я предполагаю, что это блекло, потому что у меня нет сопоставленного класса ZoneCollection, но я не знаю, как начать отображать этот б / с, весь класс, это список объектов зон.Должен ли я просто нанести на карту класс и иметь сумку внутри этого класса?

Есть предложения?

Ответы [ 2 ]

3 голосов
/ 16 сентября 2011

Возвращаясь к этому, если кто-то еще столкнется с этим.

Я действительно смог добиться нужной функциональности, но это заняло некоторую настройку.

Я отклонился от пути использования реализации 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 будет типом объекта, который вы храните в своей коллекции.

Как оказалось, элементу управления PropertyGrid это не нравится. Нужна необщая явная реализация IList, а не общая. Я думал, что со своей реализацией все будет в порядке, так как в этом случае я определял конкретный тип; однако я ошибся.

Получается, что я мог реализовать как общий, так и не общий IList, и это решило все мои проблемы. Теперь можно сопоставить коллекцию с помощью NHibernate, отобразить свойство в элементе управления PropertyGrid и отредактировать коллекцию с помощью CollectionEditor, который автоматически открывается при нажатии на коллекцию в элементе управления PropertyGrid. Вот моя реализация класса 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 голосов
/ 15 сентября 2011

есть несколько вариантов

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>
...