您的位置:首页 > 其它

由“使用存储过程”引发的一些思考(高手请进)

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

///<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、总之,有没有更好的设计呢?期待着大牛的指点,先谢谢啦

上面算是抛砖引玉吧,期待大家的回复和讨论,我们共同进步,谢谢大家!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: