2016-03-11 2 views
0

Я работаю над заданием для школы и стараюсь реализовать как можно больше функций только для обучения. Следовательно, я создал универсальный картограф, который сопоставляет таблицы баз данных с объектами, чтобы увидеть, что возможно. 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, но он кажется недействительным, потому что мне также нужно выяснить тип между <> и я не могу отправить какие-либо пользовательские запросы извне ...

Может быть, решить его с другой точки зрения?

Я действительно могу использовать некоторые советы о том, как решить эту проблему.

ответ

0

Самый простой способ приблизиться к этому будет состоять в том, чтобы иметь свойство коллекции ленивой загрузки, что ему нужно. Я также рекомендовал бы использовать IEnumerable<T> вместо ICollection<T>, потому что это представляет собой просмотр только для чтения того, что в настоящее время находится в базе данных, и никто не должен его каким-либо образом изменять.

public class Student 
{  
    private readonly Lazy<IEnumerable<Course>> courses; 

    public int Id { get; set; } 
    public IEnumerable<Course> Courses => this.courses.Value; 

    public Student() 
    { 
     this.courses = new Lazy<IEnumerable<Course>>(LoadCourses); 
    } 

    private IEnumerable<Course> LoadCourses() 
    { 
     var sql = "custom SQL query that uses this.Id after it's loaded"; 
     return MapEntitiesFromDb(sql); 
    } 
} 

Я только рекомендовать этот подход, потому что вы сказали, что это просто академическое упражнение, чтобы помочь вам узнать об инструментах, доступных для вас. В реальной производственной среде этот подход очень быстро станет громоздким, и я бы рекомендовал вместо этого использовать Entity Framework (что может быть что-то еще, о чем вы могли бы захотеть узнать).

+0

Hello Chris! Спасибо, что ответили. Я использовал Entity для других назначений, но для этого нам не разрешалось. Ваш ответ тот, кого я так ищу, спасибо вам большое! Я нашел еще один метод, и я должен выполнить sql в get acceptor of Courses, если список пуст, но это кажется более понятным и гибким. –