三层架构已经被说了很多次,今天来谈谈我的看法。
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对象的模块(当然其实就是产生sql或procedure,然后设置参数,其参数可以非常简单地进行替换,也可以使用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
server和oracle都有,而且都常用)。某些做产品的TX可能确实有这个需求,因为要迁就客户。这个问题也是非常容易解决的。
只是需要知道,不是什么重用,变化都需要用到接口+override才能实现的。
IOC相信很多人都听过,可是真正理解的又有几人呢~~
IOC干的就是这事,替换对象是小菜一碟。
在这里要切换数据库,只需要用用IOC方式把SqlHelper换成OracleHelper就行了(当然如果架构师又没有洞察力,没有将对象创建和使用分开,那就另当别论了。简单说一个OO中的开发原则,创建对象的不要使用对象,使用对象的不要创建对象,分开来,且最好将创建对象部分封装成功能赶强的基础模块,spring和castle的核心干的就是这事,不过我还是喜欢自己来,因为有的东东它们没有提供,而提供的又太多了,我用不着,裁剪一下,最简单,最适合的就是最好的,现在太多框架都是这样的,动不动就搞个赶级复杂的东东出来,有必要吗?像NbearLite的架构是我喜欢的类型,那个mapping层,动态代码产生层,数据访问层是明显分开的,合我胃口,不过有个小意见,能不能提下,mapping层能不用dataaccess层吗,虽然我知道你封装一些方法是为了让我更好地调用,谢谢~~ 但是可以使用一个继承的项目来添加这些功能,因为我有的系统只想用dataaccess功能,有的系统只想用你超级爽的mapping功能,sorry,跑题了)。
好了,点一下题,数据访问层其实只有一个SqlHelper类或OracleHelper类,非常简单。其它都属于真正的业务逻辑部分(关于业务逻辑,以后有空再谈,这些都是一些被用滥的词汇)
记得在园子里某位TX的博上看到一句话:MS的数据访问层最有意义的就是那个SqlHelper类,对此,我深表赞同。
其实划分为业务逻辑层和数据访问层99%是没有半点意义的。
比起这个分层模式更有意义的是缓存设计,验证框架,异常处理,对象构造,UI接口,事务处理,日志记录等等的设计。
因为相同的事情只作一次永远是一个合格的系统架构师首先要遵循的原则,而透过现象看到事物的本质则是对他架构系统最基本的要求。
反正我是不愿意在PetShop架构师下写代码,除非MS白养我,刚好我头痛,不能也不想思考的时候,就会机械地敲敲这些无聊的代码(当然这么多代码要开发,总要给我时间吧,心情好的时候,翠花,上代码生成器,好省下时间上cnblogs)
什么?系统有变化?不好意思,我只是一个小小程序员,只负责完成我的任务,其它的找架构师吧~~
(原谅我用这么强烈的带有主观色彩的词句,因为我觉得在阐述自己的观点时,最起码要旗帜鲜明,观点明确,否则写和看那些可说可不说的废话,真的会浪费你我宝贵的时间)