C# 2.0 实现简单的ORM工具

背景

06年8月份在公司用java实现了一套简单的orm工具,主要是针对jdk5.0提供的annotation特性,通过反射来实现对数据库操作的装箱、拆箱操作。

本来想放到blog上和大家共同share一下经验,后来测试jdk6.0的jdbc4.0特性时候发现jdbc4.0已经提供了类似类似orm的类库,只要db厂商实现某些接口,该厂商的bd-J-Driver就可以支持该特性了。目前主流的厂商还没有提供对jdbc4.0的支持,但是迟早要支持的,所以没有写这个部分。

后来改写成c#版本,和大家share一下。

可以把每个model类看作对应一个或者多个结构相同的表,因为有些应用我们要做分表操作。以下例子中以一个model类对应一个表为主。

核心思想是对类,以及对应的属性做上对应的标记,然后特定的工具通过对当前类的实例反射得到类上的标记,然后实现对数据库的相关操作。

c#里面提供了Attribue如果你对此不是很熟悉,请先看看相关资料有助于你对以下代码的理解。

我们先提供一个只对类起作用的Attribue,代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace Moqee.Db.Annotations
{
 
    /// <summary>
    /// 对类定义的属性
    /// 
    /// </summary>
 
    [AttributeUsage(AttributeTargets.Class,AllowMultiple=false,Inherited=false)]
    public class BeanAttribute:Attribute
    {
        //设置表名
        private string tabname = "";
 
        public string Tabname
        {
            get { return tabname; }
            set { tabname = value; }
        }
        //该类的所有字段是否自动映射
        private bool afm = true;
 
        public bool Afm
        {
            get { return afm; }
            set { afm = value; }
        }
 
        public BeanAttribute() { }
 
    }
}

BeanAttribute代码结束。
接下来实现一个对属性起作用的Attribute.

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Moqee.Db.Annotations
{
    /// <summary>
    /// 定义一个对类public属性描述的注解类
    /// </summary>
 
    [AttributeUsage(AttributeTargets.Property,AllowMultiple=true,Inherited=false)]
    public class PropertyAttribute:Attribute
    {
        /// <summary>
        /// 数据库表字段名称
        /// </summary>
        private string fieldName = "";
 
        public string FieldName
        {
            get { return fieldName; }
            set { fieldName = value; }
        }
       /// <summary>
       /// 对应的c#类型
       /// </summary>
        private Type fieldCSType = typeof(string);
 
        public Type FieldCSType
        {
            get { return fieldCSType; }
            set { fieldCSType = value; }
        }
        /// <summary>
        /// 是否是主键
        /// </summary>
        private bool isPrimary = false;
 
        public bool IsPrimary
        {
            get { return isPrimary; }
            set { isPrimary = value; }
        }
        /// <summary>
        /// 该方法是否映射
        /// </summary>
        private bool isMapping = true;
 
        public bool IsMapping
        {
            get { return isMapping; }
            set { isMapping = value; }
        }
 
        private System.Data.DbType fieldDbType;
 
        public System.Data.DbType FieldDbType
        {
            get { return fieldDbType; }
            set { fieldDbType = value; }
        }
 
    }
}

代码完毕。

下面有一个测试model类。

using System;
using System.Collections.Generic;
using System.Text;
using Moqee.Db.Annotations;
using Moqee.Common;
 
namespace Moqee.Book.Business
{
    [Bean(Tabname="T_CATALOG",Afm=true)]
   public class Catalog
    {
 
       private long guid;
       private int status = BookConstants.OPEN_STATUS;
       private int priority = 0;
        [Property(FieldName="priority",FieldCSType=typeof(Int32))]
       public int Priority
       {
           get { return priority; }
           set { priority = value; }
       }
        [Property(FieldName="status",FieldCSType=typeof(Int32))]
       public int Status
       {
           get { return status; }
           set { status = value; }
       }
        [Property(FieldCSType=typeof(Int64),FieldName="guid",IsPrimary=true)]
        public long Id
        {
            get { return guid; }
            set { guid = value; }
        }
        private string name;
        [Property(FieldName="name",FieldCSType=typeof(string))]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }
}

下面看如何解析这个类以得到里面的数据类型

private void ParserClassTag(Type type)
        {
            object[] atts = type.GetCustomAttributes(typeof(BeanAttribute), false);
            if (atts == null||atts.Length==0) return null;
            BeanAttribute pa = (BeanAttribute)atts[0];
 
            PropertyInfo[] props = type.GetProperties();
            if (props == null) return null;
 
            //此处可以得到props里面的所有属性
            foreach (PropertyInfo pi in props)
            {
                atts = pi.GetCustomAttributes(typeof(PropertyAttribute), false);
                if (atts == null || atts.Length == 0) continue;
                PropertyAttribute patt = (PropertyAttribute)atts[0];
                ///此处可以得到里面的所有属性,
            }
 
        }

经过上面的方法,就可以得到所定义的说有属性值,可以得到表名,字段信息,insert,update等语句都可以预先得到。

如果你的应用是mysql,并且主键是自增类型,完全可以定义bean里面的主键小于0为insert否则为update。

可以定义一个 DAO接口实现一些方法。

void save(object obj) throws Exception;
void delete(object obj) throws Exception;
void find(object obj) throws Exception;
List<E> query(object obj) throws Exception;

…….等等方法。

可以定义一个cache工具来实现对对类属性的缓存,不用每次都动态解析属性。

java版本的思路和上面非常类似,有兴趣你可以自己实现一下。