Шаблон проектирования для уровня доступа к данным
Вы можете почувствовать, что это домашнее задание, потому что я сожалею. Я искал, но не смог найти правильный ответ.
Итак, мой вопрос:
У меня есть несколько классов, и каждый класс имеет способ сохранения. Поэтому я создал отдельный класс для обработки базы данных.
namespace HospitalMgt.Data
{
public static class DBConnection
{
public static string constr = "Data Source=ABD;Initial Catalog=HospitalMgt;User Id=sa;Password=123";
public static SqlConnection con;
// public static SqlCommand com;
public static SqlConnection OpenConnection()
{
con= new SqlConnection(constr);
con.Open();
return con;
}
}
}
Тем не менее, я не считаю целесообразным реализовать все классы с классом DBConnection.
Мой вопрос:
- Какой шаблон дизайна подходит для решения этой проблемы?
- Хорошо ли создавать DBConnection как класс? (Или это должен быть интерфейс)
Я нашел несколько статей о слоях DA, используя метод Factory, но, по моим сведениям, этот шаблон не подходит для моей ситуации.
Ответы
Ответ 1
Обычно, если я не могу использовать какой-либо существующий фреймворк, я использую шаблоны Repository и Active.
Для простоты вы можете использовать только шаблон Repository. Я обычно определяю это так:
public interface IEntity<T> { }
// Define a generic repository interface
public interface IRepository<TKey, TEntity>
where TEntity : IEntity<TKey>
{
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
IEntity<TKey> Get(TKey key);
IEnumerable<TEntity> GetRange(IEnumerable<TKey> keys);
IEnumerable<TEntity> GetAll();
// ..., Update, Delete methods
}
// Create an abstract class that will encapsulate the generic code
public abstract class Repository<TKey, TEntity> : IRepository<TKey, TEntity>
where TEntity : IEntity<TKey>
{
protected Repository(/*parameter you may need to implement the generic methods, like a ConnectionFactory, table name, entity type for casts, etc */) { }
public override void Insert(IEntity<TKey> entity)
{
// do the insert, treat exceptions accordingly and encapsulate them in your own and more concise Exceptions, etc
}
// ...
}
// Create the entities classes, one for each table, that will represent a row of that table
public class Car : IEntity<string> {/* Properties */}
// Create a specific repository for each table
// If the table have a composed key, just create a class representing it
public class CarRepository : Repository<string, Car>
{
public CarRepository() {/* pass the base parameters */}
// offer here your specific operations to this table entity
public IEnumerable<Car> GetByOwner(PersonKey ownerKey)
{
// do stuff
}
}
Очевидно, что при выполнении ваших собственных реализаций вы должны учитывать безопасность потоков, обеспечивая хорошее использование транзакций, особенно в разных хранилищах сущностей.
// simple example
ITransaction t = TransactionFactory.GetNewTransaction();
t.begin();
try{
// create person entity
personRepository.Add(person, t);
// create cars assigned to person
carRepository.AddRange(cars, t);
t.commit();
}catch(Exception){
t.rollback();
}
Просто убедитесь, что вы действительно хотите создать свой собственный DAL, поскольку он может оказаться чрезвычайно сложным, особенно пытаясь разработать наиболее универсальное решение.
Ответ 2
Я предлагаю использовать ORM, Entity Framework или NHibernate будет хорошо. Тогда вам не нужно беспокоиться о контексте db или создавать инструкции SQL.
Ответ 3
Прежде всего, я хотел бы рекомендовать вам статью Шаблоны проектирования для сохранения данных Джереми Миллером.
Есть несколько типов слоев доступа к данным:
Ответ 4
Я предлагаю вам использовать RepositoryBase для всех этих общих операций. Если вы решите использовать ORM для доступа к данным, хорошо подумать о реализации репозиториев на основе репозитория Generic Type.
Вот хорошая статья:
http://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/
Ответ 5
Он слишком стар, но только пришел к этому вопросу и не смог удержаться, чтобы оставить свои мысли.
Я нашел репозиторий с UnitOfWork с некоторым спуском ORM - это хороший подход. Это минимизирует большинство проблем.
UoW, упомянутый в ссылке выше, может быть введен в репозиторий. Это увеличивает гибкость использования. Кроме того, весь код связи с БД централизован в одном месте. Пример не завершен, но является точкой запуска.
Шаблон репозитория, упомянутый в ссылке выше, на самом деле является базовым базовым классом. Вы можете создать новый класс для каждого конкретного репозитория, производного от него.
Общий репозиторий считается анти-паттерном; Есть много статей в Интернете, которые объясняют это.
Почему общий репозиторий является антишаблоном?
- Хранилище является частью моделируемого домена, и этот домен не является общим.
- Не каждый объект может быть удален.
- Не каждый объект может быть добавлен
- Не у каждого объекта есть хранилище.
- Запросы сильно различаются; API хранилища становится таким же уникальным, как и сам объект.
- Для
GetById()
типы идентификаторов могут быть разными.
- Обновление определенных полей (DML) невозможно.
- Общий механизм запросов является обязанностью ORM.
- Большинство ORM предоставляют реализацию, которая очень похожа на Generic Repository.
- Хранилища должны реализовывать СПЕЦИАЛЬНЫЕ запросы для сущностей, используя общий механизм запросов, предоставляемый ORM.
- Работа с составными ключами невозможна.
- В любом случае, это приводит к утечке логики DAL в Сервисах.
- Критерии предиката, если вы принимаете их в качестве параметра, должны быть предоставлены из сервисного уровня. Если это класс, специфичный для ORM, он пропускает ORM в Сервисы.
Я предлагаю вам прочитать эти (1, 2, 3, 4, 5) статьи, объясняющие, почему общие репозиторий является анит-шаблоном.
Решение:
- Напишите абстрактный универсальный репозиторий, который обернут конкретным репозиторием. Таким образом, вы можете управлять общедоступным интерфейсом, но при этом иметь возможность повторного использования кода, получаемого из общего хранилища.
- Используйте универсальный репозиторий, но используйте композицию вместо наследования и не выставляйте ее домену в качестве контракта.
В любом случае, не подвергайте Generic Repository вызывающему коду. Кроме того, не подвергайте IQueryable
из конкретных хранилищ.