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

C# 使用Linq to Entity中的Func作为泛型方法参数,c#,entity-framework,linq-to-entities,C#,Entity Framework,Linq To Entities,我想创建基于实体框架的函数,它将更新SQL数据库中的表 此表具有父子关系,因此我正在基于parentID更新子组 我想要一个泛型函数,但我不知道如何从编译时定义未知的对象中获取ID 我尝试使用Func,这可能非常简洁,但它不能用于Linq to实体 代码: protected static void update<TEntity>(DbSet<TEntity> set, int parentID, List<TEntity> entities, Func&

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

此表具有父子关系,因此我正在基于
parentID
更新子组

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

我尝试使用
Func
,这可能非常简洁,但它不能用于Linq to实体

代码:

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);
        }
    }
在我看来,这很好。有没有办法实现这样的功能

我做不到(我试过):


具有ID属性的接口可以是整洁的。但是我无法在这个界面中定义
parentID
,因为这个属性的名称在DB中的各个实体中都不同。

首先,正如在注释中告诉您的,您需要使用
表达式getId

要使用它,不要这样做
entities。选择(e=>getID(e))
,而只是选择这个
entities。选择(getID)
。原因是
Select
需要lamba表达式,而
getId
包含lambda表达式。
表达式是必需的,因此它是EF工作所必需的表达式树,而不是编译表达式。你可以阅读

对于接口的替代解决方案,您必须按照以下方式实施:

   update(set, parentID, someList, e => e.ID != 0, e => e.ID, e => ParentID, somefunc);
public interface IId
{
   public int GetId();
}
public int GetId() { return parentId; }
update(set, e => e.ParentID, parentID, someList, e => e.ID, somefunc);
然后,您需要在所有类中实现它,例如:

   update(set, parentID, someList, e => e.ID != 0, e => e.ID, e => ParentID, somefunc);
public interface IId
{
   public int GetId();
}
public int GetId() { return parentId; }
update(set, e => e.ParentID, parentID, someList, e => e.ID, somefunc);

您还应该向通用方法添加接口约束,如下所示:
where tenty:class,IId

但是,您会发现两个问题:

  • 您需要为所有实体类型实现
    GetId
    (或者至少对于那些将与thid方法一起使用的实体类型)
  • 无法将
    GetId
    函数转换为表达式树,因此EF不知道如何将其转换为SQL表达式。(我不确定,但它甚至可能会引起错误)

  • 我认为您的函数应该如下所示:

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

    Func将不起作用。Use expression我试图使用它,但当我使用它时,出现了以下错误:“getID”是一个“变量”,但它的用法类似于“方法”。声明:表达式getID。用法:getID(toDelete)