Должен ли я использовать дженерики для упрощения моего DAL? - PullRequest
1 голос
/ 11 января 2012

Я новичок в NHibernate и не очень хорош в C #, но я учусь.У меня есть класс DataProvider, который предоставляет данные для моего приложения, использующего NHibernate 3. Он структурирован в значительной степени аналогично летнему видео Стива Болена из NHibernate .

Я заметил, что собираюсь многократно повторять код и хочу упростить мой DataProvider.Например, у меня есть два бизнес-класса с именами Instrument и Broker.Метод добавления Instrument в мой DataProvider: 1011

    public int AddInstrument(Instrument instrument)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(instrument);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }
1013 * и класс AddBroker выглядит удивительно похожим (просто найдите и замените).Поэтому я подумал, что, возможно, смогу использовать Generics для решения проблемы.Что-то вроде:
public class DataProvider <TEntity>
{
    public int AddEntity(TEntity entity)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(entity);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }    
}

С этим я могу передать Broker, Instrument или что-нибудь еще, и я сохраняю много повторяющегося кода.У меня проблема в том, что в моем Test классе я создаю новый DataProvider каждый раз, когда запускается тест:мой DataProvider.Можете ли вы придумать способ решить эту проблему?Я начинающий программист, и мне интересно, иду ли я по правильному пути.Должен ли я делать что-то совершенно другое?

ОБНОВЛЕНИЕ

Я пытался реализовать ответ Груо, но у меня возникли некоторые проблемы.Вот что я сделал.

IRepo.cs

interface IRepo<T>
{
    int Add<Entity>(Entity entity);
    void Delete<Entity>(Entity entity);
    void GetById<Entity>(int Id);
}

BaseRepo.cs

public abstract class BaseRepo <T> : IRepo <T>
{
    private ISession _session;

    #region SessionManagement

    public BaseRepo(ISession session)
    {
        _session = session;
    }

    public ISession Session
    {
        set { _session = value; }
    }

    #endregion

    public int Add<Entity>(Entity entity)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(entity);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }

    // other methods omitted for brevity
}

IRepoFactory.cs

interface IRepoFactory
{
    IInstrumentRepo CreateInstrumentRepo(ISession s);
}

RepoFactory.cs

public class RepoFactory : IRepoFactory
{
    public IInstrumentRepo CreateInstrumentRepo(ISession s) // problem here
    {
        return new InstrumentRepo(s);
    }

}

IInstrumentRepo.cs

interface IInstrumentRepo : IRepo<Instrument>
{

}

InstrumentRepo.cs

public class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
{
    public InstrumentRepo(ISession s) : base(s) { }
}

В RepoFactory.cs я получаю эту ошибку:

Inconsistent accessibility: return type 'MooDB.Data.IInstrumentRepo' is less accessible than method 'MooDB.Data.RepoFactory.CreateInstrumentRepo(NHibernate.ISession)'

Есть идеи, что мне не хватает?

Ответы [ 3 ]

2 голосов
/ 11 января 2012

Прежде всего, для решения вопроса о настройке теста: термин «Репозиторий» может предполагать, что это должен быть долговечный, постоянный объект, но репозитории, используемые в операциях DAL, на самом деле должны быть легкими объектами без состояний с коротким временем жизни : вы создаете экземпляр, когда вам это нужно, и выбрасываете его, как только закончите. Когда вы думаете об этом с точки зрения производительности, вы можете легко создавать миллионы из них в секунду.

В сочетании с кратковременными Session экземплярами NHibernate, вот как должен выглядеть ваш код, когда все на месте:

using (var session = SessionManager.OpenSession())
{
    // create an instrument repo
    IInstrumentRepo instruments = DAL.RepoFactory.CreateInstrumentRepo(session);
    var guitar = instruments.Find(i => i.Type == "Guitar");

    // create a customer repo
    ICustomerRepo customers = DAL.RepoFactory.CreateCustomerRepo(session);
    var cust = customers.Find(c => c.Name == "Mark")

    // do something -> changes will be persisted by NH when session is disposed
    cust.Instruments.Add(guitar);
}

Это общая идея. Теперь позвольте мне объяснить это более подробно:

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

  2. Каждый интерфейс репо наследуется от базового интерфейса базового интерфейса, IRepo<T>. Это позволяет вам использовать универсальный репозиторий в 99% случаев, но при этом все еще остается место для реализации пользовательского метода запроса, характерного, скажем, только для Customer объектов:

    public interface IInstrumentRepo : IRepo<Instrument>
    { 
        // nothing to do here
    }
    
    public interface ICustomerRepo : IRepo<Customer>
    {
        // but we'll need a custom method here
        void FindByAddress(string address);
    }
    
    public interface IRepo<T> 
    {
        T GetById(object id);
        T Save(T item);
    }
    
  3. Это означает, что ваши реализации репозитория, в большинстве случаев, просто наследуют от базового абстрактного класса (я назвал его BaseRepo, но это по сути то, что ваш класс DataProvider делает прямо сейчас):

    class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
    {
        // no need to implement anything here except pass the session downwards
        public InstrumentRepo(ISession s) : base(s) { }
    }
    
  4. Вашей фабрике просто нужно будет создать соответствующий репозиторий, когда его спросят:

    public class RepoFactory : IRepoFactory
    {
         public IInstrumentRepo CreateInstrumentRepo(ISession s)
         {
             return new InstumentRepo(s);
         }
    }
    
  5. И вам нужно будет использовать шаблон Singleton в, скажем, DAL классе, чтобы сохранить фабрику (есть несколько лучшие способы сделать это, используя DI, но сейчас это будет хорошо) :

    public static class DAL 
    {
        // repo factory is pretty lightweight, so no need for fancy
        // singleton patterns
    
        private static readonly IRepoFactory _repoFactory = new RepoFactory();
        public static IRepoFactory RepoFactory
        { 
            get { return _repoFactory; }
        }
    }
    
1 голос
/ 11 января 2012

Ответ на ваш вопрос - Абсолютно Да! Для этого и предназначены дженерики.

Вы на правильном пути.

Этот аргумент слишком длинен для обсуждения здесь, но вы можете найти много полезной информации в этой статье:

http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx

Это очень помогает мне в создании моего универсального Даобского Дао

0 голосов
/ 11 января 2012

Ваш класс поставщика данных не обязательно должен быть универсальным - вы можете просто сделать сам метод AddEntity универсальным. Затем вы создаете экземпляр DataProvider и вызываете (например) его метод AddEntity<Instrument>. Ваш класс будет выглядеть так:

public class DataProvider
{
    public int AddEntity<TEntity>(TEntity entity)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(entity);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }    
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...