天天看點

ORM映射架構總結--實體分析器

1.      

什麼是資料分析器

前面一篇文章講到過資料分析器,什麼是資料分析器。其實很容易了解,就是對資料進行分析采集的一個工具,說白了就是一個小程式,在本ORM架構中對實體對象進行必要的資料分析,獲得實體對象的各種資訊緩存,以便在後續的工作中直接提取資料。這個是相對去年寫的那個ORM有所改進的,在緩存實體資訊的時候,在一定程度上可以提高該架構的性能

2.      

實體分析器的原理簡單介紹

簡單的UML圖:

ORM映射架構總結--實體分析器

圖總是能給人最直覺的感覺。從上面的圖我們可以看出,這個分析器分為了幾個過程:

(1)      

讀取實體資訊

以上實體都是通過上篇介紹過的特性來描述的,他們定義了資料庫和實體之間關系的代理橋梁。讀取該些實體的資訊,包括實體的描述的四個特性,實體的定義的變量,實體的屬性等資訊。

(2)      

緩存實體資訊

此個步驟是緊接上面一步的,可以看出将實體資訊分析之後将資訊緩存。多個實體資訊,我們采用鍵值方式來存儲。而這個緩存也是一個全局的緩存,因為這些資訊在全局程式中共享,而非某個子產品獨享

(3)      

使用資訊

為什麼讀取緩存這些資訊,必然是它有可用之處。資料讀取肯定是用來使用的。這當然是廢話。某個子產品需要實體資訊資料,就不必要去每次在分析這些資料,直接從緩存中讀取

3.      

實體的定義

在這個過程中我們定義資料庫描述的代理實體是有特定規律的,而不是我們随随便便定義的一個實體對象。因為這些實體有着特殊的含義,是以相當于普通的實體來說這些還是有着重大意義的。

ORM映射架構總結--實體分析器

從上圖可以看出,在這個ORM中定義了一個公共接口IEntity,該接口有一個實作類BaseEntity。沒有定義任何方法,IEntity但是繼承了另外一個接口IDisposable。看到這大家應該都知道為什麼這樣定義了。而BaseEntity實作了接口IEntity并實作了Dispose()方法。

 IEntity源碼

ORM映射架構總結--實體分析器
ORM映射架構總結--實體分析器

代碼

 1 /**

 2  * 2010-1-28

 3  * 

 4  * 情 緣

 5  * 

 6  * 所有實體模型的父類接口。

 7  * 

 8  * 該接口通過繼承接口 IDisposable 可以實作自動實作

 9  * 垃圾回收的效果。實體的調用Dispose() 方法來手動

10  * 釋放占用的記憶體,同時也可以使用using 關鍵字來控

11  * 制實體的作用域範圍,并及時釋放記憶體

12  * 

13  * */

14 

15 using System;

16 

17 namespace CommonData.Entity

18 {

19 

20     /// <summary>

21     /// 公共實體基類的公共接口,用于釋放對象配置設定空間

22     /// </summary>

23     public interface IEntity:IDisposable

24     {

25     }

26 }

27 

BaseEntity 源碼

ORM映射架構總結--實體分析器

/**

 * 2010-1-28

 * 

 * 情 緣

 * 這個實體類是所有實體模型類的父類,該實體類實作了

 * IEntity 接口,實作了手動釋放記憶體方法。該實體類使

 * 用特性 Serializable修飾,說明該實體類要被序列化。

 * 而實體類使用abstract 關鍵字修飾,說明該類的執行個體

 * 不能通過自身來執行個體化,必須通過該實體類的子類來實

 * 現。使用該種模式,保證了設計在總體上的結構性

 * */

using System;

namespace CommonData.Entity

{

    [Serializable]

    public abstract class BaseEntity:IEntity

    {

        /// <summary>

        /// 隐式實作接口方法,用于釋放記憶體

        /// </summary>

        public void Dispose()

        {

            GC.SuppressFinalize(this);

        }

    }

}

普通實體源碼

ORM映射架構總結--實體分析器
ORM映射架構總結--實體分析器

using System.Collections.Generic;

using System.Linq;

using System.Text;

using CommonData.Entity;

using CommonData.Model.Core;

namespace Office.Entity

    [TableAttribute(DBName = "OA_DB", Name = "TabUser", PrimaryKeyName = "Id", IsInternal = false)]

    public class TabUser : BaseEntity

        public TabUser()

        private int id;

        [ColumnAttribute(Name = "Id", IsPrimaryKey = true, AutoIncrement = true, DataType = DataType.Int, CanNull = false)]

        public int Id

            get { return id; }

            set { id = value; }

        private string userName;

        [ColumnAttribute(Name = "UserName", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]

        public string UserName

            get { return userName; }

            set { userName = value; }

        private string passWord;

        [ColumnAttribute(Name = "PassWord", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = false)]

        public string PassWord

            get { return passWord; }

            set { passWord = value; }

        private int sex;

        [ColumnAttribute(Name = "Sex", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Int, CanNull = false)]

        public int Sex

            get { return sex; }

            set { sex = value; }

        private DateTime birthday;

        [ColumnAttribute(Name = "Birthday", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Datetime, CanNull = false)]

        public DateTime Birthday

            get { return birthday; }

            set { birthday = value; }

        private string cardID;

        [ColumnAttribute(Name = "CardID", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Varchar, CanNull = true)]

        public string CardID

            get { return cardID; }

            set { cardID = value; }

        private int age;

        [ColumnAttribute(Name = "Age", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Int, CanNull = true)]

        public int Age

            get { return age; }

            set { age = value; }

        private string address;

        [ColumnAttribute(Name = "Address", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Address

            get { return address; }

            set { address = value; }

        private int isMarried;

        [ColumnAttribute(Name = "IsMarried", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Int, CanNull = true)]

        public int IsMarried

            get { return isMarried; }

            set { isMarried = value; }

        private int roleId;

        [ColumnAttribute(Name = "RoleId", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Int, CanNull = false)]

        public int RoleId

            get { return roleId; }

            set { roleId = value; }

        private int iSFobid;

        [ColumnAttribute(Name = "ISFobid", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Int, CanNull = false)]

        public int ISFobid

            get { return iSFobid; }

            set { iSFobid = value; }

        private string descript;

        [ColumnAttribute(Name = "Descript", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Descript

            get { return descript; }

            set { descript = value; }

        private string remark;

        [ColumnAttribute(Name = "Remark", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Remark

            get { return remark; }

            set { remark = value; }

        private string ext1;

        [ColumnAttribute(Name = "Ext1", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Ext1

            get { return ext1; }

            set { ext1 = value; }

        private string ext2;

        [ColumnAttribute(Name = "Ext2", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Ext2

            get { return ext2; }

            set { ext2 = value; }

        private string ext3;

        [ColumnAttribute(Name = "Ext3", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Ext3

            get { return ext3; }

            set { ext3 = value; }

        private string ext4;

        [ColumnAttribute(Name = "Ext4", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Ext4

            get { return ext4; }

            set { ext4 = value; }

        private string ext5;

        [ColumnAttribute(Name = "Ext5", IsPrimaryKey = false, AutoIncrement = false, DataType = DataType.Nvarchar, CanNull = true)]

        public string Ext5

            get { return ext5; }

            set { ext5 = value; }

4.      

實體資訊的讀取

上面說到過實體資訊的讀取其實方法很簡單,就是使用反射機制去讀取實作屬性方法,特性等資訊。反射在于很多程式設計人員來說都是一件非常具有挑戰性而且神秘的工作,有時候聽到這兩個字就望而生畏,其實不然,隻要掌握了規律,了解了原理就可以了。無論是C#還是Java 原理都是一樣的。但是反射是作為一個程式高手通往高境界的必經之路。是以掌握反射來說是對程式設計發展是很有好處的。

下面是實體資訊讀取源碼抽象類

ORM映射架構總結--實體分析器
ORM映射架構總結--實體分析器

 * 2010-2-1

 * 該類是一個抽象類,定義了一些獲得表實體資訊的方法。

 * 該類定義為抽象類,其所有的方法也是抽象方法。也就

 * 是說該類的所有方法都需要由其子類去實作。

using System.Reflection;

    public abstract class BaseEntityAbstract

        /// 根據實體的類型獲得實體表資訊

        /// <param name="type">實體類類型</param>

        /// <returns></returns>

        public abstract TableInfo GetTableInfo(Type type);

        /// 根據實體類的公共接口獲得實體表資訊

        /// <param name="entity">實體公共接口</param>

        public abstract TableInfo GetTableInfo(IEntity entity);

        /// 根據實體泛型類型獲得實體表資訊

        /// <typeparam name="T">泛型類型</typeparam>

        public abstract TableInfo GetTableInfo<T>() where T:IEntity;

        /// 根據實體的類型獲得實體表列資訊

        public abstract ColumnAttribute[] GetColumnAttribute(Type type);

        /// 根據實體類的公共接口獲得實體表列資訊

        public abstract ColumnAttribute[] GetColumnAttribute(IEntity entity);

        /// 根據實體泛型類型獲得實體表列資訊

        public abstract ColumnAttribute[] GetColumnAttribute<T>() where T : IEntity;

        /// 根據實體的類型獲得實體字段資訊

        public abstract FieldInfo[] GetFieldInfo(Type type);

        /// 根據實體類的公共接口獲得實體字段資訊

        public abstract FieldInfo[] GetFieldInfo(IEntity entity);

        /// 根據實體泛型類型獲得實體字段資訊

        public abstract FieldInfo[] GetFieldInfo<T>() where T : IEntity;

        /// 根據實體的類型獲得實體屬性資訊

        public abstract PropertyInfo[] GetPropertyInfo(Type type);

        /// 根據實體類的公共接口獲得實體屬性資訊

        public abstract PropertyInfo[] GetPropertyInfo(IEntity entity);

        /// 根據實體泛型類型獲得實體屬性資訊

        public abstract PropertyInfo[] GetPropertyInfo<T>() where T:IEntity;

        /// 根據實體的類型獲得實體字段屬性資訊

        public abstract LinkTableAttribute[] GetLinkTableAttribute(Type type);

        /// 根據實體類的公共接口獲得實體字段屬性資訊

        public abstract LinkTableAttribute[] GetLinkTableAttribute(IEntity entity);

        /// 根據實體泛型類型獲得實體字段屬性資訊

        public abstract LinkTableAttribute[] GetLinkTableAttribute<T>() where T:IEntity;

        /// 根據實體的類型獲得實體字段集合屬性資訊

        public abstract LinkTablesAttribute[] GetLinkTablesAttribute(Type type);

        /// 根據實體類的公共接口獲得實體字段集合屬性資訊

        public abstract LinkTablesAttribute[] GetLinkTablesAttribute(IEntity entity);

        public abstract LinkTablesAttribute[] GetLinkTablesAttribute<T>() where T:IEntity;

該抽象類定義了各種方式獲得實體性的方式。而且有很多都是使用的重載方式,這樣更能展現程式結構的靈活性,健壯性。這個過程中不乏使用了泛型,以及泛型限制,使用泛型限制很大程度上使得這個程式的針對的工作性質比較強,将實體分析限制到了某個特定的範圍之内,這樣可以省去很多不必要的麻煩

獲得實體的資訊代碼

ORM映射架構總結--實體分析器

/// <summary>

        public override TableInfo GetTableInfo(Type type)

            TableInfo tableInfo = EntityTypeCache.GetTableInfo(type);

            if (tableInfo == null)

            {

                tableInfo = new TableInfo();

                TableAttribute[] tableAttribute = type.GetCustomAttributes(typeof(TableAttribute),false) as TableAttribute[];

                PropertyInfo[] properties = type.GetProperties();

                foreach (PropertyInfo property in properties)

                {

                    tableInfo.DicProperties.Add(property.Name,property);

                    if (property.GetCustomAttributes(typeof(ColumnAttribute), false).Length == LENGTH)

                    {

                        tableInfo.DicColumns.Add(property.Name, property.GetCustomAttributes(typeof(ColumnAttribute), false)[0] as ColumnAttribute);

                    }

                    if (property.GetCustomAttributes(typeof(LinkTableAttribute), false).Length == LENGTH)

                        tableInfo.DicLinkTable.Add(property.Name,property.GetCustomAttributes(typeof(LinkTableAttribute),false)[0] as LinkTableAttribute);

                    if (property.GetCustomAttributes(typeof(LinkTablesAttribute), false).Length == LENGTH)

                        tableInfo.DicLinkTables.Add(property.Name, property.GetCustomAttributes(typeof(LinkTablesAttribute), false)[0] as LinkTablesAttribute);

                }

                FieldInfo[] fields = type.GetFields();

                foreach (FieldInfo field in fields)

                    tableInfo.DicFields.Add(field.Name,field);

                if (tableAttribute.Length == LENGTH)

                    tableInfo.Table = tableAttribute[0];

                else

                    throw new Exception("一個實體類上不能有相同的特性");

                tableInfo.Columns = tableInfo.DicColumns.Values.ToArray();

                tableInfo.Fields = tableInfo.DicFields.Values.ToArray();

                tableInfo.LinkTable = tableInfo.DicLinkTable.Values.ToArray();

                tableInfo.LinkTables = tableInfo.DicLinkTables.Values.ToArray();

                tableInfo.Properties = tableInfo.DicProperties.Values.ToArray();

                EntityTypeCache.InsertTableInfo(type,tableInfo);

            }

            return tableInfo;

5.      

緩存機制

其實這個這個緩存機制很簡單的,我們經常想到的緩存機制就是Session ,Application,ViewState 等對象。這些都是web程式中的緩存對象,但是如果這些運用在控制台程式或者WinForm程式中呢,這些對象顯然作用不大。于是我們需要一個能夠公用的緩存機制。

有些人一直使用static 類,static 方法,他們喜歡對這種方式百用不怠,因為他們感覺static 就是爽。深入的想一下,為什麼要這樣使用,因為這些東西是在程式加載的時候就緩存起來了。于是我們可以在這個方面下功夫來實作我們的緩存

定義的緩存類:

ORM映射架構總結--實體分析器
ORM映射架構總結--實體分析器

 * 2010-1-30

 * 該類建構了一個記憶體緩存器,這個緩存器緩存了資料庫

 * 對應實體類的特性資訊,字段和屬性。這些資訊在程式

 * 加載或第一次使用的時候緩存到這個存儲其中來。以後

 * 再吃讀取這些資訊的時候不需要重新去加載這些資訊,

 * 直接從記憶體中讀取即可

    public static class EntityTypeCache

        //表實體資訊存儲器,用于存儲表實體資訊

        private static IDictionary<Type, TableInfo> cache = null;

        /// 靜态構造方法

        /// 使用靜态構造方法,確定該構造方法隻執行一次,不會還原初始化值

        /// 因為資料不會丢失,而且是一直儲存在記憶體中,這樣就達到了一個

        /// 臨時存儲器的功能

        static EntityTypeCache()

            cache = new Dictionary<Type, TableInfo>();

        /// 将表實體資訊緩存到臨時存儲器中

        /// <param name="type">實體的類型</param>

        /// <param name="tableInfo">表實體資訊</param>

        public static void InsertTableInfo(Type type,TableInfo tableInfo)

            if (cache.ContainsKey(type))

                return;

            else 

                cache.Add(type,tableInfo);

        public static void InsertTableInfo(IEntity entity, TableInfo tableInfo)

            Type type = entity.GetType();

            InsertTableInfo(type,tableInfo);

        /// 根據實體類的類型獲得表特性資訊

        /// <param name="type">實體類的類型</param>

        public static TableInfo GetTableInfo(Type type)

                return cache[type];

            else

                return null;

        /// 根據實體公共接口獲得表特性資訊

        public static TableInfo GetTableInfo(IEntity entity)

            return GetTableInfo(type);

        /// 根據泛型類型獲得表實體資訊

        /// <typeparam name="T">泛型類</typeparam>

        public static TableInfo GetTableInfo<T>() where T:IEntity

            Type type = typeof(T);

使用靜态構造方法,確定該構造方法隻執行一次,不會還原初始化值,因為資料不會丢失,而且是一直儲存在記憶體中,這樣就達到了一個臨時存儲器的功能。

這裡和上面讀取實體資訊是一起使用的,每次讀取實體資訊的時候就從緩存中讀取,如果緩存中沒有這些資料,就重新使用實體分析器去讀取這些資料

6.      

測試分析

(1).資料讀取分析

 測試代碼

ORM映射架構總結--實體分析器

BaseEntityAbstract cache = new BaseEntityHelper();

cache.GetTableInfo(typeof(TabUser));

foreach (PropertyInfo fi in cache.GetTableInfo(typeof(TabUser)).Properties)

                    Console.WriteLine(fi.Name);

 測試結果:

ORM映射架構總結--實體分析器

從上面的代碼中可以看到從緩存中擷取了實體的相應資訊。 cache.GetTableInfo(typeof(TabUser)) 這句代碼是有特定資訊的,我們可以使用這個方法來在程式啟動的時候注冊所有實體的資訊

     (2).緩存性能分析

測試代碼

ORM映射架構總結--實體分析器

static void Main(string[] args)

            BaseEntityAbstract cache = new BaseEntityHelper();

            cache.GetTableInfo(typeof(TabUser));

            Console.WriteLine();

            Console.WriteLine("BeginTime: " + DateTime.Now.Millisecond);

            for (int i = 0; i < 10000; i++)

                foreach (PropertyInfo fi in typeof(TabUser).GetProperties())

                    //Console.WriteLine(fi.Name);

                foreach (PropertyInfo fi in cache.GetTableInfo(typeof(TabUser)).Properties)

以上我們分别用反射擷取實體資訊,和在緩存中擷取實體資訊,然後分别循環10000次。我們看看他們的所用的時間比較

ORM映射架構總結--實體分析器

 看看上面的圖,我們就之後了性能的高低,這裡說明一個道理這個緩存器起到了作用。這也達到了先前的目的。

 (注: ORM涉及内容比較多,後續期待,有興趣的可以與本人探讨)