接着早晨的写下去,为什么entityClientCommand和ExtensionCommand返回的结果的entity state不一样,原因是这行代码:var result = cmd.Materialize<OrderDetails>().Bind(context);的Bind()方法,在EF Extension 中的Bind()可以将entity加入到state manager.
EF Extension中有的确许多令人兴奋的地方,为了解决最初的话题解决sp 返回Multiple result set的问题,我对NorthwindEF模型中的Order和OrderDtails实体进行扩展:
首先添加对EF Extension .dll的引用,添加命名空间using Microsoft.Data.Extensions;由于EDM.Designer.cs是一个partial类,对一个分部(partial)类进行扩展是件容易的事,
1
namespace
NorthWind
2

{
3
public partial class NorthwindEFEntities : ObjectContext
4
{ }
5
}
只需要在上面的类中添加扩展方法即可,下面是实现Entity Set<T>,Materializer<T>的代码,
1
EntitySet
#region EntitySet<T>
2
private EntitySet<Orders> orderSet;
3
/**//// <summary>
4
/// Gets entry point for the Orders entity set.
5
/// </summary>
6
public EntitySet<Orders> OrderSet
7
{
8
get
9
{
10
if (null == this.orderSet)
11
{
12
orderSet = new EntitySet<Orders>(this, "Orders");
13
}
14
return this.orderSet;
15
}
16
}
17
private EntitySet<OrderDetails> orderDetailSet;
18
/**//// <summary>
19
/// Gets entry point for the OrderDetails entity set.
20
/// </summary>
21
public EntitySet<OrderDetails> OrderDetailSet
22
{
23
get
24
{
25
if (null == this.orderDetailSet)
26
{
27
orderDetailSet = new EntitySet<OrderDetails>(this, "OrderDetails");
28
29
}
30
return this.orderDetailSet;
31
}
32
}
33
#endregion
34

35

Materializer
#region Materializer<T>
36
// Materializer performing column renames.
37
private static readonly Materializer<Orders> s_orderMaterializer = new Materializer<Orders>(r =>
38
new Orders
39
{
40
OrderID = r.Field<int>("OrderID"),
41
EmployeeID = r.Field<int>("EmployeeID"),
42
OrderDate = r.Field<DateTime>("OrderDate"),
43
RequiredDate = r.Field<DateTime>("RequiredDate"),
44
ShippedDate = r.Field<DateTime>("ShippedDate"),
45
Freight = r.Field<decimal>("Freight"),
46
ShipName = r.Field<string>("ShipName"),
47
ShipAddress = r.Field<string>("ShipAddress"),
48
ShipCity = r.Field<string>("ShipCity"),
49
ShipRegion = r.Field<string>("ShipRegion"),
50
51
});
52
53
// Materializer performing column renames.
54
private static readonly Materializer<OrderDetails> s_orderDetailMaterializer = new Materializer<OrderDetails>(r =>
55
new OrderDetails
56
{
57
OrderID = r.Field<int>("OrderID"),
58
Quantity = r.Field<short>("Quantity"),
59
UnitPrice = r.Field<decimal>("UnitPrice"),
60
Discount = r.Field<float>("Discount"),
61
ProductID = r.Field<int>("ProductID")
62
});
63
64
private static readonly Materializer<KeyValuePair<Orders, OrderDetails>> s_OrderAndOrderDetailsMaterializer = new Materializer<KeyValuePair<Orders, OrderDetails>>(r =>
65
new KeyValuePair<Orders, OrderDetails>(
66
new Orders
67
{
68
OrderID = r.Field<int>("OrderID"),
69
OrderDate = r.Field<DateTime>("OrderDate"),
70
ShippedDate = r.Field<DateTime>("ShippedDate"),
71
ShipName = r.Field<string>("ShipName")
72
},
73
new OrderDetails
74
{
75
OrderID = r.Field<int>("OrderID"),
76
Quantity = r.Field<short>("Quantity"),
77
UnitPrice = r.Field<decimal>("UnitPrice"),
78
Discount = r.Field<float>("Discount")
79
}));
80
81
#endregion
至于为什么重新实现Materializer,而不是像ExtensionCommand中那样直接调用,只要的原因是我想使用EntitySet<T>,另外重新实现Materializer可以对sp的返回结果进行处理。通过 Materializer<KeyValuePair<Orders, OrderDetails>>将Mutilple result set中的内容分割为相对应的entity,这里顺便说一下我定义的存储过程getOrderAndOrderDetails是这样的:
select a.OrderID,a.OrderDate,a.ShippedDate,a.ShipName,b.UnitPrice,b.Quantity,b.Discount
from Orders as a left join OrderDetails as b
on a.OrderID=b.OrderID
where a.OrderID=@OrderID
下面就是对Order,OrderDetails的包装的3个方法:
1
public
Orders GetOrder(
int
OrderID)
2

{
3
DbCommand cmd = this.CreateStoreCommand("GetOrder", CommandType.StoredProcedure, new SqlParameter("@OrderID", OrderID));//specify DBCcommand
4
Orders order = s_orderMaterializer.Materialize(cmd).SingleOrDefault();// return a most single match for this store proceduce
5
return this.OrderSet.FindOrAttach(order);// //method made order be a tracked entity too
6
}
7
public
OrderDetails GetOrderDetail(
int
OrderID)
8

{
9
DbCommand cmd = this.CreateStoreCommand("GetOrderDetails", CommandType.StoredProcedure, new SqlParameter("@OrderID", OrderID));
10
OrderDetails orderDetails = s_orderDetailMaterializer.Materialize(cmd).FirstOrDefault<OrderDetails>();
11
// return this.OrderDetailSet.FindOrAttach(details);
12
return orderDetails;
13
}
14

/**/
/// <summary>
15
///return Multiple result sets
16
/// </summary>
17
/// <param name="pid">Product id</param>
18
/// <returns>Product</returns>
19
public
Orders GetOrderAndOrderDetails(
int
OrderID)
20

{
21
DbCommand command = this.CreateStoreCommand("getOrderAndOrderDetails", CommandType.StoredProcedure, new SqlParameter("@OrderID", OrderID));
22
//this store proceduce return Multiple result sets,contain Order and OrderDetails
23
Orders order = null;
24
var orderAndDetailSet = s_OrderAndOrderDetailsMaterializer.Materialize(command);
25
foreach (var orderAndDetail in orderAndDetailSet)
26
{
27
order = this.OrderSet.FindOrAttach(orderAndDetail.Key);
28
if (null != order)
29
{
30
order.OrderDetails.Add(orderAndDetail.Value);
31
// order.OrderDetails = this.OrderDetailSet.FindOrAttach(orderAndDetail.Value);
32
}
33
34
}
35
return order;
36
}
我用下面的代码来来测试上面的方法
1
Orders order
=
context.GetOrder(
10248
);
//
a single entity order
2
Console.WriteLine(
"
GetOrder:{0},OrderDetails Count:{2},Entity State:{1}
"
, order.OrderID, order.OrderDetails.Count, order.EntityState);
3
//
writer.WriteLine(order);
4
//
DisplayResults<Orders>(order);
5
OrderDetails orderDetails
=
context.GetOrderDetail(
10248
);
//
a silgle entity orderdetail
6
//
writer.WriteLine(orderDetails);
7
Console.WriteLine(
"
GetOrderDetail:{0},Entity State:{1}
"
, orderDetails.OrderID, orderDetails.EntityState);
8
Orders orderAndDetails
=
context.GetOrderAndOrderDetails(
10248
);
//
entity order with orderdetail
9
Console.WriteLine(
"
GetOrderAndOrderDetails:{0},OrderDetails Count:{1},Entity State:{2}
"
, orderAndDetails.OrderID, orderAndDetails.OrderDetails.Count, orderAndDetails.EntityState);
10
运行的结果是:
GetOrder()方法返回的order对象由于没有attach相应的OrderDetails对象,因此 order.OrderDetails.Count 为0,而GetOrderAndOrderDetails()就不同,我们将getOrderAndOrderDetails返回的Mutilple result set中的内容进行分割为Order,OrderDetails,并将OrderDetails 对象添加到Orders,这样的效果等同于
contex.Orders.Include("OrderDetails").Where(q=>q.OrderID=10248).FirstOrDefault();至于为什么GetOrderDetail返回的结果Entity State为Detach,那是因为 我将这行代码this.OrderDetailSet.FindOrAttach(details)注释了.
EF Extension真的让我太高兴了,可以说让我感受到了sp在EF用应用不再那么局限性。可能后面我还会写一些关于还有一些关于通过设计器的映射sp的随笔.最后说一下还是说一下哪儿可以找到EF Extension.