Nano Hash - криптовалюты, майнинг, программирование

Единица работы и общий репозиторий с Entity Framework 5

Я использую ASP.NET MVC 4 с Entity Framework 5. У меня есть классы моделей и карты сущностей для сопоставления существующих таблиц с этими классами моделей. Все это прекрасно настроено и отлично работает.

Теперь я хочу поиздеваться над этим. Я создал Unit Of Work, который принимает DataContext и использует универсальный репозиторий. После этого я создал сервисы, чтобы иметь возможность получать данные из многих репозиториев одновременно, и мне нужен был только один экземпляр DataContext. Это также отлично работает.

Теперь к проблеме: я хочу протестировать сервисы с фиктивными данными. Когда я создаю экземпляр Unit Of Work, я хочу иметь возможность вставлять имитируемый DataContext вместо реального DataContext.

Я попытался создать интерфейс IContext и позволил реализовать его реальному и вымышленному DataContext, но столкнулся с проблемами с DbSet. Я пытался использовать IDbSet и создать FakeDbSet, но безуспешно. Я также читал в Интернете, что издевательство над контекстом с помощью IDbSet и использование FakeDbSet — плохой подход.

У вас есть идеи, как лучше всего этого добиться? Теперь у меня есть поведение, которое я хотел бы сохранить, но мне бы очень хотелось иметь возможность имитировать данные из классов моделей в DataContext.

Мне известно, что Entity Framework уже поставляется с поведением Unit Of Work и вам не нужно добавлять к этому дополнительное поведение. Но я хотел поместить это в другой класс, который отслеживает все репозитории (называемый классом UnitOfWork).

Редактировать: я написал две статьи, объясняющие мое решение как с LINQ, так и с Entity Framework.

http://gaui.is/how-to-mock-the-datacontext-linq/

http://gaui.is/how-to-mock-the-datacontext-entity-framework/

Вот мой код:

IRepository.cs

public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    T GetById(long Id);
    IEnumerable<T> All();
    IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
}

IUnitOfWork.cs

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
    void Save();
}

Repository.cs

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDbContext _context;
    private readonly IDbSet<T> _dbset;

    public Repository(IDbContext context)
    {
        _context = context;
        _dbset = context.Set<T>();
    }

    public virtual void Add(T entity)
    {
        _dbset.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        var entry = _context.Entry(entity);
        entry.State = System.Data.EntityState.Deleted;
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Entry(entity);
        _dbset.Attach(entity);
        entry.State = System.Data.EntityState.Modified;
    }

    public virtual T GetById(long id)
    {
        return _dbset.Find(id);
    }

    public virtual IEnumerable<T> All()
    {
        return _dbset;
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return _dbset.Where(predicate);
    }
}

UnitOfWork.cs

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new()
{
    private readonly IDbContext _ctx;
    private Dictionary<Type, object> _repositories;
    private bool _disposed;

    public UnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
        _disposed = false;
    }

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        if (_repositories.Keys.Contains(typeof(TEntity)))
            return _repositories[typeof(TEntity)] as IRepository<TEntity>;

        var repository = new Repository<TEntity>(_ctx);
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }

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

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                _ctx.Dispose();
            }

            this._disposed = true;
        }
    }
}

ExampleService.cs

public class ExampleService
{
    private IRepository<Example> m_repo;

    public ExampleService(IUnitOfWork uow)
    {
        m_repo = uow.GetRepository<Example>();
    }

    public void Add(Example Example)
    {
        m_repo.Add(Example);
    }

    public IEnumerable<Example> getAll()
    {
        return m_repo.All();
    }
}

ExampleController.cs

public IEnumerable<Example> GetAll()
{
    // Create Unit Of Work object
    IUnitOfWork uow = new UnitOfWork<AppDataContext>();

    // Create Service with Unit Of Work attached to the DataContext
    ExampleService service = new ExampleService(uow);

    return service.getAll();
}

  • У меня тоже такая же проблема, как вы упомянули. Однако можете ли вы опубликовать свой код для IDbContext и конкретного класса, который реализует IDbContext? Это мне очень поможет. Заранее спасибо. 02.08.2013
  • @Nexus http://gaui.is/how-to-mock-the-datacontext-entity-framework/ 03.08.2013
  • @Gaui Не лучше ли было бы создать экземпляр UnitOfWork внутри класса ExampleService вместо контроллера? Таким образом, вашему контроллеру не нужно знать, что ему DbContext нужно, он предоставляет это сервису. 11.11.2013
  • @ GSoley83 На самом деле это просто предпочтение. Если вы хотите, чтобы многие сервисы использовали один и тот же объект uow, вам нужно будет сделать это вне сервиса. 18.11.2013
  • Я бы сказал, что если потребитель вашей услуги удален, вы не хотите, чтобы uow существовал вне службы. Вы не хотите, чтобы открытые транзакции/соединения зависали в среде без сохранения состояния. Если потребитель не является удаленным, это может не быть проблемой. 22.11.2013
  • Почему вы вызвали SaveChanges для каждой операции в классе Repository? Это не будет безопасной транзакцией 25.08.2015

Ответы:


1

Ваш класс ExampleService ожидает IUnitOfWork, это означает, что вам просто нужен еще один IUnitOfWork, который является макетом, а его метод GetRepository() вернет макет IRepository.

Например (на самом деле не макет, а заглушка в памяти):

  public InMemoryRepository<T> : IRepository<T> where T : class
  {
        ........
  }

  public InMemoryUnitOfWork : IUnitOfWork
  {
       public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
       {
            return new InMemoryRepository<TEntity>();
       }
  }

Потом:

public IEnumerable<Example> GetAll()
{
    // Create Unit Of Work object
    IUnitOfWork uow = new InMemoryUnitOfWork();

    // Create Service with Unit Of Work
    ExampleService service = new ExampleService(uow);

    return service.getAll();
}
23.05.2013
  • Отлично, работает как шарм! Мне это нравится больше, чем создание поддельного контекста с помощью IDbSet и FakeDbSet. Это кажется таким неправильным. Большое спасибо за это! 24.05.2013
  • Это кажется аккуратным и чистым способом вместо того, чтобы издеваться над DBSets, спасибо, что поделились, я тоже бродил по тому же самому. 24.06.2015

  • 2

    Вы можете перейти по следующей ссылке, это очень полезно.

    Общий шаблон репозитория в приложении MVC3 с Entity Framework

    Entity Framework и шаблоны данных

    28.12.2013
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..