I'm implementing an ASP.NET MVC application and need to implement the Unit Of Work with repositories pattern. My implementation is designed as follows:
The UnitOfWork object is in charge of issuing COMMITs and ROLLBACKs as necessary.
The UnitOfWork object contains a Transaction property, obtaied from the internal DB connection. This object provides atomicity to the operations inside the UnitOfWork.
The UnitOfWork object contains the repositories as properties injected at runtime.
Each repository needs to be provided the IDbTransaction object that UnitOfWork created to support atomicity.
So now I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated. So my question is: How do I do this? Or maybe something in the design has to be changed?
I'm using SQL Server at the moment and I use Dapper for the SQL calls. Also, I'm thinking of using Autofac as DI framework.
What I've done so far is to implement UOW and a sample repository. Code as follows.
IRepository.cs:
public interface IRepository<TObj, TKey>
{
Task<TObj> DetallesAsync(TKey id);
Task<TKey> AgregarAsync(TObj obj);
}
DbRepository.cs:
public abstract class DbRepository
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
protected IDbConnection Connection
{
get => _connection;
}
protected IDbTransaction Transaction
{
get => _transaction;
}
public DbRepository(IDbTransaction transaction)
{
_transaction = transaction;
_connection = _transaction.Connection;
}
}
RolRepository.cs:
public class MSSQLRolRepository : DbRepository, IRolRepository
{
public MSSQLRolRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<int> AgregarAsync(Rol obj)
{
var result = await Connection.ExecuteScalarAsync<int>(MSSQLQueries.RolAgregar, param: obj, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesAsync(int id)
{
var param = new { Id = id };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetalles, param: param, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesPorNombreAsync(string nombre)
{
var param = new { Nombre = nombre };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetallesPorNombre, param: param, transaction: Transaction);
return result;
}
public async Task<Rol[]> ListarAsync(int pagina, int itemsPorPagina)
{
var param = new { Pagina = pagina, ItemsPorPagina = itemsPorPagina };
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, param: param, transaction: Transaction);
return result.ToArray();
}
public async Task<Rol[]> ListarTodosAsync()
{
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, transaction: Transaction);
return result.ToArray();
}
}
IUnitOfWork.cs:
public interface IUnitOfWork : IDisposable
{
IDbTransaction Transaction { get; }
IDenunciaRepository DenunciaRepository { get; }
IUsuarioRepository UsuarioRepository { get; }
IRolRepository RolRepository { get; }
void Commit();
void Rollback();
}
MSSQLUnitOfWork.cs:
public class MSSQLUnitOfWork : IUnitOfWork
{
private bool _already_disposed = false;
private IDbConnection _connection;
private IDbTransaction _transaction;
private IDenunciaRepository _denuncia_repository;
private IUsuarioRepository _usuario_repository;
private IRolRepository _rol_repository;
public IDbTransaction Transaction
{
get => _transaction;
}
public IDenunciaRepository DenunciaRepository
{
get => _denuncia_repository;
}
public IUsuarioRepository UsuarioRepository
{
get => _usuario_repository;
}
public IRolRepository RolRepository
{
get => _rol_repository;
}
public MSSQLUnitOfWork()
{
var connection_string = ConfigurationManager.ConnectionStrings["MSSQL"].ConnectionString;
_connection = new SqlConnection(connection_string);
_connection.Open();
_transaction = _connection.BeginTransaction();
//TODO: Crear repos con transacción
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
protected virtual void Dispose(bool disposeManagedObjects)
{
if (!_already_disposed)
{
if (disposeManagedObjects)
{
_transaction?.Dispose();
_connection?.Dispose();
}
_already_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
I recommend you 3 different things.
Start, Commit and Rollback your data transactions within the repository where you are instantiatign the UnitOfWork - the least I recommend
Create a Service class where you can create an instance of UnitOfWork and pass it the instance or DBContext to the Repositories that you involve in the transactions
Create Repository instance within the UnitOfWork class tha knows the current DBContext then you can access from UnitOfWork the repository operations and starts and ends the transactions in the same context. More recommended
Something like:
UnitOfWorkInstance.MyRepositoryA.AddAsync(...);
UnitOfWorkInstance.MyRepositoryB.AddAsync(...);
UnitOfWorkInstance.Commit();
I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated.
It looks like you have a circular dependency and circular dependency is always a bad design and you should break it. If you followed Single Responsibility Principle it should not happens. If it happens the service may have too many responsibilities and you should break it in multiple services or sometime it is because you have too small service and these services should be reunited.
In your case it looks like IUnitOfWork has too many responsibility. What's the goal of this service? what are its responsibilities?
For me, this service should not have any repositories, this service doesn't need any. If any other service needs such a repository, they just have to add a dependency on it. Also the repository don't need to have a dependency on IUnitOfWork but only on IDbTransaction. Furthermore IDbTransaction and IDbConnection should be configured in your dependency injector. If these service need to be instanciated by IUnitOfWork for any reason you can do something like
builder.RegisterType<MSSQLUnitOfWork>()
.As<IUnitOfWork>()
.InstancePerLifetimeScope()
builder.Register(c => c.Resolve<IUnitOfWork>().Transation)
.As<IDbTransaction>();
builder.Register(c => c.Resolve<IUnitOfWork>().Connection)
.As<IDbConnection>();
Related
Lets start with my architech. I will try to simplify my code as much as I can. If I totally mixed up, please warn me.
IUnitOfWork
public interface IUnitOfWork<T> : IDisposable
{
IEntityRepository<T> Repository { get; }
void Commit();
}
UnitOfWork
public class UnitOfWork<T> : IUnitOfWork<T>
{
private IDbConnection _Connection;
private IDbTransaction _Transaction;
public IRepository<T> Repository { get; private set; }
public UnitOfWork(IDbConnection Connection, IRepository<T> Repository)
{
_Connection = Connection;
this.Repository = Repository;
_Transaction = _Connection.BeginTransaction();
}
}
RepositoryBase
public abstract class RepositoryBase<T> : IRepository<T>
{
protected IDbTransaction Transaction;
protected IDbConnection Connection { get { return Transaction.Connection; } }
public RepositoryBase(IDbTransaction transaction)
{
Transaction = transaction;
}
}
TestDAL
public class TestDAL : RepositoryBase<Test>, ITestDAL
{
public DpTestDAL(IDbTransaction transaction) : base(transaction) {}
}
TestService (BLL)
public class TestService : ITestService
{
private IUnitOfWork<Test> uow;
public TestService(IUnitOfWork<Test> unitOfWork)
{
uow = unitOfWork;
}
public List<Test> GetAll()
{
return uow.Repository.GetAll().ToList();
}
}
And my autofac configurations.
builder.RegisterType<TestService>().As<ITestService>();
builder.RegisterType(typeof(OracleConnection)).As(typeof(IDbConnection)).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(RepositoryBase<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>)).InstancePerDependency();
//builder.RegisterType(typeof(OracleTransaction)).As(typeof(IDbTransaction)).InstancePerLifetimeScope();
I am newbie this kind of architect and try to something my self. Please tell me if there is something wrong or totally wrong.
My problem is, I need to pass the IDbTransaction to data acess classess contructor. When I do not register IDbTransaction interface, exception is "could not resolve parameter", when I try to register with OracleTransaction the exception is "OracleTransaction" do not has a public contructor. Where did I mixed up?
As the OracleTransaction doesn't have public constructor you can use IDbConnection to create one like below,
builder.Register(c =>
{
var conn = c.Resolve<IDbConnection>();
return conn.BeginTransaction(IsolationLevel.ReadCommitted);
});
The above is an example of how you can register it for your use case.
This question already has answers here:
How should I setup my repositories to use the same context?
(2 answers)
Closed 4 years ago.
Background
In my web application, I create stored procedures and then create an edmx file to use the stored procedures to handle all the database interaction.
But I started to wonder if I'm doing this right because, as you'll see in the example below,
I'm instantiating two instances of Context every time the Controller gets called even when the called method doesn't require database work
I'm instantiating an instances of Context in each repository, so when a request needs to get data from Repository A and B, I have two instances of Context.
Repository A
public class RepositoryA
{
private readonly Context _context;
public RepositoryA()
{
_context = new Context();
}
public List<CLASS> GetA(int id)
{
return _context.GetByID(id);
}
}
Repository B
public class RepositoryB
{
private readonly Context _context;
public RepositoryB()
{
_context = new Context();
}
public List<CLASS> GetB(int id)
{
return _context.GetByID(id);
}
}
Controller
public class Controller
{
private readonly IRepositoryA _reposA;
private readonly IRepositoryB _reposB;
public Controller() : this(new RepositoryA(), new RepositoryB())
{}
public Controller(IRepositoryA a, IRepositoryB b)
{
_respoA = a;
_reposB = b;
}
public ActionResult METHOD()
{
//do something with both RepositoryA and RepositoryB
var dataFromA = _reposA.GetA(ID);
var dataFromB = _reposB.GetB(ID);
return View(someData);
}
}
Now the question is: I'm not sure if this is supposed to be the normal implementation, so I've been trying to figure out how I can set this up in more efficient and testable way, and I tried something like this.
I believe that this design solves some of my concerns:
Service gets called every time Controller gets called, but Context doesn't get instantiated every time (Context gets instantiated per request).
When a service requires both Repository A and B, it uses the same Context
However, with how Service is set up, I can't unit test Service with test data because I can't use my mock repositories.
public class Controller
{
private Service _service;
public Controller()
{
_service = new Service();
}
public ActionResult METHOD()
{
_service.DoSomethingWithRepoAandB();
return View();
}
}
public class Service
{
public void DoSomethingWithRepoAandB()
{
using (var _context = new Context())
{
RepositoryA a = new RepositoryA(_context);
RepositoryB b = new RepositoryB(_context);
something = a.DoSomethingWithA();
otherThing = b.DoOtherThingWithB();
}
}
}
So, I'm thinking I should set up Service like this.
With this design, Context will be instantiated every time Controller gets called (unless I instantiate Service in a Controller method), but I can unit test by passing mock repositories.
public class Service
{
private readonly Context _context;
private IRepositoryA _a;
private IRepositoryB _b;
public Service()
{
_context = new Context();
_a = new RepositoryA(_context);
_b = new RepositoryB(_context);
}
// Pass Mock Repositories in unit tests
public Service(RepositoryA a, RepositoryB b)
{
_a = a;
_b = b;
}
public void DoSomethingWithRepoAandB()
{
something = _a.DoSomethingWithA();
otherThing =_b.DoOtherThingWithB();
}
}
Am I doing this completely wrong or on the ok track? I'd appreciate any advice.
Your services should NOT have any information about your context, that information should only be accessible by the repository, following SOLID principles.
The access of your layers should be as follows:
Controller -> Service -> Repository -> Context
On your IRepository interfaces, you will have the methods you're going to call, and you either instantiate them manually, but to implement it correctly, you'll have to setup your dependency injection.
Also, your Repositories constructors dont receive any context as a parameter, so you cant do something like this:
RepositoryA a = new RepositoryA(_context);
On another point, your controller shouldn't access repositories as well, as it breaks your architecture
public ActionResult METHOD()
{
//do something with both RepositoryA and RepositoryB
var dataFromA = _reposA.GetA(ID);
var dataFromB = _reposB.GetB(ID);
return View(someData);
}
Here's the correct code, for further understanding:
public class RepositoryA : IRepositoryA
{
private readonly Context _context;
public RepositoryA(Context context)
{
_context = context;
}
public List<CLASS> GetA(int id)
{
return _context.GetByID(id);
}
}
public interface IRepositoryA
{
List<CLASS> GetA(int id);
}
public class RepositoryB : IRepositoryB
{
private readonly Context _context;
public RepositoryB(Context context)
{
_context = context;
}
public List<CLASS> GetB(int id)
{
return _context.GetByID(id);
}
}
public interface IRepositoryB
{
List<CLASS> GetB(int id);
}
public class Controller
{
private IService _service;
public Controller(IService service)
{
_service = service;
}
public ActionResult METHOD(int id)
{
//do something with both RepositoryA and RepositoryB THROUGH the service. The service needs to hold the business rules, and repositories should only care about querying data and handling contexts.
var data = _service.DoSomethingWithRepoAandB(id)
return View(data);
}
}
public class Service : IService
{
private IRepositoryA _a;
private IRepositoryB _b;
// Pass Mock Repositories in unit tests -> PS: You can't have 2 constructors if you're using dependency injection.
public Service(RepositoryA a, RepositoryB b)
{
_a = a;
_b = b;
}
public void DoSomethingWithRepoAandB(int id)
{
var something = _a.GetA(id);
var otherThing = _b.GetB(id);
}
}
public interface IService
{
void DoSomethingWithRepoAandB(int id);
}
public class Bootstrapper
{
//this class should be in a separated assembly, responsible for handling the dependency injection. Using Simple Injection syntax just as an example
public static void RegisterServices(Container container) //IoC Container
{
container.Register<IService, Service>(Lifestyle.Scoped);
container.Register<IRepositoryA, RepositoryA>(Lifestyle.Scoped);
container.Register<IRepositoryB, RepositoryB>(Lifestyle.Scoped);
container.Register<Context>(() => {
var options = // Configure your ContextOptions here
return new Context(options);
});
}
}
public class Startup
{
//This is your startup configuration if you're using WebApi. If you're on MVC, you can do this on your Global.asax
public void Configuration()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
BootStrapper.RegisterServices(container);
}
}
I am currently working on a WPF application using .NET 4.6. We are using the Entity Framework for Persistence and Database interaction with SQL Server. When I joined the project all the database interaction was being done in the code behind like the following. (Yes I realize this is bad practice)
IList<User> users;
using (AppDbContext db = new AppDbContext())
{
users = db.Users.Where(x => x.Active == true).ToList();
}
// do code to update UI
I basically said this is bad practice, we need to separate out the business logic and queries from the presentation layer. I thought the Repository pattern might be a nice place to start but I have doubts about how the DbContext will be managed.
Example Repository Interface
public interface IUserService : IDisposable
{
IList<applicationuser> GetUsers();
IList<AllApplicationUser> GetActiveUsers();
AllApplicationUser GetUserView(long id);
applicationuser GetUser(long id);
void CreateUser(applicationuser user);
void UpdateUser(applicationuser user);
void DeleteUser(long id);
void Save();
applicationuser Authenticate(string username, string password);
}
And the implementation
class UserService : IUserService
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly OMEGAEntities _db;
private bool _disposed = false;
public UserService()
{
_db = new OMEGAEntities();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_db.Dispose();
}
}
_disposed = true;
}
public IList<applicationuser> GetUsers()
{
// fetch only active users that do not have the role SuperAdmin
return _db.applicationusers.Where(u => u.roleid != 1 && u.activeflag == true).ToList();
}
public IList<AllApplicationUser> GetActiveUsers()
{
return _db.AllApplicationUsers.Where(u => u.activeflag == true && u.roleid != 1).ToList();
}
public AllApplicationUser GetUserView(long id)
{
return _db.AllApplicationUsers.Single(x => x.id == id);
}
public applicationuser GetUser(long id)
{
return _db.applicationusers.Find(id);
}
public void CreateUser(applicationuser user)
{
_db.applicationusers.Add(user);
}
public void UpdateUser(applicationuser user)
{
_db.Entry(user).State = EntityState.Modified;
}
public void DeleteUser(long id)
{
var user = _db.applicationusers.Find(id);
_db.applicationusers.Remove(user);
}
public void Save()
{
_db.SaveChanges();
}
public applicationuser Authenticate(string username, string password)
{
applicationuser user =
_db.applicationusers.SingleOrDefault(u => u.loginid.ToLower().Equals(username.ToLower()));
if (user != null)
{
if (Common.Utils.DecryptData(user.password).Equals(password))
{
return user;
}
}
return null;
}
}
The issue now is when there are multiple windows open there are multiple contexts open and long running contexts. One of the ideas floating around right now is to use a single context for the entire application, since it is a single user desktop application, however that doesn't seem like the best idea. Also the dbContexts need to be disposed off when the windows are closing, etc.
I've read about the idea of using a DbContext per request in ASP.NET. Maybe one could be used per Window?
I'm just wondering if the repository pattern is the best way for WPF applications? I've seen many people have success with it in ASP.NET.
You could create a sort of unitofwork on top of your repositories, and contructor inject the dbcontext into the repositories
public class UnitOfWork : IUnitOfWork
{
private IUserService userService;
private omegaentities dbcontext;
public UnitOfWork ()
{
dbcontext = new Omegaentities ();
}
public IUserService UserService {
get {
If (userService == null)
{ userService = new UserService(dbcontext);}
Return userService;
}
}
Then you just call
unitOfWork.UserService.GetUsers();
...
This gives one more layer of abstraction, you still need to handle the single instance of unitofwork, but that can be handled nicely with some DI magic :)
Note: i wrote this from the android app! (Coding c# on a phone, does not rock!)
It really depends on how you use your DbContext. If you are reading some data could be changed by some other application, I would suggest use disposable DbContext every time. If you are sure your application is the only one to touch the database, then keep a single DbContext in your repository and use a singleton repository across the entire application.
I am trying to make sense of mocking in unit testing and to integrate the unit testing process to my project. So I have been walking thru several tutorials and refactoring my code to support mocking, anyway, I am unable to pass the tests, because the DB method I am trying to test is using a transaction, but when creating a transaction, I get
The underlying provider failed on Open.
Without transaction everything works just fine.
The code I currently have is:
[TestMethod]
public void Test1()
{
var mockSet = GetDbMock();
var mockContext = new Mock<DataContext>();
mockContext.Setup(m => m.Repository).Returns(mockSet.Object);
var service = new MyService(mockContext.Object);
service.SaveRepository(GetRepositoryData().First());
mockSet.Verify(m => m.Remove(It.IsAny<Repository>()), Times.Once());
mockSet.Verify(m => m.Add(It.IsAny<Repository>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
// gets the DbSet mock with one existing item
private Mock<DbSet<Repository>> GetDbMock()
{
var data = GetRepositoryData();
var mockSet = new Mock<DbSet<Repository>>();
mockSet.As<IQueryable<Repository>>().Setup(m => m.Provider).Returns(data.Provider);
// skipped for brevity
return mockSet;
}
Code under test:
private readonly DataContext _context;
public MyService(DataContext ctx)
{
_context = ctx;
}
public void SaveRepositories(Repository repo)
{
using (_context)
{
// Here the transaction creation fails
using (var transaction = _context.Database.BeginTransaction())
{
DeleteExistingEntries(repo.Id);
AddRepositories(repo);
_context.SaveChanges();
transaction.Commit();
}
}
}
I was trying to mock the transaction part as well:
var mockTransaction = new Mock<DbContextTransaction>();
mockContext.Setup(x => x.Database.BeginTransaction()).Returns(mockTransaction.Object);
but this is not working, failing with:
Invalid setup on a non-virtual (overridable in VB) member: conn =>
conn.Database.BeginTransaction()
Any ideas how to solve this?
As the second error message says, Moq can't mock non-virtual methods or properties, so this approach won't work. I suggest using the Adapter pattern to work around this. The idea is to create an adapter (a wrapper class that implements some interface) that interacts with the DataContext, and to perform all database activity through that interface. Then, you can mock the interface instead.
public interface IDataContext {
DbSet<Repository> Repository { get; }
DbContextTransaction BeginTransaction();
}
public class DataContextAdapter {
private readonly DataContext _dataContext;
public DataContextAdapter(DataContext dataContext) {
_dataContext = dataContext;
}
public DbSet<Repository> Repository { get { return _dataContext.Repository; } }
public DbContextTransaction BeginTransaction() {
return _dataContext.Database.BeginTransaction();
}
}
All of your code that previously used the DataContext directly should now use an IDataContext, which should be a DataContextAdapter when the program is running, but in a test, you can easily mock IDataContext. This should make the mocking way simpler too because you can design IDataContext and DataContextAdapter to hide some of the complexities of the actual DataContext.
I've tried the wrapper/adapter approach, but came up against the problem that when you then go to test the code:
using (var transaction = _myAdaptor.BeginTransaction())
Your mock/fake still needs to return something so the line transaction.Commit();
can still execute.
Normally I'd set the fake of my adapter to return an interface from BeginTransaction() at that point (so I can fake that returned object too), but the DbContextTransaction returned by BeginTransaction() only implements IDisposable so there was no interface that could give me access to the Rollback and Commit methods of DbContextTransaction.
Furthermore, DbContextTransaction has no public constructor, so I couldn't just new up an instance of it to return either (and even if I could, it wouldn't be ideal as I couldn't then check for calls to commit or rollback the transaction).
So, in the end I took a slightly different approach and created a separate class altogether to manage the transaction:
using System;
using System.Data.Entity;
public interface IEfTransactionService
{
IManagedEfTransaction GetManagedEfTransaction();
}
public class EfTransactionService : IEfTransactionService
{
private readonly IFMDContext _context;
public EfTransactionService(IFMDContext context)
{
_context = context;
}
public IManagedEfTransaction GetManagedEfTransaction()
{
return new ManagedEfTransaction(_context);
}
}
public interface IManagedEfTransaction : IDisposable
{
DbContextTransaction BeginEfTransaction();
void CommitEfTransaction();
void RollbackEfTransaction();
}
public class ManagedEfTransaction : IManagedEfTransaction
{
private readonly IDataContext _context;
private DbContextTransaction _transaction;
public ManagedEfTransaction(IDataContext context)
{
_context = context;
}
/// <summary>
/// Not returning the transaction here because we want to avoid any
/// external references to it stopping it from being disposed by
/// the using statement
/// </summary>
public void BeginEfTransaction()
{
_transaction = _context.Database.BeginTransaction();
}
public void CommitEfTransaction()
{
if (_transaction == null) throw new Exception("No transaction");
_transaction.Commit();
_transaction = null;
}
public void RollbackEfTransaction()
{
if (_transaction == null) throw new Exception("No transaction");
_transaction.Rollback();
_transaction = null;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
}
}
}
I then inject that service class into whatever classes need to use a transaction. For example, using the code from the original question:
private readonly DataContext _context;
private readonly IEfTransactionManager _transactionManager;
public MyService(DataContext ctx, IEfTransactionManager transactionManager)
{
_context = ctx;
_transactionManager = transactionManager;
}
public void SaveRepositories(Repository repo)
{
using (_context)
{
// Here the transaction creation fails
using (var managedEfTransaction = _transactionManager.GetManagedEfTransaction())
{
try
{
managedEfTransaction.BeginEfTransaction();
DeleteExistingEntries(repo.Id);
AddRepositories(repo);
_context.SaveChanges();
managedEfTransaction.CommitEfTransaction();
}
catch (Exception)
{
managedEfTransaction.RollbackEfTransaction();
throw;
}
}
}
}
You can find a pretty good solution here.
In short, you need to create proxy class for DbContextTransaction and use it instead of an original one. So that you can mock your proxy and test your method with BeginTransaction().
PS. In article which I linked above, author forgot about the virtual keyword for BeginTransaction() method placed in dbContext class:
// <summary>
/// When we call begin transaction. Our proxy creates new Database.BeginTransaction and gives DbContextTransaction's control to proxy.
/// We do this for unit test.
/// </summary>
/// <returns>Proxy which controls DbContextTransaction(Ef transaction class)</returns>
public virtual IDbContextTransactionProxy BeginTransaction()
{
return new DbContextTransactionProxy(this);
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am learning Entity Framework and some design patterns such repository pattern and unit of work. I wrote a program that uses these patterns. In this class I have one DbContext object and I always use this static DbContext. Throughout my application's lifetime, there is only one DbContext object. Am I doing right using this way, think if there are lots of users. Will be any problem about database connections.
UI code:
UnitOfWork uow = GetUnitOfWork(); //There is only one uow object
uow.EmployeeRepository.Insert(new Employee
{
Name = name,
Surname = surname
});
GetUnitOfWork code:
private static UnitOfWork _uow;
public static UnitOfWork GetUnitOfWork()
{
return _uow ?? (_uow = new UnitOfWork());
}
UnitOfWork code:
public class UnitOfWork : IDisposable
{
private static FollowerEntities _context;
private DbContextTransaction _transaction;
//repositories
private EmployeeRepository _employeeRepository;
public UnitOfWork()
{
_context = new FollowerEntities();
}
public EmployeeRepository EmployeeRepository
{
get { return _employeeRepository ?? (_employeeRepository = new EmployeeRepository(_context)); }
}
public void Save()
{
try
{
_transaction = _context.Database.BeginTransaction();
_context.SaveChanges();
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
Employee Repository
public class EmployeeRepository : GenericRepository<Employee>
{
public EmployeeRepository(FollowerEntities entities)
: base(entities)
{
}
}
Generic Repository
public class GenericRepository<T> where T : class
{
private FollowerEntities _entities;
private DbSet<T> table = null;
public GenericRepository()
{
}
public GenericRepository(FollowerEntities entities)
{
this._entities = entities;
table = _entities.Set<T>();
}
public IEnumerable<T> SelectAll()
{
return table.ToList();
}
public T SelvectById(object id)
{
return table.Find(id);
}
public void Insert(T obj)
{
table.Add(obj);
}
public void Update(T obj)
{
table.Attach(obj);
_entities.Entry(obj).State = EntityState.Modified;
}
public void Delete(object id)
{
T existing = table.Find(id);
table.Remove(existing);
}
}
private static UnitOfWork _uow;
public static UnitOfWork GetUnitOfWork()
{
return _uow ?? (_uow = new UnitOfWork());
}
Although technically correct, this won't be as useful as you think. You limit the API to a single instance of the UoW. Considering shared scenarios like ASP.NET, this method won't probably be used as often as you think. I suggest removing it.
public UnitOfWork()
{
_context = new FollowerEntities();
}
This unnecessarily couples the context lifetime to UoW lifetime. Instead, separate them:
public UnitOfWork( FollowerEntities context )
{
_context = context;
}
This way, other means of lifetime management (IoC container possibly) could be used.
_transaction = _context.Database.BeginTransaction();
_context.SaveChanges();
_transaction.Commit();
Are you sure saves should always be wrapped in a transaction? What if another transaction exists on the same connection and you want to reuse it?
And the biggest issue - neither the UoW nor repositories are abstracted (with interfaces). This means that the client is still coupled to the sole implementation. You have just created a wrapped over Entity Framework but you can't benefit from it, for example, you can't switch to another implementation without rewriting it. I don't see any solid point in it, beside just excercising.