Say I have a list of protocol handlers, and the client service knows which protocol to use based on an enum value, it would be nice to selected the protocol from the list of i/fs passed in.
How can I achieve this in StructureMap?:
public EmailTransportService(interfaces...,
IDictionary<EmailAccountType, IEmailTransportHandler> transportHandlers)
At the moment, I'm using ObjectFactory with get named instance like so:
_emailTransportHandlers = new Dictionary<EmailAccountType, string>
{
{EmailAccountType.Pop3, "Pop3Handler"},
{EmailAccountType.IMAP, "IMapHandler"}
};
then resolving like so:
private IEmailTransportHandler GetTransportHandler(EmailAccountType accountType)
{
return ObjectFactory.GetNamedInstance<IEmailTransportHandler>(_emailTransportHandlers[accountType]);
}
but I don't like this as its difficult within my unit tests to verify the calls to the handlers.
My service registry looks like so:
public EmailTransportServiceRegistry()
{
Scan(x =>
{
....
});
For<IEmailTransportHandler>().Use<ActiveUpPop3Handler>().Named("Pop3Handler");
For<IEmailTransportHandler>().Use<ActiveUpIMap4Handler>().Named("IMapHandler");
}
So basically I'm relying on named instances based on the dictionary list of protocol types.
My solution was to have a static register method from the client service like so:
public static IDictionary<EmailAccountType, IEmailTransportHandler> RxHandlerRegistration()
{
return new Dictionary<EmailAccountType, IEmailTransportHandler>
{
// following registrations use ActiveUp library for pop3/imap (http://mailsystem.codeplex.com/)
{EmailAccountType.Pop3, ObjectFactory.GetInstance<ActiveUpPop3Handler>()},
{EmailAccountType.IMAP, ObjectFactory.GetInstance<ActiveUpIMap4Handler>()}
};
}
Then in the ServiceRegistry class:
public class EmailTransportServiceRegistry : ServiceRegistry
{
public EmailTransportServiceRegistry()
{
// other registries...
For<IDictionary<EmailAccountType, IEmailTransportHandler>>().Use(x => EmailTransportService.RxHandlerRegistration());
}
}
Related
I have a bunch of services for which I always want to get a unique instance with StructureMap. Now, I can get this working by configuring each type like:
ObjectFactory.Initialize(x =>
{
...
x.For<UserService>().AlwaysUnique();
...
});
But I don't really want to do that for every service type. The service types all inherit from ServiceBase - is there any way to configure StructureMap to use AlwaysUnique() for all types that inherit from ServiceBase?
I think you need to create a convention to achieve this:
using StructureMap.Pipeline;
using StructureMap.TypeRules;
using StructureMap.Configuration.DSL;
public class ConcreteLifecycleConvention : StructureMap.Graph.IRegistrationConvention
{
private readonly Type _baseType;
private readonly ILifecycle _lifecycle;
public ConcreteLifecycleConvention(Type baseType, ILifecycle lifecycle)
{
_baseType = baseType;
_lifecycle = lifecycle;
}
public void ScanTypes(TypeSet types, Registry registry)
{
foreach(var type in types.AllTypes())
{
if (type.IsAbstract || !type.CanBeCreated() || !type.CanBeCastTo(_baseType))
continue;
registry.For(type).LifecycleIs(_lifecycle);
}
}
}
Apply the convention in a scan:
ObjectFactory.Initialize(c =>
{
c.Scan(scan =>
{
scan.TheCallingAssembly();
scan.With(new ConcreteLifecycleConvention(typeof(ServiceBase),
new UniquePerRequestLifecycle()));
});
});
As a side note. With the built in conventions you can use the OnAddedPluginTypes to specify the lifecycle, but there is no built in convention that registers the concretes that is registered without an interface
scan.WithDefaultConventions().OnAddedPluginTypes(x =>
x.LifecycleIs(new UniquePerRequestLifecycle()));
Is it possible to register a type for all it's implementing interfaces? E.g, I have a:
public class Bow : IWeapon
{
#region IWeapon Members
public string Attack()
{
return "Shooted with a bow";
}
#endregion
}
public class HumanFighter
{
private readonly IWeapon weapon = null;
public HumanFighter(IWeapon weapon)
{
this.weapon = weapon;
}
public string Fight()
{
return this.weapon.Attack();
}
}
[Test]
public void Test2b()
{
Container container = new Container();
container.RegisterSingle<Bow>();
container.RegisterSingle<HumanFighter>();
// this would match the IWeapon to the Bow, as it
// is implemented by Bow
var humanFighter1 = container.GetInstance<HumanFighter>();
string s = humanFighter1.Fight();
}
It completely depends on your needs, but typically you need to use the Container's non-generic registration method. You can define your own LINQ queries to query the application's metadata to get the proper types, and register them using the non-generic registration methods. Here's an example:
var weaponsAssembly = typeof(Bow).Assembly;
var registrations =
from type in weaponsAssembly.GetExportedTypes()
where type.Namespace.Contains(".Weapons")
from service in type.GetInterfaces()
select new { Service = service, Implementation = type };
foreach (var reg in registrations)
{
container.Register(reg.Service, reg.Implementation);
}
If you need to batch-register a set of implementations, based on a shared generic interface, you can use the RegisterManyForOpenGeneric extension method:
// include the SimpleInjector.Extensions namespace.
container.RegisterManyForOpenGeneric(typeof(IValidator<>),
typeof(IValidator<>).Assembly);
This will look for all (non-generic) public types in the supplied assembly that implement IValidator<T> and registers each of them by their closed-generic implementation. If an type implements multiple closed-generic versions of IValidator<T>, all versions will be registered. Take a look at the following example:
interface IValidator<T> { }
class MultiVal1 : IValidator<Customer>, IValidator<Order> { }
class MultiVal2 : IValidator<User>, IValidator<Employee> { }
container.RegisterManyForOpenGeneric(typeof(IValidator<>),
typeof(IValidator<>).Assembly);
Assuming the given interface and class definitions, the shown RegisterManyForOpenGeneric registration is equivalent to the following manual registration:
container.Register<IValidator<Customer>, MultiVal1>();
container.Register<IValidator<Order>, MultiVal1>();
container.Register<IValidator<User>, MultiVal2>();
container.Register<IValidator<Employee>, MultiVal2>();
It would also be easy to add convenient extension methods. Take for instance the following extension method that allows you to register a single implementation by all its implemented interfaces:
public static void RegisterAsImplementedInterfaces<TImpl>(
this Container container)
{
foreach (var service in typeof(TImpl).GetInterfaces())
{
container.Register(service, typeof(TImpl));
}
}
It can be used as follows:
container.RegisterAsImplementedInterfaces<Sword>();
I am about to switch from Windsor to Structuremap for an existing project with ~100 registered components (mostly singletons).
All components inherit from a common base class that provides logging and health tracking and for this reason, contains a "Name" property used to identify component instances.
With Windsor, it was possible to set the component's Name property to the name that was used to register the component in the IOC container (We used a Facility for this).
My question: Is something like this possible with Structuremap?
(I dream of a call to c.For<IFoo>.Use<Bar>.Named("Doe") that magically results in instanceOfBar.Name = "Doe" somewhere.)
Here is what I tried:
using System;
using StructureMap;
using StructureMap.Interceptors;
using System.Diagnostics;
namespace ConsoleApplication1
{
interface IServiceA { }
interface IServiceB { }
class Base
{
public string Name { get; set; }
}
class ComponentA : Base, IServiceA { }
class ComponentB : Base, IServiceB
{
public ComponentB(IServiceA serviceA)
{
this.ServiceA = serviceA;
}
public IServiceA ServiceA { get; private set; }
}
class SetNameInterceptor : TypeInterceptor
{
public bool MatchesType(Type type) { return true; }
public object Process(object target, IContext context)
{
// *** Any other way? This does not work...
string name = context.BuildStack.Current != null ? context.BuildStack.Current.Name : context.RequestedName;
((Base)target).Name = name;
return target;
}
}
class Program
{
static void Main(string[] args)
{
Container container = new Container(c =>
{
c.RegisterInterceptor(new SetNameInterceptor());
c.For<IServiceA>().Use<ComponentA>().Named("A");
c.For<IServiceB>().Use<ComponentB>().Named("B");
});
var b = container.GetInstance<IServiceB>();
// both Fail:
Debug.Assert(((ComponentB)b).Name == "B");
Debug.Assert(((ComponentA)((ComponentB)b).ServiceA).Name == "A");
}
}
}
The above obviously does not work, I tried several variations but had no luck. The registered name of the target object does not seem to be consistently reachable via IContext.
My second best approach would be to define a new "NamedComponent(...)" extension method that resolves to Named(name).WithProperty(x => x.Name).EqualTo(name), but I wonder if this can be avoided to keep component registration as "structuremap-like" as possible?
Am I missing something?
I've never used WithProperty before but if it works the way I'd expect it should do the trick for you.
I think I would favor using EnrichWith though. Something like:
c.For<IFoo>().Use<Foo>().Named(name).EnrichWith(f => f.Name = name);
EnrichWith is a bit more explicit about what it's doing IMO, and lets you call any code on your instance before returning it to the caller. I like that this lets you do a straightforward assignment as well.
There is also a more complex handler you can use with EnrichWith that gives access to the context of the request - this would allow you to do something like this:
c.For<IFoo>().Use<Foo>().Named(name)
.EnrichWith((c, i) => {
i.Name = c.RequestedName;
return i;
});
This may be overkill for your situation but the contextual awareness can be pretty useful.
I can do this in Castle Windsor:
public abstract class AbstractFactory
{
protected AbstractFactory(Foo constructorParm)
{
// Do something with parameter...
}
}
public class DescendentFactory : AbstractFactory
{
public DescendentFactory(Foo constructorParm) : base(constructorParm)
{
}
}
// The container is configured via XML, the service AbstractFactory and the
// type DescendentFactory
container.Resolve<AbstractFactory>("DescendentFactoryId", new { constructorParm = injectedValue });
Is this possible in Unity? I've tried doing it but it complains that it can't find the constructor. It seems I can only inject via the sub-type.
You can only inject via the sub-type. It needs a public constructor.
I have a structuremap configuration that has me scratching my head. I have a concrete class that requires a interfaced ui element which requires an interfaced validation class. I want the outer concrete class to get the default ui element, but get a concrete-class-specific validation object. Something like this:
class MyView
{
IPrompt prompt
}
class GenericPrompt : IPrompt
{
IValidator validator
}
class MyValidator : IValidator
{
bool Validate() {}
}
How can I configure structuremap with the Registry DSL to only use MyValidator when creating dependencies for MyView. (And assumedly using BobsValidator when creating dependencies for BobsView)
Are you getting MyView (and BobsView) from the container? Can we assume that they will all take an instance of IPrompt?
One approach would be to register all of your validators with a name that matches the names of your view. You could implement your own type scanner that just removes the Validator suffix:
public class ValidatorScanner : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (!typeof (IValidator).IsAssignableFrom(type)) return;
var validatorName = type.Name.Replace("Validator", "");
graph.AddType(typeof(IValidator), type, validatorName);
}
}
Now, if you assume an IPrompt will always be requested by a View that follows that naming convention, your registry could look like:
public class ValidatorRegistry : Registry
{
public ValidatorRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.With<ValidatorScanner>();
});
ForRequestedType<IPrompt>().TheDefault.Is.ConstructedBy(ctx =>
{
var viewName = ctx.Root.RequestedType.Name.Replace("View", "");
ctx.RegisterDefault(typeof(IValidator), ctx.GetInstance<IValidator>(viewName));
return ctx.GetInstance<GenericPrompt>();
});
}
}
To retrieve your view with the appropriate validator, you would have to request the concrete type:
var view = container.GetInstance<MyView>();
Note that this will only work if you are retrieving your view with a direct call to the container (service location), since it depends on the "Root.RequestedType". Depending on how you plan to get your views, you might be able to walk up the BuildStack looking for a View (instead of assuming it is always Root).