I have implemented my first Generic repository in MVC app. Works fine but how to put repositories in Transaction scope?
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();
}
}
What would be best method to wrap repository code with Transaction scope in InsertUser action?
You are missing an abstraction here. You should place all your business logic inside command handlers and create a command handler decorator that implements transaction behavior. This article describes how to do this, but in short:
Define an ICommandHandler<TCommand>
interface:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
Create commands that define the contract of a business operation. Commands are simply DTOs (with only data and no behavior). For instance:
public class ShipOrderCommand
{
public int OrderId { get; set; }
public ShippingInfo Info { get; set; }
}
Implement command handlers that will contain the business logic / behavior for those commands:
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.
}
}
Let your MVC Controllers depend on the ICommandHandler<T>
abstraction:
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
});
}
}
Define a generic decorator that implements transaction logic:
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();
}
}
}
Ensure that each ShipOrderCommandHandler
is decorated with a TransactionalCommandHandlerDecorator
and injected into ShipOrderController
. You can do this with your favorite DI container, or by hand:
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);
}
With this in place you can run all your business logic inside a transaction, without the need for the business logic to be aware of that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With