代码之家  ›  专栏  ›  技术社区  ›  morganpdx

如何在泛型方法中导航子实体框架对象?

  •  0
  • morganpdx  · 技术社区  · 15 年前

    我的存储库中有一个通用方法,用于更新edmx模型中所有对象的公用属性:

        private void SetUpdateParams(TEntity entity)
        {
            PropertyInfo prop = typeof(TEntity).GetProperty("CommonProperty");
    
            prop.SetValue(entity, "Some Value", null);
        }
    

    此属性由add、update和delete方法调用。例子:

        public void Delete(TEntity entity)
        {
            SetUpdateParams(entity);
            _objectSet.DeleteObject(entity);
            txDB.SaveChanges();
        }
    

    这一切工作得非常好,直到我尝试在级联删除场景中包含子元素。由于我必须使用的存储过程要求设置此特定属性,因此我现在必须在关系中递归,并在对象集中的任何加载子对象上设置此属性。问题是我似乎想不出任何办法来做那件事。以前有人做过这样的事吗?

    1 回复  |  直到 15 年前
        1
  •  1
  •   kdawg    15 年前

    这不是最简单的解决方案,可能有点乏味,但我通过跨越对象图、在找到属性时更新属性以及跟踪我访问过的内容,实现了您在我的项目中所需的功能。它给了计算机科学本科课程一个很好的倒叙。=)

    基本上,获取对象,获取其属性,并将其推送到堆栈上。对于堆栈上的每个属性,测试它是否是您要查找的属性。如果匹配则处理,如果是简单数据类型则忽略,如果是复杂对象则添加到堆栈。

    有几件事对我的实现有帮助:

    • 试着聪明地处理要遍历的堆栈上的复杂属性类型。我使用了一个接口,该接口指定此复杂类型可能具有需要更新的属性。
    • 我将此功能作为BaseContext的一部分(继承EF的 ObjectContext )自定义Save()方法。我叫这个修理工然后 base.SaveChanges()

    正如我所说,这不是一个简单、直接的问题,但我的代码处理深度对象图并更新多个属性实例。如果有兴趣的话,我可以继续使用一些伪代码。


    如代码所示,我对更新图形中可能出现的任意数量对象的当前用户名和日期时间值感兴趣。

    笔记:

    • 接口 IInsertedInfo 包含我们需要更新的属性。所以如果一个对象实现 ,我们知道要更新它的属性。
    • 实现的对象 IRequiresCurrentUserDateTime 是一个在其图中某处有一个实现 .
    • ObjectStateManager 对于需要保存/更新的对象。

    我不认为这是最简洁的解决方案,但它对我很好。另外,检查 IRequiresCurrentUserDateTime

    private void HandleInsertedUserNames(string userName)
    {
        // grab any entity that requires a current user value
        var requiresUser = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified)
                                                  .Where(ose => ose.Entity is IRequiresCurrentUserDateTime
                                                                || ose.Entity is IInsertedInfo)
                                                  .Select(ose => ose.Entity);
    
        var now = DateTime.Now;
        object current;
        var seen = new HashSet<object>();
        var stack = new Stack<object>();
        // for each entity requiring a current user value...
        foreach (var obj in requiresUser)
        {
            // traverse its object graph and update any objects that implement IRequiresCurrentUserDateTime
            stack.Push(obj);
            while (stack.Count > 0)
            {
                current = stack.Pop();
                if (current != null && !seen.Contains(current))
                {
                    // mark object as seen
                    seen.Add(current);
                    // if object implements IInsertedInfo, then set its property
                    if (current is IInsertedInfo)
                    {
                        (current as IInsertedInfo).UserName = userName;
                        (current as IInsertedInfo).DateTime = now;
                        // we can continue on to the next object in the stack if we've hit an IInsertedInfo
                        continue;
                    }
    
        // REMOVED FILTERING TESTS I USED TO REDUCE SEARCH SPACE (e.g.: is modified?)
    
                    if (current is IRequiresCurrentUserDateTime)
                    {
                        // push any instance, public properties and push them to the stack
                        current.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
        // HERE YOU CAN FILTER THE PROPERTY COLLECTION VIA WHERE CLAUSES (e.g.: only certain namespace, type, etc)
                               // select the actual value of the property
                               .Select(type => type.GetValue(current, null))
                               // further filter -- only values NOT already in the requiresUser
                               // list and those that implement IRequiresCurrentUserDateTime or IInsertedInfo
                               .Where(value => !requiresUser.Contains(value)
                                               && (value is IRequiresCurrentUserDateTime
                                                   || value is IInsertedInfo))
                               .ToList().ForEach(stack.push);
                    }
                }
            }
        }
    
    }