由于BeforeSaveEntity中的验证失败,breezejs取消了saveChanges

时间:2013-12-02 21:40:29

标签: breeze abort savechanges

我创建了自己的ContextProvider,子类来自EFContextProvider。在BeforeSaveEntity中,我正在运行一些业务逻辑来验证事务。我需要更新为“全部或全部”,因此如果集合中的第三个实体未通过验证,则应丢弃整个批次,即使我已经为前两个实体返回“true”。

我有一个类级属性,在任何实体失败时都会被设置。在BeforeSaveEntities的最终检查中,我可以获得该标志的值。

我认为这是我可以中止更新的地方,但不知道如何。我清除地图吗?或抛出错误?

另外,我需要重新查询数据库以获取验证例程。我已经阅读了一些关于创建上下文实例的帖子,以便查询当前值。是否有一些关于这样做的文档,或者我需要注意的问题?

感谢

2 个答案:

答案 0 :(得分:2)

BeforeSaveEntities 调用中,您可以抛出 EntityErrorsException :这是一个示例,如果尝试在保存中保存任何“Order”对象,则会抛出异常捆绑:

[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,因为您的保存逻辑变得更加复杂。

答案 1 :(得分:0)

我要求对客户端上已更改的实体执行服务器端计算 - 不保存 - 并将结果返回给客户端。我提出的基于Breeze named saves的解决方案在这种情况下也很有用。

我将以下方法添加到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方法。该操作可以对所需的实体进行任何修改,并且这些更改将传播回客户端。列表中的对象是客户端识别为具有更改的实体,并将其发送到服务器以进行命名保存。 (可选)您可以传递值为true的shouldSave参数以保存实体 - 默认值为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返回arg,则返回一个空字典。

为确保更改将其返回给客户端,对实体的引用将保存在OnBeforeSaveEntities中。这在Execute中用于准备使用修改后的实体填充的SaveResult。