首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >我需要在一个事务中执行多个Breeze SaveChanges

我需要在一个事务中执行多个Breeze SaveChanges
EN

Stack Overflow用户
提问于 2020-01-06 20:27:53
回答 2查看 223关注 0票数 2

我有一个网页,接受费用支付,它必须插入6行,分散在4个表。我不得不将插入分成两个单独的SaveChanges,但是我需要它们都在同一个数据库事务中,以便在出现问题时所有插入和更新都会回滚。

我在实体框架6.2之上使用Breeze 1.6,在SPA模板中使用Oracle Mgd数据访问12.2。

这4个表是A、B、C和D。表B、C和D是A的子表,每个表都携带A的PK作为外键。我最初按照我的应用程序A1、B1、C1、C2、C3、D1的要求在这个序列中编码插入,然后再加上一个Breeze SaveChanges。C3有Oracle触发器,在插入C3时更新A1和B1中的几个列。我的问题是实体框架没有插入我编码的序列(我知道我无法控制这个序列)。我实际上得到了这个序列: A1、C1、C2、C3、B1、D1,而且由于C3有一个更新A和B的触发器,所以它遇到了一个问题,因为B还没有插入。

所以我发现了一个问题:什么逻辑决定实体框架6的插入顺序,它建议使用多个SaveChanges来获得一些控制。这项工作如下所示:

  • A1,B1 SaveChanges
  • C1,C2,C3,D1 SaveChanges所有的触发器,包括A和B的C3更新现在都运行得很好。

但是Breeze/Entity将每个SaveChanges作为事务处理,因此现在我不能在第二个SaveChanges中出现错误时回滚这两个部分。如果第2部分失败,我必须自己编写第1部分回滚代码吗?还是有个我不知道的秘密?

我熟悉BeginTransaction、提交和回滚,但不确定如何/将其与混合中的Breeze和Entity相结合。微风抓取每个SaveChanges并将其自动包装在一个事务中。

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

谢谢你的帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-01-07 21:12:50

在这种情况下,我认为最好的做法是让Breeze客户端将其视为一个单独的保存,而服务器则将保存包拆分成正确的顺序保存。

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

下面是一个实现示例。

服务器

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

代码语言:javascript
复制
[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)];

    using (var tx = context.Database.BeginTransaction()) 
    {               
        // Save A and B
        Attach(context, ainfos);
        Attach(context, binfos);
        context.SaveChanges();

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

        tx.Commit();
    }

    // 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来调用命名保存端点。你只有在省下一笔费用时才会这么做。继续使用普通的SaveChanges端点进行其他保存。

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

代码语言:javascript
复制
var saveOptions = new SaveOptions({ resourceName: "SaveFeePayment" });
return myEntityManager.saveChanges(null, saveOptions);

交易?

我还没有测试这个例子100%是关于事务行为的。我不确定是使用现有的contextProvider.Context还是在方法开始时创建一个新的DbContext。不同之处在于如何处理数据库连接。请参阅微软的指引,并希望它在甲骨文中同样有效。

希望您以前的事务管理解决方案可以应用于上面的BeforeSaveFeePayment方法。

希望这能有所帮助。

票数 3
EN

Stack Overflow用户

发布于 2020-05-10 15:26:20

在我的例子中,使用.NET核心3.1上的Breeze,我必须设置IsTemporary=true,如下面的代码示例所示:

代码语言:javascript
复制
private static void Attach(BreezeContext context, List<EntityInfo> infos)
{
  foreach (var info in infos)
  {
    var efState = entityStateMap[info.EntityState];

    context.Entry(info.Entity).Property("Id").IsTemporary = true; //Explicitly set to true
    context.Entry(info.Entity).State = efState;
  }
}

默认情况下,这将设置为false,因此,SaveChanges()抛出了以下错误:

“无法插入标识列的显式值”

这可能是因为风中的一只虫子。同时,将IsTemporary设置为true至少可以解决问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59618530

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档