代码之家  ›  专栏  ›  技术社区  ›  Shoter Liero

使用Linq to Entity中的Func作为泛型方法参数

  •  0
  • Shoter Liero  · 技术社区  · 11 年前

    我想创建基于实体框架的函数,它将更新SQL数据库中的表。

    这张桌子有亲子关系,所以我根据 parentID .

    我想要一个泛型函数,但我不知道如何从编译时不知道其定义的对象中获取ID。

    我试着用 Func 这可能非常整洁,但不能在Linq To Entities中使用。

    代码:

    protected static void update<TEntity>(DbSet<TEntity> set, int parentID,
      List<TEntity> entities, Func<TEntity, bool> isNew, Func<TEntity, int> getID = null, 
      Func<TEntity, int> getParentID, Action<TEntity, TEntity> updateSingleEntity)
            where TEntity : class
        {
            var currentIDs = entities.Select(e => getID(e));
    
            var newEntities = entities.Where(e => isNew(e));
            var existingEntities = entities.Where(e => !isNew(e));
    
            var deletedEntities 
              = set.Where(e => getParentID(e) == parentID && !currentIDs.Contains(getID(e)));
    
            foreach (var toAdd in newEntities)
            {
                set.Add(toAdd);
            }
    
            foreach (var toDelete in deletedEntities)
            {
                var entity = set.Find(getID(toDelete));
                set.Remove(entity);
            }
    
            foreach (var toUpdate in existingEntities)
            {
                var entity = set.Find(getID(toUpdate));
                updateSingleEntity(toUpdate, entity);
            }
        }
    

    我想这样使用这个函数:

       update(set, parentID, someList, e => e.ID != 0, e => e.ID, e => ParentID, somefunc);
    

    这是一个非常整洁的IMO。有什么方法可以实现这样的功能吗?

    我做不到(我试过了):

    具有ID属性的接口可以是整洁的。但我无法定义 父ID 然后在这个接口中,因为这个属性的名称在DB中的实体中有所不同。

    2 回复  |  直到 11 年前
        1
  •  1
  •   JotaBe    11 年前

    首先,正如你在评论中所说的,你需要使用 Expression<Func<TEntity,int>> getId .

    要使用它,请不要这样做 entities.Select(e => getID(e)) 但就是这样 entities.Select(getID) 。原因是 Select 需要lambda表达式,并且 getId 包含lambda表达式。这个 Expression<> 是必需的,所以它是EF工作所必需的表达式树,而不是编译表达式。你可以阅读 this for more information .

    至于接口的替代解决方案,您必须这样实现:

    public interface IId
    {
       public int GetId();
    }
    

    然后,您需要在所有类中实现它,例如:

    public int GetId() { return parentId; }
    

    或

    public int GetId(){return parentId;}
    

    您还必须向泛型方法添加接口约束,如下所示: where TEntity: class ,IId

    然而,您会发现两个问题:

    1. 您需要实施 GetId 对于所有实体类型(或至少对于将与thid方法一起使用的实体类型)
    2. 这个 获取ID 函数无法转换为表达式树,所以EF不知道如何将其转换为SQL表达式。(我不确定,但这可能会引发错误)。
        2
  •  1
  •   Ivan Stoev    11 年前

    我认为你的功能应该是这样的:

    protected static void Update<TEntity>(DbSet<TEntity> set, Expression<Func<TEntity, int>> getParentID, int parentID, List<TEntity> entities, Func<TEntity, int> getID, Action<TEntity, TEntity> updateSingleEntity)
        where TEntity : class
    {
        var filter = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(getParentID.Body, Expression.Constant(parentID)), getParentID.Parameters[0]);
        var targetEntities = set.Where(filter).ToDictionary(getID);
        foreach (var entity in entities)
        {
            var entityID = getID(entity);
            TEntity targetEntity;
            if (!targetEntities.TryGetValue(entityID, out targetEntity))
                set.Add(entity);
            else
            {
                updateSingleEntity(targetEntity, entity);
                targetEntities.Remove(entityID);
            }
        }
        if (targetEntities.Count > 0)
            set.RemoveRange(targetEntities.Values);
    }
    

    并这样使用:

    update(set, e => e.ParentID, parentID, someList, e => e.ID, somefunc);