我应该使用泛型来简化我的DAL吗?

时间:2012-01-11 16:49:14

标签: c# nhibernate

我是NHibernate的新手,并不擅长C#,但我正在学习。我有一个DataProvider类,它使用NHibernate 3为我的应用程序提供数据。它的结构与Steve Bohlen's Summer of NHibernate videos非常相似。

我注意到我要重复我的代码很多,我想简化我的DataProvider。例如,我有两个名为InstrumentBroker的业务类。在Instrument中添加DataProvider的方法是:

    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;
            }
        }
    }

AddBroker类看起来非常相似(只是查找和替换)。所以我想也许我可以使用泛型来解决问题。类似的东西:

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;
            }
        }
    }    
}

有了这个,我可以传入BrokerInstrument或其他任何内容,我可以节省很多重复的代码。我遇到的问题是,在Test课程中,每次运行测试时我都会创建一个新的DataProvider

    #region Fields

    private DataProvider _provider;
    private SessionManager _sessionManager;
    private NHibernate.ISession _session;

    #endregion

    [SetUp]
    public void Setup()
    {
        DatabaseSetUp();

        _session = _sessionManager.GetSession();
        _provider = new DataProvider(_session);    // problem here
    }

使用泛型我必须将对象类型传递给我的DataProvider。你能想出解决这个问题的方法吗?我是一名新手程序员,我想知道我是否正走在正确的道路上。我应该做一些完全不同的事情吗?

更新

我试图实现Groo的答案但是遇到了一些问题。这就是我所做的。

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 个答案:

答案 0 :(得分:2)

首先,要解决您的测试设置问题:术语存储库可能暗示它应该是一个长期存在的持久对象,但DAL操作中使用的存储库实际上应该是生命周期较短的轻量级无状态对象:你需要它时实例化一个,并在你完成后立即扔掉它。当您考虑这是性能方面时,您可以轻松地每秒实例化数百万个。

结合NHibernate的短暂Session个实例,这就是你的代码在一切就绪后的样子:

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. 每个repo接口都继承自基接口通用接口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. 这意味着在大多数情况下,您的repo实现只会从基本抽象类继承(我将其命名为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. 你需要在一个DAL类中使用Singleton模式来保存工厂(有更好的方法来实现这一点,使用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 :(得分:1)

你的问题的答案绝对是! 这就是泛型的含义。

你的方式正确。

这个论点真的太长了,无法在这里讨论,但你可以在这篇文章中找到很多有用的信息:

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

创建我的通用nhibernate Dao

对我有很大帮助

答案 2 :(得分:0)

您的数据提供程序类不一定需要是通用的 - 您只需使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;
            }
        }
    }    
}