我需要在一次交易中进行多个Breeze SaveChanges

问题描述 投票:1回答:1

我有一个网页接受费用,并且必须插入6行,分布在4个表中。我不得不将INSERTS分为两个单独的SaveChanges,但是我需要它们都位于同一个数据库事务中,以便所有INSERTS和UPDATES在出现问题时都可以回滚。

我在SPA模板中的Entity Framework 6.2上,Oracle Mgd Data Access 12.2上使用Breeze 1.6。

4个表是A,B,C和D。表IB,C和D是A的子级,每个都带有A的PK作为外键。我最初按照应用程序A1,B1,C1,C2,C3,D1的要求按此顺序对INSERTS进行编码,然后再进行一次Breeze SaveChanges。 C3具有Oracle触发器,可以在插入C3时更新A1和B1中的几列。我的问题是,实体框架未按我编码的顺序插入(而且我知道我无法控制该顺序)。我实际上得到了以下序列:A1,C1,C2,C3,B1,D1,并且由于C3具有更新A和B的触发器,因此遇到了问题,因为尚未插入B。

所以我找到了这个问题:What logic determines the insert order of Entity Framework 6,它建议使用多个SaveChanges获得控制权。如下工作:

  • A1,B1保存更改
  • C1,C2,C3,D1保存更改现在,包括A和B的C3更新在内的所有触发器都工作良好。

但是Breeze / Entity Framework将每个SaveChanges都视为事务,因此,如果第二个SaveChanges中出现错误,我现在无法回退这两个部分。万一第2部分失败,我是否必须自己编写第1部分回滚的代码?还是有一个我不知道的秘密?

我熟悉BeginTransaction,Commit和Rollback,但不确定如何/在哪里将其与Breeze和Entity Framework配合使用。 Breeze抓取每个SaveChanges并将其自动包装在事务中。

我需要将多个SaveChanges分组到一个事务中。

感谢您的帮助。

oracle entity-framework transactions breeze savechanges
1个回答
0
投票

我认为在这种情况下,最好的做法是让Breeze client将其视为一次保存,而server则将保存束分开以正确的顺序保存。] >

让Breeze ContextProvider反序列化保存包,然后使用BeforeSaveEntities钩子手动进行更改。最后,将实体标记为Unchanged,以便ContextProvider不再尝试再次保存它们。

这是示例实现。

服务器

[在服务器上的Breeze Controller中,创建一个新的保存方法,该方法添加一个BeforeSaveEntitiesDelegate仅用于此特殊保存:

[HttpPost]
public SaveResult SaveFeePayment(JObject saveBundle) {
{
    contextProvider.BeforeSaveEntitiesDelegate += BeforeSaveFeePayment;
    return contextProvider.SaveChanges(saveBundle);
}

private Dictionary<Type, List<EntityInfo>> BeforeSaveFeePayment(Dictionary<Type, List<EntityInfo>> entities)
{
    var context = contextProvider.Context;

    // Get the list of EntityInfo for each type.  Throws exception if not found.
    // A fee payment save bundle must have A, B, C, and D or it is invalid.
    var ainfos = entities[typeof(A)];
    var binfos = entities[typeof(B)];
    var cinfos = entities[typeof(C)];
    var dinfos = entities[typeof(D)];

    // Save A and B
    Attach(context, ainfos);
    Attach(context, binfos);
    context.SaveChanges();

    // Save C and D
    Attach(context, cinfos);
    Attach(context, dinfos);
    context.SaveChanges();

    // Set all states to Unchanged, so Breeze won't try to save them again
    ainfos.ForEach(info => info.EntityState = Breeze.ContextProvider.EntityState.Unchanged);
    binfos.ForEach(info => info.EntityState = Breeze.ContextProvider.EntityState.Unchanged);
    cinfos.ForEach(info => info.EntityState = Breeze.ContextProvider.EntityState.Unchanged);
    dinfos.ForEach(info => info.EntityState = Breeze.ContextProvider.EntityState.Unchanged);

    // Return the entities, so Breeze will return them back to the client
    return entities;
}

// Map Breeze EntityState to EF EntityState
private static Dictionary<Breeze.ContextProvider.EntityState, System.Data.Entity.EntityState> entityStateMap =
    new Dictionary<Breeze.ContextProvider.EntityState, System.Data.Entity.EntityState> {
      { Breeze.ContextProvider.EntityState.Added, System.Data.Entity.EntityState.Added },
      { Breeze.ContextProvider.EntityState.Deleted, System.Data.Entity.EntityState.Deleted },
      { Breeze.ContextProvider.EntityState.Detached, System.Data.Entity.EntityState.Detached },
      { Breeze.ContextProvider.EntityState.Modified, System.Data.Entity.EntityState.Modified },
      { Breeze.ContextProvider.EntityState.Unchanged, System.Data.Entity.EntityState.Unchanged }
    };

// Attach entities to the DbContext in the correct entity state 
private static void Attach(DbContext context, List<EntityInfo> infos)
{
    foreach(var info in infos)
    {
        var efState = entityStateMap[info.EntityState];
        context.Entry(info.Entity).State = efState;
    }
}

客户

在客户端上,您需要使用SaveFeePayment调用named save端点。您将执行仅在节省费用时才使用。继续使用普通的SaveChanges端点进行其他保存。

通过指定resourceName调用特殊端点:

var saveOptions = new SaveOptions({ resourceName: "SaveFeePayment" });
return myEntityManager.saveChanges(null, saveOptions);

交易?

请注意,我实际上没有对交易做任何事情!我不确定如何使用EF + Oracle处理它们。希望您先前的解决方案可以应用于上面的BeforeSaveFeePayment方法。

希望这会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.