Как передать определенный тип DbSet в класс, чтобы он определил тип нужной таблицы
У меня есть база данных с 2умя идентичными по архитектуре таблицами(отличаются только названия и данные в них).
public class CollageUnitsDb : DbContext
{
private const string CONNECTION_STRING = @"...";
public CollageUnitsDb() : base(CONNECTION_STRING)
{
}
public DbSet<Group> Groups { get; set; }
public DbSet<Teacher> Teachers { get; set; }
}
Чтобы привести их под 1 тип(DbSet<Group> и DbSet<Teacher>) я реализовал интерфейс у обоих классов
public sealed class Group : IUnit
{
public int Id { get; set; }
public string Name { get; set; }
}
public sealed class Teacher : IUnit
{
public int Id { get; set; }
public string Name { get; set; }
}
И есть 1 базовый класс, от которого наследуются пару классов. И чтобы в конструкторах наследников не писать один и то же код, я хочу это сделать в базовом классе. Но проблема в том, что я не знаю как мне Titels присваивать значение из нужной таблицы.
public abstract class BaseUnitsContainer
{
private HttpClass http = HttpClass.GetInstace();
private List<string> Titels { get; set; }
protected BaseUnitsContainer(IParser parser, IUnit unit)
{
Titels = http.GetRecentDataArray(parser);
if (Titels is null)
{
using (var context = new CollageUnitsDb())
{
Titels = context.Select(x => x.Name).ToList();
}
}
else
{
RefreshTable();
}
}
public abstract void RefreshTable();
protected List<string> GetTitels()
{
return Titels;
}
}
Краткая суть логики конструктора:
В него передается некий класс IParser с переопределенным методом парсинга, тот в свою очередь возвращает спарсенные данные. Если данных нет, мы должны их взять с таблицы(базы данных). Вот тут и получается загвоздка. Я могу с наследуемых классов передать в базовый метод нужный мне класс, чтобы он понял какую таблицу дать. Но проблема в том, что я не знаю как объяснить ему, какая таблица мне нужна
Классы наследники
public sealed class TeachersContainer : BaseUnitsContainer
{
private static readonly TeachersContainer teachersContainer = new TeachersContainer();
public List<string> TeacherTitels { get; set; }
private TeachersContainer() : base(new ParserTeachers(), new Teacher())
{
TeacherTitels = GetTitels();
}
}
public sealed class GroupsContainer : BaseUnitsContainer
{
private static readonly GroupsContainer groupsContainer = new GroupsContainer();
public List<string> GroupsTitels { get; set; }
private GroupsContainer() : base(new ParserGroups(), new Group())
{
GroupsTitels = GetTitels();
}
}
Часть кода, которая будет везде повторятся(её я хочу перенести в базовый класс)
Titels = http.GetRecentDataArray(parser);
if (Titels is null)
{
using (var context = new CollageUnitsDb())
{
Titels = context.Select(x => x.Name).ToList();
}
}
else
{
RefreshTable();
}
Ответы (2 шт):
Можно использовать дженерики
Для начала сделаем интерфейс, передавать в конструктор будем не DbSet, а интерфейс, который обязаны реализовать все классы, работающие с бд.
interface IDataStore<T>
{
//тут можно добавить и другие методы CRUD
Task<IEnumerable<T>> GetItems();
}
Реализуем этот интерфейс:
class TeacherDataStore : IDataStore<Teacher>
{
private CollageUnitsDb _context = new CollageUnitsDb();
public async Task<IEnumerable<Teacher>> GetItems()
{
return await Task.FromResult(_context.Teachers.ToList());
}
}
А наш абстрактный базовый класс сделаем дженериком:
abstract class BaseUnitsContainer<T> where T: IUnit
{
private HttpClass http = HttpClass.GetInstace();
public List<string> Titles{ get; set; }
protected BaseUnitsContainer(IParser parser, IDataStore<T> store)
{
Titles= http.GetRecentDataArray(parser);
if (Titels is null)
{
SetTitles(store);
}
else
{
RefreshTable();
}
}
protected async void SetTitles(IDataStore<T> store)
{
var items = await store.GetItems();
Titles= items.Select(x => x.Name).ToList();
}
}
Реализуем контейнер для класса Teacher:
class TeacherUnitsContainer : BaseUnitsContainer<Teacher>
{
public TeacherUnitsContainer(IParser parser, IDataStore<Teacher> store)
: base(parser, store) { }
}
Реализовал подобное следующим образом:
В служебном классе по работе с Raflaction реализовал функцию получения значения по имени переменной класса:
public static Object GetByName(this Object obj, string name)
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
PropertyInfo[] property = obj.GetType().GetProperties(flags);
foreach (var pi in property)
{
if (pi.Name == name) return pi.GetValue(obj);
}
return null;
}
далее, при обращении к базе:
void SomeFunction<TEntity>(TEntity obj) where TEntity : class
{
dynamic list = null;
dynamic dbset = null;
string typeName = obj.GetType().Name;
dbset = content.GetByName(typeName + "s");
list = (dbset as DbSet<TEntity>).Where(...).ToList();
...
}