【问题标题】:breeze - modify an entity, on the server, based upon it's navigational properties, before saving微风 - 在保存之前根据其导航属性在服务器上修改实体
【发布时间】:2016-04-02 02:36:09
【问题描述】:

有没有办法在服务器端轻而易举地在 BeforeSaveEntity(或保存前的任何其他地方)中获取实体导航属性的“当前”值?目前,我的意思是数据库中存在的内容,并合并了任何传入的更改。这不是为了验证,而是我正在计算基于两者的父属性(我不希望在客户端上)的值父字段和子字段...

例如,

public class Parent {
  public ICollection<Child> Children{ get; set; }
}

。 . .

protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
  if (entityInfo.Entity.GetType() == typeof(Parent) &&
  (entityInfo.EntityState == EntityState.Added || entityInfo.EntityState == EntityState.Updated)) {

   // Lazy load Parent's Children collection out of breeze's context 
   // so items are "current' (existing merged with changes)

   Parent parent = (Parent)entityInfo.Entity;
   Context.Entry(parent).Collection(p => p.Children).Load();

   // this throws exception Member 'Load' cannot be called for property
   // 'Children' because the entity of type 'Parent' does not exist in the context.
  }
}  

我认为它们还没有在 DBContext 中。我能想到的就是从数据库中检索现有的子项,然后在 BeforeSaveEntities 中手动合并更改,这很麻烦。

【问题讨论】:

    标签: breeze


    【解决方案1】:

    Breeze 用于保存的 DbContext 中未启用延迟加载。原因详见this SO answer

    您应该在separate DbContext 中加载任何其他实体。


    这是我在项目中如何做到这一点的示例。或许 MergeEntities 和 DetachEntities 方法应该包含在 Breeze 中,以便更简单地执行此操作。

    protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
    {
        // create a separate context for computation, so we don't pollute the main saving context
        using (var newContext = new MyDbContext(EntityConnection, false))
        {
            var parentFromClient = (Parent)saveMap[typeof(Parent)][0].Entity;
    
            // Load the necessary data into the newContext
            var parentFromDb = newContext.Parents.Where(p => p.ParentId == parentFromClient.ParentId)
                .Include("Children").ToList();
    
            // ... load whatever else you need...
    
            // Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
            var objectContext = ((IObjectContextAdapter)newContext).ObjectContext;
            var objectStateEntries = MergeEntities(objectContext, saveMap);
    
            // ... perform your business logic...
    
            // Remove the entities from the second context, so they can be saved in the original context
            DetachEntities(objectContext, saveMap);
        }
        return saveMap;
    }
    
    /// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
    Dictionary<ObjectStateEntry, EntityInfo> MergeEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
    {
        var oseEntityInfo = new Dictionary<ObjectStateEntry, EntityInfo>();
        foreach (var type in saveMap.Keys)
        {
            var entitySet = this.GetEntitySetName(type);
            foreach(var entityInfo in saveMap[type])
            {
                var entityKey = oc.CreateEntityKey(entitySet, entityInfo.Entity);
                ObjectStateEntry ose;
                if (oc.ObjectStateManager.TryGetObjectStateEntry(entityKey, out ose))
                {
                    if (ose.State != System.Data.Entity.EntityState.Deleted)
                        ose.ApplyCurrentValues(entityInfo.Entity);
                }
                else
                {
                    oc.AttachTo(entitySet, entityInfo.Entity);
                    ose = oc.ObjectStateManager.GetObjectStateEntry(entityKey);
                }
    
                if (entityInfo.EntityState == Breeze.ContextProvider.EntityState.Deleted)
                {
                    ose.Delete();
                }
                oseEntityInfo.Add(ose, entityInfo);
            }
        }
        return oseEntityInfo;
    }
    
    /// Remove the entities in saveMap from the ObjectContext; this separates their navigation properties
    static void DetachEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
    {
        foreach (var type in saveMap.Keys)
        {
            foreach (var entityInfo in saveMap[type])
            {
                try
                {
                    oc.Detach(entityInfo.Entity);
                }
                catch
                { // the object cannot be detached because it is not attached
                }
            }
        }
    }
    

    【讨论】:

    • 我看到了那个答案,但我需要导航的当前状态以及任何传入的更改。所以我需要遍历保存图并为我的“父母”合并任何“孩子”更改?
    • 您将如何处理需要了解实体及其相关实体的传入状态的保存?
    • 我在答案中添加了一个长示例。希望这会有所帮助。
    • 4 年来一直是 Breezejs 的超级快乐用户,我将在我未来的项目中重用这项技术。您在此处提供的 MergeEntitiesDetachEntitiesutility 方法是黄金,并且肯定会在微风包或其文档中的某个地方结束,因为我们经常遇到这些场景(对于高级微风用户)
    猜你喜欢
    • 1970-01-01
    • 2014-01-19
    • 2014-04-08
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多