由“使用存储过程”引发的一些思考(高手请进)
2012-02-22 23:53
435 查看
在以前ado.net时候,我们使用存储过程返回一个列表,可以将结果集放在DataTable中,如果我们需要将结果集放在一个强类型集合(如List<T>)中我们该怎么做呢?之前在网上看到过一种解决方法,忘记出处了,请谅解。大概思路是:在用DataReader读取一行记录时,将该行创建为一个对象,然后添加到列表中。
我在EF3.5中使用存储过程,需要在edmx(领域模型中)文件中做函数导入(FunctionImport),并且返回值类型必须是数据库中已存在的实体。
这样做的缺憾(不够灵活)如下:
1、必须要函数导入,如果后来修改或更新实体模型,要维护该函数。(可以接受)
2、存储过程返回的对象必须是数据库已存在的实体,不然无法选择返回值类型。(目前没找到合理方法,要在数据库中建一个空表,无法接受,但在EF4.0中返回类型哪里可以新建ComplexType)。
所以在项目中少许的存储过程就用ado.net了。现有一个示例场景,从northwind的Products表中,获取一部分数据,得到产品编号,产品名称,单价这三列。那么存储过程中的sql语句:selectProductID,ProductName,UnitPricefromProducts。
第一个版本的实现:
1、返回的数据实体如:
ViewCode
3、调用代码:
上面的代码可能存在不足,比如:
1、MyModel中的Create方法是每个model必须自己实现(是否可以约束类型必须存在Create方法)。
2、MyModel中的Field泛型方法是用来获取字段的值,它的功能独立于该Model,可以抽离出来,写在其他地方。
3、调用时候还必须传递Model中的创建对象的方法等,是否我们可以进一步的封装呢?
我的设计(第二版)是:将Field方法和Create的声明写在一个抽象基类中,Field方法作为工具使用static,Create作为抽象方法,让实现者必须实现类型的创建。
数据访问层中:
调用:
通过以上可以看出,在第二个版本中,调用时去掉了对象创建方法的传递,如果其他开发者使用ExecuteProcedureList,则强制继承ProdureModel抽象类,这样的约束。
当然上面存储过程没有做分页(数据量大的情况,会影响性能),根据需要可以自己实现。第二个版本的实现请在需要的地方合理使用。
对于第二个实现,我有一些问题想请教高手们。
1、对于创建对象的方法Create,我觉得实现为static比较好,这样每个实例都可以共用一个Create方法,无需每个对象都有Create方法,如何做?
2、对于Create方法,返回值问题。现在是返回的基类型,会设计到类型转换的问题,是否有性能影响(这里不涉及装箱拆箱)。如果返回具体类型,该如何做?
3、对于在DAL的ExecuteProcedureList中,如何获取创建对象的方法问题。目前是创建一个对象,通过多肽得到子类的实现方法,如果static如何做,(反射)?
4、总之,有没有更好的设计呢?期待着大牛的指点,先谢谢啦
上面算是抛砖引玉吧,期待大家的回复和讨论,我们共同进步,谢谢大家!
我在EF3.5中使用存储过程,需要在edmx(领域模型中)文件中做函数导入(FunctionImport),并且返回值类型必须是数据库中已存在的实体。
这样做的缺憾(不够灵活)如下:
1、必须要函数导入,如果后来修改或更新实体模型,要维护该函数。(可以接受)
2、存储过程返回的对象必须是数据库已存在的实体,不然无法选择返回值类型。(目前没找到合理方法,要在数据库中建一个空表,无法接受,但在EF4.0中返回类型哪里可以新建ComplexType)。
所以在项目中少许的存储过程就用ado.net了。现有一个示例场景,从northwind的Products表中,获取一部分数据,得到产品编号,产品名称,单价这三列。那么存储过程中的sql语句:selectProductID,ProductName,UnitPricefromProducts。
第一个版本的实现:
1、返回的数据实体如:
ViewCode
///<summary> ///执行存储过程得到集合列表 ///</summary> ///<typeparamname="TResult"></typeparam> ///<paramname="procedureName">存储过程名称</param> ///<paramname="creater">创建对象的委托</param> ///<paramname="parameters"></param> ///<returns></returns> publicList<TResult>ExecuteProcedureList<TResult>(stringprocedureName,Func<IDataRecord,TResult>creater,paramsSqlParameter[]parameters) { List<TResult>result=newList<TResult>(); //getsqlconnectionstring stringconnString=(ObjectContext.ConnectionasEntityConnection).StoreConnection.ConnectionString; SqlConnectionconn=newSqlConnection(connString); SqlCommandcmd=conn.CreateCommand(); cmd.CommandType=CommandType.StoredProcedure; cmd.Parameters.AddRange(parameters); cmd.CommandText=procedureName; try { if(conn.State!=ConnectionState.Open) { conn.Open(); } using(DbDataReaderreader=cmd.ExecuteReader()) { while(reader.Read()) { TResultmodel=creater(reader); result.Add(model); } } } catch{throw;} finally { cmd.Dispose(); conn.Close(); } returnresult; }
3、调用代码:
List<MyModel>resultList=bll.ExecuteProcedureList<MyModel>("proc_getlist",MyModel.Create);
上面的代码可能存在不足,比如:
1、MyModel中的Create方法是每个model必须自己实现(是否可以约束类型必须存在Create方法)。
2、MyModel中的Field泛型方法是用来获取字段的值,它的功能独立于该Model,可以抽离出来,写在其他地方。
3、调用时候还必须传递Model中的创建对象的方法等,是否我们可以进一步的封装呢?
我的设计(第二版)是:将Field方法和Create的声明写在一个抽象基类中,Field方法作为工具使用static,Create作为抽象方法,让实现者必须实现类型的创建。
///<summary> ///作为EF中使用存储过程返回集合对象的基类 ///</summary> ///<typeparamname="TResult"></typeparam> publicabstractclassProcedureModel { ///<summary> ///获取record中字段的值,如果不存在则返回默认值 ///</summary> ///<typeparamname="T"></typeparam> ///<paramname="record"></param> ///<paramname="fieldName"></param> ///<returns></returns> publicstaticTField<T>(IDataRecordrecord,stringfieldName) { TfieldValue=default(T); if(record[fieldName]!=DBNull.Value) { fieldValue=(T)record[fieldName]; } returnfieldValue; } ///<summary> ///创建该对象 ///</summary> ///<paramname="record"></param> ///<returns></returns> publicabstractProcedureModelCreate(IDataRecordrecord); }
publicclassMyModel:ProcedureModel { publicintProductId{get;set;} publicstringProductName{get;set;} publicdecimalUnitPrice{get;set;} #region创建实体的方法 publicoverrideProcedureModelCreate(IDataRecordrecord) { returnnewMyModel() { ProductId=Field<int>(record,"ProductID"), ProductName=Field<string>(record,"ProductName"), UnitPrice=Field<string>(record,"UnitPrice") }; } #endregion }
数据访问层中:
///<summary> ///执行存储过程返回集合 ///</summary> ///<typeparamname="TResult">实体类型</typeparam> ///<paramname="procedureName">储存过程名称</param> ///<paramname="createTResult">创建实体的方法</param> ///<paramname="parameters">存储过程的参数</param> ///<returns></returns> publicList<TResult>ExecuteProcedureList<TResult>(stringprocedureName,paramsSqlParameter[]parameters) whereTResult:ProcedureModel,new() { List<TResult>result=newList<TResult>(); //getsqlconnectionstring stringconnString=(ObjectContext.ConnectionasEntityConnection).StoreConnection.ConnectionString; SqlConnectionconn=newSqlConnection(connString); SqlCommandcmd=conn.CreateCommand(); cmd.CommandType=CommandType.StoredProcedure; cmd.Parameters.AddRange(parameters); cmd.CommandText=procedureName; try { if(conn.State!=ConnectionState.Open) { conn.Open(); } Func<IDataRecord,ProcedureModel>creater=(newTResult()asProcedureModel).Create; using(DbDataReaderreader=cmd.ExecuteReader()) { while(reader.Read()) { TResultmodel=(TResult)creater(reader); result.Add(model); } } } catch{throw;} finally { cmd.Dispose(); conn.Close(); } returnresult; }
调用:
List<MyModel>resultList=bll.ExecuteProcedureList<MyModel>("proc_getlist");
通过以上可以看出,在第二个版本中,调用时去掉了对象创建方法的传递,如果其他开发者使用ExecuteProcedureList,则强制继承ProdureModel抽象类,这样的约束。
当然上面存储过程没有做分页(数据量大的情况,会影响性能),根据需要可以自己实现。第二个版本的实现请在需要的地方合理使用。
对于第二个实现,我有一些问题想请教高手们。
1、对于创建对象的方法Create,我觉得实现为static比较好,这样每个实例都可以共用一个Create方法,无需每个对象都有Create方法,如何做?
2、对于Create方法,返回值问题。现在是返回的基类型,会设计到类型转换的问题,是否有性能影响(这里不涉及装箱拆箱)。如果返回具体类型,该如何做?
3、对于在DAL的ExecuteProcedureList中,如何获取创建对象的方法问题。目前是创建一个对象,通过多肽得到子类的实现方法,如果static如何做,(反射)?
4、总之,有没有更好的设计呢?期待着大牛的指点,先谢谢啦
上面算是抛砖引玉吧,期待大家的回复和讨论,我们共同进步,谢谢大家!
相关文章推荐
- 使用单元测试引发的一些思考
- 由使用多态引发的一些思考
- 由使用Scanf()函数导致程序逻辑错误引发的思考
- 性能测试java协议使用httpclient方法引发的思考
- 高并发读写压力如何处理?redis内存数据库使用场景引发的思考
- Java Servlet的request使用的编码引发的思考 以及解决方法
- 读《我们的改革是不是正走向一个反面:谁抛弃了中国?》引发的一些另一个角度的思考
- 关于==和equals引发的一些思考
- ViewPager使用中的一些问题与思考
- 关于使用存储过程的一些好处以及注意事项[转]
- 【WPF】ContentControl Style定义与使用出现问题后 -- 引发的思考
- 读本期《程序员》(8月刊)而引发的对计算机相关专业毕业生就业问题的一些思考
- 由802.11 隐藏节点引发的一些思考
- Code Review CheckList-Java使用规范的一些思考
- 关于“JAVA中为什么没有了多继承并出现了接口”这一问题引发的一些思考
- Topk引发的一些简单的思考
- 对于很多公司不使用STL 引发的思考
- 由@Async使用引发的思考,实现优雅关闭线程
- 由“Beeline连接HiveServer2后如何使用指定的队列(Yarn)运行Hive SQL语句”引发的一系列思考
- 开车误闯红灯的补救方法 (以及由此引发的一些思考)