forked from jasper-lai/20161125-EFTxWithSP
/
EFTxWithSP.cs
188 lines (175 loc) · 7.91 KB
/
EFTxWithSP.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Objects;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using _20161125_EFTxWithSP.Models;
using System.Configuration;
using System.Data;
using System.Data.Entity;
using System.Data.SqlClient;
namespace _20161125_EFTxWithSP
{
public class EFTxWithSP
{
// --------------------------------------------------
// 如何在 SQL Server Profiler 觀察 TRANSACTION
// https://weblogs.asp.net/dixin/where-is-transaction-events-in-sql-server-profiler
// --------------------------------------------------
/// <summary>
/// Stored Procedure 在單獨的 Transaction
/// </summary>
/// <remarks>
/// [問題] Stored Procedure 預設會單獨 1 個 Transaction, 即使 EF 沒有 SaveChanges(), 仍會 Commit !
/// </remarks>
public void CallSpWithExplicitTx()
{
using (EFTestDBEntities ctx = new EFTestDBEntities())
{
ObjectParameter orderno = new ObjectParameter("po_order_no", typeof(String));
ctx.usp_get_order_no(orderno);
Console.WriteLine(orderno.Value);
ctx.MyOrders.Add(new MyOrder()
{
OrderNo = orderno.Value.ToString(),
ShipName = "jasper",
ShipAddress = "taipei",
TotalAmt = 1000
});
//故意不作 SaveChange(), 查結果, 可以發現 OrderNoGenerators 這個 table 的資料有異動
//ctx.SaveChanges();
}
}
/// <summary>
/// Stored Procedure 仍然在單獨的 Transaction
/// </summary>
/// <remarks>
/// [探索1] 試一下 StackOverflow 的解法, 用 SQL Server Profiler 檢查, 沒有 BEGIN TRANSACTION 了,
/// 但因為 SQL Server 預設沒有 TRANSACTION 的 INSERT / UPDATE / DELETE 就會作 COMMIT, 所以沒有解決問題
/// http://stackoverflow.com/questions/19991609/ef6-wraps-every-single-stored-procedure-call-in-its-own-transaction-how-to-prev
/// </remarks>
public void CallSpWithImplicitTx()
{
using (EFTestDBEntities ctx = new EFTestDBEntities())
{
//
ctx.Configuration.EnsureTransactionsForFunctionsAndCommands = false;
//
ObjectParameter orderno = new ObjectParameter("po_order_no", typeof(String));
ctx.usp_get_order_no(orderno);
Console.WriteLine(orderno.Value);
ctx.MyOrders.Add(new MyOrder()
{
OrderNo = orderno.Value.ToString(),
ShipName = "jasper",
ShipAddress = "taipei",
TotalAmt = 1000
});
//故意不作 SaveChange(), 查結果, 可以發現 OrderNoGenerators 這個 table 的資料有異動
//ctx.SaveChanges();
}
}
/// <summary>
/// Stored Procedure 與 EF Context 在相同的 Transaction
/// 利用 EF 建立的 context 物件, 去產生 connection / transaction
/// </summary>
/// <remarks>
/// [探索2] 試一下 MSDN 的解法, EF 6.X 以後, 可自行控制 TRANSACTION, 可以解決
/// https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx
/// 注意: using (MyDBEntities ctx = new MyDBEntities()) 還沒有 open connection
/// 注意: using (var tx = ctx.Database.BeginTransaction()) 會先 open connection 至 DB, 再 BEGIN TX
/// </remarks>
public void CallSpWithSameTxInContext()
{
using (EFTestDBEntities ctx = new EFTestDBEntities())
{
using (var tx = ctx.Database.BeginTransaction())
{
ObjectParameter orderno = new ObjectParameter("po_order_no", typeof(String));
ctx.usp_get_order_no(orderno);
Console.WriteLine(orderno.Value);
ctx.MyOrders.Add(new MyOrder()
{
OrderNo = orderno.Value.ToString(),
ShipName = "jasper",
ShipAddress = "taipei",
TotalAmt = 1000
});
//試一下 SaveChanges(), 再 RollbackTransaction(), 看一下結果
try
{
ctx.SaveChanges();
//tx.Commit();
tx.Rollback();
}
catch (Exception ex)
{
tx.Rollback();
Console.WriteLine(ex.ToString());
}
}
}
}
/// <summary>
/// 測試案例_4: 自行先建立 conn 及 tx 物件, 再建立 EF 的 context 物件; 再將 conn / tx 指派到 EF 的 context
/// </summary>
/// <remarks>
/// [探索3] 試一下 MSDN 的解法, 先以 ADO.NET 執行 Stored Procedure, 再執行 EF 的工作
/// https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx
/// Database First 的 .edmx, 無法如同範例程式設定 conn 及 contextOwnsConnection; 只有 Code First 才能作到 !!
/// http://stackoverflow.com/questions/22102082/error-passing-existing-connections-to-dbcontext-constructor-when-using-database
/// </remarks>
public void CallSp_MSDN_02()
{
string connStr = ConfigurationManager.ConnectionStrings["EFTestDB"].ConnectionString;
using (var conn = new SqlConnection(connStr))
{
conn.Open();
//using (var tx = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
using (var tx = conn.BeginTransaction())
{
var sqlCommand = new SqlCommand("usp_get_order_no");
sqlCommand.Connection = conn;
sqlCommand.Transaction = tx;
sqlCommand.CommandType = CommandType.StoredProcedure;
var orderno = new SqlParameter
{
ParameterName = "po_order_no",
SqlDbType = SqlDbType.NVarChar,
Size = 16,
Direction = ParameterDirection.Output
};
sqlCommand.Parameters.Add(orderno);
sqlCommand.ExecuteNonQuery();
Console.WriteLine(orderno.Value);
//using (var db = new DbContext(conn, contextOwnsConnection: false))
//{
// //EFTestDBEntities ctx = new EFTestDBEntities();
// //這裡產生的 ctx 是 null, 所以不能這樣作 !
// EFTestDBEntities ctx = db as EFTestDBEntities;
// ctx.MyOrders.Add(new MyOrder()
// {
// OrderNo = orderno.Value.ToString(),
// ShipName = "jasper",
// ShipAddress = "taipei",
// TotalAmt = 1000
// });
// //試一下 SaveChanges(), 再 RollbackTransaction(), 看一下結果
// try
// {
// ctx.SaveChanges();
// //tx.Commit();
// tx.Rollback();
// }
// catch (Exception ex)
// {
// tx.Rollback();
// Console.WriteLine(ex.ToString());
// }
//}
}
}
} //END method
} //END Class
} //END namespace