I'm trying to read my configuration from SF configuration using the 'ConfigurationPackage' that is available from any SF service context. My class looks like this:
internal class ServiceFabricDbConfiguration : IDbConnectionConfig
{
private ConfigurationPackage _configurationPackage;
public ServiceFabricDbConfiguration(ServiceContext context)
{
_configurationPackage = context.CodePackageActivationContext.GetConfigurationPackageObject("Config");
}
public string UserName =>
_configurationPackage.Settings.Sections["Db_Configuration"]
.Parameters[
"Username"]
.Value;
}
I'm using autofac as my DI container, and can register the above class by explicitly capturing a reference to the ServiceContext when i register it with the SF runtime:
ServiceRuntime.RegisterServiceAsync("ApiType",
context =>
{
Bootstrapper.SetContext(context);
return new Api(context);
})
.GetAwaiter()
.GetResult();
Is there a way that i can register the ServiceContext with the bootstrapper, ideally within the bootstrapper class?
I'm currently experimenting with using Autofac.ServiceFabric to register my actors/services, but that hides the ServiceContext so makes the above harder to achieve again (though does make it far easier to maintain clean autofac module definitions)
There's the static method GetActivationContext() in FabricRuntime. You could perhaps use this to inject the activation context.
There's also, in development, Autofac.ServiceFabric https://github.com/autofac/Autofac.ServiceFabric which may be of use to you. There's a blog post about it here https://alexmg.com/introducing-the-autofac-integration-for-service-fabric/ which also contains links to sample code! It's in pre-release (beta) at the moment but I have been using it without issue for the past few months.
PR 8 makes the ServiceContext available in Autofac.ServiceFabric
Related
I'm making a class library intended to be used as a nuget package. I want to make it DI-friendly and not associated with any specific container.
Let's say that the project consists of 15 classes, injected to eachother by constructor injection. How can I make the consumer register my library in an easy way? Should my library expose something like IEnumerable<Type> GetTypesToRegister(), or perhaps evenIList<(Type type, Lifestyle lifestyle)> GetTypesToRegister()? How is this usually approached by library designers?
You can use extension methods to allow users of your package to easily register specific services and their dependencies. The IServiceCollection interface will work across various DI containers. Here's a quick example from a small side project I did a while back. The example is an extension method that allows easy registration of a Azure Storage service.
public static class AzureBlobStorageServiceCollectionExtensions
{
public static IServiceCollection AddAzureBlobFileStorageService(this IServiceCollection services,
Action<AzureBlobStorageServiceConfigOptions> options)
{
var configOptions = new AzureBlobStorageServiceConfigOptions();
options(configOptions);
services.AddScoped<IFileStorageService>(sp =>
{
var logger = sp.GetRequiredService<ILogger<AzureBlobStorageService>>();
var connectionString = configOptions.ConnectionString ??
throw new ArgumentNullException(nameof(configOptions.ConnectionString));
var containerName = configOptions.ContainerName ??
throw new ArgumentNullException(nameof(configOptions.ContainerName));
return new AzureBlobStorageService(logger, connectionString, containerName);
});
return services;
}
}
You can then register the service in an application like this
services.AddAzureBlobFileStorageService(options =>
{
options.ConnectionString = Configuration["Storage:ConnectionString"];
options.ContainerName = Configuration["Storage:ContainerName"];
});
So it provides a simple way to register the service and all it's dependencies and configuration data. You can use the service normally by requesting the IFileStorageService.
Not sure if this is what you were asking about but hopefully it helps. You can look up Service Collection Extension methods in the docs and read more about the specifics.
How to replicate this code with Autofac syntax?
public static class MenuConfig
{
public static void Initialize()
{
var _menuService = DependecyFactory.GetInstance<IMenuService>();
Parameters.Menu = _menuService.Menu();
}
}
Before calling this a "duplicate question" please note that I'm looking for an Autofac command. I CANNOT inject the interface anywhere and then call "Resolve". What I need to is perform an "InstancePerRequest" inline and uninjected so I don't have to do this:
var _service = new Service(new Dependency(new context()));
LightInject has a method that allows instantiation from an interface OUTSIDE of a constructor like this:
var _service = DependecyFactory.GetInstance<IService>();
What is the equivalent method for Autofac?
When calling containerBuilder.Build() you get back a container which implements IContainer and ILifetimeScope, whenever you get hold of one of these interfaces, you can resolve types from it:
container.Resolve<IService>();
If you want this container to be static, you could add the container as a static property to the Program or Startup class (depending if you're creating a Console or ASP.NET application).
Remember that the root container will be around for the entire duration of your application, so this can result in unwanted memory leaks when used incorrectly. Also see the warning in the documentation.
Still, it's perfectly possible to do the memory management yourself by resolving an Owned<> version from your interface:
using (var service = Program.Container.Resolve<Owned<IService>>())
{
service.Value.UseService();
}
Anyway, since you mention a static class in the comments, the best solution is to change that into a non-static class and register it as a singleton with Autofac. Then you can inject a Func<Owned<IService>> serviceFactory into that singleton and create/dispose an instance of the service wherever you need it.
using (var service = serviceFactory())
{
service.Value.UseService();
}
This is simply not possible with Autofac. All other solutions involving Autofac will require code refactoring which may potentially break software functionality. So unfortunately, the most elegant and least disruptive solution is this:
var _service = new Service(new Dependency(new context()));
Since this is an edge case addressing only one part of the software, this compromise is acceptable. It would be nice, however, if Autofac implemented this functionality in some future release.
I have a solution that has the following projects
Acme.Core
Acme.Domain
Acme.Repositories
Acme.Services
Acme.Web
In the past I've used Unity for DI in full framework projects. I was able to register concrete objects to interface mappings in executable projects (web apps, console app, test apps).
I'm trying to implement the same approach with .NET Core. I wanted to first try using the Microsoft.Extensions.DependencyInjection library. Within the ASP.NET Core application it works great. Unfortunately I've run into an issue when I try to share/reference that instance with the registions to other projects, such as a .NET Standard library.
My idea was to inject the ServiceProvider into the constructor of the service:
public class AddressService : BaseService, IAddressService
{
private readonly IServiceProvider _serviceProvider;
public AddressService(IServiceProvider serviceProvider, string userOrProcessName)
{
_serviceProvider = serviceProvider;
}
public IReadOnlyList<IState> GetAllStates()
{
_serviceProvider.GetService<IAddressRepository>();
// other logic removed
}
}
I tried the following inside the Startup.ConfigureServices():
services.AddTransient<IAddressService>(s => new AddressService(HttpContext.RequestServices, Environment.UserName));
The issue I ran into is that I cannot reference HttpContext.RequestServices outside of a Controller. I haven't been able to figure another way of passing the ServiceProvider instance.
My questions:
How do pass a reference for the current ServiceProvider?
Is there a better design to accomplish my goal sharing the configuration of Microsoft.Extensions.DependencyInjection in multiple libraries?
Prevent injecting IServiceProvider into your application components; that leads to the Service Locator anti-pattern.
Instead, you should build up application components solely using Constructor Injection. This means that your AddressService should require IAddressRepository as constructor argument, not IServiceProvider. For instance:
public class AddressService : IAddressService
{
private readonly IAddressRepository repo;
public AddressService(IAddressRepository repo, IUserContext userContext)
{
this.repo = repo;
}
public IReadOnlyList<IState> GetAllStates()
{
// other logic removed
}
}
Also try to prevent injecting primites into your constructors. It's not a bad practice per se, but it does complicate object graph construction. Instead, either wrap the value into a class, in case its a configuration value, or hide it behind an abstraction (as shown above) in case it's a runtime value.
Both practices simplify both your application code and the Composition Root.
For instance, this will be the result of the previous AddressService redesign:
services.AddTransient<IAddressRepository, SqlAddressRepository>();
services.AddTransient<IAddressService, AddressService>();
services.AddScoped<IUserContext, UserContext>();
services.AddHttpContextAccessor();
Here, UserContext could be defined as follows:
public class UserContext : IUserContext
{
private readonly IHttpContextAccessor accessor;
public UserContext(IHttpContextAccessor accessor) => this.accessor = accessor;
public string UserName => this.accessor.HttpContext.User.Identity.Name;
}
In order to share configuration across multiple projects, you can put the configuration into a shared assembly, and register (not resolve) them in there. Many dependency injection libraries offer that functionality. e.g.
in Autofac you create a module (https://autofaccn.readthedocs.io/en/latest/configuration/modules.html) that takes a container builder to configure:
protected override void Load(ContainerBuilder builder) { ... }
SimpleInjector provides packages: https://simpleinjector.readthedocs.io/en/latest/howto.html#package-registrations
Unity can support something similar: Can I register my types in modules in Unity like I can in Autofac?
Ninject has a similar module feature: What is the intention of Ninject modules?
A similar feature has be created for Microsoft.Extensions.DependencyInjection: https://github.com/aruss/DotNetCore_ModularApplication
At a high level, you create a method that receives the DI container and adds your registrations to that container. If your DI framework doesn't provide hooks you need to manually call the method yourself, but the general concept doesn't change.
Splitting registrations into modules allows you to easily group similar sets of functionality while maintaining the flexibility of incorporating different sets of functionality into different projects. You could of course create a single shared assembly that registered the union of all dependencies for all projects, but that would carry around unnecessary baggage and result in a less reusable implementation.
The key point as Steven points out is that you configure the container and let it inject the dependencies rather than looking from the inside out for the dependencies.
I'm new to AutoFac and am currently using custom modules inside my app config to boot up some core F# systems. The code I'm using is
var builder = new ContainerBuilder();
builder.RegisterType<DefaultLogger>().As<IDefaultLogger>();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.Build();
And inside my app config I have the appropriate logic to start up the relevant systems. I would like to have access to the DefaultLogger inside my Modules. Metadata for the Module base class has the following options available to me:
protected virtual void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration);
protected virtual void AttachToRegistrationSource(IComponentRegistry componentRegistry, IRegistrationSource registrationSource);
public void Configure(IComponentRegistry componentRegistry);
protected virtual void Load(ContainerBuilder builder);
I've only been using Load so far and I can't see any methods on the builder that would allow me to get at the logging service.
When registering something within your modules with autofac instead of using RegisterType method you might use Register method:
builder.Register(c =>
{
IComponentContext ctx = c.Resolve<IComponentContext();
IDefaultLogger logger = ctx.Resolve<IDefaultLogger>();
...do something with logger...
return ...return object you want to register...;
});
The answer turned out to be incredibly simple. I just added IComponentContext as a dependency to my Module's implementation
public class LocalActorSystemModule : Module {
private IComponentContext m_ComponentContext; // A service for resolving dependencies required by this module
public LocalActorSystemModule(IComponentContext componentContext) {
m_ComponentContext = componentContext;
}
And let AutoFac inject the IComponentContext for me. That way I can resolve any dependencies I require inside the module.
Rule of thumb for using every IoC/DI Container: Resolve once! => then you get all dependencies resolved for your requested object. If you try to resolve multiple times, register other objects (in the meantime) you're stuck in hell. Really. If you want to retrieve objects for different purposes at different places and time points (resolved from central registration) you may be looking for the Service Locator Pattern instead (but this is often described as an Anti-Pattern, too).
Modules have the purpose to bundle related registrations (conditionally) as statet in the Autofac documentation:
A module is a small class that can be used to bundle up a set of
related components behind a ‘facade’ to simplify configuration and
deployment.
... so if they are just a sum of registrations and the container has not yet been build you are not able to resolve and use an (even previously registered) component immediately (except calling a method on the registrant itself through OnActivate* hooks or when using instance registration, but I think this is not the case for your example). The components are just in the state of registration but the complete context is not ready for resolving. What would happen if you override the registration in another Module? Then you would have injected different objects... bad idea. Maybe you should rethink your application design and which objects have which responsibilities.
By the way: Logging is a cross cutting concern that is often "injected / resolved" by calling a separate static factory or service instead of doing constructor / property injection (see usage of Common.Logging for example).
public class MyModule : Module
{
private static readonly ILog Log = LogManager.GetLogger<MyModule>();
protected override void Load(ContainerBuilder builder)
{
Log.Debug(msg => msg("Hello")); // log whatever you want here
}
}
You can also try to use AOP libraries and weave the dependency into the Module (using reflection). But I don't think it's worth to try just for logging in a Module.
Anyway: #mr100 has already shown the right usage during registration. There you can also handle activation etc. but not do logging for the Module itself.
This is probably a bit trivial, but I must be doing something wrong. I have an MVC project, where I have two projects controlled by IoC using Windsor:
The MVC project
A project with services
Normally I would register the services using an XML file. However, I want to register all services and interfaces automatically.
All my services is placed in my services project called LetterAmazer.Business.Services, under the namespace LetterAmazer.Business.Services.Services. My interfaces, which the services derive from, is in the namespace LetterAmazer.Business.Services.Domain .
I have tried to set it up in my Global.asax, in the following way:
private void InitializeContainer()
{
var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(oldProvider);
Container.Register(Component.For<IWindsorContainer>().Instance(this.Container));
Container.Install(new BootstrapInstaller());
Container.Register(
Classes.FromAssemblyInDirectory(new AssemblyFilter("LetterAmazer.Business.Services"))
.InNamespace("LetterAmazer.Business.Services.Services"));
Container.Install(new WebWindsorInstaller());
var provider = new WindsorFilterAttributeFilterProvider(this.Container);
FilterProviders.Providers.Add(provider);
DependencyResolver.SetResolver(new WindsorDependencyResolver(ServiceFactory.Container));
}
public IWindsorContainer Container
{
get { return ServiceFactory.Container; }
}
But this provides several issues. First of all, it doesn't find any services when running the Classes.FromAssemblyInDirectory call. Furthermore, this wouldn't get the interfaces.
So I am basically asking how to solve this problem in a best practice way? I have 30+ services, and I want them all, so it would make sense to add them by conventation rather than hand pick.
EDIT:
The code I ended up with:
var assembly = Assembly.LoadFrom(Server.MapPath("~/bin/LetterAmazer.Business.Services.dll")); ;
Container.Register(
Classes.FromAssembly(assembly)
.InNamespace("LetterAmazer.Business.Services.Services")
.WithServiceAllInterfaces());
I haven't used Classes.FromAssemblyInDirectory() method but I usually use FromThisAssembly() or 'FromAssemblyContaining()' methods to select assemblies. Simetimes FromAssembly("AssemblyName") is used.
I would write the following code the following way if there is no base interface for all seervices:
container.Register(
Classes.FromAssembly("LetterAmazer.Business.Services")
.InNamespace("LetterAmazer.Business.Services.Services")
.WithServiceAllInterfaces());
If there is a generic interface IService<T> I would use the following registration:
container.Register(
Classes.FromAssembly("LetterAmazer.Business.Services")
.BasedOn(typeof(IService<>))
.WithServiceAllInterfaces());
I'd like to add that all your services will be registered as Singletons if life style is not specified during registration.
You can find detailed information in the article Registering components by conventions from Castle Windsor website.