Connect to database using dependency injection - c#

I am trying to learn how to use dependency injection, but I have some trouble when it comes to my database. This is my process so far:
I have an MVC project where the controllers use different repositories from my classlibrary.
All repositories use the same database.
At first, I used SimpleInjector to register the Repositories Application_start method:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
var client = new GraphClient(uri, username, password);
container.Register<IRepoA>(() => new RepoA(client);
container.Register<IRepoB>(() => new RepoB(client);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
And in every method, I did like this:
client.Connect();
client.performSomeQuery();
client.Dispose();
This works, but it means that I am reconnecting to the database every single time I call a method.
To avoid this a moved the connect-call to here:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
var client = new GraphClient(uri, username, password);
client.Connect();
container.Register<IRepoA>(() => new RepoA(client);
container.Register<IRepoB>(() => new RepoB(client);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
But then I never get my connection disposed.
I thought now is to register my databaseclient;
container.RegisterSingleton(() =>
{
var client = new GraphClient(uri, username, password);
client.Connect();
return client;
});
And then inject it like this:
container.Register<IRepoA>(() => new RepoA(container.GetInstance<GraphClient>()));
Is this the correct way to do it?
Is it corretly understood that the connection will get disposed by the end of the containers lifetime?
I do get a "Implicitly captured closure: container" when I register the client.

there is more than one way to do things ... so asking for THE correct way might get you on the wrong path sometimes ...
but here is what i'd do in your case ...
i would introduce a pattern called unit-of-work ... think of it as a business transaction
you open a unit of work and within its lifetime you perform various DB interactions, possibly all within one database transaction. All those interactions may spread across different repositories. if your whole batch of interactions is done without errors that would require a rollback, you declare the unit of work complete and leave its scope (scope as in a using(...) scope)
if there is an error you do not declare it complete before its lifetime ends ...
on the end of lifetime of your unit of work, you can either commit or roll back all underlaying db transactions (usually it's only one) depending on the fact if complete was declared or not
this unit of work object usually also holds my db connection object(s) and provides connections to repositories
again with dependency injection, you can have factory methods that provide different db connections based on the interface the repository requests during instantiation ...
usually the first repo that needs a connection causes the factory to create one and open it (optionally you can have different connections based on the used repos) while the second repo that asks for a connection gets a connection that has been created and opened before ...
the end of the unit of work (see IDisposable) also means the end of the connectionfactory ... end of connectionfactory means the end of open connections and repos ... since the later are instantiated insied of a using block, and based on the used resource, they should never leave said block

Related

Dependency injection using both AddSingleton and AddTransient C#

I'm having a huge problem with the configuration/dependency injection of an application.
I have a singleton class added through DI with AddSingleton, that has in its constructor a IRequestClient, that is scoped because
busConfigurator.AddRequestClient()
which among other things, has the same effect as AddScoped.
When I start the app, it says
"Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'.)"
Which absolutely makes sense.
The weirdest thing is that I have another app set up the exact same way, but it just works and I would really like for that class to remain singleton.
My colleague and I spent an entire day trying to find the differences between the two applications, but they are virtually the same in their configurations, so we are having trouble in understanding why one works while the other doesn't.
I'm not entirely sure on what details could be important to better define the problem, so feel free to ask.
We've looked all around the internet trying to find a solution, but it was always "Change singleton to transient", but that's not an option, first because it HAS to be a singleton, otherwise it wouldn't make sense in our app, as that thing is what caches lots of date from our db so we can't just go around keeping on collecting heaps of data, second because the first app works with singleton, not with transient and we'd like to keep it that way
// This method is called in Main()
private static void ConfigureMassTransit(IServiceCollection services)
{
services.AddMassTransit(busConfigurators =>
{
busConfigurators.AddRequestClient<ICacheRepository>();
busConfigurators.AddConsumers(typeof(Program).GetTypeInfo().Assembly);
busConfigurators.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri($"rabbitmq://{Config.Settings.RabbitMq_Host}"), hostConfigurator =>
{
hostConfigurator.Username(Config.Settings.RabbitMq_User);
hostConfigurator.Password(Config.Settings.RabbitMq_Password);
});
cfg.ReceiveEndpoint("myApp", e =>
{
e.ConfigureConsumers(context);
});
});
});
// CacheRepository
public class CacheRepository : ICacheRepository
{
private readonly IClient Client;
public CacheRepository(ICacheRepository client, ILogger<CacheRepository> logger)
{
this.client = client;
this.logger = logger;
}
}
When a dependency is scoped, the implication is that a new instance is needed for each scope (which is usually an incoming HTTP request or message.) It implies that the instance should not be re-used for multiple requests.
If you have a singleton that depends on that scoped dependency, that singleton will be created using an instance of that dependency (the request client.) Because that singleton "lives forever," so does the instance of the request client it contains.
The result is that the request client is not supposed to be re-used across different scopes, but now it is. One instance is used forever.
A likely solution is to modify the class that depends on that client so that it doesn't need to be a singleton. You mentioned that it has to be a singleton because it caches data.
How does it cache data? Does it do so by storing data in a private field? If so, perhaps you could make that field static. Now the class instance isn't re-used, but those fields are shared between instances. (Verify that interaction with those fields is thread safe if they may be accessed concurrently.)
Or if there's some other cache mechanism, you could move that into its own dependency and make that a singleton.
Then your class can be scoped. It will depend on the singleton cache, always using the same instance. It will also depend on the scoped request client, using a new instance for each scope.
You could inject IServiceProvider instead, and create a scope when the singleton needs to perform a request. That way, you're sticking to the expected use of the request client.
await using var scope = provider.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IRequestClient<T>>();
await client.GetResponse(...);

Repository pattern sharing changes across scoped instances

I am using a pretty standard generic repository pattern (for example https://codewithmukesh.com/blog/repository-pattern-in-aspnet-core/#What_would_happen_if_we_didnt_have_an_UnitOfWork_Abstraction)
In program.cs I define my DB context and generic repository services as scoped.
...
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(configuration.GetConnectionString(connectionName)) ;
services.AddScoped(typeof(IGenericRepository<,>), typeof(GenericRepository<,>));
...
In a worker service I create two scoped instances during code execution;
using (var serviceScope = _serviceProvider.CreateScope())
{
var personDataService = serviceScope.ServiceProvider.GetRequiredService<IGenericRepository<Person, MyDbContext>>();
var auditLogDataService = serviceScope.ServiceProvider.GetRequiredService<IGenericRepository<AuditLog, MyDbContext>>();
...
}
When I make a call that generates an SQL exception on the first service I want to log the error in the second service, for example;
try {
await personDataService.InsertAsync(myNewPerson);
}
catch (Exception ex)
{
var newAuditLog = new AuditLog("Exception occurred inserting a new user", ex);
await auditLogDataService.InsertAsync(newAuditLog);
}
However, when personDataService generates a SQLException, for example;
SqlException: Cannot insert the value NULL into column 'Name'"
then the catch block triggers and I get the same error again when I run InsertAsync() on the 2nd auditLogDataService service.
SqlException: Cannot insert the value NULL into column 'Name'"
It appears that the changes from the first service are also in the second service. I'm assuming that MyDbContext is shared.
How do I create an independent instance of auditLogDataService so I can save the 2nd change without the first?
You probably use services.AddDbContext<YourDbContext>(), which makes it a scoped service by default.
This means that within one scope (which you create), you get the same instance of the DbContext every time you or another service requests it.
Mark it as transient instead, to get a new instance every time you request one. See Configuring Dbcontext as Transient:
services.AddDbContext<ApplicationDbContext>(options =>
options.Use(...),
ServiceLifetime.Transient);
Meta-commentary: please don't use repository patterns with Entity Framework. EF already exposes a repository through DbSet<T>. How are you going to support Include()s? Projections (Select())? Groupings (GroupBy)?
Could you try sending the DBContext object via constructor call for each and every repositories? For instance, whenever generic repository is used for any class Type, make it's constructor call and pass the DBContext object right away. So, every time the object of repository defined for that interface is called, you get the current DBContext.
You could inject a dbContextFactory into your repository and make a new DbContext from your factory there.

Dependency injection with Unit of Work pattern

I'm trying to implement unit of work pattern for my repositories in C#/.NET. My plan is to give UoW as a parameter to repositories. Here's an example how it could be used:
using (var uow = new UnitOfWork())
{
var itemRepository = new ItemRepository(uow);
itemRepository.Add(new Item());
uow.Commit();
}
Also, for simple operations (when transactions are not needed) repositories should be able to be used without Unit of Work:
var itemRepository = new ItemRepository();
var item = itemRepository.Get(itemId);
UnitOfWork/Repository could get a database connection from ConnectionFactory. Connection factory receives connection options via dependency injection. However, here's my problem: How do repositories get reference to ConnectionFactory instance? Repositories are created manually, so they can't have dependencies injected via constructor. One option would be to have repository factories, which could have their dependencies injected. In that case usage could be like this:
using (var uow = new UnitOfWork())
{
var itemRepository = itemRepositoryFactory.Create(uow);
itemRepository.Add(new Item());
uow.Commit();
}
The drawback in that solution is that each repository will need its own factory, and there will be quite many. Are there any other solutions to circumvent this problem?
I would definitely register the UOW as a scoped dependency.
Scoped dependencies live for the lifetime of the container that creates them. Typically, frameworks would generate a child container from the parent container in order to execute some piece of work. For example, ASP.NET Core spawns a child container for a request and then disposes it when the request is finished. This would mean that the UOW instance that is getting injected is the same instance throughout the object graph for that request only.
You can also create your own scopes, if needed. I have done this twice for example:
A job scheduler, so that each job ran in it's own scope
A message handler, so that each message was processed in its own scope
This is how you would achieve this using Microsoft's DI framework:
var collection = new ServiceCollection();
collection.AddScoped<IUnitOfWork, UnitOfWork>();
var provider = collection.BuildServiceProvider();
var puow = provider.GetRequiredService<IUnitOfWork>();
for(int i = 0; i < 2; i++)
{
//Create the new scope
using var childContainer = provider.CreateScope();
//IUnitOfWork will be a singleton instance in this scope.
var c1uow = childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
var c2uow = childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
// This should true, since they come from the same scope.
var sameScope = c1uow == c2uow;
//With the requested IUnitOfWork from provider instead, it would be a different instance
//Therefore, this should be false
var diffScope = puow == c1uow;
}
This would allow you to simply inject IUnitOfWork into each repo, without having to create a factory for each repo.
This would work out of the box if you are writing an application in ASP.NET Core. Simply register the dependency as scope, as so
collection.AddScoped<IUnitOfWork, UnitOfWork>();
and then have it injected into the repos that you need. You don't have to worry about creating a new scope since the framework does that for you for each http request that the application receives.
I would really recommend reading Mark Seemann's book "Dependency Injection Principles, Practices, and Patterns". It really goes in depth about what dependency injection is and how it works. Not only that, I find him to be a great writer.

Start a new LifetimeScope when a Masstransit consumer is triggered

We have the following code to configure the recieveEndpoint:
private Action<IServiceBusReceiveEndpointConfigurator> GetReceiveEndpointConfigurator(IEnumerable<Type> consumerTypes)
{
return c =>
{
c.EnableDeadLetteringOnMessageExpiration = true;
c.SubscribeMessageTopics = false;
c.MaxDeliveryCount = 3;
c.EnableDeadLetteringOnMessageExpiration = true;
c.UseRetry(Retry.Exponential(3, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(1)));
foreach (var consumerType in consumerTypes)
{
c.Consumer(consumerType, p => _componentContext.Resolve(p));
}
};
}
All of our consumers are autodiscovered through reflection once our application starts up. We have a DbContext that we want to use in many of our consumers. The problem we face is that the DbContext is disposed due to it being registered as InstancePerLifetimeScope. More details here:
AspNet Core Autofac disposing my DbContext even if its registered as SingleInstance
Two suggestions came from this post:
Register the DbContext as InstancePerDependency
Create a new Scope within the consumer to start a new LifetimeScope
The first suggestion wont work in our application as we have a UnitOfWork which triggers the SaveChangesAsync on the DbContext. The result would be that the Repository and the UnitOfWork will get two different instances of the DbContext and SaveChangesAsync will not persist our changes as the ChangeTracker has no changes in the UnitOfWork implementation, but these changes belongs to the instance in the Repository.
The second suggestion works perfectly. Within my Consumer I create a new LifetimeScope and resolves the components that I need:
public async Task Consume(ConsumeContext<RetailerCreatedEvent> context)
{
using (var scope = _serviceProvider.CreateScope())
{
var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork<MyDbContext>>();
}
}
It works, but it doesn't look that good.
Is there a way to start a new LifetimeScope before the Consumer triggers? Or should I rewrite my UnitOfWork-pattern to ensure that the same DbContext is being reused in the Repositories and the UnitOfWork?
Suggestions are much appreciated
You need to use the MassTransit.Autofac package to resolve your consumers, which will use the AutofacScopeProvider (part of the package) to create a lifetime scope and resolve your consumer.
The documentation shows the configuration, including how to automatically discover your consumers via scanning and add them to MassTransit.
Your consumers shouldn't have any container code in them using this package, they should just add DbContext as a constructor dependency and let Autofac do the work.

Cleaner way to choose LifetimeScope in a dependency chain with Autofac

I'm using a web app with Autofac injecting services into controllers. Those services are sometimes injected with other services, and repositories. Repositories are injected with DbContexts. These 3 layers (service, repository, context) are all registered with Autofac. My default lifetime for these is InstancePerLifetimeScope.
Unfortunately, I have some code in a specific controller that I want to execute in parallel threads. Since DbContext is not thread-safe, this means I need to give a factory method to each thread to resolve a Service in a per dependency lifetime scope, which in turn will need to resolve per dependency repositories and db contexts.
The options I am considering are to create a new lifetime scope per thread, or to use a separate registration using a named or keyed registration to resolve the per-dependency services.
The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
The other method would be to use a registration key so that when I execute the factory method to resolve a service on each thread, it would resolve one in the per-dependency scope. This would work if the service had no dependencies, but since it depends on a bunch of repositories or services for which the default lifetime scope is set to InstancePerLifetimeScope, I have to write something like this:
builder.RegisterType<MyService>()
.As<IMyService>()
.Named<IMyService>(RegistrationKeys.PerDependency)
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IMyRepository),
(pi, ctx) => ctx.ResolveNamed<IMyRepository>(RegistrationKeys.PerDependency))
).InstancePerDependency();
Since the repositories depend on the DbContext, each repository has to be registered separately using this registration name. And it needs to be configured to resolve the DbContext using the registration name. And the DbContext needs to be registered using the registration name.
With 10 services each using about 4-5 repositories, I wager the amount of boilerplate registration code I will have to write will be around 10-20 full pages. It's not going to be maintainable.
So my question is, is there a way to create a specific type of lifetime scope that will allow me to easily control which objects will have a new instance or which will be inherited from the parent lifetime scope that won't break the asp.net per-request lifetime scope?
Or is there a way I can register or resolve a service to explicitly resolve all of its dependencies in the same scope without relying on their default registrations and without having to hard code an entire second set of registrations for everything?
The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
This is the challenge InstancePerRequest solve. You can create child scope and object scoped to Request will be shared amongst child scope. To do this, tagged lifetimescope and InstancePerMatchingLifetimeScope is used.
You can see InstancePerRequest and Tagging a lifetime scope in the official documentation.
Example :
builder.RegisterType<Service>().As<IService>().InstancePerMatchingLifetimeScope("KEY");
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
// ...
using (ILifetimeScope scope = container.BeginLifetimeScope("KEY"))
{
scope.Resolve<IService>(); // Instance #1
using (ILifetimeScope childScope = scope.BeginLifetimeScope())
{
childScope.Resolve<DbContext>();
childScope.Resolve<IService>(); // shared instance (#1)
}
}
but that's mean you have to change all your InstancePerLifetimeScope to InstancePerMatchingLifetimeScope and can control the creation of the unit of work lifetime scope which can be quite difficult.
Another way of doing this is by using Owned<T> with Func<T>. You can get more information here : Owned instance
builder.RegisterType<Service>().As<IService>().InstancePerLifetimeScope();
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
builder.RegisterType<Operation>().As<IOperation>().InstancePerLifetimeScope();
public class Operation : IOperation
{
public Operation(Func<Owned<DbContext>> contextFactory, IService service)
{
this._contextFactory = contextFactory;
this._service = service;
}
private readonly Func<Owned<DbContext>> _contextFactory;
private readonly IService _service;
public void Do()
{
using Owned<DbContext> context = this._contextFactory();
context.Value // => new instance
this._service // shared instance (#1)
}
}
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<IService>(); // Instance #1
IEnumerable<IOperation> operations = scope.Resolve<IEnumerable<IOperation>>();
operations.AsParallel()
.ForAll(operation => operation.Do());
}
The only downside of this solution is that your service will have dependency on Autofac but if you don't want it, it is quite easy to create your own abstraction over Owned
If you don't want to use Owned<T> or your own abstraction instead of trying to make DbContext a special case you can reverse the problem and manually share some dependency between your custom scope.
Something like :
using ILifetimeScope childScope = scope.BeginLifetimeScope(b => {
b.Register<XContext>(c => scope.Resolve<XContext>()).ExternallyOwned();
});
var operation = childScope.Resolve<IOperation>();
operation.Do();
This way IOperation would be resolved in a new scope but XContext will be from parent scope

Categories