Архитектура приложения — транзакции с RavenDB

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

Я пишу бизнес-уровни для каждой сущности (почти), и у меня есть уникальный класс репозитория, который обрабатывает запросы и DocumentStore. Я смущен тем, как я должен совместно использовать DocumentStore по слоям служб и обрабатывать транзакции.

Я показываю пример, в котором я пытаюсь сохранить и прочитать документ в одной транзакции.

Пример репозитория:

public class RavenRepository
{
    private static DocumentStore _store;
    private IDocumentSession _session;

    public RavenRepository(DocumentStore store)
    {
        _store = (_store==null) ? new DocumentStore() { Url = "http://wsk-gcardoso:8081" } : store;           
    }
    public T SingleOrDefault<T>(Func<T, bool> predicate) where T : BaseModel
    {
        using (var session = _store.OpenSession())
        {
            return session.Query<T>().SingleOrDefault(predicate);
        }
    }
    public T Add<T>(T item) where T : BaseModel
    {            
        using (var session = _store.OpenSession())
        {
            session.Advanced.AllowNonAuthoritiveInformation = this.AllowNonAuthoritiveInformation;
            session.Store(item);
            session.SaveChanges();
        }
        return item;
    }
    public void Initialize() {
        _store.Initialize();
    }
    public void Dispose() {
        _store.Dispose();
    }
}

Бизнес-уровень будет таким:

public class NewsletterBusiness
{
    private RavenRepository repository;
    public NewsletterBusiness(RavenRepository ravenRepository)
    {       
        repository = (ravenRepository == null) ? RavenRepository(null) : ravenRepository;
    }

    public Newsletter Add(Newsletter newsletter)
    {
        Newsletter news = repository.Add(newsletter);
        return news;
    }
    public Newsletter GetById(long Id)
    {
        Newsletter news = repository.SingleOrDefault<Newsletter>(x => x.Id == Id);
        return news;
    }
}

Теперь я пытаюсь сохранить и прочитать объект (бюллетени) в той же транзакции. Из того, что я прочитал, я должен установить для параметра AllowNonAuthoritativeInformation в хранилище документов значение false, чтобы дождаться завершения транзакции. Но судя по тому, как я работаю со слоями и репозиторием, могу ли я хранить и запрашивать базу данных в одной транзакции?

Честно говоря, я думаю, что меня смущает метод хранения OpenSession. Я думаю, что путаю сеанс с транзакцией.

Например, этот код:

var repository = new RavenRepository(null);
newsletterBusiness = new NewsletterBusiness(repository);
repository.Initialize();
using (var tx = new TransactionScope())
{
    Newsletter new = newsletterBusiness.Add(new Newsletter { Title = "Created by Tests", Content = "Created By Tests" });

    Newsletter objectCreated = newsletterBusiness.GetById(new.Id);
    repository.Dispose();
    tx.Complete();
}

Если я создам второй бизнес-уровень (например, для изображений) и установлю picturesBusiness.repository = репозиторий (тот же объект RavenRepository, установленный для бизнес-уровня), я буду работать в том же сеансе бюллетеньBusiness.repository?

picturesBusiness = new PicturesBusiness(repository);
Picture pic = picturesBusiness.GetById(20);

Я был бы очень признателен за помощь по этому вопросу, Привет из Португалии!


person Gui    schedule 10.10.2011    source источник


Ответы (2)


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

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

Если вы хотите иметь транзакционную запись, которая охватывает типы, учитывая вашу архитектуру, вам придется использовать DTC, потому что каждый отдельный сеанс — это другое соединение с базой данных. Если бы вы использовали модель Session per Request, у вас был бы только один сеанс, и вы бы имели транзакционную семантику, просто вызвав SaveChanges() один раз.

person Ayende Rahien    schedule 10.10.2011

Вы пытаетесь абстрагировать сеанс ворона (который в основном представляет собой UnitOfWork) с помощью шаблона репозитория.

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

public class RavenUnitOfWork {
    private IDocumentSession m_session;

    public UnitOfWork() {
        // initialize m_session here
    }

    public Repository<T> GetRepository() {
        return new Repository<T>(m_session);
    }

    public void Commit() {
        m_session.SaveChanges();
    }
}

public class RavenRepository<T> {
    public Repository(IDocumentSession session) {
        // Store the session
    }
    public T Load(string id) {...}
    public void Store(T entity) {...}
    public void Delete(T entity) {...}
    public IQueryable<T> Query() {...}
}

Здесь вы явно реализуете эти шаблоны.

P.S.: Конечно, вы можете объявить IUnitOfWork и IRepository на свой вкус...

person Igor S.    schedule 26.12.2011