I am building a desktopp app which uses WPF and EF7 with SqLite. In my service classes I have an instance of IContextScopeLocator injected, which main job is to create and reuse instances of EF DbContexts.
ContextScope
public class ContextScope : IDisposable
{
private readonly PrzylepaDbContext _context;
public ContextScope(PrzylepaDbContext context)
{
_context = context;
}
public EventHandler OnDisposed { get; set; }
public PrzylepaDbContext Context
{
get { return _context; }
}
public void Dispose()
{
OnDisposed.Invoke(this, EventArgs.Empty);
}
}
ContextScopeLocator
public class ContextScopeLocator : IContextScopeLocator
{
private readonly IContextFactory _factory;
public ContextScopeLocator(IContextFactory factory)
{
_factory = factory;
}
private PrzylepaDbContext _currentContext;
private readonly List<ContextScope> _currentScopes = new List<ContextScope>();
public ContextScope GetScope()
{
if (_currentContext == null)
{
//building new EF DbContext if nescesary
_currentContext = _factory.Create();
}
var scope = new ContextScope(_currentContext);
scope.OnDisposed += OnDisposed;
_currentScopes.Add(scope);
return scope;
}
private void OnDisposed(object sender, EventArgs eventArgs)
{
var scope = sender as ContextScope;
Debug.Assert(_currentScopes.Contains(scope));
_currentScopes.Remove(scope);
if (_currentScopes.Count == 0)
{
_currentContext.Dispose();
_currentContext = null;
}
}
}
Then in my service method I can use it like that:
public IEnumerable<Client> GetPublicClients()
{
using (var scope = _scopeLocator.GetScope())
{
return scope.Context.Clients.Where(x => x.IsPublic).IncludeStandard().ToList();
}
}
And even with nested queries I can still get the same context. I will not be calling service methods from multiple threads so I thought this approach would work more less fine for me.
Then in my viewmodel class I receive a message in the following way
private void ClientModifiedMessageHandler(NotifyEntityModifiedMessage<Client> msg)
{
if (msg.EntityId == ModifiedOffer.ClientId)
{
var client = _clientService.GetById(ModifiedOffer.ClientId);
ModifiedOffer.Client = client; //exception
}
}
Exception is raised by the DbContext which was used to get ModifiedOffer from the Db:
"The instance of entity type 'Przylepa.Data.Client' cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values."
The problem is that the old DbContext is still alive because it subscribes PropertyChanged event in the ModifiedOffer even though Dispose() was called on it (DbContext._disposed is true).
How can I make these DbContexts unsubscribe these events, so that I can do what I want with my model class instances? Thank you
Related
I'm using ThreadPool with generic repository and I'm getting this error;
'Cannot access a disposed object. A common cause of this error is disposing a context 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, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.'
private readonly AuthorizedServiceService _authorizedServiceService;
private readonly CustomerService _customerService;
public IConfigurationRoot Configuration { get; }
public UpdateService(AuthorizedServiceService authorizedServiceService, CustomerService customerService)
{
_authorizedServiceService = authorizedServiceService;
_customerService = customerService;
}
public void UpdateAllRecords()
{
_authorizedServiceService.GetByActive().ToList().ForEach(UpdateAuthorizedServiceRecords);
}
void UpdateAuthorizedServiceRecords(AuthorizedService authorizedService)
{
//UpdateStart(authorizedService);
var mywatch = new Stopwatch();
mywatch.Start();
ThreadPool.QueueUserWorkItem(new WaitCallback(state => { UpdateStart(authorizedService); }));
mywatch.Stop();
mywatch.Reset();
}
public void UpdateStart(AuthorizedService authorizedService)
{
UpdateCustomers(authorizedService);
ThreadPool.QueueUserWorkItem(new WaitCallback(state => { UpdateCustomers(authorizedService); }));
}
internal void UpdateCustomers(AuthorizedService authorizedService)
{
try
{
if (authorizedService.CustomerUpdateLocked)
return;
var carDatabaseClient = new DataCarDatabaseClient();
var result = carDatabaseClient.GetCustomers(authorizedService, authorizedService.LastCustomerUpdate);
var dataRows = Convert<Customer>(result).Select(s=>
{
s.Id = authorizedService.Code + "-" + s.DcId;
s.AuthorizedService = authorizedService;
return s;
}).ToList();
_customerService.SaveOrUpdate(dataRows.OrderBy(p=>p.Id).FirstOrDefault(),p=> p.Id != null);
}
catch (Exception e)
{
// ignored
}
}
Generic Repository Method;
public void AddOrUpdate(T entity, Expression<Func<T, bool>> predicate)
{
var exists = predicate != null ? Context.Set<T>().Any(predicate) : Context.Set<T>().Any();
if (!exists)
Context.Set<T>().Add(entity);
else
Context.Set<T>().Update(entity);
Context.SaveChanges();
}
It happens because all dependencies in the main thread are disposed when its execution finishes and you're trying to access them in another thread. To deal with this situation you need to create a scope in your background thread and resolve AuthorizedServiceService there:
private readonly IServiceScopeFactory _scopeFactory;
public UpdateService(AuthorizedServiceService authorizedServiceService, CustomerService customerService, IServiceScopeFactory scopeFactory)
{
_authorizedServiceService = authorizedServiceService;
_customerService = customerService;
_scopeFactory = scopeFactory;
}
public void UpdateStart(AuthorizedService authorizedService)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(state => {
using (scope = _scopeFactory.CreateScope())
{
var scopedAuthorizedService = scope.ServiceProvider.GetService(typeof(AuthorizedServiceService));
UpdateCustomers(scopedAuthorizedService); }));
}
}
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);
}
I am starting to use Ninject in my MVC5 code-first app. Here's my NinjectWebCommon.cs:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
kernel.Bind<IExecutiveRepository>()
.To<ExecutiveRepository>();
kernel.Bind<IExecutiveSectionRepository>()
.To<ExecutiveSectionRepository>();
kernel.Bind<IExecutiveSectionMappingRepository>()
.To<ExecutiveSectionMappingRepository>();
kernel.Bind<IUserRepository>()
.To<UserRepository>();
kernel.Bind<IContentRepository>()
.To<ContentRepository>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
I tried .InSingletonScope() as well as .InRequestScope() but I still get the 'entity object cannot be referenced by multiple instances of IEntityChangeTracker' error.
Here is my Interface:
public interface IExecutiveRepository : IDisposable
{
IEnumerable<Executive> GetExecutives();
Executive GetExecutiveById(int executiveId);
void InsertExecutive(Executive executive);
void UpdateExecutive(Executive executive);
void DeleteExecutive(int executiveId);
void Save();
}
Here is my concrete:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private CMSContext context;
public ExecutiveRepository(CMSContext context)
{
this.context = context;
}
public IEnumerable<Executive> GetExecutives()
{
return context.Executives.ToList();
}
public Executive GetExecutiveById(int id)
{
return context.Executives.Find(id);
}
public void InsertExecutive(Executive executive)
{
context.Executives.Add(executive);
}
public void DeleteExecutive(int executiveId)
{
Executive executive = context.Executives.Find(executiveId);
context.Executives.Remove(executive);
}
public void UpdateExecutive(Executive executive)
{
context.Entry(executive).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
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);
}
}
Here is the controller(top pertinent part):
public class ExecutiveController : Controller
{
private IExecutiveRepository executiveRepository;
private IUserRepository userRepository;
private IExecutiveSectionRepository executiveSectionRepository;
private IExecutiveSectionMappingRepository executiveSectionMappingRepository;
private IContentRepository contentRepository;
private Ninject.IKernel _kernel = new StandardKernel();
//[Inject]
public ExecutiveController()
{
executiveRepository = _kernel.Get<ExecutiveRepository>();
userRepository = _kernel.Get<UserRepository>();
executiveSectionRepository = _kernel.Get<ExecutiveSectionRepository>();
executiveSectionMappingRepository = _kernel.Get<ExecutiveSectionMappingRepository>();
contentRepository = _kernel.Get<ContentRepository>();
}
...
Not sure what I am doing wrong but upon adding a new 'Executive' it bombs... I do understand it's trying to use separate contexts and that's the problem, but I 'm just not sure how to fix it. Apparently, the line in the NinjectWebCommon.cs class:
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
Is supposed to be the fix, but it isn't...
any ideas/suggestions?
You should be using NUGET package Ninject.Web.Mvc if you aren't already. This configures your application ready to use Ninject, other than your bindings. It looks like you are reasonably familiar with the bindings side of things already from what I can see in your CreateKernel() method.
Once your bindings are in place, you should not be creating Kernels in your controllers, this is because the Ninject.Web.Mvc library configures Ninject to create your controllers for you under the hood. Therefore any dependencies that you add to them should be automatically resolved.
So, you can use constructor injection to resolve your dependencies:
public class ExecutiveController : Controller
{
private IExecutiveRepository ExecutiveRepository;
private IUserRepository UserRepository;
private IExecutiveSectionRepository ExecutiveSectionRepository;
private IExecutiveSectionMappingRepository ExecutiveSectionMappingRepository;
private IContentRepository ContentRepository;
public ExecutiveController(
IExecutiveRepository executiveRepository,
IUserRepository userRepository,
IExecutiveSectionRepository executiveSectionRepository,
IExecutiveSectionMappingRepository executiveSectionMappingRepository,
IContentRepository contentRepository)
{
// Set the field values
this.ExecutiveRepository = executiveRepository,
this.UserRepository = userRepository,
this.ExecutiveSectionRepository = executiveSectionRepository,
this.ExecutiveSectionMappingRepository = executiveSectionMappingRepository,
this.ContentRepository = contentRepository;
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
Or you can use the [Inject] attribute which has the same effect:
public class ExecutiveController : Controller
{
[Inject]
public IExecutiveRepository executiveRepository { get; set; }
[Inject]
public IUserRepository userRepository { get; set; }
[Inject]
public IExecutiveSectionRepository executiveSectionRepository { get; set; }
[Inject]
public IExecutiveSectionMappingRepository executiveSectionMappingRepository { get; set; }
[Inject]
public IContentRepository contentRepository { get; set; }
public ExecutiveController()
{
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
You're creating a kernel per controller.
InRequestScope only ensures one instance per request per kernel.
So you need to adapt your setup of the kernel so there's only one kernel per web application. See:
Ninject.Web.Mvc
Tutorial
Youtube
This may not answer the question. But I tend to use the IDbContextFactory that EF provides you with and do something like this:
public interface IDefaultContextFactory : IDbContextFactory<CMSContext> {}
public class DefaultContextFactory : IDefaultContextFactory
{
private readonly Lazy<CMSContext> lazyContext = new Lazy<CMSContext>(() => new CMSContext());
public CMSContext Create()
{
return lazyContext.Value;
}
}
Then you just bind that, and when you need the context you can do something like this:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private readonly CMSContext context;
public ExecutiveRepository(IDefaultContextFactory contextFactory)
{
this.context = contextFactory.Create();
}
}
I believe #BatteryBackupUnit is correct, I would also consider using the above pattern for contexts.
I have a web application where I have just began to use Entity Framework. I read the beginners tutorials, and topics about benefits of object context per request for web apps.
However, I am not sure my context is at the right place...
I found this very useful post (Entity Framework Object Context per request in ASP.NET?) and used the suggested code :
public static class DbContextManager
{
public static MyEntities Current
{
get
{
var key = "MyDb_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context == null)
{
context = new MyEntities();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
And in Global.asax :
protected virtual void Application_EndRequest()
{
var key = "MyDb_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context != null)
{
context.Dispose();
}
}
Then, I am using it in my pages :
public partial class Login : System.Web.UI.Page
{
private MyEntities context;
private User user;
protected void Page_Load(object sender, EventArgs e)
{
context = DbContextManager.Current;
if (Membership.GetUser() != null)
{
Guid guid = (Guid)Membership.GetUser().ProviderUserKey;
user = context.Users.Single(u => (u.Id == guid));
}
}
protected void _Button_Click(object sender, EventArgs e)
{
Item item = context.Items.Single(i => i.UserId == user.Id);
item.SomeFunctionThatUpdatesProperties();
context.SaveChanges();
}
}
I did read a lot but this is still a little bit confused for me.
Is the context getter okay in Page_Load ? Do I still need to use "using" or will disposal be okay with the Global.asax method ?
If I am confusing something I am sorry and I would be really, really grateful if someone could help me understand where it should be.
Thanks a lot !
Edits following nativehr answer and comments :
Here is the DbContextManager:
public static class DbContextManager
{
public static MyEntities Current
{
get
{
var key = "MyDb_" + typeof(MyEntities).ToString();
var context = HttpContext.Current.Items[key] as MyEntities;
if (context == null)
{
context = new MyEntities();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
The page :
public partial class Login : System.Web.UI.Page
{
private User user;
protected void Page_Load(object sender, EventArgs e)
{
if (Membership.GetUser() != null)
{
Guid guid = (Guid)Membership.GetUser().ProviderUserKey;
user = UserService.Get(guid);
}
}
protected void _Button_Click(object sender, EventArgs e)
{
if (user != null)
{
Item item = ItemService.GetByUser(user.Id)
item.SomeFunctionThatUpdatesProperties();
ItemService.Save(item);
}
}
}
And the ItemService class :
public static class ItemService
{
public static Item GetByUser(Guid userId)
{
using (MyEntities context = DbContextManager.Current)
{
return context.Items.Single(i => (i.UserId == userId));
}
}
public static void Save(Item item)
{
using (MyEntities context = DbContextManager.Current)
{
context.SaveChanges();
}
}
}
I would not rely on Thread.CurrentContext property.
Firstly, Microsoft says, Context class is not intended to be used directly from your code:
https://msdn.microsoft.com/en-us/library/system.runtime.remoting.contexts.context%28v=vs.110%29.aspx
Secondly, imagine you want to make an async call to the database.
In this case an additional MyEntities instance will be constructed, and it will not be disposed in Application_EndRequest.
Furthermore, ASP.NET itself does not guarantee not to switch threads while executing a request.
I had a similar question, have a look at this:
is thread switching possible during request processing?
I would use "MyDb_" + typeof(MyEntities).ToString() instead.
Disposing db context in Application_EndRequest is OK, but it produces a bit performance hit, 'cause your context will stay not disposed longer than needed, it is better to close it as soon as possible (you actually don't need an open context to render the page, right?)
Context pre request implementation would make sense if it has to be shared between different parts of your code, insted of creating a new instance each time.
For example, if you utilize the Repository pattern, and several repositories share the same db context while executing a request.
Finally you call SaveChanges and all the changes made by different repositories are committed in a single transaction.
But in your example you are calling the database directly from your page's code, in this case I don't see any reason to not create a context directly with using.
Hope this helps.
Update: a sample with Context per request:
//Unit of works acts like a wrapper around DbContext
//Current unit of work is stored in the HttpContext
//HttpContext.Current calls are kept in one place, insted of calling it many times
public class UnitOfWork : IDisposable
{
private const string _httpContextKey = "_unitOfWork";
private MyContext _dbContext;
public static UnitOfWork Current
{
get { return (UnitOfWork) HttpContext.Current.Items[_httpContextKey]; }
}
public UnitOfWork()
{
HttpContext.Current.Items[_httpContextKey] = this;
}
public MyEntities GetContext()
{
if(_dbContext == null)
_dbContext = new MyEntities();
return _dbContext;
}
public int Commit()
{
return _dbContext != null ? _dbContext.SaveChanges() : null;
}
public void Dispose()
{
if(_dbContext != null)
_dbContext.Dispose();
}
}
//ContextManager allows repositories to get an instance of DbContext
//This implementation grabs the instance from the current UnitOfWork
//If you want to look for it anywhere else you could write another implementation of IContextManager
public class ContextManager : IContextManager
{
public MyEntities GetContext()
{
return UnitOfWork.Current.GetContext();
}
}
//Repository provides CRUD operations with different entities
public class RepositoryBase
{
//Repository asks the ContextManager for the context, does not create it itself
protected readonly IContextManager _contextManager;
public RepositoryBase()
{
_contextManager = new ContextManager(); //You could also use DI/ServiceLocator here
}
}
//UsersRepository incapsulates Db operations related to User
public class UsersRepository : RepositoryBase
{
public User Get(Guid id)
{
return _contextManager.GetContext().Users.Find(id);
}
//Repository just adds/updates/deletes entities, saving changes is not it's business
public void Update(User user)
{
var ctx = _contextManager.GetContext();
ctx.Users.Attach(user);
ctx.Entry(user).State = EntityState.Modified;
}
}
public class ItemsRepository : RepositoryBase
{
public void UpdateSomeProperties(Item item)
{
var ctx = _contextManager.GetContext();
ctx.Items.Attach(item);
var entry = ctx.Entry(item);
item.ModifiedDate = DateTime.Now;
//Updating property1 and property2
entry.Property(i => i.Property1).Modified = true;
entry.Property(i => i.Property2).Modified = true;
entry.Property(i => i.ModifiedDate).Modified = true;
}
}
//Service encapsultes repositories that are necessary for request handling
//Its responsibility is to create and commit the entire UnitOfWork
public class AVeryCoolService
{
private UsersRepository _usersRepository = new UsersRepository();
private ItemsRepository _itemsRepository = new ItemsRepository();
public int UpdateUserAndItem(User user, Item item)
{
using(var unitOfWork = new UnitOfWork()) //Here UnitOfWork.Current will be assigned
{
_usersRepository.Update(user);
_itemsRepository.Update(user); //Item object will be updated with the same DbContext instance!
return unitOfWork.Commit();
//Disposing UnitOfWork: DbContext gets disposed immediately after it is not longer used.
//Both User and Item updates will be saved in ome transaction
}
}
}
//And finally, the Page
public class AVeryCoolPage : System.Web.UI.Page
{
private AVeryCoolService _coolService;
protected void Btn_Click(object sender, EventArgs e)
{
var user = .... //somehow get User and Item objects, for example from control's values
var item = ....
_coolService.UpdateUserAndItem(user, item);
}
}
I think you should read a bit more about repository pattern for EntityFramework and UnitofWork pattern.
Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC
I know this is mvc and you are problably using web forms but you can get an idea of how to implement it.
Disposing the context on each request is a bit strange, because there might be requests where you will not touch the database, so you will be doing unnecessary code.
What you should do is get a layer for data access and implement a repository pattern that you will access on whatever method you will need on the code behind of your page.
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.