If I put Lazy in constructor of my object, and X is not registered in container I got dependency resolution exception.
Why am I getting this exception? I dislike it, because I cannot choose component at runtime. Example usecase:
class Controller
{
public Controller(Lazy<A> a, Lazy<B> b) { /* (...) */ }
Lazy<A> a;
Lazy<B> b;
public IActionResult Get(){
if(someConfig)
return Json(a.Value.Execute());
else
return Json(b.Value.Execute());
}
}
To do so I need to register both components A an B. My program fails even if B is never used. I would like to have B be optional, and still managed by autofac.
This is even bigger issue if I have list of components, and want only one to be used. For example:
class Controller
{
Controller(IEnumerable<Component> components) { /* (...) */ }
IActionResult Get()
{
return components.First(n => n.Name == configuredComponent).Execute();
}
}
I am no longer getting exception is something is not registered, however still everything is constructed. Also it would be awkward to use.
If you add a reference to a Lazy<T> component, Autofac has to know (basically) how to create the internal function that will run should you want to resolve it, even if you don't resolve it.
Basically, it needs to be able to create this in memory:
var lazy = new Lazy<T>(() => scope.Resolve<T>());
Autofac requires all of the things you want to resolve to be registered. It doesn't let you register things on the fly - it must be explicit. So the thing you're trying to do won't work (as you saw).
Instead, use a single interface and two different implementations of that interface. Change the registration based on your configuration value.
var builder = new ContainerBuilder();
if(someConfig)
{
builder.RegisterType<A>().As<IService>();
}
else
{
builder.RegisterType<B>().As<IService>();
}
Then in your controller, inject the interface rather than the concrete class.
public MyController(Lazy<IService> service)
There are also other options you could do, like use metadata for components or keyed services. For example, you could add some metadata based on your configuration and resolve using that.
builder.RegisterType<A>()
.As<IService>()
.WithMetadata("Name", "a");
builder.RegisterType<B>()
.As<IService>()
.WithMetadata("Name", "b");
In the controller, you'd get a dictionary of them:
public MyController(IEnumerable<Meta<IService>> services)
{
var service = services.First(s => s.Metadata["Name"].Equals(someConfig);
}
That's a very short example, but the docs show a lot more.
In any case, the interface is really going to be your key to success. If you're just using concrete classes, they'll have to be registered whether you use them or not.
Related
I'm in the process of integrating a messaging broker within our existing application using MassTransit.
We had already implemented a kind of command handler that had generic implementations, like this:
public class MyCommandHandler: CommandHandlerBase<MyCommand>
Now it was relatively easy to make a generic Consumer that would do some boiler plating and would hand off the work to the ready command handler, requested from the DI container.
public class CommandConsumer<TCommand> : IConsumer<TCommand>
Which I could then easily register through the Microsoft DI thusly:
cfg.AddConsumer<CommandConsumer<MyCommand>>(x => some configuration...);
This all worked great, so I moved on to the next step, namely to extract the consumer registration(s) to a common helper method and this is where I'm a bit stumped. The method (currently) looks a bit like this
public static IServiceCollection ConfigureMassTransit(this IServiceCollection services, params Type[] consumerTypes)
{
return
services.AddMassTransit(cfg =>
{
foreach (var consumerType in consumerTypes)
{
cfg.AddConsumer(consumerType);
}
// or cfg.AddConsumers(consumerTypes);
cfg.AddBus(context => Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host("localhost", "/",
h =>
{
h.Username("guest");
h.Password("guest");
});
config.ConfigureEndpoints(context);
}));
});
}
which would be called as services.ConfigureMassTransit(typeof(CommandConsumer<MyCommand>));
This again works, but what I can't figure out is how to add the additional configuration to the registration; the overload that takes an Action is only available when using the generic signature, which you can't use directly when you only have the Type available. I tried adding a marker class CommandConsumer: IConsumer to the CommandConsumer<TCommand> and making CommandConsumerDefinition : ConsumerDefinition<CommandConsumer>, and changing the above to cfg.AddConsumer(consumerType, typeof(CommandConsumerDefinition));, but that doesn't work as the ConfigureConsumer override is never hit.
How am I supposed to add additional configuration to a Consumer for which I don't know the type at compile time?
Chris' answer put me on the path to a working solution. Making the CommandConsumerDefinition generic allowed me to use reflection to construct both types in the same way at runtime. This allowed MassTransit to wire up the configuration in the expected manner.
In the end, I also used a "marker" attribute that would hold the type of the command contract, so they could be discovered rather than having to be entered as a parameter at startup.
public static IServiceCollectionConfigurator ConfigureMassTransitConsumers(this IServiceCollectionConfigurator serviceConfigurator, Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var attributes = type.GetCustomAttributes(typeof(RegisterCommandConsumerAttribute), false);
if (attributes.Length <= 0) continue;
foreach (var attribute in attributes)
{
if (attribute is RegisterCommandConsumerAttribute registerCommandConsumerAttribute)
{
Type consumerType = typeof(CommandConsumer<>).MakeGenericType(registerCommandConsumerAttribute.CommandType);
Type consumerDefinitionType = typeof(CommandConsumerDefinition<>).MakeGenericType(registerCommandConsumerAttribute.CommandType);
serviceConfigurator.AddConsumer(consumerType, consumerDefinitionType);
}
}
}
return serviceConfigurator;
}
Because of the automatic discovery, we were already in the realm of reflection, so this seemed like an acceptable solution. This way we can have generic consumers and definitions without having to add a new class for every command contract we have.
Consider the following example:
public class CommunicationClient : IClient
{
public CommunicationClient(IServerSettings settings) { ... }
// Code
}
public class SettingsManager : ISettingsManager
{
SettingsManager(IDbSettingManager manager)
// Code
public IDictionary<string, string> GetSettings() { ... }
}
Problem:
While performing registrations (using SimpleInjector), I need to provide values that are obtained from an instance of SetingsManager and fill ServerSettings instance (concrete type for IServerSettings) but if I call GetInstance<ISettingsManager> before registering CommunicationClient, it gives me an error that I cannot do that
Error:
The container can't be changed after the first call to GetInstance, GetAllInstances and Verify.)
One solution could be to inject ISettingsManager as a dependency to CommunicationClient but I really don't want to pass it as it would provide more than required information to it.
EDIT: Container Registration
container.Register(typeof(ICommunicationClient), typeof(CommunicationClient));
ISettingsManager settingsManager = container.GetInstance<ISettingsManager>();
string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");
container.Register(typeof(IServerConfiguration), () =>
new ServerConfiguration(url, userName, password);
Any suggestions/alternative solutions on how to achieve above in a cleaner way? Thanks.
Simple Injector locks the container for further changes after its first use. This is an explicit design choice, which is described here. This means that you can't call Register after you called GetInstance, but there should never be a reason to do this. Or in other words, your configuration can always be rewritten in a way that you don't need this. In your case your configuration will probably look something like this:
var settingsManager = new SettingsManager(new SqlSettingManager("connStr"));
container.RegisterSingle<ISettingsManager>(settingsManager);
container.Register<ICommunicationClient, CommunicationClient>();
string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");
container.Register<IServerConfiguration>(() =>
new ServerConfiguration(url, userName, password));
There you see that SettingsManager is not built-up by the container. When using a DI container, you are not required to let the DI container build up every instance for you. Letting the container auto-wire instances for you is done to lower the maintenance burden of your Composition Root and makes it easier to apply cross-cutting concerns (using decorators for instance) to groups of related classes. In the case of the SettingsManager and SqlSettingsManager classes, it is very unlikely that their constructor will change that often that it will increase the maintenance burden of your Composition Root. It's therefore perfectly fine to manually create those instances once.
If I understand correctly, to create your CommunicationClient class, you need to pass information that are retrieved by calling a method on an instance of your ISettingsManager, but you don't want to pass the ISettingsManager as a dependency to your CommunicationClient?
One solution for that would be to create, and register, a factory that would have a dependency on ISettingsManager and that would have a CreateClient method that would return the configured client.
public class CommunicationClientFactory : ICommunicationClientFactory
{
public CommunicationClientFactory(ISettingsManager settingsManager) {...}
public CreateClient() {...}
}
This way your CommunicationClient is not dependent on the ISettingsManager and you have just this factory that does the work of creating your instance.
Edit:
An alternative, if you don't want to create a factory for this, would be to have your CommunicationClient object be created in an "invalid" state, and have a method that would set the settings and make its state valid.
Something like:
public class CommunicationClient : IClient
{
public CommunicationClient() { ... }
// Code
CommunicationClient WithSettings(IServerSettings settings) { ... }
}
Of course, then you'd have to make sure that the user don't use it when the settings have not been passed yet, potentially sending an exception if that would be the case. I like this solution less, because it's less explicit that you NEED those settings to have your object in a correct state.
When creating a StructureMap container, I typically do something like this:
var container = new Container(registry => {
registry.AddRegistry<MyRegistry>();
});
Where
public class MyRegistry : Registry {
public MyRegistry() {
Scan(x => {
x.Assembly("My.Assembly.Name");
x.RegisterConcreteTypesAgainstTheFirstInterface();
}
}
}
However, this has resulted in quite many registry.AddRegistry lines in a bootstrapper file, which is then copied into many projects. I would like to be able to call a method taking a constructed container and adding a registry to it, so I can modularize libraries.
I came up with this:
public static void Setup(ref Container container) {
container.PluginGraph.ImportRegistry(typeof(MyRegistry));
}
This works, in that the registry is properly added to the container (seen by calling container.WhatDoIHave() before and after), but it seems the actual mappings are not done - ie the interface IFoo is not registered to concrete class Foo that both are defined in My.Assembly.Name.
What is the difference in doing ImportRegistry and AddRegistry? Can my Setup method be fixed?
Did you try Container.Configure()? It exists to configure a container after it has already been initialized.
So this will be an interesting post because I must include all my code and will attempt to explain clearly how I have setup my architecture.
I have placed all my Service and DataContracts in a central assembly (DMT.WCF.Contracts). This is done so that the distributed pieces of my application can all reference the same type of service interfaces and contracts which is very nice.
I have setup a StructureMap container to inject my dependencies in the following manner, by specifying a ServiceContext, which will house all of the Service Interface properties so that they can be referenced int he application later.
public interface IServiceContext
{
}
public class ServiceContext: IServiceContext
{
public IAuthenticationService AuthenticationService { get; set; }
public ServiceContext(IAuthenticationService authenticationService)
{
AuthenticationService = authenticationService;
}
}
Then, I have my StructureMapControllerFactory which looks like the following:
public class StructureMapControllerFactory:DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null) return null;
return ObjectFactory.GetInstance(controllerType) as IController;
}
}
and this is configured in my global.asax like the following:
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Configure();
}
I wanted to decouple my services as much as possible from my appliction, so I have implemented the following ServiceFactory class that handles providing proxies to StructureMap when the IoC container is configured:
public static class ServiceFactory
{
private static readonly ClientSection _clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
public static T Create<T>()
{
T context = default(T);
foreach(ChannelEndpointElement endpoint in _clientSection.Endpoints)
{
if(endpoint.Contract == typeof(T).FullName)
{
IEnumerable<Type> assignables = typeof (Binding).Assembly.GetTypes().Where(p => typeof(Binding).IsAssignableFrom(p));
Type bindingType = assignables.Single(p => p.Name.ToLower().Equals(endpoint.Binding.ToLower()));
context = ChannelFactory<T>.CreateChannel((Binding)Activator.CreateInstance(bindingType, false), new EndpointAddress(endpoint.Address));
}
}
return context;
}
}
This allows me to pull directly from the config file when creating proxies so I do not need to select "Add Service Reference" (as that is technically adding a dependency).
In my global.asax, I can now configure my StructureMap Container like this:
protected void Configure()
{
ObjectFactory.Configure(x =>
{
x.Scan(scanner => scanner.AddAllTypesOf<IController>());
x.For<IAuthenticationService>().Use(ServiceFactory.Create<IAuthenticationService>());
x.For<IServiceContext>().Use<ServiceContext>();
});
}
Although I was initially able to use this in the following manner:
IAuthenticationService service = ServiceContext.AuthenticationService.Authenticat(...);
I am now unable to start my application without exceptions being thrown such as the following:
StructureMap configuration failures:
Error: 104
Source: Registry: StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.1.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Type Instance '685e2e2a-f271-4163-a6fa-ba074e4082d1' (Object: DMT.WCF.Contracts.Authentication.IAuthenticationService) cannot be plugged into type DMT.WCF.Contracts.Authentication.IAuthenticationService, DMT.WCF.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
I am not sure why this is occuring. Like I said, I was initially able to get this up and running, but am not sure what has changed.
I have looked at the many of hundreds of references regarding this error message, but they are all specific to problems that dont seem to match mine, unless I am overlooking my problem.
HELP!!!
Doesn't this operation use the ChannelFactory to new up a channel safe client?
context = ChannelFactory<T>.CreateChannel(
(Binding)Activator.CreateInstance(bindingType, false),
new EndpointAddress(endpoint.Address));
Well, two issues here. As Sixto Saez mentioned, there's WCF issues to consider. On the StructureMap front, my guess is that your factory method may be returning a default instance for an interface.
Two suggestions...
Right after your container configuration, add a call to ObjectFactory.AssertConfigurationIsValid()...make sure you remove it again after you figure out what's wrong :-) it should throw a very similar error, but it will actually try to resolve every instance of every configured type. Usually you'll get a very verbose error with everything that's wrong. You can then start finding where your configuration error is.
It may have something to do with your factory method for the pluggable type. You might try having those instances created on the fly. Use the IContext syntax to do that - context => // Make Foo Here.
protected void Configure()
{
ObjectFactory.Configure(x =>
{
x.Scan(scanner => scanner.AddAllTypesOf<IController>());
// Skip using this
// x.For<IAuthenticationService>()
// .Use(ServiceFactory.Create<IAuthenticationService>());
// Use the IContext syntax instead. Normally you'd grab the instance out of the
// container, but you can use this to resolve an instance "live" from
// somewhere other than the container
x.For<IAuthenticationService>()
.Use(context => ServiceFactory.Create<IAuthenticationService>());
x.For<IServiceContext>().Use<ServiceContext>();
});
// Remove this from production code because it resolves the entire container...
ObjectFactory.AssertConfigurationIsValid();
}
I'm guessing that using the IContext syntax may help fix the configuration errors. You can use the Assert to go from there if not. I think the other comments cover the WCF issues, but it's kind of hard to assess those while StructureMap is misconfigured.
Right now we have a dll file that contains all the database calls and i can't change it. However i need to call i from my Mvc 3 project. The process to call it is simple, i use the following:
ManageProvider.GetProxy<T>(ident);
T is an interface that i want to get the class back from (its like an IoC of its own) and ident is the user identification class. So by calling
var classReturned = ManageProvider.GetProxy<ICommunity>(new UserIden{ Email = "test#test.com" });
I would get a class back with all the community functions.
Now i want to implement Unity in my Mvc 3 project. The question is, can i somehow add these calls to the dll file through unity?
I want to resolve the call by using:
var classReturned = myContainer.Resolve<ICommunity>(new UserIden{ Email = "test#test.com" });
How can i register this in Unity (or is it even possible) ?
Update:
1) Is it better to call the methods with the email/user ident instead of defining a Dependency property? (ex below)
2) There is a bout 20 or so interfaces in the dll file right now. Should i add them all to the same reposatory? (ex below)
public class ProxyWrapper : IDllRepository
{
[Dependency]
public UserIdent UserIdent { get; set; }
public ICommunity GetCommunity()
{
return ManageProvider.GetProxy<ICommunity>(UserIdent);
}
public IDesktop GetDesktop()
{
return ManageProvider.GetProxy<IDesktop>(UserIdent);
}
}
public interface IDllRepository
{
ICommunity GetCommunity();
IDesktop GetDesktop();
}
Whats the best way and how would i call it from my code?
Does the [Dependency] attribute also fall into the Service Locator anti pattern?
Update 23.05.11
1) Yes, something like that. They contain all the logic that is provided to all the projects that includes the dll file.
Regarding the ManagerProvider. It accepts an interface and returns the class that is mapped to this interface. So for the community, the interface looks like this (removed a lot of calls to keep it short, there is also posts, comments, community create/update etc):
List<CommunityThread> GetThreads(int pStartRowIndex, int pMaximumRows, string pOrderBy, string pSearchExpression);
Guid? CreateThread(string pTitle, string pDescription, string pPostContent);
bool DeleteThread(Guid pThreadId);
List<CommunityThread> GetCommunityUserThreads(Guid pCommunityUserId);
2) What i can't update is how the ManageProvider.GetProxy works. The GetProxy is a class in the dll file that is hardcoded. Here is the part for the community. The class does the same for all the other interfaces as well, if typeof(interface) ... return class.
private static IManageProxy GetProxyForInterface<T>(UserIdent pIdent)
{
....
if (typeof(T).Equals(typeof(ICommunity)))
return new PCommunity();
....
}
3) Once registered using this new wrapper class, i can call it through the following code (MvcUnityContainer is a static class that only has a property called Container):
var c = MvcUnityContainer.Container.Resolve<IBackendRepository>(new PropertyOverride("UserIdent",
new UserIdent()));
Global.asax
IUnityContainer container = InitContainer();
MvcUnityContainer.Container = container;
DependencyResolver.SetResolver(new UnityMvcResolver(container));
The question is, do i need the static class MvcUnityContainer? Is it possible to configure the DependecyResolver to do that for me? Something like (problem is that it doesn't accept the override parameter):
var c = DependencyResolver.Current.GetService<IBackendRepository>(new PropertyOverride("UserIdent", new UserIdent()));
I think you need to hide the creation behind another abstraction, for instance:
public interface ICommunityRepository
{
ICommunity GetByEmailAddress(string address);
}
public class ManageProviderCommunityRepository
: ICommunityRepository
{
public ICommunity GetByEmailAddress(string address)
{
var id = new UserIden { Email = address };
return ManageProvider.GetProxy<ICommunity>(id);
}
}
This will hide both the ManageProvider and the UserIden behind this abstraction, and allows you to replace it later on with something more useful and makes testing easier.
Registration now is very easy:
RegisterType<ICommunityRepository, ManageProviderCommunityRepository>();
Instead of calling myContainer.Resolve (as you do in your example), inject the dependencies in your classes. This prevents you from using the Service Locator anti-pattern.
Perhaps you could do something like this, using the InjectionFactory:
myContainer.RegisterType<ICommunity>(
new InjectionFactory(c => ManageProvider.GetProxy<ICommunity>(new UserIden {Email = "test#test.com"})));
var classReturned = myContainer.Resolve<ICommunity>();
... Though you wouldn't be able to pass the UserIden as a parameter to the Resolve call, so I'm not sure if this is what you want.
To register all the public classes of the assembly you could perhaps iterate over Assembly.GetTypes() and register them in the same way?