Should I use generics to simplify my DAL?

前端 未结 3 1070
Happy的楠姐
Happy的楠姐 2021-01-24 12:33

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 stru

3条回答
  •  孤独总比滥情好
    2021-01-24 13:21

    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.

    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. 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.

    2. Each repo interface inherits from the base interface generic interface, IRepo. 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
      { 
          // 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);
      }
      
    3. 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, IInstrumentRepo
      {
          // no need to implement anything here except pass the session downwards
          public InstrumentRepo(ISession s) : base(s) { }
      }
      
    4. 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);
           }
      }
      
    5. 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; }
          }
      }
      

提交回复
热议问题