Как через обобщенный метод получить количество строк в 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 шт):
Воспользовавшись ссылкой @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);
}