我应该使用泛型来简化我的DAL? [英] Should I use generics to simplify my DAL?

查看:121
本文介绍了我应该使用泛型来简化我的DAL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来的NHibernate,而不是在C#很不错,但我学习。我有一个的DataProvider 类的使用NHibernate 3.我的应用程序提供的数据它的结构几乎相同的的NHibernate的视频史蒂夫·波伦的夏季的。

I'm new to NHibernate and not very good at C#, but I'm learning. I have a DataProvider class which provides data for my application using NHibernate 3. It's structured pretty much identical to Steve Bohlen's Summer of NHibernate videos.

我发现我对重复我的代码了很多,我想简化我的的DataProvider 。例如,我有叫了两个业务类仪器经纪。该方法添加一个仪器在我的的DataProvider 是:

I've noticed that I'm about to repeat my code a lot and I want to simplify my DataProvider. For example I have two business classes called Instrument and Broker. The method to add an Instrument in my DataProvider is:

    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 类看起来非常相似(只是查找和替换)。所以我想也许我可以使用泛型来解决这个问题。是这样的:

and the AddBroker class looks remarkably similar (just find and replace). So I thought maybe I could use Generics to solve the problem. Something like:

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

通过这是我可以在经纪人通过,一个仪器或任何东西,我自己节省了大量的重复的代码。我遇到的问题是,在我的测试类创建一个新的的DataProvider 每次测试运行时一样这样的:

With this I can pass in a Broker, an Instrument or anything and I save myself a lot of repetitive code. The problem I'm having is that in my Test class I create a new DataProvider each time a test is run like this:

    #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 。你可以想办法来解决呢?我是新手程序员,我想知道如果我要沿着正确的道路。我应该做的完全不同的东西?

Using generics I have to pass in the object type to my DataProvider. Can you think of a way to solve this? I am a novice programmer and I am wondering if I am going down the right path. Should I be doing something totally different?

更新

我曾尝试实施Groo的答案,但我有一些问题。这就是我所做的。

I have tried to implement Groo's answer but am having some issues. Here's what I've done.

IRepo.cs

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



BaseRepo.cs

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

IRepoFactory.cs

interface IRepoFactory
{
    IInstrumentRepo CreateInstrumentRepo(ISession s);
}



RepoFactory.cs

RepoFactory.cs

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

}



IInstrumentRepo.cs

IInstrumentRepo.cs

interface IInstrumentRepo : IRepo<Instrument>
{

}



InstrumentRepo.cs

InstrumentRepo.cs

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

在RepoFactory.cs我得到这个错误:

In RepoFactory.cs I get this error:

可访问性不一致C $ C>

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

任何想法,我缺少什么?

Any ideas what I'm missing?

推荐答案

首先,以解决您的测试设置的问题:术语库可能表明,它应该是一个长寿命,持久化对象,但在DAL操作中使用实际上应该是库轻便无状态的短寿命对象:你实例之一,当你需要它,并尽快就大功告成了把它扔掉。当你想到这是性能方面,你可以很容易地实例每秒百万人。

First of all, to address your Test Setup question: the term Repository might suggest that it should be a long lived, persistent object, but Repositories used in DAL operations should actually be lightweight stateless objects with short lifetimes: you instantiate one when you need it, and throw it away as soon as you're done. When you think about this is terms of performance, you can easily instantiate millions of them per second.

与NHibernate的短暂会话情况下,这是你的代码是如何为了看时,一切都在发生这样的:

Combined with NHibernate's short lived Session instances, this is how your code is meant to look like when everything is in place:

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

这是一般的想法。现在,让我详细解释一下:

That's the general idea. Now, let me explain it in more detail:


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

  1. You may have noticed that each repo has its own interface, and is created through a repo factory. Using a factory to create repositories means you can easily create mock repo factories, which will create any custom implementation of a repository for testing.

每个回购接口继承自底座接口通用接口, IRepo< T> 。这可以让你的情况下,99%使用一个通用的存储库,但仍留有余地,以实现特定的自定义查询方法,比方说,客户实体只有:

Each repo interface inherits from the base interface generic interface, IRepo<T>. This allows you to use a generic repository in 99% of cases, but still leave room to implement a custom query method specific to, say, Customer entities only:

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


  • 这意味着你的回购协议的实现,在大多数情况下,只需从抽象基类继承(我把它命名为 BaseRepo ,但它基本上是你的的DataProvider 类做什么现在):

  • This means that your repo implementations will, in most cases, simply inherit from the base abstract class (I named it BaseRepo, but it's essentially what your DataProvider class does right now):

    class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
    {
        // no need to implement anything here except pass the session downwards
        public InstrumentRepo(ISession s) : base(s) { }
    }
    


  • 您的工厂将只需要实例适当的存储库时问:

  • Your factory will simply need to instantiate the proper repository when asked:

    public class RepoFactory : IRepoFactory
    {
         public IInstrumentRepo CreateInstrumentRepo(ISession s)
         {
             return new InstumentRepo(s);
         }
    }
    


  • 和则需要使用Singleton在一个模式,比方说, DAL 类来保存工厂(有略更好的方法来做到这一点,使用DI,但现在这会做就好了):

  • And you will need to use the Singleton pattern in a, say, DAL class to hold the factory (there are slightly better ways to do this, using DI, but for now this will do just fine):

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


  • 这篇关于我应该使用泛型来简化我的DAL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆