Как через обобщенный метод получить количество строк в EF таблице?

У меня есть класс DataProvider, в нем свойство Context с объектом-наследником Ef'овского контекста.

public class DataProvider
{
    private CargoDbContext Context { get; }
    ...

Соответственно, через этот Context я могу получить доступ к таблицам, количеству записей и т.д.:

var carsCount = Context.Cars.Count();

Хочу сделать в DataProvider обобщенный метод, чтобы узнавать количество записей как-то так:

var data = new DataProvider();
var carsCount = data.FetchCount<Cars>();

Написал вот такую реализацию:

public int FetchCount<T>()
{
    string tableName = typeof(T).Name;
    var efTable = Context.GetType().GetProperty(tableName);
    var countMethod = efTable.GetType().GetMethod("Count");

    return (int)countMethod.Invoke(efTable, null);
}

Размышлял так: в EF есть классы сущностей по именам таблиц в БД. У ef контекста есть свойства с именами такими же как у классов сущностей и эти свойства - суть DbSet, у которых можно запросить метод Count().

Значит я указываю методу тип, получаю имя типа в виде строки и получаю на контексте свойство с таким именем. Это будет DbSet. На нем я получаю метод Count и вызываю этот метод, передавая в качестве экземпляра этот самый DbSet. И все вроде бы идет неплохо: в efTable действительно лежит значение типа DbSet, но вот countMethod упорно остается null. Я пробовал еще вызывать вот так .GetMethod("Count", BindingFlags.Public | BindingFlags.Instance), но это не помогает.

Как в итоге сделать правильно такую задачу?


Ответы (1 шт):

Автор решения: pinkertem

Воспользовавшись ссылкой @4per, я адаптировал его решение для своего вопроса и получил следующий метод:

public class DataProvider
{
    private CargoDbContext Context { get; }
    ...

    public int FetchCount<T>()
    {
        MethodInfo countMethod = typeof(Queryable)
            .GetMethods()
            .Where(m => m.Name == nameof(Queryable.Count))
            .Single(m => m.GetParameters().Length == 1)
            .MakeGenericMethod(typeof(T));

        var dbSet = Context.GetType().GetProperty(typeof(T).Name).GetValue(Context);
        return (int)countMethod.Invoke(null, new object[] { dbSet });
    }

UPD. Оказалось, можно сделать проще, без рефлексии:

public int FetchCount<T>() where T : class
{
    return Context.Set<T>().Count();
}

Кроме того, это оказалось особенно удобно в более сложном фрагменте, задействующем уже несколько методов:

public IEnumerable<T> FetchRange<T>(int startIndex, int count) where T : class
{
    return Context.Set<T>().Skip(startIndex).Take(count);
}
→ Ссылка