我有一个父对象(DAL的一部分),其中包含List<t>子对象的collection().
当我将对象保存回DB时,我输入/更新父对象,然后循环遍历每个子对象.为了可维护性,我将孩子的所有代码放入一个单独的私有方法中.
我打算使用标准的ADO事务,但在我的旅行中,我偶然发现了TransactionScope对象,我相信这将使我能够在一个事务中将父方法中的所有数据库交互(以及子方法中的所有交互)包装起来.
到现在为止还挺好..?
所以接下来的问题是如何在TransactionScope中创建和使用连接.我听说使用多个连接,即使它们是相同的DB也可以强制TransactionScope认为它是一个分布式事务(涉及一些昂贵的DTC工作).
是这样吗?或者,正如我似乎在其他地方读到的那样,使用相同的连接字符串(它将自己用于连接池)的情况会好吗?
更实际的是,我......
更新:
虽然看起来我可以使用我常用的.NET3.5 +和SQL Server 2008+,但是这个项目的另一部分将使用Oracle(10g),所以我不妨练习一种可以跨项目使用的技术.
所以我只是简单地将连接传递给子方法.
选项1代码示例:
using (TransactionScope ts = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.Connection.Open();
cmd.CommandType = CommandType.StoredProcedure;
try
{
//create & add parameters to command
//save parent object to DB
cmd.ExecuteNonQuery();
if ((int)cmd.Parameters["@Result"].Value != 0)
{
//not ok
//rollback transaction
ts.Dispose();
return false;
}
else //enquiry saved OK
{
if …Run Code Online (Sandbox Code Playgroud) 这个问题在这里的一个简单例子中不易重现,但是想知道是否有人有任何经验和提示,这里是问题:
像这样:
using (TransactionScope txScope = new TransactionScope()) {
...
}
Run Code Online (Sandbox Code Playgroud)
但这给了我:
Microsoft分布式事务处理协调器(MSDTC)已禁用网络事务.
我们的数据库管理员告诉我,MSDTC被禁用,无法安装.
因此,我正在尝试使用MetadataWorkspace创建自己的EntityConnection,并认为每个上下文将使用相同的EntityConnection.然而,这证明几乎不可能试图让它工作,例如,即使理论上两个上下文都在使用EntityConnection,我仍然继续得到上述错误.例如,很难理解实体框架在何处/为何需要MSDTC.
有没有人走过这条路,有经验或代码示例分享?
使用Entity Framework(在我的例子中是代码),我有一个操作,需要我调用SaveChanges来更新DB中的一个对象,然后再次SaveChanges来更新另一个对象.(我需要第一个SaveChanges来解决EF无法确定首先更新哪个对象的问题).
我试过做:
using (var transaction = new TransactionScope())
{
// Do something
db.SaveChanges();
// Do something else
db.SaveChanges();
tramsaction.Complete();
}
Run Code Online (Sandbox Code Playgroud)
当我运行它时,我在第二次SaveChanges调用时遇到异常,说"底层提供程序在打开时失败".内部异常表示我的机器上未启用MSDTC.
现在,我在其他地方看到了描述如何启用MSDTC的帖子,但似乎我还需要启用网络访问等.这听起来像是完全矫枉过正,因为没有涉及其他数据库,更不用说其他服务器了.我不想做一些会使我的整个应用程序不那么安全(或更慢)的东西.
当然必须有一个更轻量级的方法(理想情况下没有MSDTC)?!
msdtc entity-framework transactionscope sql-server-2012 entity-framework-5
有没有办法确定为什么System.Transaction TrasactionScope被升级到DTC?我们的一个组件似乎升级了交易,而所有其他组件(看起来似乎相似)都没有升级,我们正在碰撞.
是否提供了有关升级原因的信息以及如何找到和遵守这些信息?SQL事件探查器?日志文件?或者我在这个上运气不好?
更新:我正在运行SQL Server 2005以获取信息
我目前正在尝试使用嵌套事务作用域来对Azure SQL数据库进行数据库访问.
我正在使用以下代码(.Net 4.5.1,我的代码一直是异步,它是带有EF6.1的ASP.Net MVC):
public async Task Test()
{
// In my actual code, the DbContext is injected within the constructor
// of my ASP.Net MVC Controller (thanks to IoC and dependency injection)
// The same DbContext instance is used for the whole HttpRequest
var context = new TestContext();
using (var t1 = StartTransactionForAsync())
{
using (var t2 = StartTransactionForAsync())
{
context.Users.Add(new User { Name = Guid.NewGuid().ToString() });
await context.SaveChangesAsync();
t2.Complete();
}
... // Some more code here
t1.Complete(); …Run Code Online (Sandbox Code Playgroud) 当我们需要在我们的应用程序中进行数据库访问时,我们使用以下模式:
CreateOpenConnection只能执行new SqlConnection(myConnectionString)并调用Open()它.在我们执行查询之前调用此方法,并在查询返回后处理连接.work.Commit()如下所示:work.Commit:
using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = DapperFactory.CreateOpenConnection())
{
var count = _changeTracker.CommitChanges(conn);
tranScope.Complete();
return count;
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎非常适合作为Web服务的一部分进行一般使用,但是当我尝试将其与Rebus结合使用时,它正在给我MSDTC带来麻烦.
从我可以告诉,卤面(当它在队列中处理消息)创建一个新的TransactionScope,以便在故障处理消息的情况下,东西可以回滚.现在,到目前为止,这本身一直很好.我可以SqlConnection在没有任何问题的情况下在Rebus消息处理程序中打开一个新的内容(但是,在同一个Rebus 中使用我们的遗留实体框架查询和手动SqlConnections TransactionScope不起作用,但我现在不认为这是一个问题).但昨天我问了以下问题:
答案似乎是使用Rebus的传奇功能.我尝试实现它并配置它,以便Rebus saga持久化到新的SQL Server数据库(具有不同的连接字符串).据推测,使用该SQL Server持久性打开SqlConnection它自己的,因为每当我尝试创建一个SqlConnection现在,我得到以下异常:
已禁用分布式事务管理器(MSDTC)的网络访问.请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问.
在配置和性能开销方面,我非常非常希望避免使用MSDTC .我可能错了,但它似乎也没有必要.
我认为这里发生的事情是Rebus创建了一个环境TransactionScope,并且SqlConnection它创建了该范围的enlists.当我尝试创建自己的内容时,SqlConnection它也会尝试登记到该环境范围,并且因为涉及多个连接,它会被提升为MSDTC,但这会失败.
我知道如何解决这个问题,但我不知道这是否正确.我会做的是:
Enlist=false到我的应用程序的连接字符串,以便它永远不会访问环境事务.Commit方法,以便它不会创建一个新的TransactionScope(我的连接将不再订阅,因为我只是告诉它不应该),但它使用conn.BeginTransaction.像这样:
var …Run Code Online (Sandbox Code Playgroud) 前段时间我问了一个关于TransactionScope升级到MSDTC的问题,当时我没想到它.(上一个问题)
简而言之,在SQL2005中,为了使用TransactionScope,您只能在TransactionScope的生命周期内实例化并打开一个SqlConnection.使用SQL2008,您可以实例化多个SqlConnections,但在任何给定时间只能打开一个SqlConnections.SQL2000将始终升级到DTC ...我们的应用程序中不支持SQL2000,即WinForms应用程序BTW.
我们对单连接问题的解决方案是创建一个名为LocalTransactionScope(又名'LTS')的TransactionScope助手类.它包装了一个TransactionScope,最重要的是,它为我们的应用程序创建并维护了一个SqlConnection实例.好消息是,它可以工作 - 我们可以在不同的代码片段中使用LTS,它们都加入了环境事务.非常好.问题是,创建的每个根 LTS实例都将创建并有效地终止连接池中的连接.通过'Effectively Kill',我的意思是它将实例化一个SqlConnetion,它将打开一个新连接(无论出于何种原因,它永远不会重用来自池的连接),并且当该根LTS被释放时,它会关闭并处理SqlConnection,这是应该将连接释放回池中以便可以重用它,但是,它显然永远不会被重用.池膨胀直到最大化,然后在建立max-pool-size + 1连接时应用程序失败.
下面我附上了一个精简的LTS代码版本和一个示例控制台应用程序类,它将演示连接池耗尽.要观察连接池膨胀,请使用SQL Server Managment Studio的"活动监视器"或此查询:
SELECT DB_NAME(dbid) as 'DB Name',
COUNT(dbid) as 'Connections'
FROM sys.sysprocesses WITH (nolock)
WHERE dbid > 0
GROUP BY dbid
Run Code Online (Sandbox Code Playgroud)
我在这里附加LTS,以及一个示例控制台应用程序,您可以使用它来演示它将使用池中的连接并且永远不会重复使用也不会释放它们.您需要为LTS添加对System.Transactions.dll的引用以进行编译.
注意事项:它是打开和关闭SqlConnection的根级LTS,它始终在池中打开一个新连接.嵌套LTS实例没有区别,因为只有根LTS实例建立了SqlConnection.如您所见,连接字符串始终相同,因此应该重用连接.
是否有一些我们没有遇到的神秘条件会导致连接无法重复使用?除了完全关闭池外,还有其他解决办法吗?
public sealed class LocalTransactionScope : IDisposable
{
private static SqlConnection _Connection;
private TransactionScope _TransactionScope;
private bool _IsNested;
public LocalTransactionScope(string connectionString)
{
// stripped out a few cases that need to throw an exception
_TransactionScope = new TransactionScope(); …Run Code Online (Sandbox Code Playgroud) 我们正在尝试使用下面的代码.NET 3.5和SQL Server 2005进行间接嵌套事务.
MSDN说,当使用TransactionScope时,只要应用程序在事务中打开第二个连接(甚至是同一个数据库),事务就会升级.
void RootMethod()
{
using(TransactionScope scope = new TransactionScope())
{
/* Perform transactional work here */
FirstMethod();
SecondMethod();
scope.Complete();
}
}
void FirstMethod()
{
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
using (SqlConnection conn1 = new SqlConnection("Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI"))
{
string insertString = @"
insert into Categories
(CategoryName, Description)
values ('Laptop1', 'Model001')";
conn1.Open();
SqlCommand cmd = new SqlCommand(insertString, conn1);
cmd.ExecuteNonQuery();
}
scope.Complete();
}
}
void SecondMethod()
{
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
using (SqlConnection conn2 …Run Code Online (Sandbox Code Playgroud) 我刚刚开始使用TransactionScope,我发现总会遇到意想不到的事情需要永远调试.
我认为拥有这些的综合列表对于那些"奇怪的错误"情况来说是很好的,并且还可以扩展我们对平台奇怪性的了解.
关于我将如何使用事务范围的一些上下文:
我有2个作业在Sql Server数据库中读取和生成数据.每隔一段时间,作业就会因System.Transactions.TransactionInDoubtException而崩溃.确切的堆栈跟踪是:
Unhandled Exception: System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception: The wait operation timed out. Exitcode: -532462766
--- End of inner exception stack trace ---
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
at System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
at System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet …Run Code Online (Sandbox Code Playgroud) msdtc ×6
c# ×5
transactions ×4
.net ×3
sql-server ×2
ado.net ×1
async-await ×1
azure ×1
escalation ×1
rebus ×1
sql ×1
trace ×1