泛型 Linq 查询,用于获取具有指定键的实体数组

本文关键字:数组 实体 查询 Linq 用于 获取 泛型 | 更新日期: 2023-09-27 18:30:33

我需要对这样的东西进行通用查询,但在 Web 中找不到任何内容(可能是因为搜索不好!

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

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

  1. 主键为单个
  2. 主键类型为 int
  3. 无法更改实体定义

所以签名是这样的:

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

同样重要的是,在泛型和非泛型实现中具有相同的 SQL 输出(具有相同的性能)。

泛型 Linq 查询,用于获取具有指定键的实体数组

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

这应该可以满足您的需求。

您可以使用多态性解决问题。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
}

创建一个新接口,例如 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;
  }
}