查找DbSet <t>的最大值(Id),其中T未知

时间:2018-04-15 14:04:36

标签: c# entity-framework linq generics

如何找到Id中最大的DbSet.Set<T>()

注意:不是 DbSet<TEntity>

我不知道运行时的类型。

上下文:我有20个表/实体,我正在使用 泛型 方法进行处理。

该过程涉及查找该表/实体的最大ID并将其与手头的记录进行比较。

如果记录的id大于数据库,那么它将被插入到数据库中。

到目前为止,我尝试过使用反射:

DbSet<T> table = DbContext.Set<T>();
var lastRecord = table.LastOrDefault();  // throws not supported error
var idProperty = lastRecord.GetType().GetProperties()
                                     .FirstOrDefault(p => p.Name.Equals("Id");  
int maxId = (int)idProperty.GetValue(lastRecord);

我也尝试过使用界面演员:

interface ICommonEntity
{                              // this interface allows the generic method
    string StringId { get;}    // to know how to handle entity Id's of 
    int? IntId { get; }        // different types (string vs int).
}                              
var whatever =  table.OrderByDescending(e => (e as ICommonEntity).IntId).FirstOrDefault();
int maxId = (whatever as ICommonEntity).IntId ?? 0;

但是上面会产生以下 错误

  

不支持输入类型为xx的'TypeAs'表达式。并检查yy类型。 LINQ to Entities查询中仅支持实体类型和复杂类型

其他数据:我的所有实体都拥有Id类型的列/属性int。 我已经完成的网络搜索主要指向类型已知的解决方案,例如TEntity,db.Users.xxx()等。

<小时/>

更新

在回复Ian's answer时,我无法直接使用Id。的为什么
我的一个实体有一个名为Id的字段,但类型为string

class EntityStringId : ICommonEntity
{
    public string Id { get; set; }
    public string StringId => Id;
    public int? IntId => null;
}

class EntityIntId : ICommonEntity
{
    public int Id { get; set; }
    public string StringId => null;
    public int? IntId => Id;
}

如果我尝试使用IntId进行排序,

private void SomeMethod<T>(string file)
    //where T : class           // original
    //where T : ICommonEntity   // cannot. DbContext.Set<T>(); requires class
    where T : class, ICommonEntity  // throws exception
    {
        var table_T = DbContext.Set<T>();
        var maxId = table_T.Max(e => e.IntId); // throws exception ↓
    }
  

LINQ to Entities不支持指定的类型成员'IntId'。   仅支持初始化程序,实体成员和实体导航属性。

为了更好的画面,我方法的逻辑:

private void ProcessCsvToDb<T>(
        DbSet<T> table,
        T csvRecord) where T : class
{
    var iRecord = csvRecord as ICommonEntity;
    T dbRecord = null;
    if (!string.IsNullOrEmpty(iRecord.StringId))
    {
        dbRecord = table.Find(iRecord.StringId);
    }
    else if (iRecord.IntId != null)
    {
        dbRecord = table.Find(iRecord.IntId);
    }
}

3 个答案:

答案 0 :(得分:4)

为了在没有基类/接口的情况下执行此操作,您需要手动编写表达式:

public static IOrderedQueryable<int> OrderById(Type entityType)
{
    var dbSet = context.Set(entityType);

    var item = Expression.Parameter(entityType, "item");
    var property = Expression.Property(item, "Id");
    var lambda = Expression.Lambda<Func<T, int>>(property, item);
    // the above generates:
    // item => item.Id

    return dbSet.OrderByDescending(lambda);
}

答案 1 :(得分:1)

您可以构建表达式以按ID排序,但DynamicQueryable class会为您执行此操作:

DbSet<T> table = assignFromSomeWhere();
var maxId = table.OrderBy("Id desc").FirstOrDefault();

DynamicQueryable还为您提供了不同的扩展方法(动态Where,Select)。显然,自己构建表达式会有更大的满足感,但有时它非常复杂,而且这个库有很多帮助。

答案 2 :(得分:0)

如果你有一个界面,正如评论中所讨论的那样,你有什么理由不能这样做以避免演员:

public static int? GetMaxId<T>(DBSet<T> dbSet)
  where T : ICommonEntity
{
  return dbSet.OrderByDescending(e => e.Id).FirstOrDefault();
}