Cri*_*scu 7 transactions sql-server-ce entity-framework-4 ef-code-first
我想保存一些具有双向关系的实体(两端的导航属性).这是通过2次调用来完成的context.SaveChanges().
[关于我的模型,映射以及我如何到达那里的完整细节都在折叠之后.]
public void Save(){
var t = new Transfer();
var ti1 = new TransferItem();
var ti2 = new TransferItem();
//deal with the types with nullable FKs first
t.TransferIncomeItem = ti1;
t.TransferExpenseItem = ti2;
context.Transfers.Add(t);
context.Operations.Add(ti1);
context.Operations.Add(ti2);
//save, so all objects get assigned their Ids
context.SaveChanges();
//set up the "optional" half of the relationship
ti1.Transfer = t;
ti2.Transfer = t;
context.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
一切都很好,但如果在两次调用之间发生雷击,确保数据库不一致怎么样SaveChanges()?
输入TransactionScope...
public void Save(){
using (var tt = new TransactionScope())
{
[...same as above...]
tt.Complete();
}
}
Run Code Online (Sandbox Code Playgroud)
...但是在第一次调用context.SaveChanges()此错误时失败:
连接对象不能在事务范围中登记.
public void Save(){
using (var tt = new TransactionScope())
{
context.Database.Connection.EnlistTransaction(Transaction.Current);
[...same as above...]
tt.Complete();
}
}
Run Code Online (Sandbox Code Playgroud)
......同样的错误:
连接对象不能在事务范围中登记.
死胡同在这里......让我们采取不同的方法 - 使用显式交易.
public void Save(){
using (var transaction = context.Database.Connection.BeginTransaction())
{
try
{
[...same as above...]
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
仍然没有运气.这次,错误消息是:
BeginTransaction需要一个开放且可用的连接.连接的当前状态为Closed.
我该如何解决?
这是我的简化模型:一个引用两个引用事务的操作(TransferItem)的事务.这是 Transfer与其两个项目之间的1:1映射.
我想要的是确保在添加新内容时以原子方式保存这些内容Transfer.
这是我走过的路,以及我被卡住的地方.
该模型:
public class Transfer
{
public long Id { get; set; }
public long TransferIncomeItemId { get; set; }
public long TransferExpenseItemId { get; set; }
public TransferItem TransferIncomeItem { get; set; }
public TransferItem TransferExpenseItem { get; set; }
}
public class Operation {
public long Id;
public decimal Sum { get; set; }
}
public class TransferItem: Operation
{
public long TransferId { get; set; }
public Transfer Transfer { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我想将此映射保存到数据库(SQL CE).
public void Save(){
var t = new Transfer();
var ti1 = new TransferItem();
var ti2 = new TransferItem();
t.TransferIncomeItem = ti1;
t.TransferExpenseItem = ti2;
context.Transfers.Add(t);
context.Operations.Add(ti1);
context.Operations.Add(ti2);
context.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
这个错误打击了我的脸:
"无法确定依赖操作的有效排序.由于外键约束,模型要求或存储生成的值,可能存在依赖关系."
Thsi是一个鸡与蛋的问题.我无法使用不可为空的外键保存对象,但为了填充外键,我需要先保存对象.
看看这个问题,似乎我必须放松我的模型,并且:
像这样:
public class TransferItem: Operation
{
public Nullable<long> TransferId { get; set; }
[etc]
}
Run Code Online (Sandbox Code Playgroud)
此外,这里是映射.莫尔塔扎Manavi的上EF 1条:1间的关系是真正有帮助的.基本上,我需要与指定的FK列创建一对多的关系.'CascadeOnDelete(false)'处理有关多个级联路径的错误.(数据库可能会尝试删除转移两次,每次关系一次)
modelBuilder.Entity<Transfer>()
.HasRequired<TransferItem>(transfer => transfer.TransferIncomeItem)
.WithMany()
.HasForeignKey(x => x.TransferIncomeItemId)
.WillCascadeOnDelete(false)
;
modelBuilder.Entity<Transfer>()
.HasRequired<TransferItem>(transfer => transfer.TransferExpenseItem)
.WithMany()
.HasForeignKey(x => x.TransferExpenseItemId)
.WillCascadeOnDelete(false)
;
Run Code Online (Sandbox Code Playgroud)
用于保存实体的更新代码位于问题的开头.
为了完成这项工作,我必须添加更流畅的映射,以显式创建类TransferItem上的可选映射Transfer,以及使TransferItem.
一旦映射被修复,我将这一切包装在一个 TransactionScope 中就没有问题了。
这是整个控制台应用程序:
public class Transfer
{
public long Id { get; set; }
public long TransferIncomeItemId { get; set; }
public long TransferExpenseItemId { get; set; }
public TransferItem TransferIncomeItem { get; set; }
public TransferItem TransferExpenseItem { get; set; }
}
public class Operation
{
public long Id { get; set; }
public decimal Sum { get; set; }
}
public class TransferItem : Operation
{
public long? TransferId { get; set; }
public Transfer Transfer { get; set; }
}
public class Model : DbContext
{
public DbSet<Transfer> Transfers { get; set; }
public DbSet<Operation> Operations { get; set; }
public DbSet<TransferItem> TransferItems { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Entity<Transfer>()
.HasRequired( t => t.TransferIncomeItem )
.WithMany()
.HasForeignKey( x => x.TransferIncomeItemId )
.WillCascadeOnDelete( false );
modelBuilder.Entity<Transfer>()
.HasRequired( t => t.TransferExpenseItem )
.WithMany()
.HasForeignKey( x => x.TransferExpenseItemId )
.WillCascadeOnDelete( false );
modelBuilder.Entity<TransferItem>()
.HasOptional( t => t.Transfer )
.WithMany()
.HasForeignKey( ti => ti.TransferId );
}
}
class Program
{
static void Main( string[] args )
{
using( var scope = new TransactionScope() )
{
var context = new Model();
var ti1 = new TransferItem();
var ti2 = new TransferItem();
//deal with the types with nullable FKs first
context.Operations.Add( ti1 );
context.Operations.Add( ti2 );
var t = new Transfer();
context.Transfers.Add( t );
t.TransferIncomeItem = ti1;
t.TransferExpenseItem = ti2;
//save, so all objects get assigned their Ids
context.SaveChanges();
//set up the "optional" half of the relationship
ti1.Transfer = t;
ti2.Transfer = t;
context.SaveChanges();
scope.Complete();
}
}
}
Run Code Online (Sandbox Code Playgroud)
当运行产生这个数据库时:

这个输出:
