惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
IT之家
IT之家
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
博客园 - 【当耐特】
The Cloudflare Blog
宝玉的分享
宝玉的分享
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Visual Studio Blog
小众软件
小众软件
博客园_首页
Last Week in AI
Last Week in AI
J
Java Code Geeks
V
V2EX
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
N
Netflix TechBlog - Medium
F
Full Disclosure
B
Blog
H
Help Net Security
C
Check Point Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog
P
Proofpoint News Feed
D
Docker
Microsoft Security Blog
Microsoft Security Blog

博客园 - sobaby

Sql Server的两个小技巧 用vue.js重构订单计算页面 用vue.js实现购物车功能 一步步实现自己的ORM(五) 将Object转换成Dictionary方法 一步步实现自己的ORM(三) 一步步实现自己的ORM(二) 一步步实现自己的ORM(一) 修复Windows XP服务扩展视图显示空白 ASP.NET MVC :MVC页面验证与授权 一起学习MVC(4)-entity SQL语句 一起学习MVC(3)-搭建项目 一起学习MVC(2)-数据库设计 一起学习MVC(1)-搭建开发环境 什么是工作流 学习WWF中 关于回调函数 ASP.net:水晶报表的5种表格设计模式 ASP.NET 安全认证(一):如何运用 Form 表单认证
一步步实现自己的ORM(四)
sobaby · 2015-03-30 · via 博客园 - sobaby

通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化。

具体流程如下:

我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping,把表名、字段名信息缓存起来,EntityMapping 表示实体类信息对应数据库中的table,MemberMapping表示实体类的属性对应数据库中的Column。

EntityMapping代码:

    class EntityMapping
    {
        /// <summary>
        /// 对应实体类类型
        /// </summary>
        public Type EntityType { get; internal set; }

        /// <summary>
        /// 表名
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// 实体属性
        /// </summary>
        public List<MemberMapping> Members;

        /// <summary>
        /// 主键
        /// </summary>
        public List<MemberMapping> PrimaryKey { get; set; }
       
    }

View Code

MemberMapping代码:

    class MemberMapping
    {
        /// <summary>
        /// 列名
        /// </summary>
        public string ColumnName { get; internal set; }

        /// <summary>
        /// 属性名
        /// </summary>
        public PropertyInfo Member { get; set; }

        /// <summary>
        /// 是否为主键
        /// </summary>
        public bool IsPrimaryKey { get; set; }

        /// <summary>
        /// 是否为数据库自动生成
        /// </summary>
        public bool IsDbGenerated { get; set; }
    }

View Code

有Mapping后,我们现在要做的是把实体类里的信息存到Mapping类中,我在这里定义了一个AttributeMapping,里面方法如下:

    class AttributeMapping
    {
        private static Dictionary<Type, EntityMapping> entityMappings = new Dictionary<Type, EntityMapping>();

        public static EntityMapping Get<T>()

        public static EntityMapping Get(Type type)

        private static EntityMapping CreateMapping(Type type)
    }

其中Get方法就是根据实体类返回Mapping信息,而CreateMapping则是创建Mapping,具体代码见附件下载。

Mapping信息有了,我们需要做的是把重构之前的EntityHelper类,而这个类里有大量的链接数据库、操作数据库代码,我们把这些信息抽离到一个单独类,就好比我们常用的DbHelper类,而这个类我命名为DbProvider。

    class DbProvider
    {
        private IDbConnection conntion;
        public DbProvider(IDbConnection conntion)
        public virtual int ExecuteNonQuery(string sql, Dictionary<string, object> parameters)
        public virtual IDataReader ExecuteReader(string sql, Dictionary<string, object> parameters)
    }

EntityHelper类还有大量的SQL生成语句,我们为了简化EntityHelper类把SQL生成的代码放到单独的DbSqlBuilder类,为啥要这么做呢?除了简化代码外,还有就是为了适应不同的Db。

    class DbSqlBuilder
    {
        public string BuildInsertSql(EntityMapping entityMapping, List<string> columnNames)

        public string BuildUpdateSql(EntityMapping entityMapping, List<string> updateColumns,List<string> whereColumns)

        public string BuildDeleteSql(EntityMapping entityMapping, List<string> whereColumns)

     public string BuildSelectSql(EntityMapping entityMapping,string strWhere, string orderBy)
    }


为什么参数里要传递List<string>呢?因为我们拼SQL语句要用到列名的,修改后的EntityHelper(我在这里将名字改成DbContext)。

将数据库操作和SQL生成代码放到单独类里,回头再看下DbContext代码,比原来的简洁了很多。

    class DbContext
    {
        private DbProvider dbProvider;
        private DbSqlBuilder sqlBuilder ;

        public DbContext(string connectionString)
        {
            SqlConnection conn = new SqlConnection(connectionString);
            dbProvider = new DbProvider(conn);
            this.sqlBuilder = new DbSqlBuilder();
        }

        public int Insert<T>(object entity)
        {
            var entityMapping = AttributeMapping.Get<T>();

            //将Entity转换成Dictionary
            var parameters = DynamicMethodBuilder.ConvertFromObject(entity);
            var sql = sqlBuilder.BuildInsertSql(entityMapping, parameters.Keys.ToList());
            return dbProvider.ExecuteNonQuery(sql, parameters);
        }

        public int Update<T>(T entity)
        {
            var entityMapping = AttributeMapping.Get<T>();
            //将Entity转换成Dictionary
            var parameters = DynamicMethodBuilder.ConvertFromObject(entity);
            var columns = entityMapping.Members.Where(m => m.IsDbGenerated == false && m.IsPrimaryKey == false).Select(c => c.ColumnName).ToArray();
            var updateColumns = new Dictionary<string, object>();
            var whereColumns = new Dictionary<string, object>();

            foreach (var item in parameters)
            {
                if (columns.Contains(item.Key))
                    updateColumns.Add(item.Key, item.Value);
                if (entityMapping.PrimaryKey.All(m => m.ColumnName == item.Key))
                    whereColumns.Add(item.Key, item.Value);
            }

            var sql = sqlBuilder.BuildUpdateSql(entityMapping, updateColumns.Keys.ToList(), whereColumns.Keys.ToList());

            return dbProvider.ExecuteNonQuery(sql, parameters);
        }

        public int DeleteByKey<T>(params object[] values)
        {
            var entityMapping = AttributeMapping.Get<T>();

            if (values.Length != entityMapping.PrimaryKey.Count)
                throw new ArgumentException("参数个数和主键数不一致");

            var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            for (int i = 0; i < entityMapping.PrimaryKey.Count; i++)
            {
                parameters.Add(entityMapping.PrimaryKey[i].ColumnName, values[i]);
            }

            var sql = sqlBuilder.BuildDeleteSql(entityMapping, parameters.Keys.ToList());

            return dbProvider.ExecuteNonQuery(sql, parameters);
        }

        public T Get<T>(object[] values)
        {
            var entityMapping = AttributeMapping.Get<T>();

            if (values.Length != entityMapping.PrimaryKey.Count)
                throw new ArgumentException("参数个数和主键数不一致");

            var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            
            StringBuilder where = new StringBuilder();

            for (int i = 0; i < values.Length; i++)
            {
                if (i > 0) //考虑到有多个主键
                    where.Append(" AND ");

                where.Append(entityMapping.PrimaryKey[i].ColumnName).Append("=").Append("@p").Append(i);

                /*参数*/
                parameters.Add("@p" + i, values[i]);
            }

            var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), string.Empty);

            return GetEntityList<T>(sql, parameters).FirstOrDefault();
        }

        public List<T> GetList<T>(string where, string orderBy)
        {
            var entityMapping = AttributeMapping.Get<T>();
      
            var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), orderBy);

            return GetEntityList<T>(sql, null);
        }

        public List<T> GetEntityList<T>(string sql, Dictionary<string, object> parameters)
        {

            var reader = dbProvider.ExecuteReader(sql, parameters);
            List<T> list = new List<T>();
            var type = typeof(T);
            var properties = type.GetProperties();
            while (reader.Read())
            {
                var user = Activator.CreateInstance(type);
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句
                        pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"];
                }

                list.Add((T)user);
            }
            return list;
        }
    }

View Code

本章结束,下一章要将DataReader转换成实体类的代码优化。

下载地址:https://files.cnblogs.com/files/sobaby/ORM04.zip