Access dependency-injected Entity Framework database from static class method - c#

In my project, I have a static converter method to convert client and database objects into each other. One of those static methods needs to access the database. Before introducing dependency injection into my project, that was quite simple:
internal async static Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col) {
using MpaContext db = new MpaContext();
return new ViewerColumn() {
// ...
SourceColumnID = await db.SourceColumns
.Where(sc => sc.Key == col.DataField)
.Select(sc => sc.ID)
.SingleAsync()
};
}
I want to change this by introducing dependency injection project-wide. My first approach was to simply add the database context as a separate parameter:
internal async static Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col, MpaContext context) {
using MpaContext db = context;
// ...
}
This, however, leads to problems, if the context from the parameter gets disposed somewhere else. So my idea was to dependency-inject the context to the class inself. This, however, doesn't work, because you obviously can't use parameters for static constructors.
Here's how the method is called (currently with the context parameter):
// Controller method with dependency injection
[HttpPut("ViewerRoles/{vrID}")]
public async Task<ActionResult> UpdateViewSettings(int vrID, ViewerRoleSettings updatedData) {
using MpaContext db = _mpaContext;
await storedViewerRole.ApplyViewerRoleSettingsAsync(updatedData, _mpaContext);
}
// ViewerRole.cs
internal async Task ApplyViewerRoleSettingsAsync(ViewerRoleSettings updatedData, MpaContext context) {
// Create new entries
foreach (Client.ViewerColumnSettings col in updatedData.ViewerColumns) {
ViewerColumns.Add(await ViewerColumn.FromClientColumn(col, context));
}
}
This approach fails, because the context gets disposed in UpdateViewSettings and in FromClientColumn.
What's the best-practice approach for such a case? I could dispose the context only, if it wasn't open beforehand, but that sounds stupid to me.

Dependency Inversion / Dependency Injection does not play well with static.
Make an abstraction and derived implementation with injected context
public class ViewerColumnService : IViewerColumnService {
private readonly MpaContext db ;
public ViewerColumnService (MpaContext db) {
this.db = db;
}
public async Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col) {
return new ViewerColumn() {
// ...
SourceColumnID = await db.SourceColumns
.Where(sc => sc.Key == col.DataField)
.Select(sc => sc.ID)
.SingleAsync()
};
}
}
Register this new service and explicitly inject it where it is needed. Stop manually disposing of the context by wrapping it in a using statement. Let the DI container handle the lifetime of the components.

Related

How to instantiate DbContext from IHostingService

I have a class that derives from BackgroundService (IHostedService) for running background tasks. This will be added to my services using builder.Services.AddHostedService<BackgroundTaskService>()
BackgroundService's task runs for the entire duration of the web application, checking for queued data to process.
My question is, how do I instantiate an instance of DbContext from this code?
I could have the BackgroundTaskService constructor accept a DbContext. But wouldn't that keep the DbContext open forever?
And how else could I instantiate it without duplicating all the code to scan my settings file for the connection string, etc.?
The recemmended approach is to inject IDbContextFactory<TContext> as described in the following article: Using a DbContext factory (e.g. for Blazor)
Some application types (e.g. ASP.NET Core Blazor) use dependency injection but do not create a service scope that aligns with the desired DbContext lifetime. Even where such an alignment does exist, the application may need to perform multiple units-of-work within this scope. For example, multiple units-of-work within a single HTTP request.
In these cases, AddDbContextFactory can be used to register a factory for creation of DbContext instances.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options =>
options.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=Test"));
}
Then in your controller:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
You can use scope service factory. Check here for reference.
Here you have an example:
// Injection
public class DataApi : BackgroundService
{
private readonly ILogger<DataApi> logger;
private readonly IServiceScopeFactory scopeFactory;
public DataApi(ILogger<DataApi> _logger, IConfiguration _cfg, IServiceScopeFactory _sSF)
{
logger = _logger;
scopeFactory = _sSF;
// e.g. data from appsettings.json
// var recovery = _cfg["Api:Recovery"];
}
// ...
// Usage
protected async Task DataCollector()
{
logger.LogInformation("Collector");
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var myList = await db.MyEntity
.AsNoTracking()
.Where(t => t.active)
.ToListAsync();
if (myList.Count == 0)
{
logger.LogInformation("Empty...");
return;
}
// logic...
}
await Task.CompletedTask;
}

Blazor Server and EF Core: A second operation was started on this context instance before a previous operation completed

I have problem with ef core. I have two services which read data from database. On one page is call first service and on second page is called second service. When i click to button for create a new program i got error. I call it normally from page with inject service. Can anybody help me with it?
Show in application
builder.Services.AddDbContextPool<Context>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("Connection"));
});
TestService1:
public class TestService1 : ITestService1
{
private readonly Context _context;
private readonly IMapper _mapper;
public TestService1(Context context, IMapper mapper)
{
_kreativgangContext = kreativgangContext;
_mapper = mapper;
}
public virtual async Task<AllProgramViewModel> HandleAsync(AllProgramFilterViewModel filter)
{
var model = new AllProgramViewModel();
var data = _context.Programs.Where(x => (EF.Functions.Like(x.Name ?? "", "%" + filter.Name + "%") || string.IsNullOrEmpty(filter.Name)))
.Select(x => new Core.Models.Program() { ID = x.ID, Name = x.Name, Order = x.Order });
result.Model.TotalCount = await data.CountAsync();
result.Model.Items = data.Select(x => _mapper.Map<AllProgramItemViewModel>(x));
return model;
}
}
public interface ITestService1
{
public Task<AllProgramViewModel> HandleAsync(AllProgramFilterViewModel filter);
}
Test service 2:
public class TestService2 : ITestService2
{
private readonly Context _context;
public TestService2(Context context)
{
_context = context;
}
public virtual async Task<NewProgramViewModel> HandleAsync()
{
var model = new NewProgramViewModel();
List<ProgramOrderViewModel> items = _context.Programs.Select(x => new Core.Models.Program() { Order = x.Order, ID = x.ID })
.Select(x => new ProgramOrderViewModel()
{
ID = x.ID,
Order = x.Order
}).ToList();
return await Task.FromResult(model);
}
}
public interface ITestService2
{
public Task<NewProgramViewModel> HandleAsync();
}
Error:
Error: System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Mitar.Kreativgang.Admin.Handlers.TestService2.HandleAsync() in D:\Programming\Kreativgang\Src\Mitar.Kreativgang.Admin\Handlers\TestService2.cs:line 26
at Mitar.Kreativgang.Admin.Pages.Program.ProgramNew.OnInitializedAsync() in D:\Programming\Kreativgang\Src\Mitar.Kreativgang.Admin\Pages\Program\ProgramNew.razor:line 114
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
This is a known and documented pitfall, explained in ASP.NET Core Blazor Server with Entity Framework Core (EFCore). In Blazor Server, the DI scope is the user circuit - essentially the user session. That means that a scoped service like TestService2 or a DbContext will remain in memory for a long time and end up reused by multiple methods and actions.
As the docs explain :
Blazor Server is a stateful app framework. The app maintains an ongoing connection to the server, and the user's state is held in the server's memory in a circuit. One example of user state is data held in dependency injection (DI) service instances that are scoped to the circuit. The unique application model that Blazor Server provides requires a special approach to use Entity Framework Core.
You need to register and use a DbContextFactory (or PooledDbContextFactory) instead of a DbContextPool, and create a new DbContext instance right where it's used.
builder.Services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlServer(...));
or
builder.Services.AddPooledDbContextFactory<ContactContext>(opt =>
opt.UseSqlServer(...));
The service constructors should accept the factory instead of a context :
public TestService2(AddDbContextFactory<ContactContext> factory)
{
_factory = factory;
}
public virtual async Task<NewProgramViewModel> HandleAsync()
{
using var context=_factory.CreateContext())
{
...
}
}
Component Scope
To limit a DbContext's scope to a single component it's not enough to just inject the DbContextFactory. The DbContext instance needs to be explicitly disposed when the user navigates away from the component. To do this, the component needs to implement IDisposable. This is explained in the section Scope to the component lifetime
#implements IDisposable
#inject IDbContextFactory<ContactContext> DbFactory
...
#code
{
ContactContext? Context;
public void Dispose()
{
Context?.Dispose();
}
protected override async Task OnInitializedAsync()
{
Context = DbFactory.CreateDbContext();
...
}
}

Updating IMemoryCache once an entity has changed

I have a CacheService that uses GetOrCreateAsync to create cache based on a key. I am caching a photograph entity, which has a byte[] property.
This caches fine and is retrieved as expected. However if the photograph entity is updated, the cache still retains the old entity as you would expect because it has not expired, how can I force an update to the cache upon save of this entity? Do I remove the existing cached entity and re-add the updated one?
Example of my FromCacheAsync method in my CacheService
public async Task<T> FromCacheAsync<T>(string entityName, int clientId, Func<Task<T>> function)
{
string cacheKey = GetClientCacheKey(entityName, clientId, function);
if (!_cache.TryGetValue(cacheKey, out T entry))
{
async Task<T> factory(ICacheEntry cacheEntry)
{
return await function();
}
return await _cache.GetOrCreateAsync(cacheKey, factory);
}
return entry;
}
This is an example of using the caching.
var existingPhotograph = await _cacheService.FromCacheAsync(nameof(_context.Photograph), clientId, async () =>
await _photographRepository.GetByStaffIdAsync(staff.StaffId));
You need to invalidate the cache key, when the entity changes.
That may be a bit tricky, if you directly operate on the DbContext. But since you are using repository pattern, that`s easier to do.
It boils down to inject the IMemoryCache into your repository and invalidate it when a picture is updated.
public class PhotographRepository : IPhotograpRepository
{
private readonly IMemoryCache _cache;
public PhotographReposiory(IMemoryCache cache, ...)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
}
public async Task Update(PhotographEntity entity)
{
// update your entity here
await _context.SaveChangesAsync();
// this invalidates your memory cache. Next call to _cache.TryGetValue
// results in a cache miss and the new entity is fetched from the database
_cache.Remove(GetClientCacheKey(entityName, clientId));
}
}
Using with Decorator pattern
public class PhotographRepository : IPhotograpRepository
{
private readonly ApplicationDbContext _context;
public PhotographReposiory(ApplicationDbContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task Update(PhotographEntity entity)
{
// update your entity here
await _context.SaveChangesAsync();
}
}
public class CachedPhotographRepository : IPhotograpRepository
{
private readonly IMemoryCache _cache;
private readonly IPhotograpRepository _repository;
public CachedPhotographRepository(IPhotograpRepository repository, IMemoryCache cache)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_repository = _repository ?? throw new ArgumentNullException(nameof(repository));
}
public async Task Update(PhotographEntity entity)
{
// do the update in the passed in repository
await _repository.Update(entity);
// if no exception is thrown, it was successful
_cache.Remove(GetClientCacheKey(entityName, clientId));
}
}
The catch is, the built-in DI/IoC container doesn't support decorator registrations, so you'll have to make it yourself via factory pattern or use a 3rd party IoC container which supports it.
services.AddScoped<IPhotograpRepository>(provider =>
// Create an instance of PhotographRepository and inject the memory cache
new CachedPhotographRepository(
// create an instance of the repository and resolve the DbContext and pass to it
new PhotographRepository(provider.GetRequiredService<ApplicationDbContext>()),
provider.GetRequiredService<IMemoryCache>()
)
);
It's per se not "bad" to use new within the composition root (where you configure your DI/IoC container), but with 3rd party IoC container its just more convenient.
Of course you can also register PhotographRepository with the IoC container and have it resolved. But that would also allow you to inject PhotographRepository into your services whereas the above prevents it, because only the IPhotographRepository interface is registered.

Is it a good design to inject services as factories?

I have been reading Mark Seemann's excellent book on DI and hope to implement it in my next WPF project. However I have a query regarding object lifetime. So far, most examples seem to explain the repository pattern per request for MVC applications. In WPF there isn't really an alternative to this (I think). Seeing as the object graph of the entire application is constructed in the composition root, how can I make sure that my unit-of-work stuff is working properly. For example:
public class ContextFactory : IContextFactory
{
DBContext context;
public ContextFactory()
{
context = new MyDBContext();
}
public DBContext GetContext()
{
return context;
}
}
public class ItemOneRepository() : IItemOneRepository
{
DBContext context;
public ItemOneRepository(IContextFactory contextFactory)
{
this.context = contextFactory.GetContext();
}
public IEnumerable GetItems()
{
return context.ItemOnes;
}
}
public class ItemTwoRepository() : IItemTwoRepository
{
DBContext context;
public ItemTwoRepository(IContextFactory contextFactory)
{
this.context = contextFactory.GetContext();
}
public IEnumerable GetItemsByItemOneID(int itemOneID)
{
return context.ItemTwos.Where(i => i.itemOneID == itemOneID);
}
}
public class ThingService : IThingService
{
IItemOneRepository itemOneRepo;
IItemTwoRepository itemTwoRepo;
public ThingService(
IItemOneRepository itemOneRepository,
IItemTwoRepository itemTwoRepository)
{
itemOneRepo = itemOneRepository;
itemTwoRepo = itemTwoRepository;
}
public IEnumerable Things GetThing()
{
var ItemOnes = itemOneRepo.GetItems();
return ItemOnes.Select(i =>
new Thing(
i.FieldOne,
i.FieldFour,
itemRepoTwo.GetItemsByItemOneID(i.ID)
)
);
}
}
In this case the MyDBContext instance is created through ContextFactory in the composition root. ItemOneRepository and ItemTwoRepository are using the same unit-of-work (MyDBContext), but so is the rest of the application which is plainly wrong. What if I changed the repositories to accept a DBContext instead of ContextFactory and added a ThingServiceFactory class like:
public ThingServiceFactory : IThingServiceFactory
{
IContextFactory contextFactory;
public ThingServiceFactory(IContextFactory factory)
{
contextFactory = factory;
}
public IThingService Create()
{
MyDBContext context = contextFactory.Create();
ItemOneRepository itemOneRepo = new ItemOneRepository(context);
ItemOneRepository itemTwoRepo = new ItemTwoRepository(context);
return new ThingService(itemOneRepo, itemTwoRepo);
}
}
This is better as I can now pass the ThingServiceFactory to my ViewModels instead of an instance of ThingService (complete with DBContext). I can then create a unit-of-work whenever I need one and instantly dispose of it when I’ve finished. However, is this really the correct approach. Do I really need to write a factory for every unit-of-work operation I need? Surely there is a better way...
There's IMO only one good solution to this problem and that is to apply a command-based and query-based application design.
When you define a single ICommandHandler<TCommand> abstraction to define business transactions, you can inject closed versions of that interface into any form that needs this. Say for instance you have a "move customer" 'command' operation:
public class MoveCustomer
{
public Guid CustomerId;
public Address NewAddress;
}
And you can create a class that will be able to execute this command:
public class MoveCustomerHandler : ICommandHandler<MoveCustomer>
{
private readonly DBContext context;
// Here we simply inject the DbContext, not a factory.
public MoveCustomerHandler(DbContext context)
{
this.context = context;
}
public void Handle(MoveCustomer command)
{
// write business transaction here.
}
}
Now your WPF Windows class can depend on ICommandHandler<MoveCustomer> as follows:
public class MoveCustomerWindow : Window
{
private readonly ICommandHandler<MoveCustomer> handler;
public MoveCustomerWindows(ICommandHandler<MoveCustomer> handler)
{
this.handler = handler;
}
public void Button1Click(object sender, EventArgs e)
{
// Here we call the command handler and pass in a newly created command.
this.handler.Handle(new MoveCustomer
{
CustomerId = this.CustomerDropDown.SelectedValue,
NewAddress = this.AddressDropDown.SelectedValue,
});
}
}
Since MoveCustomerWindow lives for quite some time, it will drag on its dependencies for as long as it lives. If those dependencies shouldn't live that long (for instance your DbContext) you will be in trouble and Mark Seemann calls this problem Captive Dependency.
But since we now have a single ICommandHandler<TCommand> abstraction between our presentation layer and our business layer, it becomes very easy to define a single decorator that allows postponing the creation of the real MoveCustomerHandler. For instance:
public class ScopedCommandHandlerProxy<TCommand> : ICommandHandler<TCommand>
{
private readonly Func<ICommandHandler<TCommand>> decorateeFactory;
private readonly Container container;
// We inject a Func<T> that is able to create the command handler decoratee
// when needed.
public ScopedCommandHandlerProxy(
Func<ICommandHandler<TCommand>> decorateeFactory,
Container container)
{
this.decorateeFactory = decorateeFactory;
this.container = container;
}
public void Handle(TCommand command)
{
// Start some sort of 'scope' here that allows you to have a single
// instance of DbContext during that scope. How to do this depends
// on your DI library (if you use any).
using (container.BeginLifetimeScope())
{
// Create a wrapped handler inside the scope. This way it will get
// a fresh DbContext.
ICommandHandler<TCommand> decoratee =this.decorateeFactory.Invoke();
// Pass the command on to this handler.
decoratee.Handle(command);
}
}
}
This sounds a bit complex, but this completely allows you to hide the fact that a new DbContext is needed from the client Window and you hide this complexity as well from your business layer; you can simply inject a DbContext into your handler. Both sides know nothing about this little peace of infrastructure.
Of course you still have to wire this up. Without a DI library you do something like this:
var handler = new ScopedCommandHandlerProxy<MoveCustomerCommand>(
() => new MoveCustomerCommandHandler(new DbContext()),
container);
How to register this in a DI library is completely depending on the library of choice, but with Simple Injector you do it as follows:
// Register all command handler implementation all at once.
container.Register(
typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
// Tell Simple Injector to wrap each ICommandHandler<T> implementation with a
// ScopedCommandHandlerProxy<T>. Simple Injector will take care of the rest and
// will inject the Func<ICommandHandler<T>> for you. The proxy can be a
// singleton, since it will create the decoratee on each call to Handle.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(ScopedCommandHandlerProxy<>),
Lifestyle.Singleton);
This is just one of the many advantages that this type of design gives you. Other advantages is that it makes much easier to apply all sorts of cross-cutting concerns such as audit trailing, logging, security, validation, de-duplication, caching, deadlock-prevention or retry mechanisms, etc, etc. The possibilities are endless.
ItemOneRepository and ItemTwoRepository are using the same
unit-of-work (MyDBContext), but so is the rest of the application
which is plainly wrong.
If your factory is registered with a transient lifecycle, you will get a new instance every time it's injected, which will be a new DBContext each time.
However, I would recommend a more explicit unit of work implementation:
public DBContext GetContext() //I would rename this "Create()"
{
return new MyDBContext();
}
And:
public IEnumerable GetItemsByItemOneID(int itemOneID)
{
using (var context = contextFactory.Create())
{
return context.ItemTwos.Where(i => i.itemOneID == itemOneID);
}
}
This gives you fine-grained control over the unit of work and transaction.
You might also ask yourself if the repositories are gaining you anything vs. just using the context directly via the factory. Depending on the complexity of your application, the repositories may be unnecessary overhead.

Unity, Using Statement and PerRequestLifetimeManager

I'm using Unity in my project. But I'm unsure whether I should be using the using statement or not as I'm using the PerRequestLifetimeManager.
Simple example:
Injection:
container.RegisterType<IDataContext, MyContext>(new PerRequestLifetimeManager());
Usage:
class MyClass
{
private readonly IDataContext _context;
public MyClass(IDataContext context)
{
_context = context;
}
public string MyMethod()
{
// 1. Is this needed?
using (var u = _context)
{
var customers = u.Set<Customer>().OrderBy(o => o.Name);
// ........
}
// 2. OR can I just do this
var customers = _context.Set<Customer>().OrderBy(o => o.Name);
As I'm using PerRequestLifetimeManager() in my injection, does this negate the requirement of the using statement as the context will be disposed after every http request anyway?
If you would use using then you would immediately dispose DbContext and if you try to access it again it will result in exception, so 1 approach is generally bad idea. 2 would work but what if there would occur problem in DbContext, for instance new data that you would like to persist will be not compliant to db design (for instance indexes). In such case you will be unable to recreate DbContext. My advice would be to register and then resolve factory method for DbContexts:
class MyClass
{
private readonly Func<IDataContext> _contextFactory;
public MyClass(Func<IDataContext> contextFactory)
{
_contextFactory = contextFactory;
}
public string MyMethod()
{
// 1. Is this needed?
using (var u = _contextFactory())
{
var customers = u.Set<Customer>().OrderBy(o => o.Name);
// ........
}
}
Best practice to use Unit of Work and Repository patterns while you are working with MVC4. While you are creating every time of our context best to register your context in the UnityConfig.cs file located at App_Start folder. as
container.RegisterType<IDataContext, MyContext>(new PerRequestLifetimeManager());
after this when you start your application the context will be in open till your application closed. so, do not use the using statement at the use of context. It will dispose your context after complete of using and the context will not available further and when use context after closing using then you must got an exception like context is not available. so, just use
var customers = _context.Set<Customer>().OrderBy(o => o.Name);

Categories

Resources