Health check for AddDbContextCheck by TContextService and TContextImplementation - c#

I'm setup my DBContext by TContextService and TContextImplementation options.
Something like this:
services.AddDbContext<IMyDbContext, MyDbContext>(options => options.UseNpgsql(myDatabaseConnectionString));
I try to enable health check from Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
When I try to use this code
services.AddHealthChecks().AddDbContextCheck<MyDbContext>()
I got from health end point response that
InvalidOperationException: Unable to resolve service for type 'MyDbContext' while attempting to activate 'Microsoft.Extensions.Diagnostics.HealthChecks.DbContextHealthCheck`1[MyDbContext]'.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities+ConstructorMatcher.CreateInstance(IServiceProvider provider)
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, object[] parameters)
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(IServiceProvider provider)
Do you know any method to use two options TContextService and TContextImplementation for health check by Entity Framework?

You seem to have hit a case where the two cannot work together. I think you have two options:
First is to register the DbContext directly and then just resolve the interface from this registration.
services.AddDbContext<MyDbContext>(options => options.UseNpgsql(myDatabaseConnectionString));
services.AddTransient<IMyDbContext>(c=>c.GetRequiredService<MyDbContext>());
Another option is to implement the health check functionality yourself. Which is not exactly hard.
See : EntityFrameworkCoreHealthChecksBuilderExtensions.cs and DbContextHealthCheck.cs

Related

How can Autofac and an Options pattern be used in a .NET 5 console application?

I am trying to use an option pattern with Autofac and every attempt has just resulted in errors.
What I've tried:
Using the ConfigurationBuilder to retrieve an IConfiguration/IConfigurationRoot.
Register an instance of TestSectionOptions using the IConfiguration/IConfigurationRoot that was created before:
builder.Register(c => config.GetSection("TestSection").Get<TestSectionOptions>());
Trying to inject it via constructor injection:
private readonly TestSectionOptions _options;
public DemoClass(IOptions<TestSectionOptions> options)
{
_options = options.Value;
}
I'm getting following error:
DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'DemoApp.DemoClass' can be invoked with the available services and parameters:
Cannot resolve parameter
'Microsoft.Extensions.Options.IOptions1[DemoApp.TestSectionOptions] options' of constructor 'Void .ctor(Microsoft.Extensions.Options.IOptions1
Of course I tried other types of registration, but none of them worked.
I also know that I can simply bind the configuration file to a class, which I then register and inject without the IOptions<> part. But that would no longer correspond exactly to the option pattern, would it?
Even if it doesn't make a big difference, I'd still like to know why it doesn't work and how I could get it to work.
The problem is that this IOptions type should be registerd somewhere.
You can see e.g. this article. There is an example
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
So, somewhere inside Configure extension method it registers types for options, among others IOptions<>.
So, in your case you either have to do this explicitly, like
builder.Register(c => Options.Create(config.GetSection("TestSection").Get<TestSectionOptions>()))
This will register IOptions
or, you can create an empty service collection, then call Configure method on it, and then copy all registrations to autofac builder - there is Populate method from the package "Autofac.Extensions.DependencyInjection"
https://autofac.org/apidoc/html/B3162450.htm

How to Implement DbContextOptionsBuilder in .NET Core

I am new to ASP.Net Core and I am trying to implement ASP.NET Core DI.
I configured like below in ConfigureServices Method in Startup.cs
services.AddScoped<DbContext, AutomationDbContext>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IUserService, UserService>();
In UserService Constructor, I am trying to use DI. I think below is NOT the right way to implement this.
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext, IConfiguration configuration)
{
this.configuration = configuration;
this.optionsBuilder = new DbContextOptionsBuilder<AutomationDbContext>();
var connectionString = this.configuration.GetConnectionString("Automation");
this.optionsBuilder.UseSqlServer(connectionString);
this.automationDbContext = new AutomationDbContext(this.optionsBuilder.Options);
this.httpContext = httpContextAccessor.HttpContext;
}
I don't like building optionsbuilder in constructor and get connectionstring.
What would be the better place to build these optionsBuilder and pass in constructor.
You need to use services.AddDbContext<TContext> instead:
services.AddDbContext<AutomationDbContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Automation")));
Then, just inject your context:
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext)
As for IHttpContextAccessor, you should simply use:
services.AddHttpContextAccessor();
However, I would encourage you to strongly consider whether you actually need this in your service or not. If you need something like the current user's id, that should be passed into the method that needs it, not retrieved from within your service.
UPDATE
Since it was brought up, let me elucidate the reasons why adding your context in the way you currently are is incorrect, since it will shed a little light on how DI works in general.
First, you're binding DbContext directly to AutomationDbContext, which means you can then only use that one context. Maybe you don't need more than one context... now. That could change later. Second, when you register a service in that way, you can only inject the abstract type, i.e. DbContext here. The service registration literally means "when you see DbContext, inject an instance of AutomationDbContext". If you try to inject AutomationDbContext directly, as you're currently doing in your controller, that will actually throw an exception because that type is not actually registered as service: DbContext is. Third, AddScoped provides no real ability to configure the context, which is of course the part your were missing. There's ways to work around this such as using the factory overload of AddScoped or defining OnConfiguring on your context, but both of those are substandard to just using the right method in the first place: AddDbContext<TContext>
For what it's worth, there's also somewhat of a fourth reason, in that you can opt to use AddDbContextPool<TContext> instead of AddDbContext<TContext>, for connection pooling. There's no other way to set that up, so if you did want/need connection pooling, you'll never get there with AddScoped.

Switch database using routes

Here's my scenario:
I Have a single app, but I need to switch the database connection by route.
Example:
switch(route)
{
case(URL/A):
{
USE DATABASE 1
}
case(URL/B):
{
USE DATABASE 2
}
DEFAULT:
USE DATABASE DEFAULT
}
Is it possible?
Since you're using ASP.NET MVC, your routes depends on your controllers. Then you can imagine having ControllerA using DatabaseA and ControllerB using DatabaseB.
To use multiple database connections, you need a connection string for each one of them.
I would use the following pieces of code to inject instances of DbContextOptionsBuilder inside of Startup.ConfigureServices()
var ContextAOptionsBuilder = new DbContextOptionsBuilder<ContextA>();
var ContextBOptionsBuilder = new DbContextOptionsBuilder<ContextB>();
Then you can configure your builders this way (depending on your parameters)
ContextAOptionsBuilder.UseSqlServer(Configuration.GetConnectionString("ContextAConnectionString"), builder =>
{
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null);
});
ContextAOptionsBuilder.EnableSensitiveDataLogging();
Then you can inject them as singletons this way :
services.AddSingleton(typeof(DbContextOptionsBuilder<ContextA>),ContextAOptionsBuilder);
You can use a BaseController, whose constructor parameters can access to services this way :
public BaseController(IConfiguration configuration, IMemoryCache memoryCache,
IHttpContextAccessor contextAccessor,
DbContextOptionsBuilder<ContextA> ContextAOptionsBuilder,
DbContextOptionsBuilder<ContextB> ContextBOptionsBuilder){}
Of course, ControllerA and ControllerB being heir classes of BaseController, you can access desired builder quite simply.
public ControllerA(IConfiguration configuration,
IMemoryCache cache,
IHttpContextAccessor contextAccessor,
DbContextOptionsBuilder<ContextA> ContextAOptionsBuilder,
DbContextOptionsBuilder<ContextB> ContextBOptionsBuilder)
:base(configuration, cache, contextAccessor, ContextAOptionsBuilder,ContextBOptionsBuilder)
{
//Create your DbContext using the builder
}
This way you can use one, the other, or both database to build your context
A simpler way would have been injecting your configuration file and building your context from it's content but ppumkin's comment suggested it's a bad idea to do this at a controller level.
This solution is working for me in an ASP.NET Core MVC application, I am still learning the framework but maybe my answer gave you precisions about multiple DbContexts.
You can create 3 connection string also 3 data access Classes. First of your class uses for example DropCreateDatabaseIfModelChanges others use CreateDatabaseIfNotExists. When you call first class your database creates when you need others there will no need recreate it.
Register your context (as scoped, per request) and use factory method for dynamically creating context with specified connection string based on current route (which should be available from HttpContext or something similar). If the databases schemas are same and just data is different this should work easily. I can't provide a snippet for you because it's mostly depends on what DI framework you have.

How to inject arguments when using lazy types?

I have a Translation class that takes in ITranslationService as its argument. How do I inject translation service when registering the Lazy<Translation> type? This is what I've done so far, but no luck.
public class Translation
{
public Translation(ITranslationService translationService)
{
// code here
}
}
container.RegisterType<ITranslationService, TranslationService>();
container.RegisterType<Lazy<Translation>>(new InjectionConstructor(typeof(ITranslationService)));
Error message when trying to resolve Lazy<Translation> type:
The lazily-initialized type does not have a public, parameterless
constructor
First of all, Unity 3 now supports resolving Lazy<T>(See What's New section), so you don't need to do anything special, just register ITranslationService and you will be able to resolve Lazy<Translation>.
So the following only applies to Unity 2.
You can install this nuget extension from Piotr Wlodek. You will then need to enable it using:
container.AddNewExtension<LazySupportExtension>();
You will then be able to resolve Lazy<T> objects:
var lazy = container.Resolve<Lazy<Translation>>();
And the actual Translation object will not be constructed until you call lazy.Value.
If neither getting Unity3 nor that extension is an option, you could still try to manually configure Unity2 and resolve those objects.
In your case, you need to use one of the constructors in Lazy<T> that receives a Func<T> as parameter. (That is a function with no parameters that returns an instance of T, or Translation in your case).
You can use an InjectionFactory when registering Lazy<Translation> in Unity, which is a factory method that constructs a Lazy<Translation> object. The lazy object will receive in its constructor an initialization function that uses Unity to resolve the Translation:
container.RegisterType<Lazy<Translation>>(
new InjectionFactory(c => new Lazy<Translation>(() => c.Resolve<Translation>()) ));
Unity supports Lazy<T>, but you have to configure it:
unityContainer.AddNewExtension<LazySupportExtension>();
then don't do
container.RegisterType<Lazy<Translation>(new InjectionConstructor(typeof(ITranslationService)));
but instead do:
container.RegisterType<Translation>();
And use it as follows:
unityContainer.Resolve<Lazy<Translation>>();
For any one using MVC or Web API projects, just install the relevant Unity Bootstrapper Nuget package i.e Unity.AspNet.WebApi for Web API, and Unity.Mvc for MVC.
And then register your types as you normally would, either in code or through a config file. The Lazy instances will be injected automatically
private Lazy<IMapService> _mapService;
public HomeController(Lazy<IMapService> mapService)
{
//Lazy instance is injected automatically.
_mapService = mapService
}

ServiceStack multiple implementations of same interface in Funq.Container

In ServiceStack application, I have Funq configured to inject a session per request like this:
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB1);
container.Register<NHibernate.ISession>(c => c.Resolve<NHibernate.ISessionFactory>()
.OpenSession())
.ReusedWithin(Funq.ReuseScope.Request);
My service looks like this, and it works just fine:
public class MyNhAwareService : Service
{
public ISession Session { get; set; }
public object Any(DoSomething request)
{
...
}
}
Now, the problem comes in when I want to add a second NHibernate database into the mix with its own session factory:
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB1);
container.Register<NHibernate.ISession>(c => c.Resolve<NHibernate.ISessionFactory>()
.OpenSession())
.ReusedWithin(Funq.ReuseScope.Request);
// add a different session factory
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB2);
I've been experimenting with a variety of ways Funq can be used, and I thought I had found the way forward when I discovered the 'RegisterNamed()" method, but that still doesn't help, as I can't use anything except TryResolve() from within my service.
This seems like it should be possible, but I'm beating my head against the wall trying to work it out...Any advice would be greatly appreciated.
You have a couple ways of going about this.
Option 1: Unique Interfaces
This option is to create a distinct interface for each NHibernate database so that they can be uniquely resolved by Funq.
For example:
interface FactoryA : NHibernate.ISessionFactory
{
}
interface FactoryB : NHibernate.ISessionFactory
{
}
You could then proceed as you are now. The same applies for the session. See here for a little more detail about the process:
How to register multiple IDbConnectionFactory instances using Funq in ServiceStack.net
Option 2: Named Instance
This option I am less familiar with, but you can name your instances using Funq:
container.Register<NHibernate.ISessionFactory>("FactoryA",sessionFactoryForDB1);
And then in your service, resolve it thusly:
ServiceStackHost.Instance.Container.ResolveNamed<NHibernate.ISessionFactory>("FactoryA");
This option uses Service Location, which I personally find less attractive.

Categories

Resources