泛型方法可以返回我指定的属性

时间:2015-07-02 13:25:43

标签: c# linq entity-framework

昨天Ognyan帮我写了很多这样的方法:

public static class DbSetExtensions
{
    public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
    {
        var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
        return !exists ? dbSet.Add(entity) : null;
    }
}

当这确实添加了我的实体时,我返回实体的新ID,但如果它存在则返回null。

有时我会想要取回已经存在的实体的ID。但是,我的实体的Key ID属性将根据模型而有所不同。例如,地址模型的键是AddressId,Profile的键是ProfileId。

因此,我想修改此查询(或创建它的另一个版本)以接受Id属性名称作为参数。 (或使用EF识别主键。)并执行以下操作:

public static class DbSetExtensions
{
    public static T AddIfNotExists<T>(this DbSet<T> dbSet,  
        T entity,  
        Expression<Func<T, bool>> predicate = null,  
        Expression<Func<T, TId>> keyColumnName) where T : class, new()
    {
        var exists = predicate != null ? dbSet.Where(predicate).Select(e => e.keyColumnName) : dbSet.Any();
        return !exists ? dbSet.Add(entity) : exists ;
    }
}

我不确定这是否是定义我想要使用的属性的正确方法。我也意识到这可能不如只做一个Any(),但有时我们可能需要获取ID。

我还想更好地理解所有这些项目的含义以及它们如何协同工作。我已经全身心投入,得到了点点滴滴,但是还没有把整个难题放在一起。

2 个答案:

答案 0 :(得分:0)

你的情况应该更简单:

public static class DbSetExtensions
{
    public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity,
        Expression<Func<T, bool>> predicate = null) where T : class, new()
    {
        T exists = predicate != null ?
            dbSet.Where(predicate).FirstOrDefault():
            dbSet.FirstOrDefault();
        return exists == null ?
            dbSet.Add(entity):
            exists;
    }
}

然后,你似乎知道PK属性,你可以从返回的值中检查它。

答案 1 :(得分:0)

好吧也许有更好的解决方案,但你可以试试这个:

   public static TResult AddIfNotExists<T,TResult>(this DbSet<T> dbSet,  
                                                   T entity, 
                                                   Expression<Func<T, bool>> predicate,
                                                   Expression<Func<T, TResult>> columns  
                                                  ) where T : class, new()
   {
       if (predicate==null || columns==null)
           throw new Exception();

       //Find if already exist the entity and select its key column(s)
       var result =  dbSet.Where(predicate).Select(columns).FirstOrDefault();

       //the result could be a reference type (string or anonymous type) or a value type
       if (result!=null && !result.Equals(default(TResult)))
           return result;

       var newElement = dbSet.Add(entity);

       //Compile the Expresion to get the Func
       var func = columns.Compile();
       //To select the new element key(s), add the element to an array (or List) to apply the Select method and get the keys
       var r = new[]{newElement};

       return r.Select(func).FirstOrDefault();
   }

正如您所看到的,我总是希望参数predicate检查元素是否已经存在,并columns选择一个或多个键,以防您有一个具有复合PK的实体(但请注意可以从该实体中选择所需的所有属性,而不仅仅是键。然后,我应用表达式来过滤并在元素存在的情况下选择键。如果它不存在,我将实体添加到其DbSet<T>,但是存在问题。如何获得新的元素键?好吧,我发现的唯一解决方案是将该元素添加到数组中,编译表达式以获得Func<T,TResult>,并将Func<T,TResult>应用于数组,(我知道这可能不是最好的解决方案,但它应该工作)。

要调用此扩展方法,例如,对于具有复合PK的实体,您可以执行以下操作:

.AddIfNotExists(element, e => e.UserName== "admin", e => new{ e.Id1,e.Id2});