我认为在这种情况下最好的做法是让 Breezeclient 将其视为单个保存,而 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)];
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;
}
}
客户
在客户端上,您需要使用named save 调用SaveFeePayment 端点。 只有在保存费用时,您才会这样做。继续使用普通的SaveChanges 端点进行其他保存。
通过指定resourceName调用特殊端点:
var saveOptions = new SaveOptions({ resourceName: "SaveFeePayment" });
return myEntityManager.saveChanges(null, saveOptions);
交易?
我还没有测试这个例子是 100% 的交易行为。我不确定我们是应该使用现有的contextProvider.Context 还是在方法开始时创建一个新的 DbContext。区别在于如何处理数据库连接。请参阅Microsoft's guidance,并希望它与 Oracle 一样。
希望您之前的事务管理解决方案可以应用于上面的BeforeSaveFeePayment 方法。
希望这会有所帮助。