Я работаю над заданием для школы и стараюсь реализовать как можно больше функций только для обучения. Следовательно, я создал универсальный картограф, который сопоставляет таблицы баз данных с объектами, чтобы увидеть, что возможно. Db в этом случае является локальным. Я знаю, что я делаю нагрузки и множество звонков и должен обходить это совсем по-другому, но ...C# Generic binding
Все работает по назначению, за исключением случаев, когда класс имеет коллекцию другого класса.
Пример:
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
Мой метод для заполнения списка всех студентов в базе данных.
public List<TModel> MapEntitiesFromDb<TModel>(string tablename, string customquery = "") where TModel : class, new()
{
try
{
sql = ValidateSelectSql(tablename, customquery);
}
catch (AccessViolationException ex) { Console.WriteLine(ex.Message); }
command.CommandText = sql;
command.Connection = conn;
List<TModel> list = new List<TModel>();
try
{
using (conn)
{
Type t = new TModel().GetType();
conn.Open();
using (reader = command.ExecuteReader())
{
if (t.GetProperties().Length != reader.FieldCount)
throw new Exception("There is a mismatch between the amount of properties and the database columns. Please check the input code and try again.");
//Possible check is to store each column and property name in arrays and match them to a new boolean array, if there's 1 false throw an exception.
string columnname;
string propertyname;
//Pairing properties with columns
while (reader.Read())
{
TModel obj = new TModel();
for (int i = 0; i < reader.FieldCount; i++)
{
columnname = reader.GetName(i).ToString().ToLower();
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo propertyinfo in properties)
{
propertyname = propertyinfo.Name.ToLower();
if (propertyname == columnname)
{
propertyinfo.SetValue(obj, reader.GetValue(i));
break;
}
}
}
list.Add(obj);
}
}
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
return list;
}
My ValidateSelectSql возвращает строку sql, которая должна использоваться в запросе.
После вызова:
List<Student> = MapEntitiesFromDb<Student>("students");
Он возвращает список всех студентов, как задумано.
Дела идут не так, когда я добавить коллекцию, например:
class Student {
public Student()
{
this.Courses = new List<Course>();
string customsqlquery = ::: this works and is tested! :::
Courses = MapEntitiesFromDb<Course>("", customsqlquery);
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Course> Courses;
}
Список курсов вернулся с пустыми и с некоторой помощью инструмента отладчика я узнал в момент создания объекта свойство Id является 0 конечно. В моем запросе я фильтрую идентификатор студента, но во время выполнения метода для заполнения списка Курсов в конструкторе Id студента всегда будет 0, потому что он установлен на более позднем этапе, и результат не будет курсов в списке ,
Мне интересно, должен ли я поставить чек для свойства ICollection после того, как будут установлены другие свойства, и если это так, будет выполняться метод объекта, который в результате выполняет метод, который теперь находится внутри конструктора?
Я не могу вызывать какие-либо методы в TModel, иначе было бы так же просто, как найти, имеет ли TModel свойство collection и вызывает obj.FillCollection(); после того, как свойство Id было назначено в методе GetEntitiesFromDb.
Я также думал о рекурсии. Снова мне придется найти, если obj имеет свойство коллекции, а затем вызывает GetEntitiesFromDB, но он кажется недействительным, потому что мне также нужно выяснить тип между <> и я не могу отправить какие-либо пользовательские запросы извне ...
Может быть, решить его с другой точки зрения?
Я действительно могу использовать некоторые советы о том, как решить эту проблему.
Hello Chris! Спасибо, что ответили. Я использовал Entity для других назначений, но для этого нам не разрешалось. Ваш ответ тот, кого я так ищу, спасибо вам большое! Я нашел еще один метод, и я должен выполнить sql в get acceptor of Courses, если список пуст, но это кажется более понятным и гибким. –