Какой менеджер времени жизни мне зарегистрировать мой DbContext в контейнере Unity при написании приложения WPF? - PullRequest
0 голосов
/ 23 мая 2018

Я пишу новое приложение на C # поверх платформы Prism 6.3 , используя известный шаблон проектирования MVVM.Я использую контейнер Unity IoC, чтобы помочь мне управлять своими зависимостями.

Я использую Entity Framework Core для взаимодействия с базой данных.Однако я не хочу тесно связывать свое приложение с Entity Framework Core, поэтому я реализовал шаблоны Repository и UnitOfWork, чтобы мне было проще заменить реализацию Entity Framework Core при необходимости.

Моя реализация репозитория предоставляет метод с именем Save(), который вызывает метод SaveChanges() EF Core.Репозитории внедряются в мой бизнес-сервис, так что мой бизнес-сервис предоставляет один метод для выполнения одной задачи.Например, если я хочу создать новый заказ, я бы вызвал метод Create(orderViewModel), который внутренне вызывает метод Add() и Save() для OrderRepository.

Кроме того, UnitOfWork предоставляет Save(), BeginTransaction(), Commit() и Rollback() методы, которые позволяют мне контролировать поведение транзакции.Другими словами, это даст мне возможность либо фиксировать, либо откатывать транзакцию SQL при необходимости.

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

OrdersService.Create(orderViewModel); // this will call the `Add` and the `Save()` methods on the OrderRepository;

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

using(var transaction = UnitOfWork.BeginTransaction())
{
    try 
    {
        var order = OrdersService.Create(orderViewModel);
        OrdersService.CreateRange(order.Id, orderItemsViewModel);
        transaction.Commit();
    } 
    catch(Exception e)
    {
        Log.Add(e);
        transaction.RollBack();
    }
}

Во втором приведенном выше примере, хотя каждый из OrdersService.Save и OrdersService.SaveRange вызывает метод SaveChanges(), данные которого не передаютсябазу данных, поскольку я обертываю их транзакцией.

Вопрос : что LifeTimeManager , если я должен зарегистрировать DbContext, IUnitOfWork и каждый из моих репозиториев с помощью?

В веб-среде я регистрировал бы все, используя PerRequestLifetimeManager, тогда во время запроса я использую тот же DbContext и всеОн работает нормально, и DbContext располагается в конце http-запроса.Но не уверен, как зарегистрировать все в приложении WPF, где я все еще могу использовать транзакцию, чтобы контролировать все, позволяя хранилищу вызывать SaveChanges()

При необходимости здесь моя реализация EntityRepository

public class EntityRepository<TEntity, TKeyType> : IRepository<TEntity, TKeyType>
    where TEntity : class
    where TKeyType : struct
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> DbSet;

    public EntityRepository(DbContext context)
    {
        Context = context;
        DbSet = context.Set<TEntity>();
    }

    public TEntity Get(TKeyType id)
    {
        return DbSet.Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return DbSet.ToList();
    }

    public bool Any(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Any(predicate);
    }

    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Where(predicate);
    }

    public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.SingleOrDefault(predicate);
    }

    public virtual TEntity Add(TEntity entity)
    {
        var record = Context.Add(entity);
        record.State = EntityState.Added;

        return entity;
    }

    public virtual IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
    {
        Context.AddRange(entities);

        return entities;
    }

    public void Remove(TEntity entity)
    {
        Context.Remove(entity).State = EntityState.Deleted;
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        Context.RemoveRange(entities);
    }

    public void Update(TEntity entity)
    {
        DbSet.Attach(entity);
        var record = Context.Entry(entity);
        record.State = EntityState.Modified;
    }

    public IQueryable<TEntity> Query()
    {
        return DbSet;
    }

    public void Save()
    {
        Context.SaveChanges();
    }
}

А вот моя единица выполнения работы

public sealed class UnitOfWork : IUnitOfWork
{
    private bool IsDisposed = false;
    private readonly DbContext Context;

    public IOrderRepository Orders { get; private set; }
    public IOrderItemRepository OrderItems { get; private set; }

    public UnitOfWork(DbContext context)
    {
        Context = context;
        Orders = new OrderRepository(context);
        OrderItems = new OrderItemRepository(context);
    }

    public int Save()
    {
        Context.SaveChanges();

        return 0;
    }

    public void Dispose()
    {
        Dispose(true);
    }

    public IDatabaseTransaction BeginTransaction()
    {
        return new EntityDatabaseTransaction(Context);
    }

    private void Dispose(bool disposing)
    {
        if (IsDisposed)
        {
            return;
        }

        if (disposing)
        {
            Context.Dispose();
        }

        IsDisposed = true;
    }
}

1 Ответ

0 голосов
/ 23 мая 2018

Временное время (экземпляр на представление) будет подходящим вариантом, если ваш DI не поддерживает область видимости, но тогда вам нужно будет абстрагировать ваш DbContext, передаваемый через репозиторий и unitOfWork, в противном случае - новые экземплярыDbContext будет передан туда.При создании страницы создается новый экземпляр, а при удалении от этого представления этот DBContext должен быть удален.UnitOfWork будет следовать по тому же пути, что и вы не хотите, чтобы UnitOfWork охватывал несколько экземпляров DBContext.См. http://blogs.microsoft.co.il/gilf/2010/02/07/entity-framework-context-lifetime-best-practices/. В противном случае, если ваш DI имеет концепцию иерархии контейнеров и вы можете создать область видимости контейнера для представления, тогда в этом случае будет работать синглтон, и вам не понадобятся упомянутые выше абстракции.и было бы немного легче работать.

...