2012-04-04 17 views
6

Zaimplementowałem moje pierwsze repozytorium Generyczne w aplikacji MVC. Działa dobrze, ale jak umieścić repozytoria w zasięgu transakcji?Repozytorium ogólne i transakcja

public interface IRepository<TEntity> where TEntity : class 
    { 
     List<TEntity> FetchAll(); 
     IQueryable<TEntity> Query { get; } 
     void Add(TEntity entity); 
     void Delete(TEntity entity); 
     void Save(); 
    } 


    public class Repository<T> : IRepository<T> where T : class 
    { 
     private readonly DataContext _db; 

     public Repository(DataContext db) 
     { 
      _db = db; 
     } 

     #region IRepository<T> Members 

     public IQueryable<T> Query 
     { 
      get { return _db.GetTable<T>(); } 
     } 

     public List<T> FetchAll() 
     { 
      return Query.ToList(); 
     } 

     public void Add(T entity) 
     { 
      _db.GetTable<T>().InsertOnSubmit(entity); 
     } 

     public void Delete(T entity) 
     { 
      _db.GetTable<T>().DeleteOnSubmit(entity); 
     } 

     public void Save() 
     { 
      _db.SubmitChanges(); 
     } 

     #endregion 
    } 

     private void RegisterDependencyResolver() 
     { 
      var kernel = new StandardKernel();   
      var connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; 
      kernel.Bind(typeof(DataContext)).ToMethod(context => new DataContext(connectionString)); 
      kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));    
      DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel)); 
     } 


    public class AdminController : Controller 
    { 

     private readonly IRepository<User> _userRepository; 
     private readonly IRepository<Order> _orderRepository; 

public AdminController(IRepository<User> userRepository, IRepository<Order> orderRepository) 
     { 
      _userRepository = userRepository; 
      _orderRepository = orderRepository; 
     } 






public ActionResult InsertUser(UserViewModel model) 
     { 

//Skip Code 
//Do not commit data to database if _orderRepository is failed to save data 
     _userRepository.Add(user); 
      _userRepository.Save(); 


//Skip Code 
     _orderRepository.Add(order); 
      _orderRepository.Save(); 

} 


} 

Jaka byłaby najlepsza metoda na zawijanie kodu repozytorium za pomocą zakresu transakcji w akcji InsertUser?

+0

Zobacz także [ten artykuł] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=89). – Steven

Odpowiedz

9

Brakuje tu abstrakcji. Powinieneś umieścić całą logikę biznesową wewnątrz procedur obsługi komend i utworzyć dekorator obsługi komend, który implementuje zachowanie transakcji. This article opisuje, jak to zrobić, ale w skrócie:

  1. zdefiniować interfejs ICommandHandler<TCommand>:

    public interface ICommandHandler<TCommand> 
    { 
        void Handle(TCommand command); 
    } 
    
  2. Tworzenie poleceń, które określają umowa z działalności gospodarczej. Polecenia są po prostu DTOs (tylko z danymi i bez zachowania). Na przykład:

    public class ShipOrderCommand 
    { 
        public int OrderId { get; set; } 
    
        public ShippingInfo Info { get; set; } 
    } 
    
  3. Wdrożenie procedur obsługi poleceń, które będą zawierać logiki biznesowej/zachowanie tych poleceń:

    public class ShipOrderCommandHandler 
        : ICommandHandler<ShipOrderCommand> 
    { 
        private readonly IRepository<Order> repository; 
    
        public ShipOrderCommandHandler(
         IRepository<Order> repository) 
        { 
         this.repository = repository; 
        } 
    
        public void Handle(ShipOrderCommand command) 
        { 
         // do some useful stuf with the command and repository. 
        } 
    } 
    
  4. Niech Kontrolery MVC zależą ICommandHandler<T> abstrakcji:

    public ShipOrderController : Controller 
    { 
        private readonly ICommandHandler<ShipOrderCommand> handler; 
    
        public ShipOrderController(
         ICommandHandler<ShipOrderCommand> handler) 
        { 
         this.handler = handler; 
        } 
    
        public void Ship(int orderId, ShippingInfo info) 
        { 
         this.handler.Handle(new ShipOrderCommand 
         { 
          OrderId = orderId, 
          Info = info 
         }); 
        } 
    } 
    
  5. Zdefiniuj ogólny dekorator, który implementuje logikę transakcji:

    public TransactionalCommandHandlerDecorator<TCommand> 
        : ICommandHandler<TCommand> 
    { 
        private ICommandHandler<TCommand> decoratedHandler; 
    
        public TransactionalCommandHandlerDecorator(
         ICommandHandler<TCommand> decoratedHandler) 
        { 
         this.decoratedHandler = decoratedHandler; 
        } 
    
        public void Handle(TCommand command) 
        { 
         using (var scope = new TransactionScope()) 
         { 
          this.decoratedHandler.Handle(command); 
          scope.Complete(); 
         } 
        } 
    } 
    
  6. Upewnić się, że każdy ShipOrderCommandHandler jest ozdobiony TransactionalCommandHandlerDecorator i wstrzyknięto do ShipOrderController. Można to zrobić ze swoim ulubionym kontenera DI, lub ręcznie:

    protected override IController GetControllerInstance(
        RequestContext requestContext, Type controllerType) 
    { 
        if (controllerType == typeof(ShipOrderController)) 
        { 
         return new ShipOrderController(
          new TransactionalCommandHandlerDecorator<ShipOrderCommand>(
           new ShipOrderCommandHandler(
            new OrderRepository()))); 
        } 
    
        return base.GetControllerInstance(requestContext, controllerType); 
    } 
    

Mając to na miejscu można uruchomić całą swoją logikę biznesową wewnątrz transakcji, bez potrzeby stosowania logiki biznesowej do być świadomy że.

+0

Lub ... możesz obsługiwać zakres transakcji na poziomie BeginRequest/EndRequest (ponieważ jest to aplikacja internetowa, mimo wszystko) i unikać setek linii niepotrzebnych abstrakcji i zawiłych kodów. – Chris

+2

@ Chris: Nie zgadzam się z dwoma punktami. 1. To jest niepotrzebna abstrakcja. Kod ten jest zgodny z zasadami SOLID i umożliwia testowanie, skalowanie i konserwację aplikacji. 2. To jest setki linii dodatkowego kodu. W rzeczywistości pozwoli Ci to uniknąć wielu wierszy zduplikowanego kodu. – Steven

+0

@Steven może masz przykład kodu jak wstrzyknąć nową instancję TransactionalCommandHandlerDecorator używając programu Ninject? – Tomas

1

Istnieje wzorzec zwany Jednostką pracy. Oto an explanation.

+0

+1 UOW jest rzeczywiście stosowanym wzorem tutaj. –

+0

DBContext EntityFramework * to * Twoja jednostka pracy! – Mardoxx