Правильное удаление контекста с помощью инъекции зависимостей в шаблоне работы в WPF - PullRequest
0 голосов
/ 15 ноября 2018

Я пытался использовать DI в своем шаблоне Unit of Work / Repository в WPF.Проблема, с которой я сталкиваюсь в настоящее время, заключается в том, что я делаю вызов в хранилище, такое как _UserRepo.Add (User), и выдается исключение.Каждый новый вызов репозитория генерирует исключение, потому что контекст никогда не удаляется.

Что я пробовал

Единица работы

 public class UnitOfWork : IUnitOfWork
{
    private DbContextTransaction _trans;
    private BomConfiguratorContext _context;

    public UnitOfWork(BomConfiguratorContext context)
    {
        _context = context;
        _trans = context.Database.BeginTransaction();
    }

    public void Dispose()
    {
        try
        {
            _context.SaveChanges();
            _trans.Commit();
        }
        catch (Exception)
        {
            _trans.Rollback();
        }
        finally
        {
            _context.Dispose(); //This obviously does not work
        }
    }
}

Фабрика единиц работы

 public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private BomConfiguratorContext _context;
    public UnitOfWorkFactory(BomConfiguratorContext context)
    {
        _context = context;
    }
    public UnitOfWork Create()
    {
        return new UnitOfWork(_context);
    }
}

Мой универсальный репозиторий

public interface IRepository<TEntity> where TEntity : class
{
    void Add(TEntity entity);
    void AddRange(IEnumerable<TEntity> entities);

    void Remove(TEntity entity);
    void RemoveRange(IEnumerable<TEntity> entities);

    TEntity Get(int id);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);

    void Update(TEntity entity);
}

Реализация универсального репозитория

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly BomConfiguratorContext Context;

    public Repository(BomConfiguratorContext context)
    {
        Context = context;
    }
    public virtual void Add(TEntity entity)
    {
        Context.Set<TEntity>().Add(entity);
    }

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

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return Context.Set<TEntity>().Where(predicate);
    }

    public TEntity Get(int id)
    {
        return Context.Set<TEntity>().Find(id);
    }

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

    public void Remove(TEntity entity)
    {
        Context.Set<TEntity>().Remove(entity);
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        Context.Set<TEntity>().RemoveRange(entities);
    }
    public void Update(TEntity entity)
    {
        Context.Set<TEntity>().Attach(entity);
        Context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
    }
}

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

public class UserRepository : Repository<User>,IUserRepository
{
    public UserRepository(BomConfiguratorContext context)
        :base(context)
    {

    }
}

Вариант использования

using (var UOW = _UnitOfWorkFactory.Create())
{
     //Submit the user
     _UserRepository.Add(ExampleNewUser);

}

Так что в настоящее время яиспользуя MVVM Light для выполнения всей моей работы с DI, теперь я понимаю, что с помощью mvvm light вы можете вводить только с помощью singleton scope.Поэтому я уверен, что в конечном итоге мне придется переключиться на что-то вроде Ninject, чтобы я мог использовать их .InTransientScope или .InNamedScope (из того, что я читал).

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

Вопрос

Итак, мой вопрос к вам, если бы я переключился на использование Ninject и начал внедрять свой контекст в эти репозитории / единицы работы.Как правильно настроить его так, чтобы AWLAYS вводил новый контекст в моей единице работы для репозиториев.

Я прочитал, что в Ninject MVC есть .InRequestScope, который полностью решит проблему.Но как насчет WPF?Как вы добиваетесь инъекций такого же типа?

Кажется, я не могу найти точное решение / схему или, может быть, есть лучший способ сделать это?Будем весьма благодарны за любые предложения и помощь.

1 Ответ

0 голосов
/ 15 ноября 2018

Моим решением проблемы было создание ContextFactory.

Интерфейс

public interface IContextFactory
{
    BomConfiguratorContext Create();
    BomConfiguratorContext Get();
}

Фабрика контекста

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

public class ContextFactory : IContextFactory
{
    private BomConfiguratorContext _context;

    public ContextFactory(BomConfiguratorContext context)
    {
        _context = context;
    }

    public BomConfiguratorContext Create()
    {
        _context = new BomConfiguratorContext();
        return _context;
    }

    public BomConfiguratorContext Get()
    {
        return _context;
    }
}

Новый базовый репозиторий

При вызове метода ContextFactory.Get () я использую кэшированный контекст вместо создания нового.

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly IContextFactory ContextFactory;

    public Repository(IContextFactory factory)
    {
        ContextFactory = factory;
    }
    public virtual void Add(TEntity entity)
    {

        ContextFactory.Get().Set<TEntity>().Add(entity);
    }

    public void AddRange(IEnumerable<TEntity> entities)
    {

        ContextFactory.Get().Set<TEntity>().AddRange(entities);

    }

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return ContextFactory.Get().Set<TEntity>().Where(predicate);
    }

    public TEntity Get(int id)
    {
        return ContextFactory.Get().Set<TEntity>().Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return ContextFactory.Get().Set<TEntity>().ToList();
    }

    public void Remove(TEntity entity)
    {
        ContextFactory.Get().Set<TEntity>().Remove(entity);
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        ContextFactory.Get().Set<TEntity>().RemoveRange(entities);
    }
    public void Update(TEntity entity)
    {
        ContextFactory.Get().Set<TEntity>().Attach(entity);
        ContextFactory.Get().Entry(entity).State = System.Data.Entity.EntityState.Modified;
    }
}

Фабрика новой единицы работы

Когда вызывается метод Create (), я вызываю метод Create () фабрики контекста, чтобы создать новый контекст.

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private IContextFactory _contextFactory;

    public UnitOfWorkFactory(IContextFactory factory)
    {
        _contextFactory = factory;
    }
    public UnitOfWork Create()
    {
        return new UnitOfWork(_contextFactory.Create());
    }
}

Делая это таким образом, я теперь могу внедрить свою фабрику контекста во все мои репозитории. Я попытался использовать области действия Ninject, упомянутые выше в исходном вопросе, но в результате возникли проблемы с введением двух отдельных контекстов, одного в моей фабрике единиц измерения и одного в моих репозиториях.

...