I'm attempting to write a unit test for my repository class for its Create method that uses DbContext and the Add method.
My idea was to get a count of the number of existing records. Call the Create method. Get the new count of records and check that its incremented by 1.
However, when running the unit test, it errors when calling the Add method with the following error:
{"Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DbContext'."}
I'm trying to understand why is this and how to overcome this?
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext(DbContextOptions<MyDatabaseContext> options) : base(options)
{
}
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>();
}
}
public class Repository : IRepository
{
private readonly MyDatabaseContext _dbContext;
public Repository(MyDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public Record Create(Record record)
{
try
{
using (_dbContext)
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
}
catch (Exception ex)
{
return null;
}
}
public IEnumerable<Record> GetAll()
{
try
{
using (_dbContext)
{
return _dbContext.Records.ToList();
}
}
catch (Exception ex)
{
return null;
}
}
}
public interface IRepository
{
Record Create(Record record);
IEnumerable<Record> GetAll();
}
Startup.cs:
services.AddDbContext<MyDatabaseContext>(opt => opt.UseInMemoryDatabase("memoryDb"));
services.AddScoped<IRepository, Repository>();
Unit test:
[TestMethod]
public async Task Create_Successfully()
{
var repository = new Repository(await GetDbContext());
var existingRecords = repository.GetAll();
repository.Create(new Record());
var newRecords = repository.GetAll();
Assert.AreEqual(3, existingRecords.Count());
Assert.AreEqual(4, newRecords.Count());
}
private async Task<DbContext> GetDbContext()
{
var options = new DbContextOptionsBuilder<DbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var context = new DbContext(options);
context.Database.EnsureCreated();
if (await context.Records.CountAsync() <= 0)
{
for (int i = 1; i <= 3; i++)
{
context.Records.Add(new Records());
await context.SaveChangesAsync();
}
}
return context;
}
You need to remove the using statements in the GetAll and Create methods:
public Record Create(Record record)
{
try
{
using (_dbContext)
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
}
catch (Exception ex)
{
return null;
}
}
To:
public Record Create(Record record)
{
try
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
catch (Exception ex)
{
return null;
}
}
You don't need to worry about disposing the service as the conatiner will do that for you in production. In your tests you can do this if you want to clean things up:
[TestMethod]
public async Task Create_Successfully()
{
using (var context = await GetDbContext())
{
var repository = new Repository(context);
var existingRecords = repository.GetAll();
repository.Create(new Record());
var newRecords = repository.GetAll();
Assert.AreEqual(3, existingRecords.Count());
Assert.AreEqual(4, newRecords.Count());
}
}
Related
There are many examples on the internet about the use of unit of work and generic repository together, but I couldn't find exactly how to apply the unit of work repository pattern to my own project. Because everyone did it differently.
Actually, I set up the unit of work structure, but I don't quite understand how I can use it in my own project? How can I apply the unit of work repository I made to my own project? Can you help me with this? Can you tell me if I have a mistake? This is how I saw the unit of work repository on the internet and implemented it.
First of all, if I just talk about my project, my project is an ASP.NET Core Web API project and basically a 3-layer structure:
API layer. I have controllers in the API layer.
Second is the business layer. The business layer only serves to communicate with the data access layer.
The third layer is the data access layer, in this layer I do the database operations, such as adding, deleting, updating.
I'm doing these with the generic repository. As an example, I am sharing some of my code below.
I just shared category as an example, but I have more than one class like category.
API layer - CategoriesController:
[Route("api/[controller]")]
[ApiController]
public class categoriesController : ControllerBase
{
private ICategoryService category_service;
DatabaseContext c = new DatabaseContext();
public categoriesController(ICategoryService category_service)
{
this.category_service = category_service;
}
[HttpGet("getall")]
public async Task<IActionResult> Get()
{
return Ok(await category_service.TGetAll());
}
[HttpGet("getbyid/{id}")]
public async Task<IActionResult> GetByIdCategory(int id)
{
var category = await category_service.TGetById(id);
if (category != null)
{
return Ok(category); // 200 ok
}
else
{
return NotFound(); //404 not found
}
}
[HttpPost("add")]
public async Task<IActionResult> Add(Category category)
{
var result = category_service.TAdd(category);
if (result != null)
{
return Ok(result);
}
return BadRequest(result);
}
}
Business layer - CategoryManager:
public class CategoryManager:ICategoryService
{
ICategoryDal _categoryDal;
public CategoryManager(ICategoryDal _cateogoryDal)
{
this._categoryDal = _cateogoryDal;
}
public async Task<List<Category>> TGetAll()
{
return await _categoryDal.GetListAll();
}
public async Task<Category> TGetById(int id)
{
return await _categoryDal.GetByID(id);
}
public async Task TAdd(Category entity)
{
await _categoryDal.Insert(entity);
}
public async Task TDelete(Category entity)
{
await _categoryDal.Delete(entity);
}
public async Task TUpdate(Category entity)
{
await _categoryDal.Update(entity);
}
}
Data Access layer - CategoryRepository:
public class CategoryRepository : GenericRepository<Category>, ICategoryDal
{
}
GenericRepository:
public class GenericRepository<T> : IGenericDal<T> where T : class
{
protected DatabaseContext dbContext;
public GenericRepository(DatabaseContext context)
{
dbContext = context;
}
public async Task Delete(T t)
{
dbContext.Remove(t);
await dbContext.SaveChangesAsync();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return dbContext.Set<T>()
.Where(expression)
.AsNoTracking();
}
public async Task<T> GetByID(int id)
{
return await dbContext.Set<T>().FindAsync(id);
}
public async Task<List<T>> GetListAll()
{
return await dbContext.Set<T>().ToListAsync();
}
public async Task<List<T>> GetListAll(Expression<Func<T, bool>> filter)
{
return await dbContext.Set<T>()
.Where(filter)
.ToListAsync();
}
public async Task Insert(T t)
{
await dbContext.AddAsync(t);
await dbContext.SaveChangesAsync();
}
public async Task Update(T t)
{
var updatedEntity = dbContext.Entry(t);
updatedEntity.State = EntityState.Modified;
dbContext.SaveChanges();
}
}
UnitOfWork:
public class UnitOfWorkRepository : IUnitOfWork
{
private readonly DatabaseContext _dbContext;
private IDbContextTransaction _transaction;
private bool _disposed;
public UnitOfWorkRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public bool BeginNewTransaction()
{
try
{
_transaction = _dbContext.Database.BeginTransaction();
return true;
}
catch (Exception ex)
{
return false;
}
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IGenericDal<T> GetRepository<T>() where T : class
{
return new GenericRepository<T>(_dbContext);
}
public bool RollBackTransaction()
{
try
{
_transaction.Rollback();
_transaction = null;
return true;
}
catch (Exception ex)
{
return false;
}
}
public int SaveChanges()
{
var transaction = _transaction != null ? _transaction : _dbContext.Database.BeginTransaction();
using (transaction)
{
try
{
// Context boş ise hata fırlatıyoruz
if (_dbContext == null)
{
throw new ArgumentException("Context is null");
}
// SaveChanges metodundan dönen int result ı yakalayarak geri dönüyoruz.
int result = _dbContext.SaveChanges();
// Sorun yok ise kuyruktaki tüm işlemleri commit ederek bitiriyoruz.
transaction.Commit();
return result;
}
catch (Exception ex)
{
// Hata ile karşılaşılır ise işlemler geri alınıyor
transaction.Rollback();
throw new Exception("Error on SaveChanges ", ex);
}
}
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
bool BeginNewTransaction();
bool RollBackTransaction();
IGenericDal<T> GetRepository<T>() where T : class;
int SaveChanges();
}
You can check the source in the link.
https://www.linkedin.com/pulse/repository-unit-work-patterns-net-core-dimitar-iliev/
I have implemented custom retry strategy on an API which is in .NET Core 2.2. Retry strategy should work only for transient errors from the database(Azure SQL). How can I generate transient errors, to test this feature?
I don't know your code, but this is one solution. The idea is to separate the retry logic from the actual action that needs to be retried, so that each can be tested.
A static RetryHandler could also work, but it depends on your need.
Psedo code, does not compile.
public RetryHandler : IRetryHandler
{
private static List<Type> transientErros = new List<Type>
{
typeof(TimeoutException),
typeof(SomeotherExceptionThatRequiresRetry),
}
public void RetryOnTransientError(Action action, int attempts = 3)
{
for (var i = 0; i < attempts; i++)
{
try
{
action();
return;
}
catch(Exception e)
{
if(transientErros.Contains(e.GetType())
continue;
throw;
}
}
}
}
public class UserRepository
{
private IMyDbContext context;
private IRetryHandler retryHandler;
public Repository(IMyDbContext context, IRetryHandler retryHandler)
{
this.context = context;
this.retryHandler = retryHandler;
}
public void InsertUser(User user)
{
retryHandler.RetryOnTransientError(() => DoInsertUser(user));
}
private void DoInsertUser(User user)
{
// insert logic
context.SaveChanges();
}
}
[Test]
public void InsertUserRetriesOnTransientError()
{
// Arrange
var contextMock = new Mock<IMyDbContext>();
var repository = new Repository(contextMock);
var user = CreateUser();
contextMock.Setup(x => x.SaveChanges()).Throws<TransientException>());
// Act
Assert.Throws<TransientException>(() => repository.InsertUser(user));
// Assert
// verify SaveChanges() was called 3 times
}
I am trying to implement an asp.net web API and it is my first time trying to implement such a project. I googled a lot.
Many times lost down the road because there is a lot of misguiding articles. Now I accomplished to implement the layered architecture. I have a business entity, business service, data models class library projects and a web API itself. As I said I googled a lot since I have no experience with web API, I decided to use generic repository and unit of work in my solution. My API is working butI get the following error when I load test with 400 users:
System.Transactions.TransactionException: The operation is not valid for the state of the transaction. ---> System.TimeoutException: Transaction Timeout. The underlying provider failed on Open.
I researched and asked experienced friends, they said I am not disposing of all the DBContexts I am creating. As I am using transient scope, a new DBcontext is created for every action call (controller construction), but unless I call context.Dispose() in the action the pools are not released.
Honestly, I am not sure how can I dispose of more.
Controller:
namespace Game.Controllers
{
[System.Web.Http.RoutePrefix("api/v2/game")]
public class GameController : ApiController
{
private readonly IGameServices _gameServices;
#region Public Constructor
public GameController(IGameServices gameServices)
{
_gameServices = gameServices;
}
#endregion
[HttpPost, Route("purchase")]
public async Task<IHttpActionResult> PurchaseGame(RequestDto game)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//Call Services
var gameConfirmResponse = await _gameServices.GamePurchase(game);
switch (gameConfirmResponse.StatusCode)
{
case HttpStatusCode.NotFound:
{
return NotFound();
}
case HttpStatusCode.InternalServerError:
{
return InternalServerError();
}
case HttpStatusCode.OK:
{
if (gameConfirmResponse.Content == null)
{
return new System.Web.Http.Results.ResponseMessageResult(
Request.CreateErrorResponse((HttpStatusCode) 222, new HttpError("No Results Found")));
}
var responseStream = await gameConfirmResponse.Content.ReadAsStringAsync();
var resultResponse = JsonConvert.DeserializeObject<GameConfirmResponse>(responseStream);
if (resultResponse.coupons == null)
{
return new System.Web.Http.Results.ResponseMessageResult(
Request.CreateErrorResponse((HttpStatusCode) 222,
new HttpError("No Coupons Available for this Game")));
}
//Transform GameConfirmResponse into DTO for returning result
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<GameConfirmResponse, GameConfirmResponseDto>();
cfg.CreateMap<Coupon, CouponDto>();
});
var iMapper = config.CreateMapper();
var resultDto = iMapper.Map<GameConfirmResponse, GameConfirmResponseDto>(resultResponse);
return Ok(resultDto);
}
case HttpStatusCode.Unauthorized:
{
return Unauthorized();
}
case HttpStatusCode.RequestTimeout:
{
return InternalServerError();
}
}
return Ok(gameConfirmResponse);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
}
}
Service:
namespace BusinessService
{
public class GameServices : IGameServices, IDisposable
{
private readonly UnitOfWork _unitOfWork;
public GameServices(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<HttpResponseMessage> GamePurchase(RequestDto requestDto)
{
HttpResponseMessage response = null;
response = await CallRazerService(requestDto);
return response;
}
private async Task<HttpResponseMessage> CallRazerService(RequestDto requestDto)
{
HttpResponseMessage response = null;
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
//Added to see if I fix the error
TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
//Transform DTO into GameRequest for calling Razer Initiate
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<RequestDto, GameRequest>();
cfg.CreateMap<GameRequest, GameConfirmRequest>();
cfg.CreateMap<GameConfirmResponse, GameConfirmResponseDto>();
cfg.CreateMap<Coupon, CouponDto>();
cfg.CreateMap<GameRequest, GameRequestDto>();
});
var iMapper = config.CreateMapper();
var gameRequest = iMapper.Map<RequestDto, GameRequest>(requestDto);
//Unique reference ID
gameRequest.referenceId = Guid.NewGuid();
var gameRequestDto = iMapper.Map<GameRequest, GameRequestDto>(gameRequest);
//Create signature
gameRequest = Utilities.CreateSignature(gameRequestDto, RequestType.Initiate);
//Set service
gameRequest.service = "RAZER";
//Add initiation request into database
_unitOfWork.GameRepository.Insert(gameRequest);
#region Call Razer initiate/confirm
//Call Razer for initiation
response = await Utilities.CallRazer(gameRequest, "purchaseinitiation");
//Read response
var htmlResponse = await response.Content.ReadAsStringAsync();
var gameResponse = JsonConvert.DeserializeObject<GameResponse>(htmlResponse);
//Adding initiation response into database
_unitOfWork.GameResponseRepository.Insert(gameResponse);
if (gameResponse.initiationResultCode == "00")
{
gameRequestDto = iMapper.Map<GameRequest, GameRequestDto>(gameRequest);
gameRequestDto.validatedToken = gameResponse.validatedToken;
//Create signature
var gameConfirmRequest = Utilities.CreateSignature(gameRequestDto, RequestType.Confirm);
//Transform DTO into GameRequest for calling Razer Initiate
var gameConfirmRequests = iMapper.Map<GameRequest, GameConfirmRequest>(gameConfirmRequest);
//Add confirm request into database
_unitOfWork.GameConfirmRequestRepository.Insert(gameConfirmRequests);
//Call Razer for confirm
response = await Utilities.CallRazer(gameConfirmRequest, "purchaseconfirmation");
//Read response
htmlResponse = await response.Content.ReadAsStringAsync();
var gameConfirmResponse = JsonConvert.DeserializeObject<GameConfirmResponse>(htmlResponse);
//Set service
gameConfirmResponse.service = "RAZER";
//Add confirm response into database
_unitOfWork.GameConfirmResponseRepository.Insert(gameConfirmResponse);
}
#endregion
await _unitOfWork.SaveAsync();
scope.Complete();
}
return response;
}
public void Dispose()
{
_unitOfWork.Dispose();
}
}
}
Generic Repo (shortened):
namespace DataModels
{
public class GenericRepository<TEntity> where TEntity : class
{
internal GameContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(GameContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
#region Public member methods...
public virtual async Task<List<TEntity>> GetGamesAsync(
Expression<Func<TEntity, bool>> where)
{
return await dbSet.Where(where).ToListAsync();
}
public virtual async Task<List<T>> GetGameBankProducts<T, TType>(Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, TType>> groupBy, Expression<Func<IGrouping<TType, TEntity>, T>> select)
{
return await dbSet.Where(where)
.GroupBy(groupBy)
.Select(select)
.ToListAsync();
}
#endregion
}
}
Unit of Work:
namespace DataModels
{
public class UnitOfWork : IDisposable
{
private readonly GameContext _context;
private readonly GenericRepository<GameRequest> gameRequestRepository;
private readonly GenericRepository<GameResponse> gameResponseRepository;
private readonly GenericRepository<GameConfirmRequest> gameConfirmRequestRepository;
private readonly GenericRepository<GameConfirmResponse> gameConfirmResponseRepository;
private readonly GenericRepository<GameBank> gameBankRepository;
private readonly GenericRepository<GameBankPin> gameBankPinRepository;
private readonly GenericRepository<ConfirmCancel> confirmCancelRepository;
private readonly GenericRepository<Recon> reconRepository;
private readonly GenericRepository<Company> userRepository;
public UnitOfWork(GameContext context)
{
_context = context;
}
public GenericRepository<GameRequest> GameRepository
{
get
{
return this.gameRequestRepository ?? new GenericRepository<GameRequest>(_context);
}
}
public GenericRepository<GameResponse> GameResponseRepository
{
get
{
return this.gameResponseRepository ?? new GenericRepository<GameResponse>(_context);
}
}
public GenericRepository<GameConfirmRequest> GameConfirmRequestRepository
{
get
{
return this.gameConfirmRequestRepository ?? new GenericRepository<GameConfirmRequest>(_context);
}
}
public GenericRepository<GameConfirmResponse> GameConfirmResponseRepository
{
get
{
return this.gameConfirmResponseRepository ?? new GenericRepository<GameConfirmResponse>(_context);
}
}
public GenericRepository<GameBank> GameBankRepository
{
get
{
return this.gameBankRepository ?? new GenericRepository<GameBank>(_context);
}
}
public GenericRepository<GameBankPin> GameBankPinRepository
{
get
{
return this.gameBankPinRepository ?? new GenericRepository<GameBankPin>(_context);
}
}
public GenericRepository<ConfirmCancel> ConfirmCancelRepository
{
get
{
return this.confirmCancelRepository ?? new GenericRepository<ConfirmCancel>(_context);
}
}
public GenericRepository<Recon> ReconRepository
{
get
{
return this.reconRepository ?? new GenericRepository<Recon>(_context);
}
}
public GenericRepository<Company> UserRepository
{
get
{
return this.userRepository ?? new GenericRepository<Company>(_context);
}
}
public void Save()
{
_context.SaveChanges();
}
public async Task SaveAsync()
{
await _context.SaveChangesAsync();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
Unity config:
namespace Game
{
public static class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer Container => container.Value;
#endregion
public static void RegisterTypes(IUnityContainer container)
{
// Default empty Time manager!
container.RegisterType<IGameServices, GameServices>().RegisterType<UnitOfWork>(new TransientLifetimeManager());
container.RegisterType<IUserValidate, UserValidate>(new TransientLifetimeManager());
}
}
}
People keep telling me the best practice is to use "using" but how can I use using in the controller for the Dbcontext? I couldn't find a way.
Update1:
I have been told that instead of transaction scope, I should use dbcontext transaciton (using(var dbContextTransaction = context.Database.BeginTransaction())). I couldn't find a way to use dbcontext transaction in my Service class. I am injecting unit of work in Service class.
Update2:
Should I implement inside of unit of work and remove transactionscope inside of the Service?
UOW Save:
public void Save(){
try{
using (var transaction = _context.Database.BeginTransaction()){
try
{
_context.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}catch (Exception e)
{//TODO:Logging}}
public async Task<ActionResult> Index()
{
var service = new CoreServiceFactory().GetImpersonatingService();
try
{
var data = new Impersonation()
{
ImpersonatingId = "dac733c3-01ad-447b-b0df-3a7c21fef90b",
UserId = "dac733c3-01ad-447b-b0df-3a7c21fef90b"
};
var imp = await service.Add(data);
}catch(Exception ex) { throw ex; }
return View();
}
Above is one of my controllers action method. And this works fine when the insertion is successful. This should fail if the data already exists in database(unique constraints). So when i intentionally try to make it fail(i manually add the same record in the db and then try to add it again via this action method) the action method goes into a loop or something, the exception is never thrown , chrome keeps me showing me the loading icon , looks like it went into some deadlock state. Can someone please help me understand why it goes into deadlock state when exception is thrown and how can i handle it?
Below are the reference methods
service.Add(data)
public async Task<Impersonation> Add(Impersonation t)
{
if (ValidateData(t))
{
using (var uow = GetUnitOfWork())
{
var r = GetRepository(uow);
var item = r.Add(t);
try
{
var ret = await uow.Save();
if (ret > 0)
{
return item;
}
else
{
return null;
}
}
catch (Exception ex)
{
throw ex;
}
}
}
else
{
throw new ValidationException(null, "error");
}
}
uow.Save()
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
throw ex;
}
}
}
Here is my suggestion: in uow.Save, log the error in the catch block and return zero (do not throw any exceptions).
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
// log the error here
return 0;
}
}
}
I'm not sure if returning the null in the Add service is a good idea or not, you might need to handle that differently too.
I have a controller action that gets invoked directly, but throws this error:
The operation cannot be completed because the DbContext has been disposed.
I have only found solutions online regarding deferred excecution, but I don't think that applies here, because everywhere I use the context (in this instance) I call either .ToList() or .FirstOrDefault(). Here is my code:
CONTROLLER CONTENT
private IUnitOfWork UnitOfWork;
public MyFavouritesController(
IAccountServices accountServices,
IUnitOfWork unitOfWork
)
{
AccountServices = accountServices;
UnitOfWork = unitOfWork;
}
public ActionResult Index()
{
int? id = AccountServices.GetCurrentUserId();
if (!id.HasValue)
{
return RedirectToAction("Login", "Account", new { ReturnUrl = this.HttpContext.Request.Url.AbsolutePath });
}
var user = UnitOfWork.UserRepo.Get(id.Value, "Favourites", "Favourites.County", "Favourites.Country");
//THE ABOVE CALL GETS THE ERROR
//.....
return View();
}
REPOSITORY BASE CLASS
public class RepositoryBase<C, T> : IDisposable
where C:DbContext, new()
where T : ModelBase
{
private DbContext _context;
public DbContext Context
{
get
{
if (_context == null)
{
_context = new C();
this.AllowSerialization = true;
}
return _context;
}
set
{
_context = value;
}
}
public virtual T Get(int Id, params string[] includes)
{
if (Id > 0)
{
var result = Context.Set<T>().Where(t => t.Id == Id);
foreach (string includePath in includes)
{
result = result.Include(includePath);
}
return result.FirstOrDefault(); //This is where the error occurs.
}
else
{
throw new ApplicationException("Id is zero (0).");
}
}
//... (More CRUD methods)
public void Dispose()
{
if (Context != null)
{
Context.Dispose(); //Debugger never hits this before the error
}
}
}
UNIT OF WORK CLASS
public class UnitOfWork:IUnitOfWork
{
public UnitOfWork(
//... DI of all repos
IUserRepository userRepo
)
{
//... save repos to an local property
UserRepo = userRepo;
//create a new instance of the context so that all the repo's have access to the same DbContext
Context = new Context();
//assign the new context to all the repo's
//...
UserRepo.Context = Context;
}
public Context Context { get; set; }
public IUserRepository UserRepo { get; set; }
//... (some more repositories)
public void Dispose()
{
Context.Dispose(); //THIS IS NOT HIT AT ALL
}
}
LASTLY, THE MODEL CONTAINER HAS THIS LINE
_Instance.RegisterType<IUnitOfWork, UnitOfWork>(new PerThreadLifetimeManager());
As you can see, the index action will recieve a new instance of UnitOfWork which contains a new DbContext object. But at the first call to this context, it throws the above error. This pattern works everywhere else in my code.
Thanks
UPDATE
The answer below was to use a perRequestLifetimeManager. Here is the implimentation of one in unity:
public class HttpRequestLifetimeManager : LifetimeManager
{
private string _key = Guid.NewGuid().ToString();
public override object GetValue()
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(_key))
return HttpContext.Current.Items[_key];
else
return null;
}
public override void RemoveValue()
{
if (HttpContext.Current != null)
HttpContext.Current.Items.Remove(_key);
}
public override void SetValue(object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[_key] = newValue;
}
}
I noticed you're using a PerThreadLifetimeManager to control the creation and disposal of your unit of work class. You should probably change it to something like PerRequestLifetimeManager if your IoC container supports that.
Its because your are disposing the Unit Of Work, after wich you are requesting your data, store your data in a Variable after the query then you can release the Unit Of Work instance as well.