以前都是在用 Linq2sql来作 orm 的,但最微软对 Linq2sql 不再作升级,但又因为 entity framework 和 linq2sql 有很多的相似之处,也就作一下学习。
体会下 EF 大体上和 linq 差不多,基本的增删改等 操作都相似。在功能是执行效率上 EF要好于linq 但有个地方不如 linq2sql 了,在linq2sql 中存储过程是直接映射成方法的,但EF中就没那么方便了。
自己写了个存储过程:
GetNewBillCode
目地是为了生成订单号,
如 OD-001-2011-04-06-001
但在操作中 怎么 new 也 出来这个存储过程来。 原因在于EF对 存储过程支持的不是很好。
所以要自己通方法来实现存储过程。
该存储过程有三个参数和一个还回值参数
shopid:部门
billDate:订单日期
mastTableName:表名称
out billCode:还回订单编码
自己写个方法来实现它
public string GetNewBillCodeByDate(int shipid,string billDate,string mastTableName,out string billCode)
{
var pars = new System.Data.EntityClient.EntityParameter[]
{
//分支机构
new System.Data.EntityClient.EntityParameter{ ParameterName="shopid", DbType=System.Data.DbType.Int32,Value=shipid},
//订单日期
new System.Data.EntityClient.EntityParameter{ ParameterName="billDate", DbType=System.Data.DbType.String,Value=billDate},
//表名称
new System.Data.EntityClient.EntityParameter{ ParameterName="mastTableName", DbType=System.Data.DbType.String,Value=mastTableName},
//订单编码
new System.Data.EntityClient.EntityParameter{ParameterName="billCode", DbType=System.Data.DbType.String, Direction=System.Data.ParameterDirection.Output,Size = 50}
};
ExecuteFunction("getnewbillcodebydate", pars);
return billCode= pars[3].Value.ToString();
}
//执行存储过程
public void ExecuteFunction(string functionName, System.Data.EntityClient.EntityParameter[] parameters)
{
System.Data.EntityClient.EntityCommand cmd = ((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddRange(parameters);
cmd.CommandText = this.DefaultContainerName + "." + functionName;
try
{
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
cmd.ExecuteScalar();
}
catch (System.Exception)
{
throw;
}
finally
{
cmd.Connection.Close();
}
}
虽然代码是有了,但执行的时候却还是出错了
如下是解决:
调用EF的存储过程报“存储区数据提供程序返回的数据读取器所具有的列数对于所请求的查询不够”问题最近使用EF的函数导入(设置映射的存储过程)遇到该问题。从错误描述很难知道问题发生在哪里?一遍又一遍核对了代码的参数名称和存储过程的参数名称是否一致,也使用SQL探查器跟到执行的sql语句都正常返回结果。
存储过程简要说明如下(代码一):
CREATE PROCEDURE [dbo].[pmsvr_ChangeBillingMode]
(
@in_iPhysicalRegisterId int,
@in_bIsBillingOnline bit,
@in_iModifiedEmployeeId int,
@out_sErrMsg nvarchar(255) output
)
AS
SET NOCOUNT ON
... ...
set @out_sErrMsg = ''
select 1 as Result
return 1
SET NOCOUNT OFF
Edmx中的封装方法如下(代码二):
public bool ChangeBillingMode(int physicalRegisterId, bool isBillingOnline, int modifiedEmployeeId, out string errMsg)
{
this.Connection.Open();
EntityCommand cmd = ((EntityConnection)this.Connection).CreateCommand();
cmd.CommandText = this.DefaultContainerName + ".ChangeBillingMode";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("in_iPhysicalRegisterId", physicalRegisterId);
cmd.Parameters.AddWithValue("in_bIsBillingOnline", isBillingOnline);
cmd.Parameters.AddWithValue("in_iModifiedEmployeeId", modifiedEmployeeId);
EntityParameter para2 = new EntityParameter("out_sErrMsg", DbType.String);
para2.Direction = ParameterDirection.Output;
para2.Value = string.Empty;
cmd.Parameters.Add(para2);
EntityParameter ret = new EntityParameter("ReturnValue", DbType.Int32);
ret.Direction = ParameterDirection.ReturnValue;
ret.Value = 0;
cmd.Parameters.Add(ret);
cmd.ExecuteNonQuery();
errMsg = cmd.Parameters["out_sErrMsg"].Value.ToString();
int returnValue = (int)cmd.Parameters["ReturnValue"].Value;
return (returnValue > 0);
}
最后发现在添加函数导入时指定了返回类型为标量Boolean,而当时我理所当然的认为该返回类型是指存储过程最后的Return语句的返回值类型(就这样设置了,因为成功Return 1,失败Return 0);下面是添加函数导入画面(图1):
当时存储过程代码(参见代码一)没有Select 1 as Result这句的,所以总是报如下错误:
后来在Return前增加了上述Select语句就OK,至此才明白添加函数导入的返回类型是指最后一个SELECT语句的字段类型(仅返回一个字段的情况);所以如果没有使用SELECT语句返回则应该置返回类型为“无”;如果SELECT多个字段或者数据表行时必须将返回类型指定为“实体”(即DTO类型);如果是表就必须建一个对应的DTO;如果返回若干字段则必须为这些字段手工创建DTO类型。如果是返回DTO类型的则无需在分部类封装方法了,直接使用导入的函数即可。
总结:如果不使用最后SELECT结果的话,则不必理会返回类型,将返回类型设置为“无”即可,当然我们封装的函数可以获取存储过程Return结果及OUTPUT类型的参数。所以上面例子代码可以删除存储过程中的Return前的Select语句,同时添加函数导入时指定返回类型为“无”。