StructureMap: how to correctly set up default dependencies - structuremap

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>());

Related

How to use multiple global ISiteMapNodeVisibilityProvider implementations at a time?

I'm trying to port the following StructureMap code from the MvcSiteMapProvider official documentation to Castle Windsor.
// Visibility Providers
// Explicitly set the visibility providers, using CompositeSiteMapNodeVisibilityProvider to combine the AclModuleVisibilityProvider
// with all other ISiteMapNodeVisibilityProvider implementations.
this.For<ISiteMapNodeVisibilityProviderStrategy>().Use<SiteMapNodeVisibilityProviderStrategy>()
.EnumerableOf<ISiteMapNodeVisibilityProvider>().Contains(x =>
{
x.Type<CompositeSiteMapNodeVisibilityProvider>()
.Ctor<string>("instanceName").Is("filteredAndTrimmedAndCustom")
.EnumerableOf<ISiteMapNodeVisibilityProvider>().Contains(y =>
{
// Note that the visibility providers are executed in
// the order specified here, but execution stops when
// the first visibility provider returns false.
y.Type<FilteredSiteMapNodeVisibilityProvider>();
y.Type<TrimEmptyGroupingNodesVisibilityProvider>();
y.Type<CustomVisibilityProvider>();
});
})
.Ctor<string>("defaultProviderName").Is("filteredAndTrimmedAndCustom");
Here is my code for Castle Windsor:
// Visibility Providers
container.Register(
Component.For<ISiteMapNodeVisibilityProvider>()
.Named("filteredAndTrimmedAndCustom")
.ImplementedBy<CompositeSiteMapNodeVisibilityProvider>()
.DependsOn(Dependency.OnValue(
"instanceName",
"filteredAndTrimmedAndCustom"))
.DependsOn(Dependency.OnComponentCollection(
typeof(FilteredSiteMapNodeVisibilityProvider),
typeof(TrimEmptyGroupingNodesVisibilityProvider),
typeof(CustomVisibilityProvider))));
container.Register(
Component.For<ISiteMapNodeVisibilityProviderStrategy>()
.ImplementedBy<SiteMapNodeVisibilityProviderStrategy>()
.DependsOn(Dependency.OnValue(
"defaultProviderName",
"filteredAndTrimmedAndCustom")));
The problem is that all my implementations of ISiteMapNodeVisibilityProvider are used globally after this code. If I have a local visibility provider named ``ABCVisibilityProvider` it will also be used globally. This line does not seem to be working like it should:
.DependsOn(Dependency.OnComponentCollection(
typeof(FilteredSiteMapNodeVisibilityProvider),
typeof(TrimEmptyGroupingNodesVisibilityProvider),
typeof(CustomVisibilityProvider))));
Did I not port the code properly?
I was not using the right method overload for OnComponentCollection. The constructor for CompositeSiteMapNodeVisibilityProvider has a parameter named siteMapNodeVisibilityProviders.
.DependsOn(Dependency.OnComponentCollection(
"siteMapNodeVisibilityProviders",
typeof(FilteredSiteMapNodeVisibilityProvider),
typeof(TrimEmptyGroupingNodesVisibilityProvider),
typeof(PropertyTypeGlobalNodeVisibilityProvider)))

StructureMap and Proto.Actor in .NET Core 2

I have been using Proto.Actor and specifically the ActorFactory to spawn actors. To be able to use these features I need to add services.AddProtoActor() to the ConfigureServices method of my startup class.
However, now I want to transition to using StructureMap as my IoC container, but the two do not appear to play nicely together - when I add the following code from guides I have found online:
public IServiceProvider ConfigureIoC(IServiceCollection services)
{
// static class method that scans assemblies
IContainer container = IocContainer.SetupContainer();
container.Configure(config =>
{
config.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
When it tries to run config.Populate I get the following error:
System.ArgumentOutOfRangeException: Specified argument was out of the
range of valid values. Parameter name: EventStream must have at
least one public constructor to be plugged in by StructureMap
Does anyone have any ideas how to get the IActorFactory created correctly as a singleton in StructureMap (or have a workaround)?
In the end, using StructureMap removes the need I had for the ActorFactory itself. So instead of getting the actor's PID from the factory I have two lines:
var props = Actor.FromProducer(() => container.GetInstance<MyActorType>());
var pid = Actor.Spawn(props);

How do you resolve instances of IInterceptionBehavior from the container when using StructureMap DynamicProxyInterceptor?

I'm migrating from Unity to StructureMap. I've made some use of Unity's InterceptionBehavior.
I thought I could switch that to use StructureMap .InterceptWith and the DynamicProxyInterceptor but my interceptors have dependencies and I can't work out how to compose the interceptors using StructureMap.
var container = new Container(x =>
{
x.For<IMathService>().Use<MathService>()
.InterceptWith(new DynamicProxyInterceptor<IMathService>(new IInterceptionBehavior[]
{
// I WANT TO COMPOSE THESE INTERCEPTORS
new NegatingInterceptor(),
new CachingInterceptor()
}));
});
At the moment the only thing I can think that might be a solution is to expose my IContainer from the static IoC class and resolve my dependencies manually in my interceptor.
Eventually I'll probably get around to replacing my dynamic proxies with decorators but I'm not quite at that stage yet. I just want to get it up and running again as soon as possible so I can prove the other changes are all successful before I start to make additional changes.
Okay, I'm an idiot, you just pass an array of types instead of instances to the DynamicProxyInterceptor constructor
var container = new Container(x =>
{
x.For<IMathService>().Use<MathService>()
.InterceptWith(new DynamicProxyInterceptor<IMathService>(
new Type[]
{
// I WANT TO COMPOSE THESE INTERCEPTORS
typeof(NegatingInterceptor),
typeof(CachingInterceptor)
}));
});

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.

No default instance or named instance 'Default' for requested plugin type

I'm trying to avoid referencing the concrete type library in my main project, but I'm getting this error:
No default instance or named instance 'Default' for requested plugin type StackExchangeChatInterfaces.IClient
1.) Container.GetInstance(StackExchangeChatInterfaces.IClient ,{username=; password=; defaultRoomUrl=; System.Action`2[System.Object,System.Object]=System.Action`2[System.Object,System.Object]})
I've setup my container to scan for assemblies, like so:
var container = new Container(x =>
{
x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.ExcludeNamespace("StructureMap");
scan.WithDefaultConventions();
scan.AddAllTypesOf<IMessageHandlers>();
});
//x.For<IClient>().Use<Client>(); //GetInstance will work if this line is not commented out.
});
When I try to get an instance, I get the error, my code for getting an instance is here:
chatInterface = container
.With("username").EqualTo(username)
.With("password").EqualTo(password)
.With("defaultRoomUrl").EqualTo(roomUrl)
.With<Action<object, object>>(delegate(object sender, object messageWrapper)
{
string message = ((dynamic)messageWrapper).Message;
Console.WriteLine("");
Console.WriteLine(message);
foreach (var item in messageHandlers)
{
item.MessageHandler.Invoke(message, chatInterface);
}
}).GetInstance<IClient>();
If I explicitly map the concrete class to the interface, everything works hunky dory, but that means I need to reference the project that Client is in, which I don't want to do.
This is really interesting. Looks like default conventions are not able to register types with such constructor (tried on both versions 2.6.3 and 3+). I was only registered when only parameterless constructor was specified. Looking at sources of both versions it is really suspicious as it should be registered. Deeper dive into the code would be needed...
Anyway try using custom registration convention:
public class ClientConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (type.IsClass && !type.IsAbstract && !type.IsGenericType &&
type.GetInterfaces().Contains(typeof(IClient)))
{
registry.For(typeof(IClient)).Use(type);
}
}
}
Configure it like this:
var container = new Container(
c => c.Scan(
s =>
{
s.ExcludeNamespace("StructureMap");
s.WithDefaultConventions();
s.Convention<ClientConvention>();
s.AddAllTypesOf<IMessageHandlers>();
}));
and this should work just fine.
The default type scanning will not pick up concrete types whose constructor functions contain primitive arguments like strings, numbers, or dates. The thinking is that you'd effectively have to explicitly configure those inline dependencies anyway.
"but that means I need to reference the project that Client is in, which I don't want to do."
Does that actually matter? I think you're making things harder than they have to be by trying to eliminate the assembly reference.

Resources