能否在单个事务中包含 linq-to-sql 更改和 ADO.NET 数据集表适配器更新?
以下是我正在使用的相关技术:
- Devart 的 dot Connect for Oracle(以促进 Oracle 的 Linq-to-Sql)。
- 强类型 ADO.NET 数据集。
- Oracle 数据库。
挑战如下:
- 我的旧代码使用 ADO.NET 数据集和表适配器提交数据库更新。
- 我想开始将该代码转换为 Linq-to-Sql,但我想逐步进行,以最大限度地减少代码改动和风险。
这是我的概念证明模式:
Parent Table
- Parent.Id
- Parent.Name
Child Table
- Child.Id
- Child.ParentId
- Child.Name
这是我的概念证明代码块:
using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;
namespace DevArtTry1
{
class Program
{
static void Main(string[] args)
{
using (DataContext1 dc = new DataContext1())
{
dc.Connection.Open();
using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
dc.Transaction = transaction;
Parent parent = new Parent();
parent.Id = 1;
parent.Name = "Parent 1";
dc.Parents.InsertOnSubmit(parent);
dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.
DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
DataSet1.CHILDRow row = dt.NewCHILDRow();
row.ID = 1;
row.PARENTID = 1;
row.NAME = "Child 1";
dt.AddCHILDRow(row);
CHILDTableAdapter cta = new CHILDTableAdapter();
// cta.Transaction = transaction; Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
transaction.Commit();
}
}
Console.WriteLine("Successfully inserted parent and child rows.");
Console.ReadLine();
}
}
}
- 正如上面的注释所示,线程将在子数据适配器上无限期地停止update 调用,因为它将无限期地等待子表上的写锁。 【注意外键关系:Parent.Id -> Child.ParentId (M:N)]
这是我的问题:
- 我想包装整个代码块 在一笔交易中。
- 我可以这样做吗? 考虑到:
- 我想在父表上提交更新 Linq-to-Sql 的 SubmitChanges 方法...
- 我想承诺 更新子表 ADO.NET 数据集表适配器。
这里有两个有趣的脚注:
- 这一切有效 撤销。 也就是说,如果我想 将更改提交到父表 使用数据适配器并更改为 具有 linq-to-sql 的子表... 这会起作用。
我尝试将事务显式附加到数据适配器,但编译器不允许这样做,因为它是不同类型的事务。
CHILDTableAdapter cta = new CHILDTableAdapter(); cta.Transaction = 交易; // 不允许,因为您无法将源类型“System.Data.Common.DbTransaction”转换为目标类型“System.Data.OracleClient.OracleTransaction”。 cta.更新(dt); 事务.Commit();
Here are the relevant technologies that I'm working with:
- Devart's dot Connect for Oracle (to facilitate Linq-to-Sql for Oracle).
- Strongly Typed ADO.NET Datasets.
- An Oracle database.
Here's the challenge:
- My legacy code submits database updates with ADO.NET datasets and table adapters.
- I'd like to begin converting that code over to Linq-to-Sql, but I'd like to do it piecemeal to minimize code churn and risk.
Here's my proof of concept schema:
Parent Table
- Parent.Id
- Parent.Name
Child Table
- Child.Id
- Child.ParentId
- Child.Name
Here's my proof of concept code block:
using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;
namespace DevArtTry1
{
class Program
{
static void Main(string[] args)
{
using (DataContext1 dc = new DataContext1())
{
dc.Connection.Open();
using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
dc.Transaction = transaction;
Parent parent = new Parent();
parent.Id = 1;
parent.Name = "Parent 1";
dc.Parents.InsertOnSubmit(parent);
dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.
DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
DataSet1.CHILDRow row = dt.NewCHILDRow();
row.ID = 1;
row.PARENTID = 1;
row.NAME = "Child 1";
dt.AddCHILDRow(row);
CHILDTableAdapter cta = new CHILDTableAdapter();
// cta.Transaction = transaction; Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
transaction.Commit();
}
}
Console.WriteLine("Successfully inserted parent and child rows.");
Console.ReadLine();
}
}
}
- As the comments above indicate, the thread will halt indefinitely on the child data adapter's update call because it will wait indefinitely for a write lock on the Child table. [Note the foreign key relationship: Parent.Id -> Child.ParentId (M:N)]
Here's my question:
- I want to wrap the entire code block
in a transaction. - Can I do this? Considering that:
- I want to commit an update on the Parent table with
Linq-to-Sql's SubmitChanges method... - And I want to commit an
update on the Child table with an
ADO.NET dataset table adapter.
- I want to commit an update on the Parent table with
Here are two interesting footnotes:
- This whole things works in
reverse. That is, if I wanted to
submit changes to the parent table
with a data adapter and changes to
the child table with linq-to-sql...
that would work. I tried to explicitly attach the transaction to the dataadapter, but the compiler won't allow it because it is a different type of transaction.
CHILDTableAdapter cta = new CHILDTableAdapter(); cta.Transaction = transaction; // Not allowed because you can't convert source type 'System.Data.Common.DbTransaction' to target type 'System.Data.OracleClient.OracleTransaction'. cta.Update(dt); transaction.Commit();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我对 Oracle 的事务一无所知...但在 dotnet 方面,您应该可以自己控制事务。 确保两种技术使用相同的连接实例。
当我们通过连接而不是通过 ORM 控制事务时,我们使用事务范围: http://msdn.microsoft.com/en-us/library/ms172152.aspx
I don't know anything about Oracle's transactions... but on the dotnet side you should be fine to control the transaction yourself. Make sure both technologies are using the same connection instance.
When we control transactions through the connection instead of through the ORM, we use transaction scope: http://msdn.microsoft.com/en-us/library/ms172152.aspx
我遇到了同样的问题,遇到这两个错误:
问题是子对象的标识列设置不正确。 如果 DotConnect LINQ 不采用身份密钥,则对象属性似乎是临时设置的,导致非连续更新,从而导致完整性违规。
修复方法如下:
首先创建一个序列:
<块引用>
接下来创建 OnInsert 触发器:
<块引用>
修改存储模型以合并新的自动生成的主键:
在代码中,创建子对象后,将其附加到父对象,如下所示:
<块引用>
以下是更多资源:
干杯!
I had the same issue, encountering these two errors:
The problem was that the child object's identity column was not set properly. If DotConnect LINQ does not assume an identity key, then objects properties seem to be set ad hoc, resulting in non-sequential updates, leading to integrity violations.
Here's the fix:
First create a sequence:
Next create the OnInsert trigger:
Modify the storage model to incorporate the new auto-generated primary key:
In code, after creating the child object, attach it to the parent, as below:
Here are further resources:
Cheers!
使用 TransactionScope 类。
请注意,如果您使用不同的数据库(或者它们驻留在不同的服务器上),则需要检查您的 DTC 配置。
Use a TransactionScope class.
Beware that if you are using different databases (or they reside on distinct servers) you need to check your DTC configuration.