Autofac Property Injection in Nancy Module - c#

I am using Autofac for DI and i have NacyModule like:
public class TestModule: NancyModule
{
public ISessionFactory SessionFactory { get; set; }
public IMapper Mapper { get; set; }
public TestModule(ITestRepository testRepository)
{
Get("hello", _ => "hello world");
}
}
My AutoFac configuration
In Startup.cs
var builder = new ContainerBuilder();
builder.RegisterModule(new ServicesModule());
builder.RegisterModule(new NHibernateModule(configuration.GetConnectionString("DefaultConnection")));
builder.RegisterModule(new AutomapperModule());
builder.Populate(services);
container = builder.Build();
return new AutofacServiceProvider(container);
in ServiceModule.cs
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(t => new[]
{
"Processor",
"Process",
"Checker",
"Indexer",
"Searcher",
"Translator",
"Mapper",
"Exporter",
"Repository" }.Any(y =>
{
var a = t.Name;
return a.EndsWith(y);
}))
.AsSelf()
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerLifetimeScope();
in NHibernateModule.cs
builder.Register(c => CreateConfiguration(connectionString)).SingleInstance();
builder.Register(c => c.Resolve<Configuration>().BuildSessionFactory()).As<ISessionFactory>().SingleInstance().PropertiesAutowired();
And in my nancy bootstraper I have something like this
public class Bootstrapper : AutofacNancyBootstrapper
{
private static readonly ILogger logger = LogManager.GetLogger(typeof(Bootstrapper).FullName);
private readonly ILifetimeScope _container;
public Bootstrapper(ILifetimeScope container)
{
_container = container;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _container;
}
public override void Configure(INancyEnvironment environment)
{
base.Configure(environment);
environment.Tracing(false, true);
}
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
container.Update(builder =>
{
builder.Register(c =>
{
var sf = c.Resolve<ISessionFactory>();
return new Lazy<NHibernate.ISession>(() =>
{
var s = sf.OpenSession();
s.BeginTransaction();
return s;
});
}).InstancePerLifetimeScope();
builder.Register(c => c.Resolve<Lazy<NHibernate.ISession>>().Value).As<NHibernate.ISession>();
});
}
}
I now about constructor injection, works ok, and property injection works ok in other classes, but not works in nancy modules
Note I tried adding .PropertiesAutowired() in ConfigureRequestContainer after the container update
thanks.

The AutofacNancyBootstrapper class automatically register the module in Autofac even if the service is already registered :
AutofacNancyBootstrapper.cs
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
return container.Update(builder => builder.RegisterType(moduleType)
.As<INancyModule>())
.Resolve<INancyModule>();
}
With the default implementation the module is always registered and PropertiesAutoWired is not applied.
To change this, you can override the method like this :
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
return container.Update(builder => builder.RegisterType(moduleType)
.As<INancyModule>())
.Resolve<INancyModule>()
.PropertiesAutoWired();
}
Or change it like this :
protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
{
INancyModule module = null;
if (container.IsRegistered(moduleType))
{
module = container.Resolve(moduleType) as INancyModule;
}
else
{
IEnumerable<IComponentRegistration> registrations = container.ComponentRegistry.RegistrationsFor(new TypedService(typeof(INancyModule)));
IComponentRegistration registration = registrations.FirstOrDefault(r => r.Activator.LimitType == moduleType);
if (registration != null)
{
module = container.ResolveComponent(registration, Enumerable.Empty<Parameter>()) as INancyModule;
}
else
{
module = base.GetModule(container, moduleType);
}
}
return module;
}
and then register the module in your composition root
builder.RegisterType<TestModule>()
.As<INancyModule>()
.PropertiesAutoWired()

Related

How do I register modules with constructors in autofac?

How do I register modules with constructors in autofac?
I have the following modules:
public class MediatRModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterMediatR(typeof(ApplicationAssemblyMarker).Assembly);
}
}
public class PersistenceModule : Module
{
public string ConnectionString { get; }
public PersistenceModule(string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(connectionString));
ConnectionString = connectionString;
}
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SqlConnectionFactory>()
.As<ISqlConnectionFactory>()
.WithParameter("connectionString", ConnectionString)
.InstancePerLifetimeScope();
builder
.RegisterGeneric(typeof(AsyncRepository<>))
.As(typeof(IAsyncRepository<>))
.InstancePerLifetimeScope();
}
}
PersistenceModule it contains a constructor and I can't register all modules via RegisterAssemblyModules in the startup method.
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder
.RegisterModule(new PersistenceModule(Configuration.GetConnectionString("DefaultConnection")));
containerBuilder
.RegisterModule(new DomainModule());
containerBuilder
.RegisterModule(new MediatRModule());
containerBuilder
.RegisterModule(new CommandsModule());
containerBuilder
.RegisterModule(new QueriesModule());
}
You cannot have dependencies in your Module constructor. But you can provide a value for a parameter by resolving it from the container:
builder.RegisterType<SqlConnectionFactory>()
.As<ISqlConnectionFactory>()
.WithParameter(
// match parameter
(info, context) => info.Name == "connectionString",
// resolve value
(info, context) =>
{
var config = context.Resolve<Microsoft.Extensions.Configuration.IConfiguration>();
return config.GetConnectionString("DefaultConnection");
}
);
References
https://autofac.readthedocs.io/en/latest/register/parameters.html?highlight=withparameter#parameters-with-reflection-components
If the module takes a constructor parameter you can't auto-register it. You have to provide the value in a registration directly.

Change Autofac container to SimpleContainer (Caliburn.Micro)

I would like to switch from Autofac to SimpleContainer (Caliburn.Micro) due the really cool WindowManager function and a I would like to use Caliburn.Micro.
Right now my Autofac stuff looks like this:
Configure
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbContext>().As<IDbContext>();
builder.RegisterType<DepartmentRepository>().As<IDepartmentRepository>();
return builder.Build();
}
Usage
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var test = scope.Resolve<IDepartmentRepository>();
dept = test.GetById(DepartmentFK);
}
I already created a new Bootstrapper file
#region SimpleContainer Creation, Configuration and Registration
private SimpleContainer _container = new SimpleContainer();
protected override void Configure()
{
_container.Instance(_container);
_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IEventAggregator, EventAggregator>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
#endregion
How would the using stuff (scope.resolve<>) look like using SimleContainer instead of Autofac?

Get the HubContext using SimpleInjector

This guide does not appear to work for SimpleInjector.
My OWIN startup looks like this:
container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
new HubConfiguration()
{
EnableDetailedErrors = true,
Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
});
container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => GlobalHost.ConnectionManager);
container.Verify();
GlobalHost.DependencyResolver = container.GetInstance<SimpleInjectorSignalRDependencyResolver>();
app.Use(async (context, next) =>
{
using (container.BeginExecutionContextScope())
{
await next();
}
});
app.MapSignalR(container.GetInstance<HubConfiguration>());
And The HubContextAdapter looks like this:
public class HubContextAdapter<THub, TClient>
where THub : Hub
where TClient : class
{
private readonly IConnectionManager _manager;
public HubContextAdapter(IConnectionManager manager)
{
_manager = manager;
}
public IHubContext<TClient> GetHubContext()
{
return _manager.GetHubContext<THub, TClient>();
}
}
And SimpleInjectorSignalRDependencyResolver looks like:
public class SimpleInjectorSignalRDependencyResolver : DefaultDependencyResolver
{
public SimpleInjectorSignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
return _serviceProvider.GetService(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var #this = (IEnumerable<object>)_serviceProvider.GetService(
typeof(IEnumerable<>).MakeGenericType(serviceType));
var #base = base.GetServices(serviceType);
return #this == null ? #base : #base == null ? #this : #this.Concat(#base);
}
private readonly IServiceProvider _serviceProvider;
}
And StockTicker looks like:
public class StockTicker : IStockTicker
{
private readonly HubContextAdapter<StockTickerHub, IStockTickerHubClient> _context;
public StockTicker(HubContextAdapter<StockTickerHub, IStockTickerHubClient> context)
{
_context = context;
}
}
When the StockTicker ticks and calls all clients to update the client method is not invoked and there is no network traffic.
SimpleInjector wants to instantiate the singletons after verification or after the first GetInstance call. This is too early for SignalR and the StockTicker and it will take an instance of GlobalHost.ConnectionManager before SimpleInjectorSignalRDependencyResolver is the resolver.
I chose to change the dependency on IConnectionManager to be Lazy<IConnectionManager> and the dependency on IStockTicker to be Lazy<IStockTicker> so that registration became like the following:
container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.RegisterSingleton(() => new SimpleInjectorSignalRDependencyResolver(_container));
container.RegisterSingleton(() =>
new HubConfiguration()
{
EnableDetailedErrors = true,
Resolver = _container.GetInstance<SimpleInjectorSignalRDependencyResolver>()
});
container.RegisterSingleton<IHubActivator, SimpleInjectorHubActivator>();
container.RegisterSingleton<IStockTicker,StockTicker>();
container.RegisterSingleton<Lazy<IStockTicker>>(() => new Lazy<IStockTicker>(() => container.GetInstace<IStockTicker>()) );
container.RegisterSingleton<HubContextAdapter<StockTickerHub, IStockTickerHubClient>>();
container.RegisterSingleton(() => new Lazy<IConnectionManager>(() => GlobalHost.ConnectionManager));
container.Verify();

Self-Registering Libraries with Autofac 4 and vNext

i'd like to create a Plugin Enviroment for my ASP.Net 5.0 / MVC 6 Application. I'm using Autofac as IOC Container and i like to load the Plugins (Class Libraries) from the build in DNX LibraryManager. The goal of using the Library Manager is, that i don't have to care about NuGet Packages and Frameworks.
The Problem i have is the LifeCycle, i have to build the IOC Container before the instance of the LibraryManager is available. Because the Autofac Container provides his own IServiceProvider Instance which i have to inject within the ConfigureService() Method call (AddAutofac).
Does anyone know how to get this working?
Update: I have fixed my problem with Davids help and updated the code to get it working with the release candidates. Also i have added support for configuration.
In my DNX Class Library i implemented a Class for Self-Registration:
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new SimpleService())
.As<IService>()
.InstancePerLifetimeScope();
}
}
In my MVC WebApplication i have added the Class Library as Dependency.
Startup.cs
public IConfiguration Configuration { get; set; }
public class Startup
{
public Startup( IApplicationEnvironment applicationEnvironment )
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath( applicationEnvironment.ApplicationBasePath );
configurationBuilder.AddJsonFile( "appsettings.json" );
configurationBuilder.AddJsonFile( "autofac.json" );
configurationBuilder.AddEnvironmentVariables();
this.Configuration = configurationBuilder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDependencies();
}
public void Configure(IApplicationBuilder applicationBuilder, IHostingEnvironment hostingEnvironment)
{
applicationBuilder.UseDependencies( this.Configuration );
applicationBuilder.UseStaticFiles();
applicationBuilder.UseMvc();
}
}
I have created an DependencyResolver to keep the ContainerBuilder instance.
DependencyResolver.cs
public class DependencyResolver : IDependencyResolver
{
private IContainer container;
private readonly ContainerBuilder builder;
public DependencyResolver()
{
this.builder = new ContainerBuilder();
}
public void RegisterModule( IModule module )
{
this.builder.RegisterModule( module );
}
public void RegisterModules( IEnumerable<Assembly> assemblies )
{
this.builder.RegisterAssemblyModules(assemblies.ToArray());
}
public void Populate( IServiceCollection services)
{
this.builder.Populate( services );
}
public void Build()
{
this.container = this.builder.Build();
}
public T Resolve<T>() where T : class
{
return this.container?.Resolve<T>();
}
}
IDependencyResolver.cs
public interface IDependencyResolver
{
void RegisterModule( IModule module );
void RegisterModules( IEnumerable<Assembly> assemblies );
void Populate(IServiceCollection services);
void Build();
T Resolve<T>() where T : class;
}
Last but not least i have created an Extension Class
DependencyResolverExtensions.cs
public static class DependencyResolverExtensions
{
public static IServiceCollection AddDependencies( this IServiceCollection services )
{
DependencyResolver dependencyResolver = new DependencyResolver();
dependencyResolver.Populate(services);
ServiceDescriptor serviceDescriptor = new ServiceDescriptor(typeof ( IDependencyResolver ), dependencyResolver );
services.TryAdd(serviceDescriptor);
return services;
}
public static IApplicationBuilder UseDependencies(this IApplicationBuilder applicationBuilder, IConfiguration configuration)
{
IDependencyResolver dependencyResolver = applicationBuilder.GetService<IDependencyResolver>();
if (dependencyResolver == null) return applicationBuilder;
ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>();
if (libraryManager == null) return applicationBuilder;
IEnumerable<Assembly> assemblies = libraryManager.GetLoadableAssemblies();
dependencyResolver.RegisterModules(assemblies);
ConfigurationModule configurationModule = new ConfigurationModule( configuration );
dependencyResolver.RegisterModule( configurationModule );
dependencyResolver.Build();
IServiceProvider serviceProvider = dependencyResolver.Resolve<IServiceProvider>();
applicationBuilder.ApplicationServices = serviceProvider;
return applicationBuilder;
}
public static IEnumerable<Assembly> GetLoadableAssemblies(this ILibraryManager libraryManager)
{
List<Assembly> result = new List<Assembly>();
IEnumerable<Library> libraries = libraryManager.GetLibraries();
IEnumerable<AssemblyName> assemblyNames = libraries.SelectMany(e => e.Assemblies).Distinct();
assemblyNames = Enumerable.Where(assemblyNames, e => e.Name.StartsWith("MyLib."));
foreach (AssemblyName assemblyName in assemblyNames)
{
Assembly assembly = Assembly.Load(assemblyName);
result.Add(assembly);
}
return result;
}
public static T GetService<T>(this IApplicationBuilder applicationBuilder) where T : class
{
return applicationBuilder.ApplicationServices.GetService(typeof (T)) as T;
}
}
If you need to switch between different implementations, like mock and real data you can use the Autofac Configuration.
autofac.json
{
"components": [
{
"type": "MyLib.Data.EF.EntitiesData, MyLib.Data.EF",
"services": [
{
"type": "MyLib.Abstractions.IDataRepository, MyLib.Abstractions"
}
]
}
]
}
It's a shame that ConfigureServices is not injectable, that would make this a lot easier.
Looking at the code you should be safe to replace the IServiceProvider inside Configure(...) instead of inside ConfigureServices(...) and get the intended behavior. ApplicationServices is setable.
In your UseAutofac method you should be able to do something like:
public static IApplicationBuilder UseAutofac( [NotNull] this IApplicationBuilder applicationBuilder )
{
IAutofacResolver autofacResolver = applicationBuilder.GetService<IAutofacResolver>();
ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>();
autofacResolver.RegisterLibraryModules( libraryManager);
applicationBuilder.ApplicationServices = autofacResolver.Resolve();
return applicationBuilder;
}
I've come up with a solution that uses part of this, but also uses a ComponentContainer that addresses the potential memory leaks in the DependencyResolver. This also works with RC1. Not sure yet about RC2 as it's not complete enough for me to test.
The ComponentContainer looks like this:
public static class ComponentContainer {
static IContainer _container;
static ContainerBuilder _containerBuilder;
public static ContainerBuilder Builder {
get {
if (_containerBuilder == null)
_containerBuilder = new ContainerBuilder();
return _containerBuilder;
}
}
public static IServiceProvider ServiceProvider {
get {
if (_container == null)
_container = _containerBuilder.Build();
return _container.Resolve<IServiceProvider>();
}
}
public static ComponentFactory<TObject> Component<TObject>() => new ComponentFactory<TObject>(_container);
public static void RegisterAssembly(Assembly assembly) {
if (assembly == null) return;
foreach (var obj in assembly.GetTypes().Where(t => t.GetCustomAttribute<ExportAttribute>() != null)) {
ExportAttribute att = obj.GetCustomAttribute<ExportAttribute>();
if (att.ContractType != null) {
_containerBuilder.RegisterType(obj).As(att.ContractType);
} else {
foreach (var intf in obj.GetInterfaces())
_containerBuilder.RegisterType(obj).As(intf);
}
}
}
}
public class ComponentFactory<TObject> : IDisposable {
protected TObject CurrentObject;
protected ILifetimeScope CurrentScope;
public TObject Current => (TObject)CurrentObject;
public ComponentFactory(IContainer container) {
CurrentScope = container.BeginLifetimeScope();
CurrentObject = CurrentScope.Resolve<TObject>();
}
public TObject Component => CurrentObject;
public void Dispose() {
(CurrentObject as IDisposable)?.Dispose();
CurrentScope.Dispose();
}
}
Then in Startup.cs I do the following:
public virtual IServiceProvider ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddOptions();
services.AddSession();
services.AddCaching();
var assemblyLoadContextAccessor = services.FirstOrDefault(s => s.ServiceType == typeof(IAssemblyLoadContextAccessor)).ImplementationInstance as IAssemblyLoadContextAccessor;
var libraryManager = services.FirstOrDefault(s => s.ServiceType == typeof(ILibraryManager)).ImplementationInstance as ILibraryManager;
var loadContext = assemblyLoadContextAccessor.Default;
foreach(var library in libraryManager.GetLibraries()) {
var assembly = loadContext.Load(library.Name);
if(assembly != null) {
var module = assembly.GetTypes().FirstOrDefault(t => t == typeof(IModule));
if(module != null)
ComponentContainer.Builder.RegisterAssemblyModules(assembly);
else
ComponentContainer.RegisterAssembly(assembly);
}
}
ComponentContainer.Builder.Populate(services);
return ComponentContainer.ServiceProvider;
}
To export modules within an assembly, I either mark them with an ExportAttribute or add a class to the assembly that implements Autofac's IModule. The code in ConfigureServices will enumerate through the application's modules and feed them to the static Builder in ComponentContainer. Once the container has been built, you can either resolve modules through injection in a constructor or you can request a specific type by:
(using var myComponentFactory = ComponentContainer.Component<IMyModule>()) {
//You can now access your component through myComponentFactory.Component
//Once it passes out of scope of using, it will be properly disposed of
//along with the scope from which it was created.
}
Edit: With the release of RC2, this code is no longer valid as the enumeration of assemblies and classes will fail. I haven't come up with a good solution yet. If anyone else has any suggestions for enumerating assemblies in RC2, please let me know.

Register different assembly types in autofac container in Asp.net

I have 3 tier architecture application. my Data Access is like below:
ICategoryRepository.cs
public interface ICategoryRepository
{
void Add(Category category);
}
CategoryRepository.cs
internal class CategoryRepository : ICategoryRepository
{
void ICategoryRepository.Add(Category category)
{
// Dbcontext goes here
}
}
And I have a Autofac model to register the above classes in autofac container is:
public class RepositoryModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
base.Load(builder);
}
}
And I have a service layer as below:
ICategoryService.cs
public interface ICategoryService
{
void Add(Category category);
}
CategoryService.cs
internal class CategoryService : ICategoryService
{
private ICategoryRepository _categoryRepository;
public CategoryService(ICategoryRepository categoryRepository)
{
_categoryRespoitory = categoryRepository;
}
void ICategoryService.Add(Category category)
{
_categoryRepository.Add(category);
}
}
Similarly, I have a module to register the above class in container as,
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}
And in my Web, Global.asax.cs:
public class Global : System.Web.HttpApplication, IContainerProviderAccessor
{
static IContainerProvider _containerProvider;
public IContainerProvider ContainerProvider
{
get { return _containerProvider; }
}
void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
_containerProvider = new ContainerProvider(builder.Build());
}
}
Here my question is, how can I call the service and DataAccess module from the web.
I don't have the data access reference in my web, but I have reference of service module in my web
You can use the RegisterAssemblyModules method
void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies();
if (HostingEnvironment.InClientBuildManager)
{
assemblies = assemblies.Union(BuildManager.GetReferencedAssemblies()
.Cast<Assembly>())
.Distinct();
}
builder.RegisterAssemblyModules(assemblies);
_containerProvider = new ContainerProvider(builder.Build());
}
In order to let this code works, all your assemblies should be available on the bin folder.
I use BuildManager.GetReferencedAssemblies() to avoid issue after ASP.net recycling process. See IIS Hosted Web Application on autofac documentation for more explanation.

Categories