通用Linq查询以获取具有指定键的实体数组

时间:2014-11-15 14:57:19

标签: c# linq entity-framework generics

我需要一个像这样的通用查询但在网络上找不到任何东西(可能是因为搜索不好!)

int[] ids = new int[]{1,2,3} 
var q = context.where(o=> ids.contains(o.id));

我的实体类型未知,但我确定:

  1. 主键是单
  2. 主键类型为int
  3. 无法更改实体定义
  4. 所以签名是这样的:

    Public IQueryable<T> GetRecords(int[] keys)
    {
       var dbset = context.Set<T>();
       ...//the generic query
    }
    

    在通用和非通用实现中,同样的SQL输出(具有相同的性能)也很重要。

3 个答案:

答案 0 :(得分:1)

您可以使用多态来解决您的问题。 Crete是一个声明整数Id属性并在您的实体中实现的接口,因为它对所有这些属性都是通用的。然后对泛型参数T进行约束,强制它成为实现接口的类型,因此编译器将知道T将是具有Id属性的类型,你将能够访问它:

// IId is the interface that should be implemented
public IQueryable<T> GetRecords<T>(int[] keys) where T : IId
{
   var dbset = context.Set<T>();
    ...//the generic query
}

答案 1 :(得分:1)

private IDictionary<Type, string> _primaryKeyCache = new Dictionary<Type, string>();

public IQueryable<T> GetRecords(int[] keys)
{
   var dbSet = context.Set<T>();

   var type = typeof(T);

   string primaryKeyName;
   if(!_primaryKeyCache.TryGetValue(type, out primaryKeyName)){
      var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
      var set = objectContext.CreateObjectSet<YourEntity>();
      var keyName = set.EntitySet.ElementType
                                            .KeyMembers
                                            .Select(k => k.Name)
                                            .First();

      _primaryKeyCache[type] = primaryKeyName = keyName;
   }

   return dbSet.DynamicContains<T, int>(primaryKeyName, keys);
}

private static IQueryable<T> DynamicContains<T, TProperty>(
        this IQueryable<T> query, 
        string property, 
        IEnumerable<TProperty> items)
    {
        var pe = Expression.Parameter(typeof(T));
        var me = Expression.Property(pe, property);
        var ce = Expression.Constant(items); 
        var call = Expression.Call(typeof(Enumerable), "Contains", new[] { me.Type }, ce, me);
        var lambda = Expression.Lambda<Func<T, bool>>(call, pe);
        return query.Where(lambda);
    }

那应该做你需要的。

答案 2 :(得分:0)

创建一个新界面,例如IKeyedByIntEntity

public interface IKeyedByIntEntity
{
  int Key { get; }
}

让查询中使用的所有实体都实现该接口。在理想的世界中,该字段具有相同的名称,因此您可以将其用作界面中的属性名称,因此无需更改实体的实现。

public class MyEntity : IKeyedByIntEntity
{
  public int Key { get; set; }
  public string Value { get; set; }
}

更新您的GetRecords函数或类以要求接口,例如其中一个......

public class Fetch
{
  public IQueryable<T> GetRecords<T>(int[] keys)
    where T : IKeyedByIntEntity
  {
    IQueryable<T> records = this.context.Where(e => keys.Contains(e.Key));
    return records;
  }
}

public class Fetch<T> where T : IKeyedByIntEntity
{
  public IQueryable<T> GetRecords(int[] keys)
  {
    IQueryable<T> records = this.context.Where(e => keys.Contains(e.Key));
    return records;
  }
}