【IT168技术文档】
本文介绍NBear的接口式实体定义方式下的自定义实体属性和实体多根继承。本文中的介绍的内容及对应的代码同样包含于最新版本的NBear及其用户手册中。
最新版本的NBear中除了本文中提到的两个功能之外,还包括如下内容:
1)支持EntityFactory.CreateObject和CreateObjectList现在支持基于DataSet或IDataReader中的字段名称而不仅仅是原来的基于字段顺序的数据填充了;
2)Gateway.Save和Insert方法现在支持自动返回新插入的纪录的自增长ID字段了(当然,前提是,这个实体对应的表确实使用自增长主键字段)。
自定义实体属性
什么是CustomProperty呢?CustomProperty是一种可以为Entity添加的,不映射到数据表字段的,只读的,用于解析Clob或Blog属性的,自定义类型的属性。
简单的说,如果你的实体包含Clob或Blob大字段,而又想方便的直接读取大字段真正代表的内容,就可以给Entity定义CustomProperty,来封装对大字段内容的访问。
让我们用一个实例来说明:
假设有这样一个Entity:
public interface EntityWithCustomProperty : IEntity { [PrimaryKey] int ID { get; } string Name { get; set; } string XmlServerConfig { get; set; } string XmlContactConfig { get; set; } [CustomProperty("XmlServerConfig", "XmlContactConfig")] SampleCustomPropertyType SampleProperty { get; } }
我们这个Entity除了一些标准的映射到数据表字段的属性(ID,Name,XmlServerConfig和XmlContactConfig)之外,还包含了一个SampleProperty属性。
首先,这个属性是只读的(只有get,注意,必须设为只读,否则,运行时使用该Entity会报错),它的返回类型是一个自定义类型SampleCustomProperty,它的定义如下:
[Serializable] public class SampleServerConfig { public string ServerAddress; public int ServerPort; } [Serializable] public class SampleContactConfig { public string WorkPhone; public string HomePhone; } public class SampleCustomPropertyType : CustomPropertyType { public SampleServerConfig ServerConfig { get { return SerializeHelper.Deserialize<SampleServerConfig>(typeof(SampleServerConfig), (string)values[0]); } } public SampleContactConfig ContactConfig { get { return SerializeHelper.Deserialize<SampleContactConfig>(typeof(SampleContactConfig), (string)values[1]); } } public SampleCustomPropertyType(object[] inputValues) : base(inputValues) { } }
请注意加粗的代码,这个SampleCustomProperty类是一个定义于任意位置的自定义类,对该类的唯一约束是必须从基类NBear.Common.CustomPropertyType继承。因为基类包含一个protected的构造函数,所以,实现类必须包含同样列表的构造函数,并调用基类构造函数。
这个类的内容很简单,它包含两个属性,分别也是两个自定义的config类。values[0]和values[1]分别是这两个config类的XML序列化文本。在这两个属性的get实现中,
只是简单地进行反序列化。
values是哪儿来的呢?它是定义于基类CustomPropertyType的一个protected字段。包含了构造函数传入的inputValues参数。
inputValues的值又是哪来的呢?换句话说,既然Entity只是一个接口,谁负责实例化Entity的SampleProperty属性,并传入SampleCustomProperty类的构造函函数需要的inputValues参数呢?
我们注意到,EntityWithCustomProperty.SampleProperty属性包含一个CustomPropertyAttribute修饰,它的参数XmlServerConfig和XmlContactConfig表示,名为这两个名称的属性的值,将会被Entity的实现类用来构造成一个obejct[]数组,传递给SampleCustomPropertyType的构造函数。
注意,NBear.Common.CustomPropertyAttribute的构造函数接受一个params string[]类型的参数,可以是任意数量的Property的名称。
那么,这样一个SampleProperty属性,到底有什么用呢?
让我们来看看测试代码:
[TestMethod] public void TestCustomPropertyMethod() { //provided the entity obj's value is read from database EntityWithCustomProperty obj = EntityFactory<EntityWithCustomProperty>.CreateObject(); SampleServerConfig sc = new SampleServerConfig(); sc.ServerAddress = "127.0.0.1"; sc.ServerPort = 8888; obj.XmlServerConfig = SerializeHelper.Serialize(sc); SampleContactConfig cc = new SampleContactConfig(); cc.WorkPhone = "110"; cc.HomePhone = "119"; obj.XmlContactConfig = SerializeHelper.Serialize(cc); //now we can use Custom Property like following Assert.AreEqual(obj.SampleProperty.ServerConfig.ServerAddress, "127.0.0.1"); Assert.AreEqual(obj.SampleProperty.ServerConfig.ServerPort, 8888); Assert.AreEqual(obj.SampleProperty.ContactConfig.WorkPhone, "110"); Assert.AreEqual(obj.SampleProperty.ContactConfig.HomePhone, "119"); }
在这段测试代码中,我们先构造了一个obj对象,它是一个EntityWithCustomProperty实体的实例。我们假设,这个实例的数据是从数据库中读取的,ID,Name,XmlServerConfig和XmlContactConfig属性分别对应于数据表中的字段值。XmlServerConfig和XmlContactConfig的值,分别是两组XML序列化文本。
很显然,如果我们直接读取XmlServerConfig和XmlContactConfig属性,因为他们是XML,并不易于使用(XML也许还好,如果它是一组二进制压缩数据呢?)。但是如果我们调用SampleProperty属性,我们就能很方便的读取XML中的真实内容(XML被自动反序列化为SampleProperty属性实例中的值了)。