In my project using .NET framework 4.6.1, EF 6.1.4 and IdentityServer3, I set the following DbContext:
public class ValueContext : DbContext
{
public IValueContext(bool lazyLoadingEnabled = false) : base("MyConnectionString")
{
Database.SetInitializer<IValueContext>(null);
Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
}
public DbSet<NetworkUser> NetworkUser { get; set; }
public DbSet<User> User { get; set; }
[...]
And my Entity model User:
[Table("shared.tb_usuarios")]
public class NetworkUser
{
[Column("id")]
[Key()]
public int Id { get; set; }
[Required]
[StringLength(255)]
[Column("email")]
public string Email { get; set; }
[...]
public virtual Office Office { get; set; }
[...]
So far I think its all good.
Then I set this following query in my UserRepository (using DI)
protected readonly ValueContext Db;
public RepositoryBase(ValueContext db)
{
Db = db;
}
public async Task<ImobUser> GetUser(string email)
{
//sometimes I get some error here
return await Db.User.AsNoTracking()
.Include(im => im.Office)
.Include(off => off.Office.Agency)
.Where(u => u.Email == email &&
u.Office.Agency.Active)
.FirstOrDefaultAsync();
}
And everything runs well, until it starts to get many sequential requests, then I begin to get these type of errors, randomly in any function that uses my ValueContext as data source:
System.NotSupportedException: 'A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.'
This is my last hope, as I tried a bunch of different things. Some of them work, and some dont, like:
Convert dbContext to use DI: no difference.
Use context lifetime to run the queries: works, but isnt the solution I want.
Remove asyncronous from requests: works, but also I feel is not the correct way to do.
What Im doing wrong?
EDIT 1
This is how I set up DI in Startup.cs:
private void AddAuth()
{
Builder.Map("/identity", app =>
{
var factory = new IdentityServerServiceFactory()
{
//here I implemented the IdentityServer services to work
ClientStore = new Registration<IClientStore>(typeof(ClientStore)),
[...]
};
AddDependencyInjector(factory);
}
[...]
}
private void AddDependencyInjector(IdentityServerServiceFactory factory)
{
//here I inject all the services I need, as my DbContext
factory.Register(new Registration<ValueContext>(typeof(ValueContext)));
[...]
}
And this is how my UserService is working:
public class UserService : IUserService
{
[Service injection goes here]
//this is a identityServer method using my dbContext implementation on UserRepository
public async Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
SystemType clientId;
Enum.TryParse(context.SignInMessage.ClientId, true, out clientId);
switch (clientId)
{
case 2:
result = await _userService.GetUser(context.UserName);
break;
case 3:
//also using async/await correctly
result = await _userService.Authenticate(context.UserName, context.Password);
break;
default:
result = false;
break;
}
if (result)
context.AuthenticateResult = new AuthenticateResult(context.UserName, context.UserName);
}
Update - After code posted
When using ASP.Net DI and IdentityServer DI together, we have to be careful to make sure that both the IdentityServer and the underlying DbContext are scoped to the OWIN request context, we do that by Injecting the DbContext into the IdentityServer context. this answer has some useful background: https://stackoverflow.com/a/42586456/1690217
I suspect all you need to do is resolve the DbContext, instead of explicitly instantiating it:
private void AddDependencyInjector(IdentityServerServiceFactory factory)
{
//here I inject all the services I need, as my DbContext
factory.Register(new Registration<ValueContext>(resolver => new ValueContext()));
[...]
}
Supporting dicussion, largely irrelevant now...
With EF it is important to make sure that there are no concurrent queries against the same DbContext instance at the same time. Even though you have specified AsNoTracking() for this endpoint there is no indication that this endpoint is actually the culprit. The reason for synchronicity is so that the context can manage the original state, there are many internals that are simply not designed for multiple concurrent queries, including the way the database connection and transactions are managed.
(under the hood the DbContext will pool and re-use connections to the database if they are available, but ADO.Net does this for us, it happens at a lower level and so is NOT an argument for maintaining a singleton DbContext)
As a safety precaution, the context will actively block any attempts to re-query while an existing query is still pending.
EF implements the Unit-Of-Work pattern, you are only expected to maintain the same context for the current operation and should dispose of it when you are done. It can be perfectly acceptable to instantiate a DbContext scoped for a single method, you could instantiate multiple contexts if you so need them.
There is some anecdotal advice floating around the web based on previous versions of EF that suggest there is a heavy initialization sequence when you create the context and so they encourage the singleton use of the EF context. This advice worked in non-async environments like WinForms apps, but it was never good advice for entity framework.
When using EF in a HTTP based service architecture, the correct pattern is to create a new context for each HTTP request and not try to maintain the context or state between requests. You can manually do this in each method if you want to, however DI can help to minimise the plumbing code, just make sure that the HTTP request gets a new instance, and not a shared or recycled one.
Because most client-side programming can create multiple concurrent HTTP requests (this of a web site, how many concurrent requests might go to the same server for a single page load) it is a frivolous exercise to synchronise the incoming requests, or introduce a blocking pattern to ensure that the requests to the DbContext are synchronous or queued.
The overheads to creating a new context instance are expected to be minimal and the DbContext is expected to be used in this way especially for HTTP service implementations, so don't try to fight the EF runtime, work with it.
Repositories and EF
When you are using a repository pattern over the top of EF... (IMO an antipattern itself) it is important that each new instance of the repository gets its own unique instance of the DbContext. Your repo should function the same if you instead created the DbContext instance from scratch inside the Repo init logic. The only reason to pass in the context is to have DI or another common routine to pre-create the DbContext instance for you.
Once the DbContext instance is passed into the Repo, we lose the ability to maintain synchronicity of the queries against it, this is an easy pain point that should be avoided.
No amount of await or using synchronous methods on the DbContext will help you if multiple repos are trying to service requests at the same time against the same DbContext.
Related
I wanted to create a background task for my application and in that task I need DbContext to do some operation on data for every 5 seconds.I tried 2 way to get DbContext and it seems like both of them worked but I wanna now what is the difference between them(if there is any)
Here is the first approach
private readonly IServiceScopeFactory _serviceScopeFactory;
public worker(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
private void DoWork(object? state)
{
var scope = _serviceScopeFactory.CreateScope();
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
}
And this is the second approach that I tried
private void DoWork(object? state)
{
using (var db = new ApplicationDbContext(new DbContextOptions<ApplicationDbContext>()))
{
//do job
}
}
both of them seems working.Isn't both of them disposed and recreated when method called ? what is the difference? which one would you prefer and why?
In general if you use dependency injection for the dbcontext it is scoped for the whole request. That means that using the dbcontext in different classes or methods will be using the same transaction which for example u can after all changes commit with .SaveChanges(). The dependency injection method is also good for making abstractions. If you create interface for your dbcontext then You can make two implementations - one will be your existing dbcontext and the other for example will not use the real database but some data in memory for the development.
If you use the using method then the transaction is within the using brackets and it's not accessible from multiple places but only in the place where it's instantiated and you are also tightly coupled with the framework.
I have a transient EF DB context & a transient service.
I have a controller with an action using session service to update the session, and then use session service to query back the updated session.
However, even I use transient, when I query back the updated session, the session is still the old value but not the updated one. Can someone answer why?
My expected behavior is if DBContext & SessionService are transient, get after my updated session should return a new updated value instead of old value. Because DBContext should be disposed after the update.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connectionString = Configuration.GetConnectionString("ASPState");
services.AddDbContext<ASPStateContext>(options =>
options.UseSqlServer(connectionString, builder=> {
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
}),ServiceLifetime.Transient);
services.AddTransient<ISessionService, SessionService>();
services.AddControllers();
}
SessionService.cs - using DI
private ASPStateContext aspStateContext;
public SessionService(ASPStateContext dbContext)
{
aspStateContext = dbContext;
}
public async Task<IActionResult> UpdateSession(string id) {
await this.sessionService.UpdateSession(id);
var session = this.sessionService.GetSession(id); // return the value before update
}
ASPStateContext.cs
public partial class ASPStateContext : DbContext
{
public ASPStateContext()
{
}
I think there may be a misunderstanding about how the Transient scope works. A transient dependency is instantiated every time it is injected. Within your controller, a transient and a scoped dependency behave the same way. The difference emerges when another service depends on the transient service; that other service will receive a new instance of the transient service.
Therefore, your controller will use the same SessionService (and the same EF context) for an entire request.
To ensure that Entity Framework is retrieving the latest value from the database, use the AsNoTracking query extension (documentation here). This will completely bypass EF caching and query the underlying database.
Also, make sure that your EF call to save changes after updating the session data is awaited. Otherwise, your SELECT statement may execute before the UPDATE statement is applied.
We have several classes that depend on Entity Framework 6 in our application. As such, we inject our DbContext into various areas. However, certain modules implement multithreaded methods that require DbContext is injected as a transient service to prevent any threading issues. Other modules are capable of being strung together and saved wholesale by simply calling SaveChanges on any sub-module or module that receives the same shared DbContext. However, this approach requires the DbContext be added as a scoped service.
Aside from building some subclass or interface, that simply inherits from my DbContext, is there any way to dynamically determine whether a class gets a scoped version or transient version of a given service?
An example of the subclassed context may look something like
public class TransientDbContext : DbContext {}
public class ScopedDbContext : DbContext {}
// in services
services.AddTransient<TransientDbContext>();
services.AddScoped<ScopedDbContext>();
Which works, but I'm looking for something a bit more dynamic where I could potentially pass a parameter to indicate that a class should utilize a shared context.
For some additional context, image I have the following interfaces
public interface IRepository<TEntity>
{
void Add(TEntity entity);
Task SaveAsync(CancellationToken token = default);
}
public interface IUserManager
{
Task AddAsync(User user, bool commitChanges = true, CancellationToken = default);
}
public interface IUserPhoneNumberManager
{
Task AddAsync(UserPhoneNumber number, bool commitChanges, CancellationToken token = default)
}
And behind the scenes, I may have the follow concrete implementations
public class UserRepository<User> : IRepository<User>
{
private readonly DbContext _dbContext;
public UserRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public void Add(User entity)
{
_dbContext.Users.Add(entity);
}
public Task SaveAsync(CancellationToken token = default)
{
return _dbContext.SaveChangesAsync(token);
}
}
public class UserPhoneNumberRepository<UserPhoneNumber> : IRepository<UserPhoneNumber>
{
private readonly DbContext _dbContext;
public UserRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public void Add(UserPhoneNumber entity)
{
_dbContext.UserPhoneNumbers.Add(entity);
}
public Task SaveAsync(CancellationToken token = default)
{
return _dbContext.SaveChangesAsync(token);
}
}
Now in some cases, I want the underlying repositories to be injected with a singular scoped context, and other times I want a transient context. Those transient contexts will obviously commit their own changes when used. But the scoped contexts will commit their changes as a singular unit.
I think the core of your issue lies in the following observation:
certain modules implement multithreaded methods that require DbContext is injected as a transient service to prevent any threading issues.
This implies that your application code itself is responsible for handling multi-threadedness; you are likely starting new threads or tasks. This is something you should prevent.
Instead, only your Composition Root should know about multitheadedness and should spin off new threads. This centralizes the knowledge around thread safety. But not only that, but many components are not thread-safe and only the Composition Root should be aware of which components are and aren't. A component itself, should always just call its dependencies in a sequential way and assume there is just a single instance of that dependency.
This means that when you are starting parallel operations, you should go back to the Composition Root to let it resolve a new object graph. The Composition Root could then decide to inject new instances of components into the graph (for instance your DbContext).
When you apply this way of working, you won't need to have a transient and a scoped version of your DbContext any longer.
For more information, see: working with DI in multi-threaded applications. My book DI PP&P does contain some material explaining this.
We are using Ninject in an ASP.NET Web Api application, and we bind our DbContext with InRequestScope. This works well with most of our requests, because they do all their work synchronously, so the context can be safely disposed after the request is completed.
However, we have on request in which we do an asynchronous web service call, that has a continuation method passed as a callback, and that callback method needs to use the database context. However our request shouldn't wait for the asynchronous service call to finish, but return immediately (this is an explicit requirement).
Here is a simplified example of the situation.
public class MyController : ApiController
{
private readonly MyDbContext dbContext;
private readonly SomeWebService service;
public MyController(MyDbContext dbContext, SomeWebService service)
{
this.dbContext = dbContext;
this.service = service;
}
public IHttpActionResult MyActionWithAsyncCall()
{
// Doing stuff.
// Calling webservice method, passing the Callback as the continuation.
service.MethodWithCallback(param1, param2, this.Callback);
// Returning without waiting for the service call to be completed.
return Ok();
}
private void Callback()
{
// Trying to use the DbContext:
var person = dbContext.People.First();
// The above line sometimes throws exception, because the context has been disposed.
}
}
How should this situation be handled with Ninject? Is there a way to somehow "prolong" the lifetime of a bound DbContext instance explicitly? Or should the Callback method create completely new DbContext? If it should, what scope should it use?
There's is no way to explicitly prolong the lifetime of an object with .InRequestScope() to extend to after the request end.
If there's not a business requirement that the work during the request and # callback must happen in a single transaction i would go for using two DbContext instances. One during the request and one during the callback. Note: As far as i know this also means you can't take an entity from the first context and update/save it in the second context. This means you must only pass identifier (and other data relevant to the operation) from request to callback. The callback has to "create" a new DbContext and retrieve the according entitites from the context.
Conditional Binding Alternative
As an alternative you might declare a special binding for this special case. Ninject supports so called contextual bindings. This means you would have two bindings, the standard binding and a contextual, special case binding:
Bind<DbContext>().ToSelf().InRequestScope();
Bind<DbContext>().ToSelf()
.WhenInjectedInto<SomeController>();
Notice that the second binding does not specify a scope - that means SomeController is responsible to call .Dispose(). In your case that would mean the callback would have to dispose the context. You'd also need to dispose of the context in all errors cases (errors in the callback code, errors occurring before callback is triggered,....).
Also, in reality your application is probably a bite more complex and .WhenInjectedInto<SomeController> is not going to be enough/correct, because you might want to inject the same instance into the controller plus a repository plus a query object.. what not.
That means you will need scoping, but a scope different from .InRequestScope(). You might use .InCallScope() or named scope - both are included in the named scope extension.
Furthermore you would need to adapt the When condition. You could adapt it so to traverse the requests and see if there is FooController anywhere in the request chain. But that's not very performant, instead i would recommend using a ninject IParameter to specify that you want special case treatment. The parameter would be:
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
which would be applied at the bindings like:
kernel.Bind<SomeController>().ToSelf()
.WithParameter(new NonRequestScopedParameter());
kernel.Bind<DbContext>().ToSelf()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope(); // or whatever scope you're using
I am using EntityFramework.Extended library to perform batch updates. The only problem is EF does not keep track of the batch updates performed by the library. So when I query the DbContext again it does not return the updated entities.
I found that using AsNoTracking() method while querying disables the tracking and gets fresh data from the database. However, since EF does not keep track of the entities queried with AsNoTracking(), I am not able to perform any update on the queried data.
Is there any way to force EF to get the latest data while tracking changes?
Please try this to refresh a single entity:
Context.Entry<T>(entity).Reload()
Edit:
To get fresh data for a collection of entities is worth trying to dispose the DbContext instance after each request.
I stumbled upon this question while searching for a solution to a problem I was having where the navigation properties were not populating after updating the entity. Whenever I attempted to reload the entity from the database, it would grab the entry from the local store instead which would not populate the navigation properties via lazy loading. Instead of destroying the context and recreating one, I found this allowed me to get fresh data with the proxies working:
_db.Entry(entity).State = EntityState.Detached;
The logic behind it was - my update attached the entity so it would track changes to it. This adds it to the local store. Thereafter, any attempts to retrieve the entity with functional proxies would result in it grabbing the local one instead of going out to the db and returning a fresh, proxy-enabled entity. I tried the reload option above, which does refresh the object from the database, but that doesn't give you the proxied object with lazy-loading. I tried doing a Find(id), Where(t => t.Id = id), First(t => t.Id = id). Finally, I checked the available states that were provided and saw there was a "Detached" state. Eureka! Hope this helps someone.
I declared the entity variable, without assignment, as part of the class. This allowed me to dispose of an instance without losing the variable for reference by other methods. I just came across this so it doesn't have alot of runtime under it's belt, but so far it seems to be working fine.
public partial class frmMyForm
{
private My_Entities entity;
public frmMyForm()
{
InitializeComponent();
}
private void SomeControl_Click(object sender, EventArgs e)
{
db.SaveChanges();
db.Dispose();
entity = new My_Entities();
//more code using entity ...
}
Stumbled onto this problem. My app wasn't returning fresh data from the database.
These seems to be 3 solutions:
Reload on select: first you select the object, then reload. Loading it twice if it's not cached?
Detach after use: if you forget to detach an object after use, it's going to cause bugs in completely separate parts of the application that are going to be extremely hard to track down.
Disposing the DbContext after use. Definitely seems like the way to go.
I was creating my DbContext instance in the Repository class. If the DbContext is declared at the Repository level, then I have no control over how it gets disposed. That's a no-no. If I create a new DbContext on every call, then I cannot call Select, modify data, and then call Update.
Seems like something is fundamentally missing in my Repository pattern.
After some research on fundamental Repository pattern, I found the solution: Unit of Work pattern alongside the Repository pattern.
This is an excellent article on the Unit of Work pattern
Or this article from Microsoft. What I currently have is the Repository further up in the page, and what's missing is the section "Implement a Generic Repository and a Unit of Work Class"
Basically, instead of injecting repositories into your services, you access all repositories via a UnitOfWork that you inject into your service. It will solve many problems.
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationContext _context;
public UnitOfWork(ApplicationContext context)
{
_context = context;
Developers = new DeveloperRepository(_context);
Projects = new ProjectRepository(_context);
}
public IDeveloperRepository Developers { get; private set; }
public IProjectRepository Projects { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
Remains the question: how to create the IUnitOfWork instance?
If I create it in the class constructor to be injected just like the repository, then it gets created and destroyed exactly the same way and we're back to the same problem. In ASP.NET and MVC, class instances are short-lived so injecting in the constructor may be fine, but in Blazor and desktop apps, class instances are much more long-lived and it's more of a problem.
This article from Microsoft clearly states that Dependency Injection isn't suitable to manage the lifetime of DbContext in Blazor:
In Blazor Server apps, scoped service registrations can be problematic
because the instance is shared across components within the user's
circuit. DbContext isn't thread safe and isn't designed for concurrent
use. The existing lifetimes are inappropriate for these reasons:
Singleton shares state across all users of the app and leads to
inappropriate concurrent use.
Scoped (the default) poses a similar
issue between components for the same user.
Transient results in a new
instance per request; but as components can be long-lived, this
results in a longer-lived context than may be intended.
They suggest using the Factory pattern, which can be implemented like this
/// <summary>
/// Creates instances of UnitOfWork. Repositories and UnitOfWork are not automatically injected through dependency injection,
/// and this class is the only one injected into classes to give access to the rest.
/// </summary>
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly IDateTimeService _dateService;
private readonly DbContextOptions<PaymentsContext> _options;
public UnitOfWorkFactory(IDateTimeService dateService, DbContextOptions<PaymentsContext> options)
{
_dateService = dateService;
_options = options;
}
/// <summary>
/// Creates a new Unit of Work, which can be viewed as a transaction. It provides access to all data repositories.
/// </summary>
/// <returns>The new Unit of Work.</returns>
public IUnitOfWork Create() => new UnitOfWork(CreateContext(), _dateService);
/// <summary>
/// Creates a new DbContext.
/// </summary>
/// <returns>The new DbContext.</returns>
public PaymentsContext CreateContext() => new(_options);
}
Neither IWorkOfUnit nor any repository will be registered into the IoC container. Only IWorkOfUnitFactory.
And finally... how to share a transaction between various services?
I have a SetStatus method that updates the status field in the database. How is this method supposed to know whether it's a stand-alone operation or part of a larger transaction?
Since class-level dependency injection isn't suitable to manage and share the Work of Unit, then the only option is to pass it as parameters to the methods that need it.
I add an optional IUnitOfWork? workScope = null parameter to every method that needs it, and call Save only if this parameter is null. Here's an implementation.
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create();
var work = workScope ?? unitOfWork;
var order = await work.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
if (workScope == null)
{
await work.SaveAsync();
}
}
return order;
}
Another option is to have IUnitOfWorkFactory.Create take the workScope parameter, and when set:
Re-use the existing DbContext
Do not dispose
IUnitOfWork.Save won't submit
My final implementation can be used like this
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create(workScope);
var order = await unitOfWork.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
await unitOfWork.SaveAsync(); // Ignored if workScope != null
}
return order;
}
Pheww! That bug was a rabbit hole. It's a pretty long solution but should solve it for good with a solid architecture.
Making the code run on the same context will not yield you updated entities. It will only append new entities created in the database between runs. EF force reload can be done like this:
ObjectQuery _query = Entity.MyEntity;
_query.MergeOption = MergeOption.OverwriteChanges;
var myEntity = _query.Where(x => x.Id > 0).ToList();
For me ...
I access my DbContext like this:
_viewModel.Repo.Context
To force EF to hit the database I do this:
_viewModel.Repo.Context = new NewDispatchContext();
Overwriting the current DbContext with a new instance. Then the next time I use my data services they get the data from the database.
Reloading specific entities was not an option for me because I didn't know the exact entity. I also did not want to create a new DbContext as it is injected by DI. So I resorted to the following trick to "reset" the whole context.
foreach (var entry in db.ChangeTracker.Entries())
{
entry.State = EntityState.Detached;
}