В общем, описал все сущности БД в виде отдельных классов.
Для каждой сущности создал собственный класс Provider, который описывает CRUD операции.
Подскажите, стоит ли все Provider'ы объединять в нечто более, например в DbProvider, который содержит в себе все провайдеры к сущностям?
В каком месте лучше задавать логику порядок вставки данных в сущности?
Как лучше описать связи между сущностями?
Попробуйте зайти с другой стороны, и посмотреть, что сейчас существует для работы с БД. Например, для этого очень хорошо подходит книга Архитектура корпоративных программных приложений за авторством коллектива разработчиков, главный из которых Мартин Фаулер.
В книге большую часть занимает описания паттернов для работы с БД. Кроме того, книга вводит стандартные названия для этих паттернов. То, что вы назвали провайдером, скорее всего является хранилищем (repository). Использование единой терминологии упрощает понимание. Кроме того, там описаны важные построители запросов (query builders), преобразователи данных (data mappers) и единицы работы (units of work).
Если вы пишите ORM самостоятельно, вам, несомненно, потребуется всё это реализовать. Второе соображение — можно посмотреть на существующие ORM, и определить, как эти вопросы решаются там. Например, в Entity Framework есть понятие наборов данных (DbSet<TEntity>
). Это не паттерн, это название класса, в котором реализованы сразу несколько паттернов, и, в частности, хранилища.
Все они объединены в класс DbContext
, который является аналогом вашего DbProvider
. Он также реализует несколько паттернов, в том числе единицу работы. Вся логика вставки реализована в единице работы, в методе DbContext.SaveChanges
. Сначала с помощью DbSet<TEntity>.Add
надо зарегистрировать в контексте новые объекты, а затем вызвать DbContext.SaveChanges
, который решит, что надо записать сначала, а что потом.
Контекст также выполняет обратную операцию заполнения объектов значениями из таблиц, то есть реализует паттерн преобразователь данных. Я не смотрел глубоко в код, но, насколько я понимаю, используется один универсальный преобразователь для всех таблиц и классов.
И последний вопрос — о связях между сущностями. В EF и NHibernate связи реализуются с помощью так называемых навигационных свойств:
[Table("Categories")]
public class Category
{
[Key]
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
[Table("Products")]
public class Product
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
В данном примере все свойства напрямую отображаются на поля в таблицах, кроме двух навигационных: Category.Products
и Product.Category
. При этом EF сам понимает, что эти свойства навигационные и их не надо загружать из базы или сохранять в ней. Также он понимает, что речь идёт о связи один-ко-многим и что связь происходит по полю Product.CategoryId
. Впрочем, как видно из примера, умолчания EF можно менять с помощью атрибутов.
EF реализует паттерн ленивая загрузка (lazy loading), поэтому при определённых условиях можно подгружать объекты, доступные через навигационные свойства. Если это окажется слишком медленным, можно дать подсказку EF, какие объекты потребуются точно, и которые лучше подгрузить сразу.
Возможно, в вашей ORM нужно ориентироваться на подобные решения. Или для начала поиграться с существующими ORM, такими как Entity Framework, NHibernate, Dapper. Возможно, вы откажитесь от идеи создавать собственную ORM, поскольку большая часть того, что вам нужно, уже реализована.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Необходимо сделать авто обновление для программы созданной в winformsНашел решение тут, но по внедрению в проект, плохо работает, либо скачивает...
Очередной вопрос по навигации в WPFПытаюсь использовать MVVM Light Toolkit 5