In my current project I'm currently trying to replace the Windsor IoC in favour of structure map (2.6.1). But having a bit of problem registering some generic types. How would I register IFilterConverter<T> to use FilterConverter<SomeSpecificType>. I've tried ConnectImplementationsToTypesClosing(IFilterConverter) but from what I've read (Jimmy Bogard's article) I would need a concrete type defined like so:- SomeConcreteType : IFilterConverter<SomeSpecificType> for that to work and I don't have that.
So to reiterate if I have a type that takes a constructor argument IFilterConverter<SomeSpecificType>, I want structure map to provide me with FilterConverter<SomeSpecificType>.
With Windsor I was using the XML config option (which I want to get away from) But all I did was just set up the configuration like so:
<component id="IFilterConverter" service="SomeNamespace.IFilterConverter`1, SomeNamespace" type="SomeNamespace.FilterConverter`1, SomeNamespace" lifestyle="PerWebRequest">
How do I do the equivalent in SM (using code, not XML config files)
Thanks
I think this should do it.
_container = new Container();
_container.Configure(x =>
{
x.For(typeof (IFilterConverter<>)).Use(typeof (FilterConverter<>));
});
Related
I am trying to create a dependency graph in StructureMap 3 with a chain of decorators:
Each instance has a constructor with multiple arugments, but exactly one argument of an inner IGeocoder, e.g.
public SomeCachingGeocoder(IGeoCoder inner, IFoo somethingElse)
I am hooking them up like this:
For<OviGeoCoder>().Use<OviGeoCoder>();
For<SqlCachingGeocoder>().Use<SqlCachingGeocoder>().Ctor<IGeoCoder>().Is<OviGeoCoder>();
For<RedisCachingGeocoder>().Use<RedisCachingGeocoder>().Ctor<IGeoCoder>().Is<SqlCachingGeocoder>();
For<IGeoCoder>().Use<RedisCachingGeocoder>();
But I get
Bi-directional dependency relationship detected! Check the StructureMap stacktrace below:
1.) Instance of SOAM.Services.IGeoCoder (SOAM.Services.Geocoding.RedisCachingGeocoder)
2.) new RedisCachingGeocoder(Default of IDatabase, Default of IGeoCoder)
3.) SOAM.Services.Geocoding.RedisCachingGeocoder
4.) Instance of SOAM.Services.IGeoCoder (SOAM.Services.Geocoding.RedisCachingGeocoder)
5.) new HomeController(Default of IGeoCoder, Default of IAlertService)
6.) SOAM.Web.Controllers.HomeController
7.) Instance of SOAM.Web.Controllers.HomeController
8.) Container.GetInstance(SOAM.Web.Controllers.HomeController)
Any ideas how to solve this?
DecorateAllWith allows auto-wiring by default and allows stacking decorators in a quite easy way:
For<IGeoCoder>().Use<OviGeoCoder>();
For(typeof(IGeoCoder)).DecorateAllWith(typeof(SqlCachingGeocoder));
For(typeof(IGeoCoder)).DecorateAllWith(typeof(RedisCachingGeocoder));
If for some reason you cannot use DecorateAllWith() then this should work:
var container = new Container(
c =>
{
c.For<IFoo>().Use<Foo>();
c.For<IGeoCoder>().Add<OviGeoCoder>().Named("default");
c.For<IGeoCoder>()
.Add<SqlCachingGeocoder>()
.Ctor<IGeoCoder>()
.Is(ctx => ctx.GetInstance<IGeoCoder>("default"))
.Named("SqlCaching");
c.For<IGeoCoder>()
.Use<RedisCachingGeocoder>()
.Ctor<IGeoCoder>()
.Is(ctx => ctx.GetInstance<IGeoCoder>("SqlCaching"));
});
Wanna find what's the difference when using Use vs Add? Take a look here
I'm using WebAPI + Autofac + Automapper, with a repository for data access. I need to map a model to my domain entities, specifically, I need to convert an identity value to the actual entity. No big deal, right? I've done this in MVC with no problem. I will simplify what I am doing to expose the essentials.
public class EntityConverter<T> : ITypeConverter<int, T>
where T : Entity
{
public EntityConverter(IRepository<T> repository)
{
_repository = repository;
}
private readonly IRepository<T> _repository;
public T Convert(ResolutionContext context)
{
_repository.Get((int) context.SourceValue);
}
}
Repositories are registered with Autofac, and are managed as InstancePerApiRequest because of session/transaction management. So, I need to register my converter in that same scope:
builder.RegisterGeneric(typeof(EntityConverter<>))
.AsSelf()
.InstancePerApiRequest();
The Automapper config looks something like:
var container = builder.Build(); // build the Autofac container and do what you will
Mapper.Initialize(cfg => {
cfg.ConstructServicesUsing(container.Resolve); // nope nope nope
// configure mappings
cfg.CreateMap<int, TestEntity>().ConvertUsing<EntityConverter<TestEntity>>()
});
Mapper.AssertConfigurationIsValid();
So here's the part that sucks. I am to understand Automapper requires the ConstructServicesUsing guy to be set before you build your config. If you set it later, it won't be used. The example above won't work because container is the root scope. If I try and resolve EntityConverter<TestEntity>, Autofac will complain that the requested type is registered for a different scope, and the one you're in ain't it. Makes sense, I want the scope created by WebApi.
Let me pause a sec and cover one fact about WebApi dependency injection (I don't really think this is Autofac-specific). WebApi creates an IDependencyScope for the request, and stashes it in the HttpRequestMessage.Properties. I can't get it back again unless I have access to that same HttpRequestMessage instance. My AsInstancePerApiRequest scoping on IRepository and my converter thus rely on that IDependencyScope.
So, that's really the meat and potatoes of the problem, and I really frustrated with this difference from MVC. You can't do
cfg.ConstructServicesUsing(GlobalConfiguration.Configuration.DependencyResolver.GetService);
That's equivalent to using container.Resolve. I can't use
GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetService
because A) that creates a new scope next to the one I actually want B) doesn't really let me clean up the new scope I created. Using Service Locator is a new way to have the same problem; I can't get to the scope WebApi is using. If my converter and its dependencies were single instance or instance per dependency, it wouldn't be a problem, but they aren't, so it is, and changing that would create lots more problems for me.
Now, I can create AutoMapper config with Autofac and register it as a single instance. I can even create per-request IMappingEngine instances. But that doesn't do me any good if the service constructor always uses that single delegate you register at the beginning, which has no access to the current scope. If I could change that delegate per each mapping engine instance, I might be in business. But I can't.
So what can I do?
Another option, this time it's built-in, is to use the per-map options:
Mapper.Map<Source, Destination>(dest, opt => opt.ConstructServicesUsing(type => Request.GetDependencyScope().GetService(typeof(YourServiceTypeToConstruct))));
Don't bother with setting up the global IoC config in your mapping configuration.
Another option is to use your IoC tool to configure how to instantiate the MappingEngine:
public MappingEngine(
IConfigurationProvider configurationProvider,
IDictionary<TypePair, IObjectMapper> objectMapperCache,
Func<Type, object> serviceCtor)
The first one is just Mapper.Configuration, the second should probably be a singleton, and the third you can fill in with the current nested container's resolution. This would simplify from having to call the Map overload every time.
Update: Automapper was updated to support that feature. See #Jimmy Bogard 's answer
This solution could be not very nice, but it works. The solution relates to WebAPI 2, I'm not sure about previous versions.
In WebAPI 2 you can get current IDependencyScope from current HttpRequestMessage via GetDependencyScope() extension method. Current HttpRequestMessage is stored in the Items property of the current HttpContext. Knowing that your factory could look like:
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(serviceTypeToConstruct =>
{
var httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
var currentDependencyScope = httpRequestMessage.GetDependencyScope();
return currentDependencyScope.GetService(serviceTypeToConstruct);
});
// configure mappings
// ...
});
This may or may not be suitable for you.. but here goes:
We recently did this.. for model binders in MVC. Our model binders (on GET requests) now use Ninject-managed Services to build models.
Basically, we inject a factory (using Ninject's Factories extension.. perhaps there is a similar one for Autofac) into an "AutomapperBootstrapper" class, which in turn creates Automapper mapping Profile's and adds them to Automapper. Somewhat like this:
Mapper.Initialize(cfg =>
{
cfg.AddProfile(_factory.CreateServiceViewModelMappingProfile());
// etc..
});
The mappings Profile's themselves use MapFrom(), which is evaluated each time a mapping occurs. Something like this:
Mapper.CreateMap<Service, ServiceViewModel>()
.ForMember(x => x.Regions,
opt =>
opt.MapFrom(x => getRegions()))
private IEnumerable<Region> getRegions() {
return _factory.CreateTheService().GetRegions();
}
Each time the model binder is fired up, Ninject still wires up all dependencies for the request and it all filters down.
(For those interested, this setup basically lets us do this: /Area/Controller/Action/12, and our controller action method is this:
[HttpGet]
public ActionResult Action(ServiceViewModel model) {
// ...
}
).
I've been trying to setup AutoMapper to instantiate all objects via Ninject.
I've got the following code in my global.asax file
Mapper.Configuration.ConstructServicesUsing(x => kernel.Get(x));
And as an example I have the following mapping
Mapper.CreateMap<TestModel, IndexViewModel>();
However, this does not appear to be working. I get an error that 'IndexViewModel' does not have a default constructor.
I can get the mapper to work by explicitly telling automapper to use ninject in the mapping.
Mapper.CreateMap<TestModel, IndexViewModel>().ConstructUsingServiceLocator();
However, I'd rather not have to do this for every single mapping. Am I missing something?
Just create a function to do this for you somewhere in your initialisation code
void CreateMapWithServiceLocator<T1,T2>()
{
Mapper.CreateMap<T1,T2>().ConstructUsingServiceLocator();
}
I am trying to create an index agent service for a multi-instance Solr install using SolrNet. I have created the service, which will use an interface to create multiple agents to index with. These agents are specified in an external configuration file and instantiated dynamically. There can be 0-n of each agent type, for instance (note the url differences):
PersonAgent http://localhost:8080/solr
ProductAgent http://localhost:8080/solr
ProductAgent http://localhost:9999/solr
This of course needs to map to something like this:
ISolrOperations<Person>
ISolrOperations<Product>
ISolrOperations<Product>
Based on my needs and the fact that SolrNet does not support multiple instances for its default container, I am trying to use Castle Windsor for this. According to the SolrNet wiki at http://code.google.com/p/solrnet/wiki/MultiCoreAccess this is pretty straightforward.
var solrFacility = new SolrNetFacility("http://localhost:8983/solr/defaultCore");
solrFacility.AddCore("core0-id", typeof(Product), "http://localhost:8983/solr/product");
solrFacility.AddCore("core1-id", typeof(Product), "http://localhost:8983/solr/product2");
solrFacility.AddCore(typeof(Person), "http://localhost:8983/solr/person"); // no need to set an explicit ID since it's the only core for Person
container.AddFacility("solr", solrFacility);
ISolrOperations<Person> solrPerson = container.Resolve<ISolrOperations<Person>>();
ISolrOperations<Product> solrProduct1 = container.Resolve<ISolrOperations<Product>>("core0-id"); // use proper Windsor service overrides instead of resolving like this
ISolrOperations<Product> solrProduct2 = container.Resolve<ISolrOperations<Product>>("core1-id");
I'm not completely lost with the idea of IoC, but I am unsure what the wiki author meant with the comment to "use proper Windsor service overrides instead of resolving like this" as stated in the code sample. Obviously the example explicitly identifies the core via an id, but is there a better/more flexible way?
What I meant is that you normally don't resolve ISolrOperations<T> directly from the container.
Instead, you use service overrides or other Windsor mechanisms to define which ISolrOperations<T> component (which core) to pass to other components, especially when you have multiple cores with the same document type, e.g. in this example there are two components registered under the service type ISolrOperations<Product>.
All examples of fluent nhibernate make such(or similar) call:
c.AddMappingsFromAssembly(typeof(Product).Assembly);
I don't want tu use "typeof(Product).Assembly" as i don't want to have reference to my domain project here ("Procuct" class). In ordinary NHibernate I would just create hbm.xml files and make following entry in web.config:
<mapping assembly="TestingFluentHN"/>
but this entry does not work with FluentNHibernate. Is there an elegant way of providing assemblies in my session-building method? Preferably from configuration file.
Resources:
Context of unwanted code/dependency:
static NHSessionManager()
{
Configuration c = new Configuration();
//change following to sth that does not need refernce to domain
c.AddMappingsFromAssembly(typeof(Product).Assembly);
c.Configure();
sessionFactory = c.BuildSessionFactory();
}
My first idea was to read assemblies names from appSettings and load them:
var assembliesToMap = new List<string>();
foreach (var assemblyName in assembliesToMap)
{
var assembly = Assembly.LoadFile(assemblyName);
c.AddMappingsFromAssembly(assembly);
}
but that is my last option. I'm looking for sth build in fluent nhibernate.
I'm not aware of anything built into fluent nhibernate that will do what you want. You'll probably need to use the method you laid out in the end of your question.
I'm not sure if I'm just not getting the right picture of how your application is laid out, but the whole idea seems a bit misguided. You will need to take a dependency on the domain objects to query the session anyway, and it seems likely that this would be in the same assembly as the session factory's creation. If not, you may consider using dependency injection to provide a session manager (from a project that is aware of the domain objects).
If I'm missing something please let me know.