Класс, связывающий лучшие практики в C # - PullRequest
15 голосов
/ 29 февраля 2012

Прежде всего, EF не подходит для нашей среды разработки, поэтому, пожалуйста, не отвечайте "просто используйте EF" ...

Я думаю, что это довольно стандартная дилемма, поэтому я уверен, что должна бытьтак, как это делают большинство профессионалов, с которыми я просто не сталкивался ... поэтому я надеюсь, что вы все покажете мне, что это такое.

Допустим, у вас есть следующие таблицы базы данных:

tblCompanies 
ID 
NAME

tblDepartments 
ID 
COMPANY_ID 
NAME

tblEmployees
ID 
DEPARTMENT_ID 
FIRSTNAME 
LASTNAME

... как лучше всего представить это в классах в вашем коде?

Я предполагаю, что best выглядит следующим образом:

public class Company
{
     public int ID { get; set; }
     public string Name { get; set; }
     public List<Department> Departments { get; set; }
}

public class Department
{
     public int ID { get; set; }
     public string Name { get; set; }
     public List<Employee> Employees { get; set; }
}

public class Employee
{
     public int ID { get; set; }
     public string FirstName { get; set;}
     public string LastName { get; set; }
}

Я полагаю, что это будет "ООП Правильный подход" к этому.Однако то, что всегда происходит, выглядит примерно так:

public class Department
{
     public int ID { get; set; }
     public string Name { get; set; }
     public int CompanyID { get; set; }
     public List<Employee> Employees { get; set; }
}

... главным образом потому, что когда вы извлекаете из базы данных только отдел, у вас будет только идентификатор компании, а не все другие необходимые атрибутычтобы полностью заполнить экземпляр класса Company.

(я использовал здесь довольно ванильный пример, но тот, который я фактически использую в моем текущем проекте, имеет 3 поля, которые он использует для связывания данных вместе, такмысль о том, чтобы иметь три одинаковых поля в нескольких классах, кажется мне неправильной)

Существует ли Наилучшая практика для этих сценариев?Мне не нравится мысль о сохранении одних и тех же данных в нескольких классах просто из-за лени, но я также не люблю возвращать экземпляр класса с заполненным только одним из его полей, потому что это все, что у меня было в то время,

Ответы [ 6 ]

5 голосов
/ 29 февраля 2012

Это распространенная проблема, которую ORM пытаются решить.Конечно, это непросто, в зависимости от того, что ваши хотят и каковы ваши ограничения .

Есть только два основных способа сохранить одну копиюинформации.Лениво загружайте данные в соответствии с запросом или загружайте их все для начала (Жадная загрузка).В противном случае вам придется дублировать данные.

При ленивой загрузке вы в основном настраиваете так, чтобы при переходе к свойству вы вызывали базу данных и собирали информацию, необходимую для загрузки объекта, представляющего свойство, к которому вы обращаетесь.Сложнее всего разобраться с проблемой SELECT N + 1.Вы сталкиваетесь с этой проблемой, когда заканчиваете итерацией набора родительских сущностей и запускаете ленивую загрузку для каждой дочерней сущности, что приводит к N + 1 вызовам базы данных для загрузки набора сущностей (1) и их дочерних элементов (N).

Жадная загрузка в основном говорит о загрузке всего, что вам нужно для начала.ORM (там, где они работают) хороши тем, что заботятся о многих деталях через LINQ и создают решения, которые могут быть производительными и обслуживаемыми, как правило, наряду со способностью позволять вам манипулировать использованием Greedy и Lazy Loading.

Другая важная особенность - это отношения многих ко многим.Вы должны убедиться, что у вас нет циклической инициализации, и получить весь багаж циклических зависимостей.Наверняка я скучаю по многим другим вещам.

По моему скромному мнению, я не настолько уверен, что существует лучшая практика столько же, сколько практик с некоторыми изим плохо - нет ничего идеального.Вы можете:

  1. Начать развертывание собственного реляционного картографа объектов , позволяющего избавиться от дубликата идентификатора

  2. Использованиеболее легкая платформа ORM для обработки некоторых из этих , позволяющая избавиться от дублированного идентификатора

  3. Создание специализированных запросов для загрузки совокупностей данных , позволяющих вамизбавьтесь от дубликата ID (* кашель * DDD)

  4. Просто сохраните дубликат идентификатора , как вы упомянули выше, и не беспокойтесь о созданииЯвная реляционная модель в вашем домене.

Этот вариант позволяет вам выбирать, что лучше всего, исходя из ваших ограничений.Это глубокая тема, и мой опыт ограничен ... так что возьмите то, что я говорю, с большим количеством соли .

3 голосов
/ 29 февраля 2012

Я не думаю, что есть руководство по "наилучшей практике" для такого рода вещей, и, конечно, это зависит от того, как будут использоваться ваши классы. Но в моем личном опыте я придерживался этого подхода:

public class Company
{
   public int ID { get; set; }
   public string Name { get; set; }

   public IEnumerable<Department> GetDepartments()
   {
      // Get departments here
   }
}

public class Department
{
   public int ID { get; set; }
   public string Name { get; set; }
   protected int CompanyID { get; set; }

   private Company _Company;
   public Company Company
   {
      get
      {
         // Get company here
      } 
   }

   public IEnumberable<Employee> GetEmployees()
   {
      // Get employees here
   }
}

public class Employee
{
   public int ID { get; set; }
   public string Name { get; set; }
   protected int DepartmentID { get; set; }

   private Department _Department;
   public Department Department
   {
      get
      {
         // Get department here
      } 
   }

   public IEnumberable<Employee> GetEmployees()
   {
      // Get employees here
   }
}

В некоторых случаях я предоставлял некоторые свойства «навигации» моих классов как public (например, CompanyID и DepartmentID), чтобы предотвратить создание экземпляра нового класса для получения значения, которое уже было загружено.

Как уже отмечали другие, вы также можете имитировать "ленивую загрузку", но это потребует дополнительных усилий с вашей стороны.

2 голосов
/ 29 февраля 2012

Я думаю, это зависит от требований. Вам нужно пройти вверх (получить компанию из отдела, отдел от сотрудника и т. Д.). Если вы это сделаете, то лучше всего предоставить вам средство для этого. В идеале это должно быть что-то вроде свойства компании или департамента, конечно, вы не захотите получать данные, которые вам не нужны, поэтому вы, скорее всего, сохраните частный идентификатор компании и будете иметь публичную функцию getCompany, которая запрашивает данные .

1 голос
/ 29 февраля 2012

Я считаю, что это не совсем вопрос ООП, в вашем случае у вас просто есть модель базы данных (представление базы данных в классах), которая не содержит никакой логики, и все классы используются как структуры, и это правильный путь. сопоставить вашу базу данных с классами - структурами. Таким образом, в вашем следующем модуле, который будет представлять логику вашей программы, вы должны отобразить свой модуль базы данных на реальные классы, которые будут содержать логику (я имею в виду методы, которые ее реализуют), конечно, если они вам действительно нужны. Так что, по моему мнению, вопрос ОО должен быть в логической части вашего приложения. С другой стороны, вы можете взглянуть на nhibernate и на то, как выполненное там отображение даст подсказку для реализации модели базы данных bes.

0 голосов
/ 04 марта 2012

Есть еще один вариант.Создайте класс DataController, который обрабатывает загрузку и «запоминание» ваших объектов.DataController поддерживает словарь [CompanyIDs, объекты Company] и [DepartmentIDs, объекты Department].Когда вы загружаете новый отдел или компанию, вы ведете запись в этом словаре DataController.Затем, когда вы создаете экземпляр нового отдела или сотрудника, вы можете либо напрямую установить ссылки на родительские объекты, либо вы можете использовать объект Lazy [Company / Department] и установить его с помощью лямбда-выражения (в конструкторе), который будет поддерживать область действия объекта.DataController без ссылки непосредственно внутри объектов.Одна вещь, которую я забыл упомянуть, вы также можете поместить логику в метод getter / get для словарей, которые запрашивают базу данных, если определенный идентификатор не найден.Использование всего этого вместе позволяет вашим классам (моделям) быть очень чистыми и в то же время достаточно гибкими в отношении того, когда и как загружаются их данные.

0 голосов
/ 29 февраля 2012

Я полагаю, что именно так будут выглядеть ваши классы в NHibernate:

public class Company
{
     public int ID { get; set; }
     public string Name { get; set; }
     public IList<Department> Departments { get; set; }
}

public class Department
{
     public int ID { get; set; }
     public string Name { get; set; }
     public Company Company { get; set; }
     public IList<Employee> Employees { get; set; }
}

public class Employee
{
     public int ID { get; set; }
     public string FirstName { get; set;}
     public string LastName { get; set; }
     public Department Department { get; set; }
}

Обратите внимание, что существует способ перехода от сотрудника к отделу и от отдела к компании (в дополнение к тому, что вы уже указали).

В NHibernate есть все возможности, чтобы это работало.И это работает очень, очень хорошо.Основной трюк - прокси-объекты во время выполнения, чтобы обеспечить ленивую загрузку.Кроме того, NHibernate поддерживает множество различных способов быстрой и ленивой загрузки именно так, как вы этого хотите.

Конечно, вы можете получить те же функции без NHibernate или аналогичного ORM, но почему бы не использоватьпросто используйте многофункциональную основную технологию вместо ручного кодирования вашей собственной функции бедных пользовательских ORM?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...