breezejs отменить saveChanges из-за сбоя проверки в BeforeSaveEntity

Я создал свой собственный ContextProvider, субкласс, классифицированный из EFContextProvider. В BeforeSaveEntity я запускаю некоторую бизнес-логику для проверки транзакции. Мне нужно, чтобы обновления были "все или ничего", поэтому, если 3-й объект в коллекции не проходит проверку, весь пакет следует отбросить, даже если Ive уже вернул "true" для первых 2 объектов.

У меня есть свойство уровня класса, которое устанавливается, когда любая сущность терпит неудачу. В последней проверке в BeforeSaveEntities я могу получить значение флага.

Я думаю, что именно здесь я могу прервать обновление, но не знаю как. Я очищаю карту? Или скинуть ошибку?

Кроме того, мне нужно будет повторно запросить в БД мои процедуры проверки. Я прочитал несколько постов, в которых говорится о создании второго экземпляра контекста для выполнения запросов к текущим значениям. Есть ли какие-то документы по этому вопросу, или я должен знать об этом?

Спасибо

2 ответа

Решение

В вашем вызове BeforeSaveEntities вы можете генерировать исключение EntityErrorsException: Вот пример, где мы генерируем исключение, если есть попытка сохранить какие-либо объекты "Порядок" в пакете сохранения:

[HttpPost]
public SaveResult SaveWithEntityErrorsException(JObject saveBundle) {
  ContextProvider.BeforeSaveEntitiesDelegate = ThrowEntityErrorsException;
  return ContextProvider.SaveChanges(saveBundle);
}

private Dictionary<Type, List<EntityInfo>> ThrowEntityErrorsException(Dictionary<Type, List<EntityInfo>> saveMap) {
  List<EntityInfo> orderInfos;
  if (saveMap.TryGetValue(typeof(Order), out orderInfos)) {
    var errors = orderInfos.Select(oi => {
      return new EntityError() {
        EntityTypeName = typeof(Order).FullName,
        ErrorMessage = "Cannot save orders with this save method",
        ErrorName = "WrongMethod",
        KeyValues = new object[] { ((Order) oi.Entity).OrderID },
        PropertyName = "OrderID"
      };
   return new EFEntityError(oi, "WrongMethod", "Cannot save orders with this save method", "OrderID");
    });
    var ex =  new EntityErrorsException("test of custom exception message", errors);
    // if you want to see a different error status code use this.
    // ex.StatusCode = HttpStatusCode.Conflict; // Conflict = 409 ; default is Forbidden (403).
    throw ex;
  }
  return saveMap;
}

И вы должны использовать BeforeSaveEntities исключительно вместо BeforeSaveEntity, поскольку ваша логика сохранения становится более сложной.

У меня было требование выполнить вычисления на стороне сервера для сущностей, которые были изменены на клиенте - без сохранения - и вернуть результаты клиенту. Решение, основанное на названных спасениях Breeze, которое я придумала, может быть полезным и в этой ситуации.

Я добавил следующий метод в базовый класс для моих контроллеров Breeze.

protected SaveResult OverrideSaveChanges(JObject saveBundle, Action<List<object>> action, bool shouldSave = false)
{
    var saveChangesDelegate = new SaveChangesOverride(action, shouldSave);

    return saveChangesDelegate.Execute(saveBundle, ContextProvider);

Это позволяет конкретным контроллерам реализовать именованные сохранения очень просто. SaveBundle плюс Action<List<object>> передаются в метод OverrideSaveChanges. Действие может внести любые необходимые изменения в сущности, и эти изменения будут переданы обратно клиенту. Объекты в списке - это объекты, которые клиент распознал как имеющие изменения и отправленные на сервер для именованного сохранения. При желании вы можете передать аргумент shouldSave со значением true, чтобы сохранить сущности - по умолчанию - false.

OverrideChanges делегатов для SaveChangesOverride для большей части тяжелой работы.

public class SaveChangesOverride
{
    public SaveChangesOverride(Action<List<object>> action, bool shouldSave = false)
    {
        Action = action;
        ShouldSave = shouldSave;
    }

    private readonly Action<List<object>> Action;
    private readonly bool ShouldSave;

    public List<object> Entities;

   public SaveResult Execute(JObject saveBundle, ContextProvider contextProvider)
    {
        contextProvider.BeforeSaveEntitiesDelegate = OnBeforeSaveEntities;

        contextProvider.SaveChanges(saveBundle);

        return new SaveResult
        {
            Entities = Entities,
            KeyMappings = new List<KeyMapping>()
        };
    }

    private Dictionary<Type, List<EntityInfo>> OnBeforeSaveEntities(Dictionary<Type, List<EntityInfo>> arg)
    {
        Entities = arg.SelectMany(x => x.Value).Select(x => x.Entity).ToList();

        Action(Entities);

        if (!ShouldSave)
        {
            return new Dictionary<Type, List<EntityInfo>>();
        }

        return arg;
    }
}

Хотя у нас есть доступ ко всем измененным объектам в saveBundle, фактически выполнение изменений в OnBeforeSaveChanges позволяет нам работать с объектами, а не с JObject.

Кроме того, contextProvider.SaveChanges должен вызываться независимо от того, хотим ли мы сохранить сущности. Это то, что вызывает OnBeforeSaveEntities для вызова. Чтобы гарантировать, что объекты не сохраняются, несмотря на вызов SaveChanges (если это то, что нужно), а не на возврат аргумента из OnBeforeSaveEntities, возвращается пустой словарь.

Чтобы гарантировать, что изменения вернутся к клиенту, ссылка на сущности сохраняется в OnBeforeSaveEntities. Это используется в Execute для подготовки SaveResult, который заполнен измененными объектами.

Другие вопросы по тегам