Can I clone DbContext from existing one? - c#

I'm working on .NET Core Web API and I have one endpoint where I want to run three operations in parallel. All three of them use the same database, so I need three copies of DbContext. I created a simple Factory class, which I later inject into my "Data" class.
Is it possible (if it's, is a good practice), to inject DbContext into my factory class (using built in .NET Core IoC) and when someone calls "CreateMyDbContext" method, just deep clone the one which was injected at the beginning?
EDIT:
Here is the example with the DbContext Pool:
public class FooData : IFooData
{
private readonly Func<DisposableScopedContextWrapper> _func;
public FooData(Func<DisposableScopedContextWrapper> func)
{
_func = func;
}
public async Task<List<Apple>> GetApples()
{
using (var wrapper = _func())
{
var apples = await wrapper.Context.Apples.FromSqlRaw("SELECT.... complicated query").ToListAsync();
return apples;
}
}
public async Task<List<Orange>> GetOranges()
{
using (var wrapper = _func())
{
var oranges = await wrapper.Context.Oranges.FromSqlRaw("SELECT.... complicated query").ToListAsync();
return oranges;
}
}
}
public class FooService
{
private readonly IFooData _fooData;
public FooData(IFooData fooData)
{
_fooData = fooData;
}
public async Task<List<Fruit>> GetFruits()
{
var appleTask = _fooData.GetApples();
var orangeTask = _fooData.GetOranges();
(var result1, var result2) = await (appleTask, orangeTask).WhenAll();
// ...
}
}

I definitely would not recommend any deepcloning for multiple reasons, one of them being that you will need to figure out a lot of EF internals to make it right, and internals can change (and you will need to spend some time on it).
Second option would be just creating your context manually, which I would recommend against also cause modern infrastructure uses DbContext pooling.
So what you can to register Func<DbContext> (or create your own factory) like this:
services.AddSingleton<Func<DbContext>>(provider => () =>
{
var scope = provider.CreateScope();
return scope.ServiceProvider.GetRequiredService<DbContext>();
});
the issue here is that scope here would not be disposed and you can't (if you have default scope for your DbContext) dispose the scope inside the Func cause your context will be disposed also. So you can try creating some disposable wrapper so you can manually dispose everything like this:
public class DisposableScopedContextWrapper : IDisposable
{
private readonly IServiceScope _scope;
public DbContext Context { get; }
public DisposableScopedContextWrapper(IServiceScope scope)
{
_scope = scope;
Context = _scope.ServiceProvider.GetService<DbContext>();
}
public void Dispose()
{
_scope.Dispose();
}
}
services.AddSingleton<Func<DisposableScopedContextWrapper>>(provider =>() =>
{
var scope = provider.CreateScope();
return new DisposableScopedContextWrapper(scope);
});
Inject in your classes Func<DisposableScopedContextWrapper> func and use it
using (var wrapper = func())
{
wrapper.Context...
}

Related

How to resolve scoped service inside singleton object

I have MemoryCache objects (Application,Configuration etc) which I registered them as Singleton. Also there are scoped repositories which selects data from db to fill cache.
For example here is the Singleton registered class,
public class ApplicationCache : MultipleLoadCache<Application>
{
public ApplicationCache()
{
}
}
MultipleLoadCache overrides the CacheItemPolicy, (there is also SingleLoadCache),
public class MultipleLoadCache<TEntity> : SmartCache<TEntity> where TEntity : class
{
public MultipleLoadCache()
{
}
protected override CacheItemPolicy SetPolicy()
{
return new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(15)
};
}
}
And base class is,
public class SmartCache<TEntity> : IDisposable where TEntity : class
{
public bool TryGetList(IRepository<TEntity> repository, out List<TEntity> valueList)
{
valueList = null;
lock (cacheLock)
{
GenerateCacheIfNotExists(repository, out valueList);
if (valueList == null || valueList.Count == 0)
{
valueList = (List<TEntity>)_memoryCache.Get(key);
}
}
return valueList != null;
}
I know that scoped services can't be injected to singleton class. So I prefer to use method injection.
private void GenerateCacheIfNotExists(IRepository<TEntity> repository, out List<TEntity> list)
{
list = null;
if (!_memoryCache.Any(x => x.Key == key)) // if key not exists, get db records from repo.
{
IEnumerable<TEntity> tempList = repository.GetList();
list = tempList.ToList();
_cacheItemPolicy = SetPolicy();
SetCacheList(list);
}
}
}
And at controller I try to get cache values, but this part seems wrong to me. If I try to get cache values, I shouldn't pass repository as parameter.
private readonly ApplicationCache _appCache;
public LogController(ApplicationCache appCache)
{
_appCache = appCache;
}
[HttpPost]
[Route("Register")]
public List<Application> Register([FromServices] IApplicationRepository repository)
{
List<Application> cf;
_appCache.TryGetList(repository, out cf);
return cf;
}
Also, by doing Method Injection. I am also unable to use RemovedCallBack event of CacheItemPolicy. Because, when callback triggers (reload cache), I need repository to get records from db again.
Is this design seems nice, what is the best design to do this by using callback events of MemoryCache?
Update 1-
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.AddSingleton(x => new ApplicationCache());
services.AddScoped<IApplicationRepository, ApplicationRepository>();
}
Thanks,
I had the same issue. Since static classes is compiled at the beginning it cannot inject the required services later. I figured it out by using IServiceScopeFactory.
You basically inject IServiceScopeFactory serviceScopeFactory in the constructer .
static SampleClass(IServiceScopeFactory serviceScopeFactory){
//serviceScopedFactory will act as Singleton, since it is a static class
_serviceScopeFactory = serviceScopeFactory;
}
And use it like this in the method :
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IService>();
//Here you can use the service. This will be used as Scoped since it will be
//recreated everytime it is called
}

Can I use an AutFac factory to create my DbContext

I am trying to implement an auto-refresh using MemoryCache by specifying a CacheEntryUpdateCallback delegate that is called when the cached item expires.
The delegate calls a method in my repository:
public async Task<List<Foo>> GetFoos()
{
return await _dbContext.Foos.ToListAsync();
}
That throws an exception in the callback because the context has already been disposed (the original HttpRequest has long since returned)
So I tried using an Autofac factory to inject my dependency instead:
public FooRepository(Func<<IFooContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<List<Foo>> GetFoos()
{
return await _dbContextFactory().Foos.ToListAsync();
}
That gave me a different exception:
Instances cannot be resolved and nested lifetimes cannot be created
from this LifetimeScope as it has already been disposed.
What about this "Owned" factory thing?
public FooRepository(Func<Owned<IFooContext>> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<List<Foo>> GetFoos()
{
using(var factory = _dbContextFactory())
{
return await factory.Value.Foos.ToListAsync();
}
}
Nope, same problem:
Instances cannot be resolved and nested lifetimes cannot be created
from this LifetimeScope as it has already been disposed.
What can I do to get around this problem?
you should have hosted service for long run process and kind of refresh queue to feed it
with hosted service you can get DbContext in temporary scope as follow
public class TimedHostedService : IHostedService
{
private readonly IServiceScopeFactory scopeFactory;
public TimedHostedService(IServiceScopeFactory scopeFactory)
{
this.scopeFactory = scopeFactory;
}
private void DoWork()
{
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
}
}
}
about hosted service

couldnt able to make Autofac nested scopes work (nested multiple Unit of works should have a new dbcontext )

Im trying to implement Unit of Work with Autofac and Mediatr.
Here how is the flow
but i couldn't make Autofac to send same instance of Unit OfWork (which takes DbContext as parameter) inside a scope.
I want to execute that whole scope inside a single transaction, that means when
i get to the point processHandler it should create a instance of DbContext and
share the same instance into nested handlers. such that i can create a transaction on processhandler level and share the same transaction to nested handlers.
here is my DI setup
builder.Register(ctx =>
{
var contextSvc = ctx.Resolve<IContextService>(); // owin context
var connBuilder = ctx.Resolve<IDbConnectionBuilder>();
return SapCommandDb.Create(contextSvc.GetMillCode(), connBuilder.BuildConnectionAsync(IntegrationConnectionName, contextSvc.GetMillCode()).Result);
}).AsSelf().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IDomainRepository<>)).InstancePerLifetimeScope();
builder.RegisterType<EFUnitOfWork>().As<IEFUnitOfWork>().InstancePerLifetimeScope();
public class ProcessHandler : AsyncRequestHandler<IntermediateDocument.Command>
{
IMediator _mediator;
Func<Owned<IEFUnitOfWork>> _uow;
ILifetimeScope _scope;
public ProcessHandler(
ILifetimeScope scope,
Func<Owned<IEFUnitOfWork>> uow,
IMediator mediator)
{
_mediator = mediator;
_scope = scope;
_uow = uow;
}
protected async override Task Handle(Command request, CancellationToken cancellationToken)
{
foreach (var transaction in request.Transactions)
{
using (var scope = _scope.BeginLifetimeScope("custom"))
{
using (var uo = _uow())
{
await uo.Value.Execute(async () =>
{
await _mediator.Send(new NestedHandlerGetBySwitch.Command(transaction));
});
}
}
}
}
}
the above one is the process handler
public class NestedHandler1 : AsyncRequestHandler<NestedHandler.Command>
{
IMediator _mediator;
IEFUnitOfWork _uow;
public NestedHandler1(
IEFUnitOfWork uow,
IMediator mediator)
{
_mediator = mediator;
_uow = uow;
}
protected async override Task Handle(Command request, CancellationToken cancellationToken)
{
_uow.Repository.Add(request);
}
}
the above one is an example of nested handler.
I want the same _uow instance from processhandler.
EFUNitOFWork looks like
public class EfUnitOfWork : IEFUnitOfWork {
private DbContext _context;
ABCRepository aBCRepository;
public ABCRepository ABCRepository { get {
return aBCRepository = aBCRepository ?? new ABCRepository(_context);
} }
public EfUnitOfWork(DbContext context)
{
_context = context;
}
public Task Add(Entity entity) {
await _context.AddAsync(entity);
}
}
what am i doing wrong ?
Thankyou.
IMediator is asking AutoFac to create an instance of NestedHandler1, so it is going to have the same lifetime scope as IMediator.
One way of solving it is to resolve IMediator from the "custom" lifetime scope and use that one instead of injecting it in the constructor, and make sure that the UnitOfWork is properly registered in this scope:
using (var uo = _uow())
{
using (var scope = _scope.BeginLifetimeScope("custom", x => x.RegisterInstance(uo.Value))
{
var mediator = scope.Resolve<IMediator>();
await uo.Value.Execute(async () =>
{
await mediator.Send(new NestedHandlerGetBySwitch.Command(transaction));
});
}
}
You have a bit of a mess between UnitsOfWork, Mediators and stuff.
Let's keep things simple and deduce the implementation from the requirements.
You need to have a single DbContext shared by multiple components.
A single request could process multiple operations, by multiple handlers.
Given this two facts, we can infer that we need two distinct lifetime scopes:
the first to share the DbContext (we will call this "UnitOfWork"), and the second that corresponds to each
and every operation (let's call this "Operation").
The handling of this structure will be handled like so:
public class ProcessHandler : AsyncRequestHandler<IntermediateDocument.Command>
{
// ...ctor
protected async override Task Handle(Command request, CancellationToken cancellationToken)
{
// inside this using every component asking for an
// IEFUnitOfWork will get the same instance
using (var unitOfWorkScope = _scope.BeginLifetimeScope("UnitOfWork"))
{
foreach (var transaction in request.Transactions)
{
// we don't need this inner scope to be tagged, AFAICT
// so probably "Operation" could be omitted.
using (var scope = unitOfWorkScope.BeginLifetimeScope("Operation"))
{
// I'm not sure what a "mediator" is, I'm just copying the example code
var mediator = scope.Resolve<IMediator>();
await mediator.Send(...do something with transaction);
} // here mediator will be disposed, once for each transaction instance
}
} // here everything resolved inside unitOfWorkScope will be disposed (and possibly committed).
}
}
The dbContext must be registered as
builder.RegisterType<EFUnitOfWork>().As<IEFUnitOfWork>().InstancePerMatchingLifetimeScope("UnitOfWork");
Quite possibly you don't need the IEFUnitOfWork, but you can simply share the DbContext, registering
it in the UnitOfWork scope. In other words, the tagged scope of Autofac could replace your
class entirely, AFAICT.
Reference to the Autofac documentation:
https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope

Overriding AutoFac scoping configuration when using factory

How to configure AutoFac so that I get a new instance of Context every time I hit the factory. The Content component is set to InstancePerLifetimeScope(), which is perfect for 99% of my usage, but now I need a little extra control over how the Context component is scoped.
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<Box>();
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var x = scope.Resolve<Box>();
}
Console.ReadKey();
}
}
class Box
{
public Box(DbContext.Factory factory)
{
factory();
factory(); // Want this to generate a NEW instance
Console.WriteLine("Box: {0}", GetHashCode());
}
}
class DbContext
{
public delegate DbContext Factory();
public DbContext()
{
Console.WriteLine("Context: {0}", GetHashCode());
}
}
Obviously, this is a rather simplified snippet of code. The problem I am trying to solve is that I have a huge stream of data coming into a service and I am trying to batch-save to the database. So, if Box can create new UOWs on demand, and release them back for disposal in a timely fashion, then I get a nice clean solution.
Thanks!
You can use Func<Owned<>> which works like a small ILifetimeScope :
public Box(Func<Owned<DbContext>> factory)
{
using (Owned<DbContext> ownedDbContext = factory())
{
// instance1
}
using (Owned<DbContext> ownedDbContext = factory())
{
// instance2
}
}
You can find more details on the Autofac documentation : Owned Instances
Another solution is to inject ILifetimeScope and then create a sub lifetimescope :
public Box(ILifetimeScope scope)
{
using (ILifetimeScope subScope = scope.BeginLifetimeScope())
{
DbContext dbContext = subScope.Resolve<DbContext>();
}
}
or
public Box(ILifetimeScope scope)
{
ILifetimeScope subScope = scope.BeginLifetimeScope();
scope.Disposer.AddInstanceForDisposal(subScope);
DbContext dbContext = subScope.Resolve<DbContext>();
// no need to dispose subScope,
// subScope (and dbContext) will be disposed at the same time as scope
}

Unit of work in mongodb and C#

I know that MongoDB is not supposed to support unit of work, etc. But I think it would be nice to implement the repository which would store only the intentions (similar to criteria) and then commit them to the DB. Otherwise in every method in your repository you have to create connection to DB and then close it. If we place the connection to DB in some BaseRepository class, then we tie our repository to concrete DB and it is really difficult to test repositories, to test IoC which resolve repositories.
Is creating a session in MongoDB a bad idea? Is there a way to separate the connection logic from repository?
Here is some code by Rob Conery. Is it a good idea to always connect to your DB on every request? What is the best practice?
There is one more thing. Imagine I want to provide an index for a collection. Previously I did in a constructor but with Rob's approach this seems out of logic to do it there.
using Norm;
using Norm.Responses;
using Norm.Collections;
using Norm.Linq;
public class MongoSession {
private string _connectionString;
public MongoSession() {
//set this connection as you need. This is left here as an example, but you could, if you wanted,
_connectionString = "mongodb://127.0.0.1/MyDatabase?strict=false";
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
//not efficient, NoRM should do this in a way that sends a single command to MongoDB.
var items = All<T>().Where(expression);
foreach (T item in items) {
Delete(item);
}
}
public void Delete<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.GetCollection<T>().Delete(item);
}
}
public void DeleteAll<T>() where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.DropCollection(typeof(T).Name);
}
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
T retval = default(T);
using(var db = Mongo.Create(_connectionString))
{
retval = db.GetCollection<T>().AsQueryable()
.Where(expression).SingleOrDefault();
}
return retval;
}
public IQueryable<T> All<T>() where T : class, new() {
//don't keep this longer than you need it.
var db = Mongo.Create(_connectionString);
return db.GetCollection<T>().AsQueryable();
}
public void Add<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(item);
}
}
public void Add<T>(IEnumerable<T> items) where T : class, new() {
//this is WAY faster than doing single inserts.
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(items);
}
}
public void Update<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().UpdateOne(item, item);
}
}
//this is just some sugar if you need it.
public T MapReduce<T>(string map, string reduce) {
T result = default(T);
using(var db = Mongo.Create(_connectionString))
{
var mr = db.Database.CreateMapReduce();
MapReduceResponse response =
mr.Execute(new MapReduceOptions(typeof(T).Name) {
Map = map,
Reduce = reduce
});
MongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>();
MapReduceResult<T> r = coll.Find().FirstOrDefault();
result = r.Value;
}
return result;
}
public void Dispose() {
_server.Dispose();
}
}
Don't worry too much about opening and closing connections. The MongoDB C# driver maintains an internal connection pool, so you won't suffer overheads of opening and closing actual connections each time you create a new MongoServer object.
You can create a repository interface that exposes your data logic, and build a MongoDB implementation that is injected where it's needed. That way, the MongoDB specific connection code is abstratced away from your application, which only sees the IRepository.
Be careful trying to implement a unit-of-work type pattern with MongoDB. Unlike SQL Server, you can't enlist multiple queries in a transaction that can be rolled back if one fails.
For a simple example of a repository pattern that has MongoDB, SQL Server and JSON implementations, check out the NBlog storage code. It uses Autofac IoC to inject concrete repositories into an ASP.NET MVC app.
While researching design patterns, I was creating a basic repository pattern for .Net Core and MongoDB. While reading over the MongoDB documentation I came across an article about transactions in MongoDB. In the article it specified that:
Starting in version 4.0, MongoDB provides the ability to perform
multi-document transactions against replica sets.
Looking around the intertubes I came across a library that does a really good job of implementing the Unit of Work pattern for MongoDB.
If you are interested in an implementation similar to Rob Connery's and NBlog storage code but using the mongodb csharp driver 2.0 (that is asynchronous), you can look at:
https://github.com/alexandre-spieser/mongodb-generic-repository
You can then write a custom repository inheriting from BaseMongoRepository.
public interface ITestRepository : IBaseMongoRepository
{
void DropTestCollection<TDocument>();
void DropTestCollection<TDocument>(string partitionKey);
}
public class TestRepository : BaseMongoRepository, ITestRepository
{
public TestRepository(string connectionString, string databaseName) : base(connectionString, databaseName)
{
}
public void DropTestCollection<TDocument>()
{
MongoDbContext.DropCollection<TDocument>();
}
public void DropTestCollection<TDocument>(string partitionKey)
{
MongoDbContext.DropCollection<TDocument>(partitionKey);
}
}
Briefly
You can use this nuget-package UnitOfWork.MongoDb. This is a wrapper for MongoDb.Driver with some helpful functions and features. Also you can find sample code and video (ru).
Read settings for connection
// read MongoDb settings from appSettings.json
services.AddUnitOfWork(configuration.GetSection(nameof(DatabaseSettings)));
// --- OR ----
// use hardcoded
services.AddUnitOfWork(config =>
{
config.Credential = new CredentialSettings { Login = "sa", Password = "password" };
config.DatabaseName = "MyDatabase";
config.Hosts = new[] { "Localhost" };
config.MongoDbPort = 27017;
config.VerboseLogging = false;
});
Injections
namespace WebApplicationWithMongo.Pages
{
public class IndexModel : PageModel
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<IndexModel> _logger;
public IndexModel(IUnitOfWork unitOfWork, ILogger<IndexModel> logger)
{
_unitOfWork = unitOfWork;
_logger = logger;
}
public IPagedList<Order>? Data { get; set; }
}
}
After injection you can get repository.
Get repository
public async Task<IActionResult> OnGetAsync(int pageIndex = 0, int pageSize = 10)
{
var repository = _unitOfWork.GetRepository<Order, int>();
Data = await repository.GetPagedAsync(pageIndex, pageSize, FilterDefinition<Order>.Empty, HttpContext.RequestAborted);
return Page();
}
GetPagedAsync one of some helpful implementations
Transactions
If you need ACID operations (transactions) you can use IUnitOfWork something like this. (Replicate Set should be correctly set up). For example:
await unitOfWork.UseTransactionAsync<OrderBase, int>(ProcessDataInTransactionAsync1, HttpContext.RequestAborted, session);
Method ProcessDataInTransactionAsync1 can be looks like this:
async Task ProcessDataInTransactionAsync1(IRepository<OrderBase, int> repositoryInTransaction, IClientSessionHandle session, CancellationToken cancellationToken)
{
await repository.Collection.DeleteManyAsync(session, FilterDefinition<OrderBase>.Empty, null, cancellationToken);
var internalOrder1 = DocumentHelper.GetInternal(99);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder1, null, cancellationToken);
logger!.LogInformation("InsertOne: {item1}", internalOrder1);
var internalOrder2 = DocumentHelper.GetInternal(100);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder2, null, cancellationToken);
logger!.LogInformation("InsertOne: {item2}", internalOrder2);
var filter = Builders<OrderBase>.Filter.Eq(x => x.Id, 99);
var updateDefinition = Builders<OrderBase>.Update.Set(x => x.Description, "Updated description");
var result = await repositoryInTransaction.Collection
.UpdateOneAsync(session, filter, updateDefinition, new UpdateOptions { IsUpsert = false }, cancellationToken);
if (result.IsModifiedCountAvailable)
{
logger!.LogInformation("Update {}", result.ModifiedCount);
}
throw new ApplicationException("EXCEPTION! BANG!");
}
This nuget is open-source Calabonga.UnitOfWork.MongoDb

Categories