• 接口式实体定义之——自定义实体属性+实体多根继承


    本文介绍NBear的接口式实体定义方式下的自定义实体属性和实体多根继承。本文中的介绍的内容及对应的代码同样包含于最新版本的NBear及其用户手册中。

    从SF.Net下载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属性实例中的值了)。

    --

    实体多根继承

    NBear中的Entity因为是以接口形式定义的,所以,它可以很方便的支持实体继承关系,甚至支持多根继承。也就是从2个以上的基类(接口)继承。 

    NBear支持两种类型的非常自然的继承关系映射:单表继承体系方式一实体一表方式。 

    让我们先看第一个例子: 

           [Table("AllInOneTable")]
           public interface Parent : IEntity
           {
               [PrimaryKey]
               int ID { get; }
               string Name { get; set; }
           }
     
           [Table("AllInOneTable")]
           public interface AnotherParent : IEntity
           {
               [PrimaryKey]
               int ID { get; }
               int Age { get; set; }
           }
     
           [Table("AllInOneTable")]
           public interface Child : Parent, AnotherParent
           {
               [PrimaryKey]
               new int ID { get; set; }
               DateTime Birthday { get; set; }
           }
     
    我们可以看到,在上例中,我们定义了两个基实体Parent和AnotherParent,Child实体同时从两个基类继承。注意,代码中加粗的行,如果多个不同的基接口包含相同名称的属性,代码会编译失败,此时,需要像这样使用new关键字来避免编译失败。 

    再注意,在这个例子中,我们和数据库表是如何对应的呢? 

    这里,我们采用的是,单表继承体系方式。也就是说,用一张单独的表,存储整个继承体系的类。注意每个实体都映射到AllInOneTable这个表。 

    那么,是不是只能使用单表继承体系方式来映射继承关系的实体呢?当然不是(见是比较推荐,因为,该方案相对比较方便灵活)。 

    我们也可以采用一实体一表方式。代码如下: 

           [Table("ParentTable")]
           public interface Parent : IEntity
           {
               [PrimaryKey]
               int ID { get; }
               string Name { get; set; }
           }
     
           [Table("AnotherParentTable")]
           public interface AnotherParent : IEntity
           {
               [PrimaryKey]
               int ID { get; }
               int Age { get; set; }
           }
     
           [Table("ChildTable")]
           public interface Child : Parent, AnotherParent
           {
               [PrimaryKey]
               new int ID { get; set; }
               DateTime Birthday { get; set; }
           }
     
    这种方案下,每个实体对应一张表,每张表包含冗余的父类的数据。

  • 相关阅读:
    maven 构建 war文件&&Glassfish运行+部署war文件+访问(命令行模式)
    配置Glassfish服务器、部署Java web项目、Maven安装配置及JDK版本匹配性问题
    配置apache-maven-3.6.0时所遇到的坑(二)
    配置apache-maven-3.6.0时所遇到的坑(一)
    对Functional Language的认识
    论Injection的前世今生
    简单实现美团手机版应用源码
    WindowsPhone8可缩放图片控件的实现
    MallBuilder 多用户商城管理系统 v5.8.1.1
    Windows Phone 带文本信息的进度条
  • 原文地址:https://www.cnblogs.com/teddyma/p/472393.html
Copyright © 2020-2023  润新知