Я думаю, что в этом случае лучше всего позволить Breeze client рассматривать его как одно сохранение, в то время как server разбивает пакет сохранения, чтобы сохранить его в правильный порядок.
Пусть Breeze ContextProvider
десериализует пакет сохранения, а затем использует хук BeforeSaveEntities для внесения изменений вручную. Наконец, пометьте сущности как Unchanged
, чтобы ContextProvider не пытался их снова сохранить.
Вот пример реализации.
Сервер
На сервере, в контроллере Breeze создайте новый метод сохранения, который добавляет 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;
}
}
Клиент
На клиенте вы нужно будет вызвать конечную точку SaveFeePayment
, используя с именем save . Вы будете делать это только при сохранении комиссионного платежа . Продолжайте использовать обычную SaveChanges
конечную точку для других сохранений.
Вызовите специальную конечную точку, указав resourceName
:
var saveOptions = new SaveOptions({ resourceName: "SaveFeePayment" });
return myEntityManager.saveChanges(null, saveOptions);
Транзакции?
У меня нет протестировал этот пример на 100% о поведении транзакции. Я не уверен, должны ли мы использовать существующий contextProvider.Context
или создавать новый DbContext в начале метода. Разница в том, как обрабатываются соединения с базой данных. См. руководство Microsoft и надеемся, что оно работает так же с Oracle.
Надеемся, что ваше предыдущее решение по управлению транзакциями можно применить к методу BeforeSaveFeePayment
, описанному выше.
Надеюсь, это поможет.