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

推荐订阅源

T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Engineering at Meta
Engineering at Meta
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
MongoDB | Blog
MongoDB | Blog
U
Unit 42
B
Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
P
Privacy International News Feed
L
LINUX DO - 最新话题
博客园_首页
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
C
Cisco Blogs
博客园 - 【当耐特】
阮一峰的网络日志
阮一峰的网络日志
I
Intezer
罗磊的独立博客
MyScale Blog
MyScale Blog
Last Week in AI
Last Week in AI
A
About on SuperTechFans
G
GRAHAM CLULEY
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
D
DataBreaches.Net
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
AWS News Blog
AWS News Blog
I
InfoQ
T
The Exploit Database - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 叶小钗
Project Zero
Project Zero

博客园 - Kevin Zou

vs2008 ctrl+D 查找出错 No files were found to look in. Error while trying to retrieve text for error ORA-03113 的一种解决方式 IE不能直接顯示PDF的原因分析和解決方法 [2012-01-06] 流程系统设计--消息和通知 企业应用整合(1)--一个账号 2010年终随笔--漫谈技术 Schtasks命令理解排程的應用 用Adobe ImageReady制作简单GIF动画 Console输出UTF-8 JQuery1.4+ Ajax IE8 内存泄漏 权限设计的三层境界续 闲话权限设计三层境界 - Kevin Zou - 博客园 一个时代的终结 Jquery乱码的一次解决过程 asp.net真的是并行处理request的吗? 十年磨一剑—序 十年磨一剑--缓存模块设计 系统设计其实很有趣--从业务逻辑谈起
系统架构随笔--我看三层架构的数据访问层"怪胎"
Kevin Zou · 2009-06-01 · via 博客园 - Kevin Zou

三层架构已经被说了很多次,今天来谈谈我的看法。

 .net下典型的三层架构就是UI,业务逻辑层和数据访问层。

 UI层暂且不表,就谈谈争论最大的业务逻辑层和数据访问层吧,这是争论最多,也是被误解的最多的一点。

 petshop4为例,什么都先别说,一看代码就闻到了bad smell,为什么,看看那个IDal,SqlServerDAL,还有OracleDA),到处都是重复的代码,下面几个随便找的方法:

        public IList<ProductInfo> GetProductsBySearch(string[] keywords) {

            IList<ProductInfo> productsBySearch = new List<ProductInfo>();

            //Create a new query string

            int numKeywords = keywords.Length;

            StringBuilder sql = new StringBuilder(SQL_SELECT_PRODUCTS_BY_SEARCH1);

            //Add each keyword to the query

            for (int i = 0; i < numKeywords; i++) {

                sql.Append(string.Format(SQL_SELECT_PRODUCTS_BY_SEARCH2, PARM_KEYWORD + i));

                sql.Append(i + 1 < numKeywords ? SQL_SELECT_PRODUCTS_BY_SEARCH3 : SQL_SELECT_PRODUCTS_BY_SEARCH4);

            }

            //See if we have a set of cached parameters based on a similar qquery

            string sqlProductsBySearch = sql.ToString();

            OracleParameter[] parms = OracleHelper.GetCachedParameters(sqlProductsBySearch);

            // If the parameters are null build a new set

            if (parms == null) {

                parms = new OracleParameter[numKeywords];

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

                    parms[i] = new OracleParameter(PARM_KEYWORD + i, OracleType.VarChar, 80);

                // Cache the new parameters

                OracleHelper.CacheParameters(sqlProductsBySearch, parms);

            }

            // Bind the new parameters

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

                parms[i].Value = keywords[i];

            //Finally execute the query

            using (OracleDataReader rdr = OracleHelper.ExecuteReader(OracleHelper.ConnectionStringLocalTransaction, CommandType.Text, sqlProductsBySearch, parms)) {

                while (rdr.Read()) {

                    ProductInfo product = new ProductInfo(rdr.GetString(0), rdr.GetString(1), rdr.GetString(2), rdr.GetString(3), rdr.GetString(4));

                    productsBySearch.Add(product);

                }

            }

            return productsBySearch;

        }

        public void Insert(OrderInfo order) {

            int orderId = 0;

            // Get the parameters

            OracleParameter[] completeOrderParms = null;

            OracleParameter[] orderParms = GetOrderParameters();

            OracleParameter statusParm = new OracleParameter(PARM_ORDER_ID, OracleType.Number);

            // Bind the parameters

            orderParms[1].Value = order.UserId;

            orderParms[2].Value = order.Date;

            orderParms[3].Value = order.ShippingAddress.Address1;

            orderParms[4].Value = order.ShippingAddress.Address2;

            orderParms[5].Value = order.ShippingAddress.City;

            orderParms[6].Value = order.ShippingAddress.State;

            orderParms[7].Value = order.ShippingAddress.Zip;

            orderParms[8].Value = order.ShippingAddress.Country;

            orderParms[9].Value = order.BillingAddress.Address1;

            orderParms[10].Value = order.BillingAddress.Address2;

            orderParms[11].Value = order.BillingAddress.City;

            orderParms[12].Value = order.BillingAddress.State;

            orderParms[13].Value = order.BillingAddress.Zip;

            orderParms[14].Value = order.BillingAddress.Country;

            orderParms[15].Value = order.OrderTotal;

            orderParms[16].Value = order.BillingAddress.FirstName;

            orderParms[17].Value = order.BillingAddress.LastName;

            orderParms[18].Value = order.ShippingAddress.FirstName;

            orderParms[19].Value = order.ShippingAddress.LastName;

                        orderParms[20].Value = order.AuthorizationNumber.Value;

            // Create the connection to the database

            using (OracleConnection conn = new OracleConnection(OracleHelper.ConnectionStringOrderDistributedTransaction)) {

                // Open the database connection

                conn.Open();

                // Get the order id for the order sequence

                orderId = Convert.ToInt32(OracleHelper.ExecuteScalar(conn, CommandType.Text, SQL_GET_ORDERNUM));

                orderParms[0].Value = orderId;

                statusParm.Value = orderId;

                // Total number of parameters = order parameters count + 1 + (5 * number of lines)

                int numberOfParameters = orderParms.Length + 1 + (5 * order.LineItems.Length);

                //Create a set of parameters

                completeOrderParms = new OracleParameter[numberOfParameters];

                //Copy the parameters to the execution parameters

                orderParms.CopyTo(completeOrderParms, 0);

                                completeOrderParms[orderParms.Length] = statusParm;

                //Create a batch statement

                StringBuilder finalSQLQuery = new StringBuilder("BEGIN ");

                // Append the order header statements

                finalSQLQuery.Append(SQL_INSERT_ORDER);

                finalSQLQuery.Append("; ");

                finalSQLQuery.Append(SQL_INSERT_STATUS);

                finalSQLQuery.Append("; ");

                                int index = orderParms.Length + 1;

                int i = 1;

                // Append each line item to the batch statement

                foreach (LineItemInfo item in order.LineItems) {

                    //Add the appropriate parameters

                    completeOrderParms[index] = new OracleParameter(PARM_ORDER_ID + i, OracleType.Number);

                    completeOrderParms[index++].Value = orderId;

                    completeOrderParms[index] = new OracleParameter(PARM_LINE_NUMBER + i, OracleType.Number);

                    completeOrderParms[index++].Value = item.Line;

                    completeOrderParms[index] = new OracleParameter(PARM_ITEM_ID + i, OracleType.Char, 10);

                    completeOrderParms[index++].Value = item.ItemId;

                    completeOrderParms[index] = new OracleParameter(PARM_QUANTITY + i, OracleType.Number);

                    completeOrderParms[index++].Value = item.Quantity;

                    completeOrderParms[index] = new OracleParameter(PARM_PRICE + i, OracleType.Number);

                    completeOrderParms[index++].Value = item.Price;

                    // Append the statement to the batch

                    finalSQLQuery.Append(string.Format(SQL_INSERT_ITEM, i));

                    finalSQLQuery.Append("; ");

                    i++;

                }

                //Close the PL/SQL block

                finalSQLQuery.Append("END;");

                // Finally execute the query

                OracleHelper.ExecuteNonQuery(conn, CommandType.Text, finalSQLQuery.ToString(), completeOrderParms);

            }

        }

不知道你感觉如何,反正我受不了。

这些代码如果叫我去写,我不愿意,有人说代码生成器生成(一个优秀的系统架构是很少需要什么代码生成器的,因为重复的地方已经作了抽象,剩下需要写的确实是必须for具体的情况来开发的,如果你的代码需要代码生成器,那你就应该考虑有没有东西需要抽象出来重用了)

透过现象看本质,这些代码无非就是在作几件事情,获取参数,组织sql使用ado.net访问数据库,如果有结果则作一个mapping到对象。

代替这几个项目的功能其实非常简单。

首先只要有一个稍微封装了ado.net的类(类似于SqlHelper的东东),然后一个产生command对象的模块(当然其实就是产生sqlprocedure,然后设置参数,其参数可以非常简单地进行替换,也可以使用SqlParameter传入,如何更灵活,可以试试自己的抽象能力和设计功底),sql可以配置,不过也可以傻得像petshop那样类里面变量hardcode起来。

Like this:

private const string SQL_SELECT_CATEGORIES = "SELECT CategoryId, Name, Descn FROM Category";

private const string SQL_SELECT_CATEGORY = "SELECT CategoryId, Name, Descn FROM Category WHERE CategoryId = @CategoryId";

private const string PARM_CATEGORY_ID = "@CategoryId";

最后你可能说怎么切换数据库。在我开发了这么多系统,还没有一个系统有这样的要求,当然可能我从事的是for单个企业的软件开发(不过我们公司sql serveroracle都有,而且都常用)。某些做产品的TX可能确实有这个需求,因为要迁就客户。这个问题也是非常容易解决的。

只是需要知道,不是什么重用,变化都需要用到接口+override才能实现的。

IOC相信很多人都听过,可是真正理解的又有几人呢~~

IOC干的就是这事,替换对象是小菜一碟。

在这里要切换数据库,只需要用IOC方式SqlHelper换成OracleHelper就行了(当然如果架构师又没有洞察力,没有将对象创建和使用分开,那就另当别论了。简单说一个OO中的开发原则,创建对象的不要使用对象,使用对象的不要创建对象,分开来,且最好将创建对象部分封装成功能赶强的基础模块,springcastle的核心干的就是这事,不过我还是喜欢自己来,因为有的东东它们没有提供,而提供的又太多了,我用不着,裁剪一下,最简单,最适合的就是最好的,现在太多框架都是这样的,动不动就搞个赶级复杂的东东出来,有必要吗?像NbearLite的架构是我喜欢的类型,那个mapping层,动态代码产生层,数据访问层是明显分开的,合我胃口,不过有个小意见,能不能提下,mapping层能不用dataaccess层吗,虽然我知道你封装一些方法是为了让我更好地调用,谢谢~~ 但是可以使用一个继承的项目来添加这些功能,因为我有的系统只想用dataaccess功能,有的系统只想用你超级爽的mapping功能,sorry,跑题了)

好了,点一下题,数据访问层其实只有一个SqlHelper类或OracleHelper类,非常简单。其它都属于真正的业务逻辑部分(关于业务逻辑,以后有空再谈,这些都是一些被用滥的词汇)

记得在园子里某位TX的博上看到一句话:MS的数据访问层最有意义的就是那个SqlHelper类,对此,我深表赞同。

其实划分为业务逻辑层和数据访问层99%是没有半点意义的。

比起这个分层模式更有意义的是缓存设计,验证框架,异常处理,对象构造UI接口,事务处理,日志记录等等的设计

因为相同的事情只作一次永远是一个合格的系统架构师首先要遵循的原则,而透过现象看到事物的本质则是对他架构系统最基本的要求。

反正我是不愿意在PetShop架构师下写代码,除非MS白养我,刚好我头痛,不能也不想思考的时候,就会机械地敲敲这些无聊的代码(当然这么多代码要开发,总要给我时间吧,心情好的时候,翠花,上代码生成器,好省下时间上cnblogs

什么?系统有变化?不好意思,我只是一个小小程序员,只负责完成我的任务,其它的找架构师吧~~

(原谅我用这么强烈的带有主观色彩的词句,因为我觉得在阐述自己的观点时,最起码要旗帜鲜明,观点明确,否则写和看那些可说可不说的废话,真的会浪费你我宝贵的时间)