Service Fabric DI implementation from Unity to Autofac and Child Scopes - autofac

I'm currently porting a Service Fabric DI implementation from Unity to Autofac and I need some advice.
Original Implementation:
https://github.com/s-innovations/S-Innovations.ServiceFabric.DependencyInjection
The Unity implementation late registers two dependencies inside the ActorFactory which and then uses the DI container inside the scope of the Actor.
I'm considering the best way to do this using Autofac.
I think I need to create a new ContainerBuilder, and register the two Actor Scoped dependencies using a named scope (maybe using the ActorId as the scope Id ?) so that the instances are scope to this particular Actor Instance.
This is what I have so far:
public static ILifetimeScope WithActor<TActor>(this Container container,
ActorServiceSettings settings = null) where TActor : ActorBase
{
ContainerBuilder builder = new ContainerBuilder();
if (!container.IsRegistered<IActorDeactivationInterception>())
{
builder.RegisterType<OnActorDeactivateInterceptor>()
.As<IActorDeactivationInterception>()
.EnableInterfaceInterceptors();
builder.Update(container);
}
builder.Register(c => ActorProxyTypeFactory.CreateType<TActor>())
.As(typeof(TActor))
.InstancePerLifetimeScope();
builder.Update(container);
ActorRuntime.RegisterActorAsync<TActor>((context, actorType) => {
try
{
return new ActorService(context,
actorTypeInfo: actorType,
actorFactory: (service, id) =>
// container.BeginLifetimeScope()
// .RegisterInstance(service, new ContainerControlledLifetimeManager())
// .RegisterInstance(id, new ContainerControlledLifetimeManager()).Resolve<TActor>(),
{
var actorBuilder = new
ContainerBuilder();
actorBuilder
.RegisterInstance(service)
.InstancePerDependency();
actorBuilder
.RegisterInstance(id)
.InstancePerDependency();
actorBuilder.Update(container);
return container.Resolve<TActor>();
}, settings: settings);
}
catch (Exception ex)
{
throw;
}
}).GetAwaiter().GetResult();
return container;
}
Any ideas, Comments most welcome! Anyone have a view on the performance impact of resolving the Actors and their dependencies through DI / this approach ?
The complete code is here:
https://github.com/Applicita/S-Innovations.ServiceFabric.DependencyInjection/tree/Autofac/src/Applicita.ServiceFabric.Autofac

Related

How to use AutoFac with .NET MAUI

A few years ago I created a suite of nuget packages that I use for CQRS which use AutoFac modules to wire up the internals. I'd like to use that in my .NET MAUI development so I've updated them to .NET 6.0 and they link in with my MAUI project nicely, but I'm uncertain what's missing from my registrations. My framework's AutoFac Module registers an IDateTimeService but when I add that to a registered class' constructor it can't be resolved.
So, following the AutoFac guide for .NET Core I've added the Populate call and then Load the AutoFac module.
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Pages;
using Perigee.Framework.Services;
using Services;
using ViewModels;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddSingleton<IAppNavigationService, AppNavigationService>();
builder.Services.AddSingleton<AppShellViewModel>();
builder.Services.AddTransient<MainPageViewModel>();
builder.Services.AddSingleton<AppShell>();
builder.Services.AddSingleton<MainPage>();
// Hook in AutoFac for the PerigeeFramework services
var autofacBuilder = new ContainerBuilder();
autofacBuilder.Populate(builder.Services);
autofacBuilder.RegisterModule(new ServicesModule());
autofacBuilder.Build(); // Invokes the Load method on the registered Modules.
return builder.Build();
}
}
The AutoFac Module starts like this:
public class ServicesModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c =>
{
var config = c.IsRegistered<IDateTimeConfig>() ? c.Resolve<IDateTimeConfig>() : null;
return new DateTimeService(config);
}).As<IDateTimeService>().InstancePerLifetimeScope();
and this is the definition of AppShellViewModel
public AppShellViewModel(IDateTimeService dateTimeService)
which is injected into the AppShell:
public partial class AppShell : Shell
{
public AppShell(AppShellViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
At run time the IDateTimeService doesn't resolve. I've also tried just registering with AutoFac without a module and it won't resolve:
// Hook in AutoFac for the PerigeeFramework services
var autofacBuilder = new ContainerBuilder();
autofacBuilder.Populate(builder.Services);
autofacBuilder.RegisterType<DateTimeService>().As<IDateTimeService>().SingleInstance();
var cont = autofacBuilder.Build();
return builder.Build();
}
The key reason I needed something other than .NET DI was because the architecture leverages decorators, which SimpleInjector and AutfoFac provide out of the box so I chose AutoFac. In either case I need to use this "crosswire" approach to use AutoFac and .NET DI as MAUI is using the built in one. Does anyone know what step I'm missing that is preventing the registrations from an AutoFac module from appearing in the IServiceCollection, or can I completely replace the .NET DI with AutoFac on the MauiApp?
EDIT:
I've put together a trimmed down version of my app. I figured maybe I need to pass a new AutoFacServiceProvider through to the App and the ISomeService does resolve when registered with AutoFac
But the call to MainPage = serviceProvider.GetService<AppShell>() fails to resolve if I try to inject ISomeService into another registered class. If the service is registered with the standard DI it will work.
Anyone know how to propogate the AutoFac Service Provider as the one Maui will use? The project is here
The MauiAppBuilder (called from the MauiProgram) has a method called ConfigureContainer, it takes an IServiceProvider factory that Autofac provides as AutofacServiceProviderFactory and optionally it can take an Action<ContainerBuilder> delegate, where you can define your configuration.
In your case that could look like this:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureContainer(new AutofacServiceProviderFactory(), autofacBuilder => {
// Registrations
// Don't call the autofacBuilder.Build() here - it is called behind the scenes
});
return builder.Build();
}
}

Time-driven lifetime scope in singleton ASP.NET Web API controller

Consider the web controller that implements some API by wrapping downstream service that requires token to be called. The token has the expiration, so I'm after some kind of time-driven scope that re-acquires the token and re-creates client in case the token is expired:
MyController: Controller
{
IServiceAPI _downstreamServcie;
MyController (IServiceAPI downstreamService)
{
}
}
....
builder.Register(c => {
Token token = generateToken() ..
return new ServiceAPIClient(token) ;
})
.As<IServiceAPI>()
I don't want to register MyController with per-request-scope because of performance issues.
Having spring background, such kind of captive dependency is resolved in spring by injecting singleton dynamic proxy that forwards the call to the right scoped-object (request/session/custom).
What would be the right way to implement the same with Autofac?
Thanks
[UPDATE]
Digging into Autofac documentation, I've found IResolveMiddleware interface that can be used to dynamically create/change scope :
class TokenScopeResolverMiddleware : IResolveMiddleware {
private ISharingLifetimeScope _currentTokenScope;
private ISharingLifetimeScope _prevTokenScope;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next) {
if (null == _currentTokenScope) {
lock (this) {
if (null == _currentTokenScope) {
RolloverScope(context);
}
}
}
if (!CanUseCurrentToken()) {
lock (this) {
if (!CanUseCurrentToken()) {
RolloverScope(context);
}
}
}
context.ChangeScope(_currentTokenScope);
next(context);
}
private bool CanUseCurrentToken() {
AuthenticationResult authResult = _currentTokenScope.Resolve<AuthenticationResult>();
TimeSpan expiresIn = authResult.ExpiresOn - DateTime.Now;
return expiresIn > TimeSpan.FromSeconds(20);
}
private void RolloverScope(ResolveRequestContext context) {
if (null != _prevTokenScope) {
_prevTokenScope.Dispose();
}
_prevTokenScope = _currentTokenScope; // give another `expiration time` grace period before disposing token scope
_currentTokenScope =
context.ActivationScope.RootLifetimeScope.BeginLifetimeScope("token") as ISharingLifetimeScope;
}
public PipelinePhase Phase { get; } = PipelinePhase.ScopeSelection;
}
Usage :
builder.Register(c => {
AuthenticationResult result = // acquire token
return result;
})
.InstancePerMatchingLifetimeScope("token");
builder.Register(c => {
return new Client(c.Resolve<AuthenticationResult>().Token)
})
.InstancePerMatchingLifetimeScope("token");
builder.RegisterServiceMiddleware<Client>(new TokenScopeResolverMiddleware());
Any better suggestions ?
I think you're likely looking for the Func<T> relationship, or something like it, where you inject a factory that dynamically resolves the client as you need it.
public class MyController
{
private readonly Func<IClient> _clientFactory;
public MyController(Func<IClient> clientFactory)
{
this._clientFactory = clientFactory;
}
public void DoWork()
{
var client = this._clientFactory();
client.CallApi();
}
}
Your lambda could be just about anything as long as it runs synchronously. Don't forget DI is more about injecting dependencies (object construction) than it is about managing your application's state, orchestrating logic, or executing factories on your behalf, though admittedly it's pretty convenient to try to multipurpose it in those ways.
var builder = new ContainerBuilder();
builder.Register(ctx =>
{
var token = GetOrRefreshToken();
return new Client(token);
}).As<IClient>();
A word of warning - you may run into memory leak trouble.
If the IClient implementation is also IDisposable, Autofac is going to hold onto every IClient created until the lifetime scope is disposed because the container is responsible for creating objects... and disposing them. If your controller is a singleton, that means the Func<IClient> will be resolving from the root lifetime scope (the container itself), which further means you can't dispose the captured IClient instances without disposing the whole application container.
You can disable that with ExternallyOwned but then you also will have to dispose things yourself.
It may be better to unwind things just a little and try to do less in DI, more with your own code. For example, actually create your own client factory that knows when to refresh the token, how to construct and dispose of clients, etc. You may even want to look at stuff like IHttpClientFactory which is specifically meant for stuff like this. Then instead of injecting the client, inject the factory and use the factory to get a client instance as you need it. That is, instead of injecting Func<IClient>, inject IHttpClientFactory or something similar, thus reducing the need to try to force the captive dependency to behave and instead addressing the challenge with a solution possibly more appropriate.

Adding Autofac to .NET core 6.0 using the new single file template

I am trying to add Autofac to a .Net 6.0 web API.
I'm using the latest ASP.NET Core Web API template that generates a single start-up Program.cs file.
Installed Autofac versions:
Autofac 6.3.0
Autofac.Extensions.DependancyInjection (7.2.0-preview.1)
Installed .Net 6.0 versions:
Microsoft.AspNetCore.App 6.0.0-rc.2.21480.10
Microsoft.NETCore.App 6.0.0-rc.2.21480.5
Microsoft.WindowsDesktop.App 6.0.0-rc.2.21501.6
Just in case of doubt, this is the entire content of the Program.cs file (yes, no namespaces or class definition. Only a Program.cs file and no StartUp class or Startup.cs file)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run()
I've tried looking through the latest Autofac documentation but the examples there, despite saying for .Net Core 3.x and later don't seem to fit with .Net 6.0 code. I can't figure out how to add Autofac into the middleware pipeline.
Any help is greatly appreciated.
Code snippet from Autofac website
public class Program
{
public static void Main(string[] args)
{
// ASP.NET Core 3.0+:
// The UseServiceProviderFactory call attaches the
// Autofac provider to the generic hosting mechanism.
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder => {
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}
Autofac documention:
https://docs.autofac.org/en/latest/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting
I found this.
https://learn.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-5.0
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Register services directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory.
builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new MyApplicationModule()));
var app = builder.Build();
At "Program.cs"
You'll find
var builder = WebApplication.CreateBuilder(args);
Add below
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new AutofacBusinessModule());
});
Answer above, I've assumed that you had everything else set up.
I'm using Autofac and Autofact.Extras.DynamicProxy
Sharing below my AutofacBusinessModule just for clarification.
public class AutofacBusinessModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ProductManager>().As<IProductService>().SingleInstance();
builder.RegisterType<EfProductDal>().As<IProductDal>().SingleInstance();
}
}
I have attached examples of both manual declarations and Reflection API of how to add Autofac to .NET Core 6.0
Call UseServiceProviderFactory on the Host sub-property
Call ConfigureContainer on the Host sub-property
Declare your services and their lifetime
Example of a manual services declaration
var builder = WebApplication.CreateBuilder(args);
// Call UseServiceProviderFactory on the Host sub property
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Call ConfigureContainer on the Host sub property
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
// Declare your services with proper lifetime
builder.RegisterType<AppLogger>().As<IAppLogger>().SingleInstance();
builder.RegisterType<DataAccess>().As<IDataAccess>().InstancePerLifetimeScope();
});
Example of Assembly scanning "Reflection API"
var builder = WebApplication.CreateBuilder(args);
// Call UseServiceProviderFactory on the Host sub property
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Call ConfigureContainer on the Host sub property
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterAssemblyTypes(Assembly.Load(nameof(DemoLibrary))).Where(t => t.Namespace?.Contains("Practicing.Services") == true)
.As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == "I" + t.Name));
});

Issue using AutofacWebApiDependencyResolver with OWIN and WebAPI 2.1

I can't seem to use the DependencyResolver in my OAuthAuthorizationServerProvider.
DependencyResolver.Current
returns the MVC one which I don't use, and
GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IXXX))
throws the following error:
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
Any ideas if I am doing something wrong or I simply can't use a dependency where I'm trying?
This is what my Startup.Auth.cs looks like:
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<XXX>().As<IXXX>().InstancePerRequest();
var container = builder.Build();
//I've tried both approached here!
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(webApiConfig);
app.UseWebApi(webApiConfig);
And this is my OAuth provider code:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public SimpleAuthorizationServerProvider(string publicClientId)
{
if (publicClientId == null)
throw new ArgumentNullException("publicClientId");
_publicClientId = publicClientId;
}
public IXXX XXX
{
get { return (IXXX)(_xxx??GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IXXX))); }
set { _xxx= value; }
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//Dependency IXXX used here
}
private readonly string _publicClientId;
private IXXX _xxx;
}
You can use OwinContext.GetAutofacLifetimeScope()
See the nuget package:
http://alexmg.com/owin-support-for-the-web-api-2-and-mvc-5-integrations-in-autofac/
Funnily enough I'm working through a similar problem at the minute and using the following OSS library to achieve this: https://github.com/DotNetDoodle/DotNetDoodle.Owin.Dependencies
This is an IoC container adapter for OWIN middleware, which puts a request level container into the environment dictionary of the OWIN middleware. The container can then be accessed from within your OWIN middleware implementation from which per-request scoped services can be resolved.
This is taken from the documentation from the github repository:
public override async Task Invoke(IOwinContext context)
{
IServiceProvider requestContainer = context.Environment.GetRequestContainer();
IRepository repository = requestContainer.GetService(typeof(IRepository)) as IRepository;
// use repos
}
The following additional links may be useful for you:
A gist discussing this issue: https://gist.github.com/tugberkugurlu/9054704. Specifically this comment: https://gist.github.com/tugberkugurlu/9054704#comment-1172998
The blog entry relating to the DotNetDoodle.Owin.Dependencies library: http://www.tugberkugurlu.com/archive/owin-dependencies--an-ioc-container-adapter-into-owin-pipeline
Hope some of this may be of help to you.

Converting From Castle Windsor To StructureMap In An MVC2 Project

I am learning about best practices in MVC2 and I am knocking off a copy of the "Who Can Help Me" project (http://whocanhelpme.codeplex.com/) off Codeplex. In it, they use Castle Windsor for their DI container. One "learning" task I am trying to do is convert this subsystem in this project to use StructureMap.
Basically, at Application_Start(), the code news up a Windsor container. Then, it goes through multiple assemblies, using MEF, in ComponentRegistrar.cs:
public static class ComponentRegistrar
{
public static void Register(IContainer container)
{
var catalog = new CatalogBuilder()
.ForAssembly(typeof(IComponentRegistrarMarker).Assembly)
.ForMvcAssembly(Assembly.GetExecutingAssembly())
.ForMvcAssembliesInDirectory(HttpRuntime.BinDirectory, "CPOP*.dll") // Won't work in Partial trust
.Build();
var compositionContainer = new CompositionContainer(catalog);
compositionContainer
.GetExports<IComponentRegistrar>()
.Each(e => e.Value.Register(container));
}
}
and any class in any assembly that has an IComponentRegistrar interface will get its Register() method run.
For example, the controller registrar's Register() method implementation basically is:
public void Register(IContainer container)
{
Assembly.GetAssembly(typeof(ControllersRegistrarMarker)).GetExportedTypes()
.Where(IsController)
.Each(type => container.AddComponentLifeStyle(
type.Name.ToLower(),
type,
LifestyleType.Transient ));
}
private static bool IsController(Type type)
{
return typeof(IController).IsAssignableFrom(type);
}
Hopefully, I am not butchering WCHM too much. I am wondering how does one do this with StructureMap? I'm assuming that I use Configure() since Initialize() resets the container on each call? Or, is tit a completely different approach? Do I need the MEF-based assembly scan, used to find all registrars and run each Register(), or is there something similar in StructureMap's Scan()?
Have a look at StructureMap's registries (http://structuremap.github.com/structuremap/RegistryDSL.htm). To control the lifecycle use something like:
For<ISomething>().Use<Something>().LifecycleIs(new SingletonLifecycle());
(Transient is the default).
When you bootstrap the container you can say:
ObjectFactory.Initialize(c => c.Scan(s => {
s.WithDefaultConventions();
s.LookForRegistries();
}
Feel dirty, answering my own question, but I did the following:
public class ControllerRegistrar : IComponentRegistrar
{
public void Register(IContainer container)
{
container.Configure(x =>
{
x.Scan(scanner =>
{
scanner.Assembly(Assembly.GetExecutingAssembly());
scanner.AddAllTypesOf<IController>().NameBy(type => type.Name.Replace("Controller", ""));
});
});
}
}
I am not 100% sure this is right, but it works. Pulled it primarily from the "Registering Types by Name" section of this StructureMap doc page.