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

推荐订阅源

腾讯CDC
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tenable Blog
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
TaoSecurity Blog
TaoSecurity Blog
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
I
Intezer
Security Latest
Security Latest
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
O
OpenAI News
A
Arctic Wolf
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
M
MIT News - Artificial intelligence
F
Full Disclosure
P
Privacy International News Feed
The GitHub Blog
The GitHub Blog
T
Troy Hunt's Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
Cloudbric
Cloudbric
大猫的无限游戏
大猫的无限游戏
Google Online Security Blog
Google Online Security Blog
Recent Announcements
Recent Announcements
H
Help Net Security
量子位
V
V2EX
美团技术团队
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Schneier on Security
V2EX - 技术
V2EX - 技术
D
Docker
博客园 - 【当耐特】
Project Zero
Project Zero
博客园 - 司徒正美

博客园 - Andrew Yin

数据访问层组件设计以及选型意识流 第四次扩展的讨论 数据访问层组件设计以及选型意识流 第三次封装(极致、极简而不简单) 数据访问层组件设计以及选型意识流 第一次封装 数据访问层组件设计以及选型意识流 开篇 UTF-8, Unicode, GB2312三种编码方式解析, 深入研究汉字编码 C++中的模板实例:链表模板 一步一步学Ruby系列(二):Ruby中的函数 一步一步学Ruby系列(一):Ruby基础知识 .NET下的AOP: PostSharp 原理分析 AST,DLR,Expression Tree-----------推荐一位牛人的博客! C# 交互式SHELL C#中的 eval System.Web.Routing命名空间代码解析(四) Route解析中用到的实体类,一些以"Segment”为名的类 System.Web.Routing命名空间代码解析(三) RouteCollection类 System.Web.Routing命名空间代码解析(二) Routing类(上) System.Web.Routing命名空间代码解析(一) RouteBase类,RouteData类,RouteValueDictionary类 ASP.NET MVC中的各种上下文对象 有关string和Cookie的几个有用的扩展方法 反射加异步,根据枚举值来异步执行方法 操作Enum的一些实用方法
数据访问层组件设计以及选型意识流 第二次封装以及各种牢骚
Andrew Yin · 2012-08-06 · via 博客园 - Andrew Yin

我们的组件已经相对可用了,可我们的用户是挑剔的,有的用户提出了三个要求:

1. 加入分布式事务的支持

2. 我不想写任何SQL,我就是想做一些简单的单表数据库操作,能不能自动生成SQL语句?

3. 我想要像EF或者NH那样的父对象的SAVE调用会把子对象都SAVE的功能,我称为级联SQL操作。

我们本次的封装将实现这些需求。

首先分析一下,

实现第一个需求,我不想动原来的任何方法的接口,因此我想到了线程本地存储的概念。

另外,分布式事务,我这里单指数据库分布式事务,就是说,一个事务里访问了 A数据库,也访问了B数据库,分布式事务维持一个本地事务的列表,当所有本地事务都符合提交条件时,才提交分布式事务。

因此,我写了一个TransactionScope类,这个应该和EF里的做法类似。代码如下:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Remoting.Messaging;

namespace DianPing.BA.Framework.DAL
{
    public class TransactionScope :IDisposable
    {
        public IEnumerable<IDbTransaction> Transactions { getset;  }

        public TransactionScope()
        {
            Transactions = new List<IDbTransaction>();

            CallContext.SetData("TransactionScope"this);
        }

        public void RollBack()
        {
            try
            {
                foreach (var tran in Transactions.Reverse())
                {
                    if (tran.Connection.State != ConnectionState.Closed)
                    {
                        tran.Rollback();
                        tran.Connection.Close();
                        tran.Connection.Dispose();
                        tran.Dispose();
                    }
                }
            }
            catch
            {
                //TODO: 记个日志啥的
            }

            Transactions = new List<IDbTransaction>();
        }

        public void Comit()
        {
            try
            {
                foreach (var tran in Transactions)
                {
                    if (tran.Connection.State != ConnectionState.Closed)
                    {
                        tran.Commit();
                        tran.Connection.Close();
                        tran.Connection.Dispose();
                        tran.Dispose();
                    }                }
            }
            catch
            {
                //TODO: 记个日志啥的
            }

            Transactions = new List<IDbTransaction>();
        }

        public void Dispose()
        {
            try
            {
                foreach (var tran in Transactions)
                {
                    if (tran.Connection.State != ConnectionState.Closed)
                    {
                        tran.Rollback();
                        tran.Connection.Close();
                        tran.Connection.Dispose();
                        tran.Dispose();
                    }
                }
            }
            catch
            {
                //TODO: 记个日志啥的
            }

            Transactions = new List<IDbTransaction>();
            CallContext.FreeNamedDataSlot("TransactionScope");
        }
    }
}

 使用时只需要:

            

using (var transactionScope = new TransactionScope())
            {
                try{
                   // 数据库操作
                }
                catch (Exception e)
                {
                    //TODO: 记了条日志
                    transactionScope.RollBack();
                } 
            }

 实现第二个需求:

 自动生成简单SQL语句而已,有何难?

哥马上创建了一个DACBase<T> 类,简单的查询你就不要创建自己的DAC类了,直接用它吧:

 

好了,第二个需求搞定,现在来看看第三个需求,我一直觉得实体对象中应该是贫血的,不应该有操作,最好不要有级联数据操作。

比如说,所谓的级联Select,就是这样的吧:

 

 TestClassA的ClassB属性存的不是ClassB的主键,而是直接存的实体,然后在属性的GET方法中做了手脚。这就是级联SELECT.

那么级联Insert和Update呢?

请看DACBase<T> 的InsertCascade和UpdateCascade方法。

 

我还创建了 DACBaseObject类来辅助。

    

public static class DACBaseObject
    {
        private static readonly DACBase<object> DACBase = new DACBase<object>();

        public static int Insert(object record)
        {
            return DACBase.InsertCascade(record);
        }

        public static void Update(object record)
        {
            DACBase.UpdateCascade(record);
        }
    }

 也许,我们已经超越了IBitas和DAAB很多了,可是我们的用户永远都是得寸进寸的。

现在用户又提出了更高的要求:

我完全不像写SQL语句,我就想操作数据库就想操作内存数据一样,不关心数据库,只关心业务。

这,不是把我们的组件往重量级OR Mapping的方向逼吗?

我一直以为,重量级OR Mapping与轻量级的区别是:有没有实现数据库表间关系的Mapping.

要实现这个,很复杂,具体一点,自动生成多表查询的语句,很复杂!你想想,你依据什么生成这种语句啊,还不是表与表、实体对象与实体对象间的一对多、多对多的关系?

NH实现了这个,但它很多方面都被诟病,在乎性能和灵活性的开发团队是不用它的,这就是为什么IBITAS现在在JAVA阵营使用量越来越高的原因。

EF也实现了这个,而且有两种方式:

一种还是配置,只不过它的配置不全是在文件里,而还有给实体加一些丑陋的Attribute,另外读取Attribute信息耗资源的。

另一种是CODE FIRST,直接先写代码,然后生成服务代码要求的数据库 ,这明显只适用于完全从0开始的项目。

 EF还有一个弱点是:它生成语句完全和LINQ TO SQL类似,都是基于LAMADA表达式树的解析,关于表达式树,具体请参看

http://rednaxelafx.iteye.com/blog/248317

表达式树解析对大部分开发者而言是个黑盒子,虽然源代码据说也公开了,但是没几个人真去读它,或者说读得懂它,对于很多像我这样的"打破沙锅问到底"的开发人员,直接放弃。

(PS: 我真的是这种人,我08年曾把ASP.NET MVC当时版本的源代码全部看完了才认可它的,不相信的去看我08年关于MVC的博文)

依据表达式树生成SQL语句的一个大问题是扩展问题,比如 我要用EF访问PostgreSQL的数据库,我怎么办?我并不知道,但是直觉告诉我,要修改表达式树生成SQL的逻辑了,直接杯具了吧!!!!

这是EF的致命弱点。

PS: 查了一下, EF貌似也是支持 PostgreSQL, http://stackoverflow.com/questions/1211475/entity-framework-postgresql 具体怎么样支持我就真不知道了,这个链接只是把读者引向

http://npgsql.projects.postgresql.org/


 

另外,把数据库关系Mapping管理起来,势必会带来复杂性,就算你做得再好,也只是说你处理方式好,并不是你的复杂性就不在了。

复杂性带来的问题是

        性能不好

        灵活性不好(想想要该数据库关系的情况吧,想想,要在那些已有的设计不符合EF标准的数据库基础上进行开发的情况吧,想想要写的一大堆本来该BI做的报表却轮在你的头上吧)

        可控性不好(它生成的多表查询语句你能控制吗?只能完全依赖于其作者的能力和人品了,要不就祈求菩萨保佑吧,更别说EF是用没几个人看懂的表达式树生成语句)

        扩展性不好(这个前面已经讲到了)

        还有还与日志监控等组件结合得不太好

说了这么多重量级OR Mapping的不好,我的目的只有一个,就是说服老板放弃使用重量级的OR Mapping工具,其实也很简单,你去查查有多少公司和团队在寻求轻量级的数据访问解决方案。有多少公司正在或者已经在放弃使用Hibernate而转向IBITAS或者其他的组件。再查查有多少大型的公司在用EF. 查查哪些团队的血泪史吧。

 在查查在博客园中有多少大牛在说着LINQ TO SQL已死,ObjectSpace已死(EF前称),为什么?

还有就是微软企业库里为什么DAAB没有变成EF? 你可以说 微软企业库 是鸡肋,对, 我承认,可是,既然 微软企业库 是鸡肋, EF是好东西,那么微软为什么不把DAAB换成 EF从而让企业库不那么鸡肋呢??

原因只有一个: 他们不敢!多少人对EF这个东西 没有信任的,微软自己都不信任!

结论是:如果 微软企业库 是鸡肋,那么EF其实更鸡肋,出来也好几年了,离死不远了,等着瞧吧。

其实有时候自私的认为,EF我也能用,我干嘛引火上身要推荐自己的组件?到时候还有费时费力做技术支持。对自己没半点好处啊。

等到团队对EF有怨言或者 EF不能做某些事 的时候再 推出自己的东西来收拾残局最符合个人的利益。 好吧,老衲很傻。

我放弃争论是因为我不善于“舌战群儒”, 在争论的时候我的思路是混乱的,上次有人说微软企业库 是鸡肋,我居然无言以对。。。

其实归根结底,微软最错误的做法是: 它总是擅自为用户做决定,总是把用户当傻瓜,推出傻瓜式的产品,总认为你不需要了解得更深。

两大开发阵营摸爬滚打N年加比较就得出以上的结论。我想如果我是与一些JAVA阵营的团队去说EF,EF早就被枪毙了!

这里将一个刚刚发生的事:

我们公司最近组织了一次名叫Hackson的24小时极限编程竞赛,9个团队参加,我所在的团队应该本事最不被看好的。拿大家的话说,是被挑剩下的人。

我被挑剩下是因为之前我一心扑在某个基础组件的开发上,前期对该活动不闻不问,所以没人选。

我们选用的主题大概就是为销售人员提供一个最有价值的小工具, 拿到一些商业分析的结果去和客户谈生意,既然是商业分析,那么就离不开BI数据,这可是个难点。这个数据只有一个Provider, 没有基础组件的支持。

我们的结果很有意思,

多个团队自己杜撰数据(或者让销售录入数据,复杂的录入界面啊,想让销售做录入员),

多个团队直接用一个作业用那个Provider抓取数据,存放在自己的SQL SERVER数据库中(那是数据仓库海量数据啊,你居然拷贝一份?如果你不完全拷贝一份,你的数据就是不完整的)

他们为什么这么做?我认为他们用EF的是不想让人看到他们居然直接用了BI数据的Provider(我们的比赛也看代码质量的)

只有一个团队在用真实完整的数据说话(这个团队也许也是公认配合最好的团队,更是唯一一个超额提前完成任务的,完成了原计划两倍以上的任务),那就是我所在的团队 ,我花了半个小时就把那个BI数据的Provider封装在我们的数据访问组件里,完全没改任何一行现有代码,只是加了一个实现。(开闭原则)

有几个团队想把大家的目光引到花哨的界面上来,引到团队合作精神(感到啊)上来 ,但是,怎么能掩盖他们的数据是不完整的这个致命伤呢?数据不真实完整,真个程序的价值是不是就等于0?

让销售录入大量信息,可能吧,好吧,如果我是一个销售,我肯定是个懒销售!

回过头来,我在想怎么绕过数据关系Mapping, 又支持不写任何SQL语句就能做开发。

 这就引入了下一篇的主题了,敬请期待。

PS:

一,也许我错了,让销售录入信息可能是只有我这样的懒销售才感到头痛,大部分销售是不会头痛的,他们可以一边谈客户一边录入,可以在家打开笔记本录入~~

二,也许我又错了,一个系统里应该可以存在两个以及以上的数据访问组件,然后优势互补