Set instance lifecycle with StructureMap convention based registration - structuremap

I'm trying to use StructureMap 2.6.1 to register all my repositories at once using the convention based registration. See the code bellow :
x.Scan(s =>
{
s.TheCallingAssembly();
s.IncludeNamespaceContainingType<RepositoryRegistration>();
s.SingleImplementationsOfInterface();
}
It works but now I'm trying to add a lifecycle (HybridHttpOrThreadLocalScope) to all the registered types. Is it possible without rewriting the SingleImplementationsOfInterface convention from scratch and if it is, how ?
Thanks.

Have you tried:
x.Scan(s =>
{
s.TheCallingAssembly();
s.IncludeNamespaceContainingType<RepositoryRegistration>();
s.SingleImplementationsOfInterface().OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped());
}

Related

Request instance within ConfigureServices

Within ConfigureServices I'm setting up a number of services (this works). In the same method I'm trying to configure a custom file provider (for Razor). It looks like this:
services.AddMvc()
.AddRazorOptions(options =>
{
options.FileProvider = new CustomFileProvider(???);
});
CustomFileProvider has a few dependencies (that are all configured), but how can I ask the DI to give me an instance of CustomFileProvider right after all the services have been configured?
From what I can see the DI only injects in constructors, but in my case I need a "please give me an instance of CustomFileProvider right here" option.
If you want to be able to resolve services from the container while configuring some options, you need to leverage the IConfigureOptions<TOption> infrastructure, which most of MVC uses to set up the option defaults. See RazorViewEngineOptionsSetup for an example.
First, add the services you need to the collection:
services.AddSingleton<IDependency1, Dependency1>();
services.AddSingleton<IDependency2, Dependency2>();
Then, implement the setup class:
public class CustomFileProviderRazorViewEngineOptionsSetup : ConfigureOptions<RazorViewEngineOptions>
{
public CustomFileProviderRazorViewEngineOptionsSetup(IServiceProvider serviceProvider)
: base(options => ConfigureRazor(options, serviceProvider))
{
}
private static void ConfigureRazor(RazorViewEngineOptions options, IServiceProvider serviceProvider)
{
// Alternative 1 - Resolve each service and new up the instance.
var dependency1 = serviceProvider.GetService<IDependency1>();
var dependency2 = serviceProvider.GetService<IDependency2>();
options.FileProviders.Add(new CustomFileProvider(dependency1, dependency2));
// Alternative 2 - Same as alternative 1, but with moar magic ;)
options.FileProviders.Add(ActivatorUtilities.CreateInstance<CustomFileProvider>(serviceProvider));
// Alternative 3 - Just resolve CustomFileProvider from the service provider.
// This requires it to be registered first, of course.
options.FileProviders.Add(serviceProvider.GetService<CustomFileProvider>());
}
}
This is resolved from the container, so it'll allow you to inject an IServiceProvider, which can again be used to resolve the services you need. You could optionally add the CustomFileProvider to the container and resolve that directly instead, like in alternative 3.
Finally, add the setup class to the service collection:
services.TryAddEnumerable(
ServiceDescriptor.Transient<
IConfigureOptions<RazorViewEngineOptions>,
CustomFileProviderRazorViewEngineOptionsSetup>());
This will add the setup to the options builder pipeline, which means it'll run with the other registered instances of IConfigureOptions<RazorViewEngineOptions> to setup the options object.

MassTransit Integration with StructureMap 3.0.3

I am attempting to use MassTransit in my application that is StructureMap based. I have followed the code examples provided by the MassTransit site, but seem to be having some issues getting it to work.
Here is my StructureMap Registry class:
using MassTransit;
using StructureMap;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;
using System;
using System.Configuration;
namespace MyApp
{
public class MassTransitRegistry : Registry
{
public MassTransitRegistry()
{
Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory(a => a.FullName.StartsWith("MyCompany"));
scan.AddAllTypesOf<IConsumer>();
});
try
{
// Register the ServiceBus.
var bus = ServiceBusFactory.New(sbc =>
{
sbc.UseRabbitMq();
sbc.ReceiveFrom(ConfigurationManager.AppSettings["MassTransitQueue"]);
sbc.UseControlBus();
sbc.UseJsonSerializer();
sbc.SetConcurrentConsumerLimit(12);
sbc.Subscribe(s => s.LoadFrom(ObjectFactory.Container));
});
ObjectFactory.Container.Inject<IServiceBus>(bus);
}
catch (Exception)
{
throw;
}
}
}
}
The problem seems to be with the MassTransit.StructureMap integration NuGet package. It seems that this package requires StructureMap 2.6.4.0. I cannot really change out my StructureMap version since I have code written that now works with the new way StructureMap is written in 3.0.
Does anyone know of a work around to this issue?
Regards,
Richard Onorato
There is currently no way to do this. MassTransit requires a signed version of StructureMap and Jeremy had made it clear he's not going to be generating a signed version.
Also, MT still supports .NET 3.5 and StructureMap 3.0 does not. You'll have to downgrade to StructureMap 2.6 or just ignore the integration package for MT and register your handlers manually.
There's an existing issue around this topic https://github.com/MassTransit/MassTransit/pull/238.

Convention Based Dependency Injection with Ninject 3.0.0

I have two projects in my solution... a domain project and MVC3 web project (e.g. MyApp.Domain and MyApp.Web). Previously, when using Ninject.Extensions.Conventions ver. 2, I was able to use the following statement in the NinjectMVC3.cs file, and required dependencies throughout my solution (both web and domain) were injected properly (e.g. IFoo automatically bound to Foo).
kernel.Scan(x =>
{
x.FromAssembliesMatching("*");
x.BindWith<DefaultBindingGenerator>();
});
I have just upgraded to Ninject 3.0.0 (pre-release) and Ninject.Extensions.Conventions 3.0.0 (another pre-release) but the syntax for convention based binding has changed. I have figured out that I can use the following statement with the new version, but it only automatically binds the convention based interfaces in MyApp.Web and not in MyApp.Domain. The previous version bound interfaces throughout the application.
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses()
.BindToAllInterfaces());
Any clue how I can configure convention based binding with the new Ninject version? I assume it has to do with specifying the assembly, but I have tried using FromAssembliesMatching("*") and it fails for everything then.
-- Edit to show my exisiting code in RegisterServices method: --
private static void RegisterServices(IKernel kernel)
{
// This code used to work with v.2 of Ninject.Extensions.Conventions
// kernel.Scan(x =>
// {
// x.FromAssembliesMatching("*");
// x.BindWith<DefaultBindingGenerator>();
// });
// This is the new v3 code that automatically injects dependencies but only for interfaces in MyApp.Web, not MyApp.Domain
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().BindToAllInterfaces());
// I tried this code, but it throws "Error activating IDependencyResolver" at "bootstrapper.Initialize(CreateKernel)"
// kernel.Bind(x => x.FromAssembliesInPath(AppDomain.CurrentDomain.RelativeSearchPath).SelectAllClasses().BindToAllInterfaces());
// These are dependencies in MyApp.Web that ARE being bound properly by the current configuration
// kernel.Bind<IMemberQueries>().To<MemberQueries>();
// kernel.Bind<IGrantApplicationQueries>().To<GrantApplicationQueries>();
// kernel.Bind<IMailController>().To<MailController>();
// These are dependencies in MyApp.Domain that ARE NOT being bound properly by the current configuration, so I have to declare them manually
// They used to be injected automatically with version 2 of the conventions extention
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IMemberServices>().To<MemberServices>();
kernel.Bind<IGrantApplicationServices>().To<GrantApplicationServices>();
// These are dependencies that SHOULD NOT be bound by convention as they require a different scope or have unique naming
kernel.Bind(typeof(EfDbContext)).ToSelf().InRequestScope();
kernel.Bind<IConfigurationProvider>().To<WebConfigConfigurationProvider>().InSingletonScope();
kernel.Bind<IAuthorizationProvider>().To<MyAppAuthorizationProvider>();
kernel.Bind<IPrincipal>().ToMethod(ctx => HttpContext.Current.User).InRequestScope();
kernel.Bind<IGrantApplicationDocumentServices>().To<MySpecialNameGrantAplicationDocumentServices>().InRequestScope();
}
The equivalent is:
kernel.Bind(x => x
.FromAssembliesMatching("*")
.SelectAllClasses()
.BindDefaultInterface());

StructureMap: how to correctly set up default dependencies

An approach we've taken recently is to include a StructureMap registry in each of our assemblies that set's up the default dependencies.
We use a scanner to do this:
cfg.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.Assembly("Assembly1");
scanner.Assembly("Assembly2");
scanner.Assembly("Assembly3");
scanner.LookForRegistries();
});
The idea is that we can then override the default dependencies from the main application.
The question is, where should we register these overrides? i.e. before we scan?, after we scan?
Furthermore, does the order of the assemblies specified in the scan expression, effect the order in which the dependencies are registered. So in the example above, would registries contained in the main application (TheCallingAssembly) get overrided by those in "Assembly3"?
Many thanks
Ben
Registries in TheCallingAssembly will be overridden by those you register in Assembly1, 2 etc.
So if you register ISomeInterface in everyone of those assemblies, the one in Assembly3 will be the default. The ones registered in the others assemblies you can get from structuremap by calling
container.GetAllInstances<ISomeInterface>();
This will return a IList of all the registered ISomeInterface in structuremap.
If you want to override the ones you get by scanning you have to configure the container again for some reason. If you don't do that, the last scanned type is the default.
In this example the ISomeInterface registered in Assembly1 is the default. When you look at the code, one would belive that SomeOtherClass is the default. But it is actually the one registered in Assembly1 that is the default.
var container = new Container(x => {
x.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.Assembly("Assembly1");
scanner.LookForRegistries();
});
x.For<ISomeInterface>().Use<SomeOtherClass>();
});
So to override the mappings from the scanned assemblies. You have to configure the container again like this example. Here SomeOtherClass is the default for ISomeInterface.
var container = new Container(x => {
x.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.Assembly("Assembly1");
scanner.LookForRegistries();
});
});
container.Configure(x => x.For<ISomeInterface>().Use<SomeOtherClass>());

StructureMap and the decorator pattern

I'm using StructureMap, v. 2.5.3 and am having trouble with chaining together implementations on an interface to implement the Decorator pattern.
I'm used to Windsor, where it is possible to name variations on interface implementations and send the named impl. into another (default) impl.
This is the default, non decorated version, which works fine:
ObjectFactory.Initialize(registry =>
{
registry.ForRequestedType<ICommentService()
.TheDefault.Is.OfConcreteType<CommentService>();
... }
This is the ctor on the decorator, that I want to call:
public CommentAuditService( ICommentService commentService,
IAuditService auditService )
This works, but does not give me access to the decorated object:
registry.ForRequestedType<ICommentService>()
.TheDefault.Is.OfConcreteType<CommentService>()
.EnrichWith(x => new CommentAuditService());
This takes me int an inf. loop:
registry.ForRequestedType<ICommentService>()
.TheDefault.Is.OfConcreteType<CommentService>()
.EnrichWith(x => new CommentAuditService( new CommentService(),
new AuditService()));
So far this is what seems to me should work:
registry.ForRequestedType<ICommentService.()
.TheDefault.Is.OfConcreteType<CommentAuditService>()
.WithCtorArg("commentService")
.EqualTo(new CommentService());
However it send it into an endless loop of creating new instances of CommentAuditService
Does anyone have an quick answer? (other than switching to Castle.Windsor, which I'm very close to at the moment)
You were very close. Try:
registry.ForRequestedType<ICommentService>()
.TheDefaultIsConcreteType<CommentService>()
.EnrichWith(original => new CommentAuditService(original,
new AuditService()));
If AuditService might itself have dependencies, you would do:
registry.ForRequestedType<ICommentService>()
.TheDefaultIsConcreteType<CommentService>()
.EnrichWith((ioc, original) => new CommentAuditService(original,
ioc.GetInstance<AuditService>()));
Or, if you change the last part to:
ioc.GetInstance<IAuditService>()
you can register the concrete type of your audit service separately.

Resources