我应该使用generics来简化我的DAL吗?
我是NHibernate的新手,并不擅长C#,但我正在学习。 我有一个DataProvider
类,它使用NHibernate 3为我的应用程序提供数据。它的结构与Steve Bohlen的Summer of NHibernatevideo完全相同。
我注意到我将要重复我的代码,我想简化我的DataProvider
。 例如,我有两个名为Instrument
and Broker
业务类。 在DataProvider
添加Instrument
的方法是:
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
类看起来非常相似(只是查找和替换)。 所以我想也许我可以使用generics来解决问题。 就像是:
public class DataProvider { 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
:
#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 }
使用generics我必须将对象类型传递给我的DataProvider
。 你能想出解决这个问题的方法吗? 我是一名新手程序员,我想知道我是否正走在正确的道路上。 我应该做一些完全不同的事吗?
UPDATE
我试图实现Groo的答案,但我遇到了一些问题。 这就是我所做的。
IRepo.cs
interface IRepo { int Add(Entity entity); void Delete(Entity entity); void GetById(int Id); }
BaseRepo.cs
public abstract class BaseRepo : IRepo { private ISession _session; #region SessionManagement public BaseRepo(ISession session) { _session = session; } public ISession Session { set { _session = value; } } #endregion public int Add(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 { }
InstrumentRepo.cs
public class InstrumentRepo : BaseRepo, 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)'
我缺少什么想法?
首先,要解决您的测试设置问题:术语库可能暗示它应该是一个长期存在的持久对象,但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); }
这是一般的想法。 现在,让我更详细地解释一下:
-
您可能已经注意到每个仓库都有自己的界面,并通过仓库工厂创建。 使用工厂创建存储库意味着您可以轻松创建模拟仓库工厂,这将创建用于测试的存储库的任何自定义实现。
-
每个repo接口都inheritance自基本接口通用接口
IRepo
。 这允许您在99%的情况下使用通用存储库,但仍然留有空间来实现特定于仅Customer
实体的自定义查询方法:public interface IInstrumentRepo : IRepo
{ // nothing to do here } public interface ICustomerRepo : IRepo { // but we'll need a custom method here void FindByAddress(string address); } public interface IRepo { T GetById(object id); T Save(T item); } -
这意味着在大多数情况下,您的repo实现只是从基本抽象类inheritance(我将其命名为
BaseRepo
,但它实际上是您的DataProvider
类现在所做的):class InstrumentRepo : BaseRepo
, IInstrumentRepo { // no need to implement anything here except pass the session downwards public InstrumentRepo(ISession s) : base(s) { } } -
当被问到时,您的工厂只需要实例化正确的存储库:
public class RepoFactory : IRepoFactory { public IInstrumentRepo CreateInstrumentRepo(ISession s) { return new InstumentRepo(s); } }
-
而且你需要在
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; } } }
你的问题的答案是绝对的! 这是generics的意思。
你是在正确的方式。
这个论点真的太长了,无法在这里讨论,但你可以在这篇文章中找到很多有用的信息:
http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx
它帮助我创建了我的通用nhibernate Dao
您的数据提供程序类不一定需要是通用的 – 您只需使AddEntity
方法本身通用即可。 然后,您实例化一个DataProvider
实例,并调用(例如)其AddEntity
方法。 你的课将如下所示:
public class DataProvider { 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; } } } }