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

时间:2015-09-03 07:44:57

标签: c# entity-framework linq-to-entities

我想基于Entity Framework创建函数,它将更新SQL数据库中的Table。

此表具有父子关系,因此我根据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属性的接口可以很整洁。但是我无法在此界面中定义parentID,因为此属性的名称因DB中的实体而异。

2 个答案:

答案 0 :(得分:1)

首先,正如您在评论中被告知的那样,您需要使用Expression<Func<TEntity,int>> getId

要使用它,请不要执行此操作entities.Select(e => getID(e)),而只需执行此操作entities.Select(getID)。原因是Select期望lamba表达式,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. GetId函数无法转换为表达式树,因此EF不知道如何将其转换为SQL表达式。 (我不确定,但它甚至可能引发错误)。

答案 1 :(得分:1)

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

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);