之前一段时间对存储过程(store proceduce 简称sp)在EF中的使用非常感兴趣,所以看了一些相关的资料,也断断续续的做 了一些练习。最近2个月下班后就不愿意再写代码了,所以也只能在周末睡眠足够之后做些感兴趣的事.关于sp在EF的使用,最基本资料就是msdn了,不过中午睡醒起来看到Ado.team blog上出现了一篇文章关于EF的msdn添加了新 内容,其中有关于如何自定义function,DefineQuery。
使用EF也有一段时间了,不过对于entity client的了解却知之甚少,因为通过设计EDM,访问ObjectContext,可以说在某种程度上甩掉了那一层层所谓的包装。不过之前学习存储过程sp在EF中的使用时,我发现在某些时候EDM并不能完全解决.如果对于sp在EDM的映射有一些了解的话,可以很清楚的明白,在EDM中映射一个sp返回的类型包括:Entity Type,Scalars Type,None;这三中类型分别对应的sp返回内容对应到数据库,表的层面就是:(Entity)一张表的所有字段或若干字段(必须包括主键);(Scalars)返回一个常量(int, varchar)可以是数据库的一个字段,可以是sum,count,left...等函数操作的结果;(none)自然是什么都不返回.这里自然就有一个严重的问题,对于返回(multiple result sets)多张表的结果集怎么办?似乎entity client能做到,于是我做了一些尝试:
//execute stored proceduce with entityclient
public static IList<OrderDetails> entityClientCommand(NorthWind.NorthwindEFEntities context, int
OrderID)
{
EntityConnection conn = context.Connection as
EntityConnection;
IList<OrderDetails> list = new List<OrderDetails>
();
//EntityConnection expose a StoreConnection
DbConnection dbconn =
conn.StoreConnection;
DbCommand dbcmd =
dbconn.CreateCommand();
dbcmd.CommandText = "GetOrderDetails"
;
dbcmd.CommandType =
CommandType.StoredProcedure;
dbcmd.Parameters.Add(new SqlParameter("OrderID"
, OrderID));
if ((dbcmd.Connection.State ==
ConnectionState.Closed))
dbcmd.Connection.Open();
try
{
using (DbDataReader reader =
dbcmd.ExecuteReader())
{
while
(reader.Read())
{
// Gets the column ordinal given the name of the column,
int IDOrdinal = reader.GetOrdinal("OrderID"
);
int UnitPriceOrdinal = reader.GetOrdinal("UnitPrice"
);
int QuantityDOrdinal = reader.GetOrdinal("Quantity"
);
OrderDetails od = new
OrderDetails
{
OrderID =
reader.GetInt32(IDOrdinal),
UnitPrice = reader.IsDBNull(UnitPriceOrdinal) ? decimal
.Zero : reader.GetDecimal(UnitPriceOrdinal),
Quantity =
reader.GetInt16(QuantityDOrdinal)
};
list.Add(od);
}
}
}
finally
{
if (dbcmd.Connection.State ==
ConnectionState.Open)
{
dbcmd.Connection.Close();
}
}
return
list;
}
从上面的这段代码可以看出我们可以使用EntityClient通过EntityConnection.StoreConnection来执行存储过程,因为StoreConnection是一个Dbconnection,所以的操作都和SqlClient很类似。通过DbDataReader将sp执行的结果具体为某一个实体或多个结果(上面的示例是返回一个实体,返回多个实体也肯定可以,不过需要更麻烦一些).尽管上面的做法可以处理返回内容为multiple result sets,但毕竟不能直接返回。不过后来看到了ms发布的EF Extension,在那个扩展包里就添加了对于sp返回multiple result sets的扩展。下面是我是我添加EF Extension之后在EntityClient调用sp的代码:
//exec stored proceduce with EF Extension
public static IEnumerable<OrderDetails> ExtensionCommand(NorthwindEFEntities context, int
OrderID)
{
DbCommand cmd = context.CreateStoreCommand("GetOrderDetails", CommandType.StoredProcedure, new SqlParameter("@OrderID"
, OrderID));
var result = cmd.Materialize<OrderDetails>
().Bind(context);
return
result;
}
可以看出entityClientCommand与ExtensionCommand在代码量上的差距(entityClientCommand返回的OrderDetails实体的属性不是所有都有值,因为在DbDataReader里面我没有取出所有的column,否则会更多,再看看执行的效果
using (NorthwindEFEntities context = new
NorthwindEFEntities())
{
IList<OrderDetails> list = StoredProduceExtension.entityClientCommand(context, 10248
);
foreach (var item in
list)
{
Console.WriteLine("entityClientCommand:{0},{1},{2},{3}"
, item.OrderID, item.UnitPrice, item.Quantity, item.EntityState);
}
IEnumerable<OrderDetails> enumerable = StoredProduceExtension.ExtensionCommand(context, 10248
);
foreach (var item in
enumerable)
{
Console.WriteLine("ExtensionCommand:{0},{1},{2},{3}"
, item.OrderID, item.UnitPrice, item.Quantity, item.EntityState);
}
//
DisplayResults<OrderDetails>(enumerable);
// IEnumerable<OrderDetails> enumerable1 = StoredProduceExtension.ExtensionCommand1(context, 10248);
}
输出结果并不是完全一样,OrderDetails实体的内容是一样的,但是EntityState的值不一样,在EF中object services 的实体跟踪服务Tracking Service是一种很重要的行为,entityClientCommand返回的实体OrderDetails状态值是Detached表明实体存在,但是不在当前objectcontext对象的object Services 跟踪之内。Unchanged表明已经被Tracking.具体关于查看EntityState值的说明。
没想到已经是12.21了, 不过好像我还没有说明EF Extension是怎样处理mulplite result set,就已经很困了。对于EF Extension还有很多值得令人兴奋的地方。最后再介绍2篇学习sp&EF的文章Stored Procedure Mapping,Migrating from LINQ to SQL to the Entity Framework: Stored Procedures for data retrieval