Шаблон проектирования для уровня доступа к данным

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

Итак, мой вопрос:

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

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.

Ответ 4

Я предлагаю вам использовать RepositoryBase для всех этих общих операций. Если вы решите использовать ORM для доступа к данным, хорошо подумать о реализации репозиториев на основе репозитория Generic Type.

Вот хорошая статья:

http://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/

Ответ 5

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

Я нашел репозиторий с UnitOfWork с некоторым спуском ORM - это хороший подход. Это минимизирует большинство проблем.

UoW, упомянутый в ссылке выше, может быть введен в репозиторий. Это увеличивает гибкость использования. Кроме того, весь код связи с БД централизован в одном месте. Пример не завершен, но является точкой запуска.

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

Общий репозиторий считается анти-паттерном; Есть много статей в Интернете, которые объясняют это.

Почему общий репозиторий является антишаблоном?

  1. Хранилище является частью моделируемого домена, и этот домен не является общим.
    • Не каждый объект может быть удален.
    • Не каждый объект может быть добавлен
    • Не у каждого объекта есть хранилище.
    • Запросы сильно различаются; API хранилища становится таким же уникальным, как и сам объект.
    • Для GetById() типы идентификаторов могут быть разными.
    • Обновление определенных полей (DML) невозможно.
  2. Общий механизм запросов является обязанностью ORM.
    • Большинство ORM предоставляют реализацию, которая очень похожа на Generic Repository.
    • Хранилища должны реализовывать СПЕЦИАЛЬНЫЕ запросы для сущностей, используя общий механизм запросов, предоставляемый ORM.
  3. Работа с составными ключами невозможна.
  4. В любом случае, это приводит к утечке логики DAL в Сервисах.
    • Критерии предиката, если вы принимаете их в качестве параметра, должны быть предоставлены из сервисного уровня. Если это класс, специфичный для ORM, он пропускает ORM в Сервисы.

Я предлагаю вам прочитать эти (1, 2, 3, 4, 5) статьи, объясняющие, почему общие репозиторий является анит-шаблоном.

Решение:

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

В любом случае, не подвергайте Generic Repository вызывающему коду. Кроме того, не подвергайте IQueryable из конкретных хранилищ.