帮助使用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所有的类都有一个区域对象列表。我只需要映射类并在类中放置一个包吗?
有什么建议吗?
回到这里,以防别人看到这个。
我实际上能够实现我一直在寻找的功能,但它需要一些调整。
我偏离了使用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>