Azure Stateful Service - with remoting and custom singleton service - c#

I would like to use remoting in a .net core stateful service. I have a custom class which needs to be added as a singleton. Is this possible?
First I tried to register the custom class in Startup.cs's ConfigureServices() method but then I realized this method will be never called, since I used return this.CreateServiceRemotingReplicaListeners(); to generate my replica listeners in the CreateServiceReplicaListeners() method, and removed the Kestrel configuration (which would make this method to be called).
Is there a way to make the Startup.cs's ConfigureServices() method to be called, or add the singleton service at another place while keeping the remoting configuration?
The CreateServiceReplicaListeners() method in the MyStefulService.cs class looks like the following:
protected override IEnumerable<ServiceReplicaListener>
CreateServiceReplicaListeners()
{
return this.CreateServiceRemotingReplicaListeners();
}
The ConfigureServices method in the Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyHandler>(x => new MyHandler());
}

Finally i found a solution to my problem: I used Autofac to make sure the class I register is the same instance wherever it is used.
I extended the Program.cs with the Autofac container, so this way I didn't need the Startup.cs class at all:
I defined static variable of my custom class and an Autofac container, then in Main() method I added the implementation:
public static IContainer AutofacContainer;
private static IMyHandler _handler;
private static void Main()
{
try
{
if (_autofacContainer == null)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyHandler>()
.As<IMyHandler>()
.SingleInstance();
_autofacContainer = builder.Build();
_handler = autofacContainer.Resolve<IMyHandler>();
}
//[...] normal service registration continues here
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
Since the container is static and public, all the other classes in the project can access it and get the singleton instance.
Configuration, environment variables, etc. can also be configured from here, similarly to the Startup.cs.

Related

Use IServiceCollection extensions with DryIoc

There are many extensions for the IServiceCollection - in my case I use AddHttpClient.
My scenario is that I register general stuff in the ConfigureServices method in the Startup.cs where IServiceCollection is used to register services. Everything that is needed only in specific projects is registered in an extension method in the respective project, but there the DryIoc IContainer is used due to how the DryIoc container must be integrated in an ASP .NET Core project.
Now I have a HttpClient that I only need in a specific project. Therefore I would like to put the registration for it in the respective project. Problem is I want to use AddHttpClient for it which I normally can only use with IServiceCollection.
My question: Is there any way to use it in my other project. Maybe by getting it from the DryIoc container or something else.
This is the general structure of the described files:
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.RegisterSomeService();
// register other stuff
}
}
Program.cs
public class Startup
{
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseServiceProviderFactory(new DryIocServiceProviderFactory())
.ConfigureContainer<Container>(SomeProject.ContainerSetup.Initialize);
}
ContainerSetup.cs in SomeProject
public static class ContainerSetup
{
public static void Initialize(HostBuilderContext hostContext, IContainer container)
{
container.Register<SomeService>();
// register other stuff
// here I want to use AddHttpClient
}
}
I was able to solve the problem by using the IContainer extension Populate which is part of DryIoc.Microsoft.DependencyInjection.
With it I edited my ContainerSetup.cs as follows
public static class ContainerSetup
{
public static void Initialize(HostBuilderContext hostContext, IContainer container)
{
var services = new ServiceCollection();
services.AddHttpClient<MyTypedClient>()
.Configure[...];
container.Populate(services); // with this call all services registered above will be registered in the container
// register other stuff if necessary
container.Register<SomeService>();
}
}
I suggest to look inside the AddHttpClient https://source.dot.net/#Microsoft.Extensions.Http/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs,72bc67c4aadb77fc
and maybe make the same registrations with IContainer instead of the service collection.
Update:
Another idea is to register IServiceCollection into itself (or maybe it is already automatically registered?), then resolve it from IContainer and AddHttpClient..

Initialize class as part of DI

I am writing a new ASP.NET Core Application and I am using the inbuilt DI Framework.
I have a service that I need to run an Initaliaze method as part of the DI - is this possible with the in built Framework DI?
I have done something like this before with Simple Injector using the following code:
container.RegisterInitializer<MyService>(instance =>
{
instance.Initialize("Parameter to Initialize method");
});
I am registering most of my service in the .NET Core as below:
public static void RegisterServiceDependencies(this IServiceCollection services)
{
services.AddTransient<IServiceA, ServiceA>();
services.AddTransient<IServiceB, ServiceB>();
//etc etc
However looking at the services intellisense I don't see anything like RegisterInitializer.
Something like this?
public static void RegisterServiceDependencies(this IServiceCollection services)
{
services.AddTransient(sp =>
{
var instance = sp.GetService<MyClass>(); /* depends on your type */
instance.Initialize("Parameter to Initialize method");
return instance;
});
});

How to start an injected service before a call has been made to the API?

In ASP.NET Core I have a service that prepare for injection in startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IService, Service>();
services.AddMvc();
}
If I make a call to the API, it will start up.
However, I'd like it to initialize before hand (when the app starts).
I'm simply putting a breakpoint in the controller to see if that happens.
The AddSingleton method can take either a type or an object to give. So you can simply do:
public void ConfigureServices(IServiceCollection services)
{
var serviceInstance = new Service();
serviceInstance.DoWhatever();
services.AddSingleton<IService, serviceInstance>();
services.AddMvc();
}
ASP.NET Core apps use a Startup class, which is named Startup by convention. The Startup class:
Can optionally include a ConfigureServices method to configure the app's services.
Must include a Configure method to create the app's request processing pipeline.
ConfigureServices and Configure are called by the runtime when the app starts:
public class Startup
{
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
}
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
...
}
}
Specify the Startup class with the WebHostBuilderExtensions UseStartup method:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
**.UseStartup<Startup>();**
}
So if you want to cal something at start the project in Startup put your method in Confirgure
GoodLuck.
you could try placing your initialization logic in the constructor so you don't need to worry about calling the "proper method". this will also help you in the case when your service is dependent on another one in order to function properly. long live dependency injection :)

Use custom IServiceProvider implementation in asp.net core

I have implemented an adapter that implement IServiceProvider and returned it from the ConfigureServices method in the Startup. class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var kernel = new StandardKernel();
var container = new NinjectComponentContainer(kernel);
// ...
return ServiceProviderFactory.Create(container, services);
}
However, my implementation doesn't seem to be used everywhere. I even tried to override the IHttpContextAccessor to return a modified HttpContext:
public HttpContext HttpContext {
get
{
var result = _httpContextAccessor.HttpContext;
result.RequestServices = _serviceProvider;
return result;
}
set => _httpContextAccessor.HttpContext = value;
}
To test whether I could get to my implementation I used a filter in order to see what the HttpContext.RequestServices would return:
public class AuthorizationTestAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var service = context.HttpContext.RequestServices.GetService(typeof(IAccessConfiguration));
}
}
The type returned by context.HttpContext.RequestServices is:
My main issue was trying to get registered components resolved in the constructor of a filter but it always seems to fail saying the component is not registered. However it does seem to work when using the TypeFilter attribute:
[TypeFilter(typeof(RequiresSessionAttribute))]
However, my attribute does inherit from TypeFilter:
public class RequiresSessionAttribute : TypeFilterAttribute
{
public RequiresSessionAttribute() : base(typeof(RequiresSession))
{
Arguments = new object[] { };
}
private class RequiresSession : IAuthorizationFilter
{
private readonly IAccessConfiguration _configuration;
private readonly IDatabaseContextFactory _databaseContextFactory;
private readonly ISessionQuery _sessionQuery;
public RequiresSession(IAccessConfiguration configuration,
IDatabaseContextFactory databaseContextFactory, ISessionQuery sessionQuery)
{
Guard.AgainstNull(configuration, nameof(configuration));
Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory));
Guard.AgainstNull(sessionQuery, nameof(sessionQuery));
_configuration = configuration;
_databaseContextFactory = databaseContextFactory;
_sessionQuery = sessionQuery;
}
I did come across this question but there is no definitive answer.
Any ideas on how to correctly provider a custom implementation of the IServiceProvider interface that will be used throughout the solution?
Even though Microsoft states that it is possible to replace the built-in container it appears as though it is not quite this simple, or even possible.
As stated by Steven in his very first comment, if you choose to use your container of choice, you should run them side-by-side.
The guidance from Microsoft suggests changing the ConfigureServices in the Startup class from this:
public void ConfigureServices(IServiceCollection services)
{
// registrations into services
}
to the following:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var container = new YourContainer(); // Castle, Ninject, etc.
// registrations into container
return new YourContainerAdapter(container);
}
However, there are a number of issues with this since there are already framework registrations in services that we do not necessarily know how to re-register in our own container. Well, there is a descriptor so if our container supports all the various methods then it is actually possible to re-register all the components. The various DI containers have different mechanisms when it comes to registration and service resolution. Some of them have a very hard distinction between the two making it quite tricky at times to accommodate a "common" solution.
My initial idea was to provide an adapter that accepts both my own container as well as the services collection from which I would then get the built-in service provider by calling services.BuildServiceProvider(). In this way I could attempt to resolve from the built-in provider and then, if the resolving bit failed, attempt to resolve from my own container. However, it turns out that the .net core implementation does in fact not use the returned IServiceProvder instance.
The only way I could get this to work was to wire up my own container and use it to resolve my controllers. That could be done by providing an implementation of the IControllerActivator interface.
In this particular implementation I was fiddling with Ninject although I typically prefer Castle but the same applies to any DI container:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKernel>(new StandardKernel());
services.AddSingleton<IControllerActivator, ControllerActivator>();
}
public class ControllerActivator : IControllerActivator
{
private readonly IKernel _kernel;
public ControllerActivator(IKernel kernel)
{
Guard.AgainstNull(kernel, nameof(kernel));
_kernel = kernel;
}
public object Create(ControllerContext context)
{
return _kernel.Get(context.ActionDescriptor.ControllerTypeInfo.AsType());
}
public void Release(ControllerContext context, object controller)
{
_kernel.Release(controller);
}
}
In order to register the controller types I did my DI wiring in the Configure method since I have access to the IApplicationBuilder which can be used to get to the controller types:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime)
{
var kernel = app.ApplicationServices.GetService<IKernel>();
// kernel registrations
var applicationPartManager = app.ApplicationServices.GetRequiredService<ApplicationPartManager>();
var controllerFeature = new ControllerFeature();
applicationPartManager.PopulateFeature(controllerFeature);
foreach (var type in controllerFeature.Controllers.Select(t => t.AsType()))
{
kernel.Bind(type).ToSelf().InTransientScope();
}
applicationLifetime.ApplicationStopping.Register(OnShutdown);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(
options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
);
app.UseMvc();
}
This worked swimmingly for the controllers but resolving "filters" was still a problem given that they use the IFilterFactory on the filter itself to implement a factory method:
public IFilterMetadata CreateInstance (IServiceProvider serviceProvider);
Here we can see that the IServiceProvider implementation is provided in order to resolve any depedencies. This applies when using the TypeFilterAttribute or when defining new filters that inherit from TypeFilterAttribute as I have in my question.
This mechanism is actually a very good example of the difference between "Inversion of Control" and "Dependency Injection". The control lies with the framework (inversion) and we have to provide the relevant implementations. The only issue here is that we are not able to hook in properly since our provided IServiceProvider instance is not passed to the CreateInstance method which then results in a failure when attempting to create an instance of the filter. There are going to be a number of ways to fix this design but we'll leave that to Microsoft.
In order to get my filters working I decided to go the "cross-wiring" route as alluded to by Steven by simply registering the depedencies required by my filters in the services collection also:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKernel>(new StandardKernel());
services.AddSingleton<IControllerActivator, ControllerActivator>();
services.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>();
services.AddSingleton<IDatabaseGateway, DatabaseGateway>();
services.AddSingleton<IDatabaseContextCache, ContextDatabaseContextCache>();
// and so on
}
Since I do not have many dependencies in my filter it works out OK. This does mean that we have "duplicate" registrations that we need to be careful of depending on how the instances are used.
I guess another option may be to forego your DI container of choice and use only the built-in container.

How rebind service in TestHost

I am preparing environment tests for my application. And I have problem how can i rebind earlier registred services in my startup class?.
I am using TestHost in my tests and this is how looks my base class
public abstract class IntegrationTestBase : IDisposable
{
private readonly TestServer _server;
public IntegrationTestBase()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<Startup>();
_server = new TestServer(webHostBuilder);
}
public HttpClient CreateClient()
{
return _server.CreateClient();
}
public virtual void Dispose()
{
_server.Dispose();
}
}
The way I've been handling overriding the registration for integration tests is:
Make the ConfigureServices method on your Startup class virtual.
Create a derived class of Startup: e.g: IntegrationTestStartup
Override ConfigureService like:
override ConfigureServices(IServiceCollection s)
{
base.ConfigureServices(s);
s.AddSingleton<IService, IMockService>();
}
Since the last registration you make of a service is the one provided by the DI container when a constructor injection happens, this works well.
In case you depend on IEnumerable<IService>, all components registered for that service will be resolved.
If you can't make your ConfigureServices from the Startup class virtual I assume using new would do the job since the WebHostBuilder creates an instance of the type passed to it.
Now you can build your WebHostBuilder with:
var webHostBuilder = new WebHostBuilder()
.UseStartup<IntegrationTestStartup>();
Does this help or could you be more specific what kind of 'rebind' do you need?

Categories