Как использовать шаблон единицы работы в ASP.NET Core

автор vadim


В большинстве бизнес-приложений вы будете хранить и извлекать данные из хранилища данных, выполняя операции CRUD (создание, чтение, обновление и удаление). В связи с этим существует несколько технологий и инструментов, которые вы можете использовать. Например, вы можете выбрать одну из доступных платформ ORM, таких как Entity Framework, Dapper или NHibernate.

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

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

Чтобы использовать примеры кода, представленные в этой статье, в вашей системе должна быть установлена ​​Visual Studio 2022. Если у вас еще нет копии, вы можете скачать Visual Studio 2022 здесь.

Создайте проект веб-API ASP.NET Core 7 в Visual Studio 2022.

Прежде всего, давайте создадим проект ASP.NET Core 7 в Visual Studio 2022. Выполните следующие действия:

  1. Запустите интегрированную среду разработки Visual Studio 2022.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «ASP.NET Core Web API» из списка отображаемых шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и расположение нового проекта.
  6. При желании установите флажок «Поместить решение и проект в один каталог», в зависимости от ваших предпочтений.
  7. Нажмите “Далее.
  8. В окне «Дополнительная информация» оставьте флажок «Использовать контроллеры (снимите флажок, чтобы использовать минимальные API)», поскольку в этом примере мы не будем использовать минимальные API. Оставьте «Тип аутентификации» как «Нет» (по умолчанию).
  9. Убедитесь, что флажки «Включить поддержку Open API», «Настроить для HTTPS» и «Включить Docker» сняты, поскольку мы не будем использовать эти функции здесь.
  10. Щелкните Создать.

Мы будем использовать этот проект веб-API ASP.NET Core 7 для работы с шаблоном проектирования единиц работы в следующих разделах.

Что такое шаблон проектирования единиц работы?

Шаблон проектирования единиц работы гарантирует целостность и непротиворечивость данных в приложениях. Это гарантирует, что все изменения, внесенные в несколько объектов в приложении, будут зафиксированы в базе данных или отменены. Он обеспечивает организованный и последовательный способ управления изменениями и транзакциями базы данных.

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

Реализация шаблона проектирования единицы работы на C#

Чтобы реализовать шаблон проектирования единицы работы, вы должны указать операции, которые поддерживает единица работы, в интерфейсе или контракте, а затем реализовать эти методы в конкретном классе. Вы можете воспользоваться преимуществами любой технологии доступа к данным, например Entity Framework, Dapper или ADO.NET.

Типичная единица реализации работы будет включать компоненты, перечисленные ниже:

  • Интерфейс для единицы работы. Операции, которые будет выполнять единица работы, определяются контрактом или интерфейсом, содержащим объявление всех методов добавления, изменения и удаления данных, а также фиксации или отката изменений в базе данных.
  • Конкретная реализация интерфейса единицы работы. Конкретная реализация интерфейса, который мы только что описали. Эта реализация будет включать в себя все операции, выполняющие какие-либо транзакции или операции с данными, а также операции по откату или фиксации изменений.
  • Класс репозитория и его интерфейс. Код, необходимый для доступа или изменения данных, содержится в классе репозитория и его интерфейсе. Другими словами, у вас должен быть класс репозитория, который использует каждый из методов интерфейса, и интерфейс для вашего репозитория, определяющий разрешенные действия.

Итак, приступим!

Создайте класс CustomDbContext

В Entity Framework или Entity Framework Core DbContext представляет собой шлюз к базе данных. Ниже приведен класс DbContext, который может быть создан автоматически при использовании Entity Framework Core. Мы будем использовать этот класс несколько раз в следующих фрагментах кода.

public class CustomDbContext : DbContext
{
    public CustomDbContext(DbContextOptions<CustomDbContext> options)
       : base(options)
    {
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    }
    public DbSet<Author> Authors { get; set; }
}

Определить интерфейс для единицы работы

Создайте новый файл .cs с именем IUnitOfWork.cs в своем проекте и введите следующий код.

public interface IUnitOfWork : IDisposable
{
    void Commit();
    void Rollback();
    IRepository<T> Repository<T>() where T : class;
}

Реализовать конкретный класс единицы работы

Затем вы должны создать конкретную реализацию интерфейса IUnitOfWork. Для этого создайте новый класс с именем UnitOfWork в файле с таким же именем и расширением .cs и введите следующий код.

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private readonly DbContext _dbContext;
    private bool _disposed;
    public UnitOfWork(DbContext dbContext)
    {
        _dbContext = dbContext;
    }
    public void Commit()
    {
        _dbContext.SaveChanges();
    }
    public void Rollback()
    {
        foreach (var entry in _dbContext.ChangeTracker.Entries())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.State = EntityState.Detached;
                    break;
            }
        }
    }
    public IRepository<T> Repository<T>() where T : class
    {
        return new Repository<T>(_dbContext);
    }
    private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _dbContext.Dispose();
            }
        }
        this.disposed = true;
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Определить интерфейс репозитория

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

Следующим шагом является определение интерфейса для вашего репозитория. Этот интерфейс должен предоставлять объявления методов, поддерживаемых репозиторием.

Создайте новый интерфейс с именем IRepository в файле с именем IRepository.cs и замените созданный по умолчанию код следующим кодом.

public interface IRepository<T> where T : class
{
    T GetById(object id);
    IList<T> GetAll();
    void Add(T entity);
}

Реализовать интерфейс репозитория

Наконец, вы должны реализовать интерфейс репозитория, который мы только что создали. Для этого создайте новый файл с именем Repository.cs и замените сгенерированный по умолчанию код следующим кодом.

public class Repository<T> : IRepository<T> where T : class
{
    private readonly CustomDbContext _dbContext;
    private readonly DbSet<T> _dbSet;
    public T GetById(object id)
    {
        return _dbSet.Find(id);
    }
    public Repository(DbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<T>();
    }
    public IList<T> GetAll()
    {
        return _dbSet.ToList();
    }
    public void Add(T entity)
    {
        _dbSet.Add(entity);
    }
}

Зарегистрируйте DbContext

В качестве бонуса вы должны зарегистрировать DbContext как службу в файле Program.cs, чтобы вы могли воспользоваться внедрением зависимостей для извлечения экземпляров DbContext в классах Repository и UnitOfWork.

Это можно сделать двумя способами: с помощью метода AddDbContext или с помощью метода AddDbContextPool. Метод AddDbContextPool предпочтительнее, поскольку использование пула экземпляров поможет вашему приложению масштабироваться.

builder.Services.AddDbContextPool<CustomDbContext>(o=>o.UseSqlServer("Specify the database connection string here..."));

В этой статье мы создали универсальную реализацию репозитория, которая использует интерфейс IRepository и класс Repository. Вы можете создать репозиторий для определенного объекта, создав класс, реализующий универсальный интерфейс IRepository, как показано ниже.

public class AuthorRepository : IRepository<Author>
{
   //Write code here to implement the members of the IRepository interface
}

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

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

Related Posts

Оставить комментарий