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

C# 我应该使用泛型来简化DAL吗?,c#,nhibernate,C#,Nhibernate,我是NHibernate的新手,C#不是很好,但我正在学习。我有一个DataProvider类,它使用nhibernate3为我的应用程序提供数据。它的结构与 我注意到我要重复很多代码,我想简化我的数据提供程序。例如,我有两个业务类,分别称为Instrument和Broker。在myDataProvider中添加仪器的方法是: public int AddInstrument(Instrument instrument) { using (ITransaction

我是NHibernate的新手,C#不是很好,但我正在学习。我有一个
DataProvider
类,它使用nhibernate3为我的应用程序提供数据。它的结构与

我注意到我要重复很多代码,我想简化我的
数据提供程序
。例如,我有两个业务类,分别称为
Instrument
Broker
。在my
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;
            }
        }
    }    
}
使用泛型,我必须将对象类型传递给我的
数据提供者
。你能想出解决这个问题的办法吗?我是一个新手程序员,我想知道我是否走上了正确的道路。我应该做些完全不同的事情吗

更新

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

IRepo.cs

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

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

}
IInstrumentRepo.cs

interface IInstrumentRepo : IRepo<Instrument>
{

}
public class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
{
    public InstrumentRepo(ISession s) : base(s) { }
}
接口IInstrumentRepo:IRepo { }
报告书

interface IInstrumentRepo : IRepo<Instrument>
{

}
public class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
{
    public InstrumentRepo(ISession s) : base(s) { }
}
公共类InstrumentRepo:BaseRepo、IInstrumentRepo
{
公共仪器报告(ISession s):基本(s){
}
在RepoFactory.cs中,我遇到以下错误:

可访问性不一致:返回类型“MooDB.Data.iInstrumEnterpo”的可访问性低于方法“MooDB.Data.RepoFactory.CreateInstrumentRepo(NHibernate.ISession)”


你知道我遗漏了什么吗?

你的数据提供程序类不一定需要是泛型的-你可以把
AddEntity
方法本身变成泛型的。然后实例化一个
DataProvider
实例,并调用(例如)它的
AddEntity
方法。您的类将如下所示:

    #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
    }
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;
            }
        }
    }    
}
公共类数据提供程序
{
公共整数加法(tenty实体)
{
使用(ITransaction tx=\u session.BeginTransaction())
{
尝试
{
int newId=(int)_session.Save(实体);
_session.Flush();
tx.Commit();
返回newId;
}
捕获(NHibernate.hibernateeexception)
{
tx.回滚();
投掷;
}
}
}    
}

您的问题的答案绝对是肯定的! 这就是泛型的含义

你的方法是对的

这一论点实在太长,无法在此讨论,但您可以在本文中找到许多有用的信息:


创建我的通用nhibernate Dao对我有很大帮助。首先,解决您的测试设置问题:术语存储库可能暗示它应该是一个长寿命的持久对象,但是DAL操作中使用的存储库实际上应该是生命周期短的轻量级无状态对象:需要时实例化一个,一旦完成就扔掉。当您考虑到这是性能方面时,您可以很容易地每秒实例化数百万个

结合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);
}
这是总的想法。现在,让我更详细地解释一下:

  • 您可能已经注意到,每个回购都有自己的接口,并且是通过回购工厂创建的。使用工厂创建存储库意味着您可以轻松地创建模拟回购工厂,这将创建存储库的任何自定义实现以进行测试

  • 每个repo接口都继承自基本接口通用接口,
    IRepo
    。这允许您在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);
    }
    
  • 您的工厂只需在被询问时实例化适当的存储库:

    public class RepoFactory : IRepoFactory
    {
         public IInstrumentRepo CreateInstrumentRepo(ISession s)
         {
             return new InstumentRepo(s);
         }
    }
    
  • 您需要在一个,比如,
    DAL
    类中使用单例模式来保持工厂(有一些更好的方法可以做到这一点,使用DI,但目前这很好):


  • 是否存在非泛型的
    数据提供程序
    类?还是你有编译错误?我知道你正在学习,但仅供参考。在DAO/存储库后面抽象Nh实际上限制了Nh的潜力。更不用说NH是数据层抽象,因此在其上添加抽象只会产生噪音。@Groo我正在考虑从非泛型转换为泛型。@JasonMeckley:对于这样的泛型基类,它不会增加太多工作或开销,但它允许您在需要时自由操作。有时候,在一个地方对特定实体进行特定查询是很有用的,而不是在代码中重复这些查询。@Jason我真的不明白你的意思。我只是想保持代码紧凑,避免重复,因此我想探索解决这个问题的方法。这是正确的,但可能不是最好的方法。这个通用的repo应该作为一个基类,其他存储库可以根据需要继承和扩展它。如果将其方法设置为泛型,则将所有DAL调用限制为单个类。每个实体都有自己的存储库的工作单元方法是通常的做法,IMHO。@DavidM有趣的方法,我喜欢。@Groo你似乎对这个主题很了解,如果你有时间,你能发布你的解决方案吗?@MarkAllison:当然,我马上就要离开,但稍后会做。太好了-非常感谢。我正在努力解决一些问题,但我会坚持下去,看看我是否能让它发挥作用。我有一个问题-请有人看看我在这个问题上的更新?抓我的头。@马克:接口缺少
    public
    关键字。我会更新。但是,请注意,具体类(如
    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; }
        }
    }