I'm having some trouble with the whole dependency injection with/through reflection.
The scenario is as following;
User authenticates via AzureAD through our identity server
If user is not in local database, save the user together with some other information
I keep restructuring my logic and I still can't make the puzzle work.
Currently this is my chain:
OWIN Startup:
I'm specifying a method to run after the OnTokenValidated event has triggered: ProfileEvents.ValidatedToken
services.AddAuthentication()
.AddOpenIdConnect("oidc", o =>
{
o.ClientId = $"{configuration["Globals:AzureApplication:AppId"]}";
o.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
o.Authority = $"https://login.microsoftonline.com/{configuration["Globals:AzureApplication:TenantId"]}";
o.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
o.Events = new OpenIdConnectEvents()
{
OnTokenValidated = ProfileEvents.ValidatedToken
};
});
DatabaseContext is added like this:
public void ConfigureServices(IServiceCollection services)
{
.... // other stuff
services.Configure<Config.ConnectionStrings>(configuration.GetSection("ConnectionStrings"));
services.AddDbContext<ModelContext>(options => options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
}
The my ValidatedToken method looks like this:
internal static class ProfileEvents
{
internal static Task ValidatedToken(TokenValidatedContext context)
{
var dbContext = new ProfileDAL();
dbContext.SaveToDb(context.Principal);
return Task.FromResult(0);
}
}
And finally my ProfileDAL looks like this:
public class ProfileDAL
{
internal async Task SaveToDb(ClaimsPrincipal principal)
{
var nameClaim = principal.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn");
var emailClaim = principal.FindFirstValue("email");
// TODO: Save user..
}
}
Now which way I turn I either have to use and pass IOptions through the chain so that ModelContext can override "OnConfiguring" to actually get the connection string or I have to pass the context.
Is there really no way to access either the connection string outside of the DI?
For me I feel like something like this would solve all of my current issues:
public partial class ModelContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer( ** Magic way to access connection string in appsettings ** );
}
}
When you speak of 'passing down the chain`, that shouldn't be necessary. If we're using a dependency injection container then we don't have to pass a dependency to one class, have that pass a dependency to another class, and so on. In fact, that's part of what we're avoiding by using the container.
We don't have to do that because each class is receiving its dependencies as abstractions (usually interfaces.) That means that one class can't pass something to its dependencies because it doesn't even know what that dependency needs.
Rather than passing something along from one class to another as if in a chain, each individual class is resolved with just the dependency it needs. None of the classes in the chain are responsible for supplying constructor arguments to their dependencies.
That becomes more difficult when we use static classes like ProfileEvents. You can't inject ProfileDAL into it because it's static. Then, because ProfileDAL isn't getting "newed" - new ProfileDAL() - there's no way to inject its dependencies.
That in turn means that you won't use constructor injection with ProfileDAL, and you'll look for other ways to create and pass in its dependencies. But use of the DI container should prevent that. All the way down the dependency graph everything is following the same pattern, where each class gets its dependencies in its constructor without knowing or caring what dependencies those classes have.
Related
I'm trying to write an ASP.NET Core 2.2 integration test, where the test setup decorates a specific service that would normally be available to the API as a dependency. The decorator would give me some additional powers I'd need in my integration tests to intercept calls to the underlying service, but I can't seem to properly decorate a normal service in ConfigureTestServices, as my current setup will give me:
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.Abstractions.dll but was not handled in user code
No service for type 'Foo.Web.BarService' has been registered.
To reproduce this, I've just used VS2019 to create a fresh ASP.NET Core 2.2 API Foo.Web project...
// In `Startup.cs`:
services.AddScoped<IBarService, BarService>();
public interface IBarService
{
string GetValue();
}
public class BarService : IBarService
{
public string GetValue() => "Service Value";
}
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IBarService barService;
public ValuesController(IBarService barService)
{
this.barService = barService;
}
[HttpGet]
public ActionResult<string> Get()
{
return barService.GetValue();
}
}
...and a companion xUnit Foo.Web.Tests project I utilize a WebApplicationfactory<TStartup>...
public class DecoratedBarService : IBarService
{
private readonly IBarService innerService;
public DecoratedBarService(IBarService innerService)
{
this.innerService = innerService;
}
public string GetValue() => $"{innerService.GetValue()} (decorated)";
}
public class IntegrationTestsFixture : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(di.GetRequiredService<BarService>()));
});
}
}
public class ValuesControllerTests : IClassFixture<IntegrationTestsFixture>
{
private readonly IntegrationTestsFixture fixture;
public ValuesControllerTests(IntegrationTestsFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public async Task Integration_test_uses_decorator()
{
var client = fixture.CreateClient();
var result = await client.GetAsync("/api/values");
var data = await result.Content.ReadAsStringAsync();
result.EnsureSuccessStatusCode();
Assert.Equal("Service Value (decorated)", data);
}
}
The behavior kind of makes sense, or at least I think it does: I suppose that the little factory lambda function (di => new DecoratedBarService(...)) in ConfigureTestServices cannot retrieve the concrete BarService from the di container because it's in the main service collection, not in the test services.
How can I make the default ASP.NET Core DI container provide decorator instances that have the original concrete type as their inner service?
Attempted solution 2:
I've tried the following:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(Server.Host.Services.GetRequiredService<BarService>()));
});
}
But this surprisingly runs into the same problem.
Attempted solution 3:
Asking for IBarService instead, like this:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(Server.Host.Services.GetRequiredService<IBarService>()));
});
}
Gives me a different error:
System.InvalidOperationException: 'Cannot resolve scoped service 'Foo.Web.IBarService' from root provider.'
Workaround A:
I can work around the issue in my small repro like this:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(new BarService()));
});
}
But this hurts a lot in my actual application, because BarService doesn't have a simple parameterless constructor: it has a moderately complex dependency graph, so I really would like to resolve instances from the Startup's DI container.
PS. I've tried to make this question fully self-contained, but there's also a clone-and-run rep(r)o for your convenience.
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
What we generally want is to overwrite the registration of the regular implementation by the decorated one, making use of the original one as a parameter to the decorator. As a result, asking for an IDependency should lead to a DecoratorImplementation wrapping the OriginalImplementation.
(If we merely want to register the decorator as a different TService than the original, things are even easier.)
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddSingleton<IDependency, OriginalImplementation>();
// Wouldn't it be nice if we could do this...
services.AddDecorator<IDependency>(
(serviceProvider, decorated) => new DecoratorImplementation(decorated));
// ...or even this?
services.AddDecorator<IDependency, DecoratorImplementation>();
}
The above code works once we add the following extension methods:
public static class DecoratorRegistrationExtensions
{
/// <summary>
/// Registers a <typeparamref name="TService"/> decorator on top of the previous registration of that type.
/// </summary>
/// <param name="decoratorFactory">Constructs a new instance based on the the instance to decorate and the <see cref="IServiceProvider"/>.</param>
/// <param name="lifetime">If no lifetime is provided, the lifetime of the previous registration is used.</param>
public static IServiceCollection AddDecorator<TService>(
this IServiceCollection services,
Func<IServiceProvider, TService, TService> decoratorFactory,
ServiceLifetime? lifetime = null)
where TService : class
{
// By convention, the last registration wins
var previousRegistration = services.LastOrDefault(
descriptor => descriptor.ServiceType == typeof(TService));
if (previousRegistration is null)
throw new InvalidOperationException($"Tried to register a decorator for type {typeof(TService).Name} when no such type was registered.");
// Get a factory to produce the original implementation
var decoratedServiceFactory = previousRegistration.ImplementationFactory;
if (decoratedServiceFactory is null && previousRegistration.ImplementationInstance != null)
decoratedServiceFactory = _ => previousRegistration.ImplementationInstance;
if (decoratedServiceFactory is null && previousRegistration.ImplementationType != null)
decoratedServiceFactory = serviceProvider => ActivatorUtilities.CreateInstance(
serviceProvider, previousRegistration.ImplementationType, Array.Empty<object>());
if (decoratedServiceFactory is null) // Should be impossible
throw new Exception($"Tried to register a decorator for type {typeof(TService).Name}, but the registration being wrapped specified no implementation at all.");
var registration = new ServiceDescriptor(
typeof(TService), CreateDecorator, lifetime ?? previousRegistration.Lifetime);
services.Add(registration);
return services;
// Local function that creates the decorator instance
TService CreateDecorator(IServiceProvider serviceProvider)
{
var decoratedInstance = (TService)decoratedServiceFactory(serviceProvider);
var decorator = decoratorFactory(serviceProvider, decoratedInstance);
return decorator;
}
}
/// <summary>
/// Registers a <typeparamref name="TService"/> decorator on top of the previous registration of that type.
/// </summary>
/// <param name="lifetime">If no lifetime is provided, the lifetime of the previous registration is used.</param>
public static IServiceCollection AddDecorator<TService, TImplementation>(
this IServiceCollection services,
ServiceLifetime? lifetime = null)
where TService : class
where TImplementation : TService
{
return AddDecorator<TService>(
services,
(serviceProvider, decoratedInstance) =>
ActivatorUtilities.CreateInstance<TImplementation>(serviceProvider, decoratedInstance),
lifetime);
}
}
This seems like a limitation of the servicesConfiguration.AddXxx method which will first remove the type from the IServiceProvider passed to the lambda.
You can verify this by changing servicesConfiguration.AddScoped<IBarService>(...) to servicesConfiguration.TryAddScoped<IBarService>(...) and you'll see that the original BarService.GetValue is getting called during the test.
Additionally, you can verify this because you can resolve any other service inside the lambda except the one you're about to create/override. This is probably to avoid weird recursive resolve loops which would lead to a stack-overflow.
There's actually a few things here. First, when you register a service with an interface, you can only inject that interface. You are in fact saying: "when you see IBarService inject an instance of BarService". The service collection doesn't know anything about BarService itself, so you cannot inject BarService directly.
Which leads to the second issue. When you add your new DecoratedBarService registration, you now have two registered implementations for IBarService. There's no way for it to know which to actually inject in place of IBarService, so again: failure. Some DI containers have specialized functionality for this type of scenario, allowing you to specify when to inject which, Microsoft.Extensions.DependencyInjection does not. If you truly need this functionality, you can use a more advanced DI container instead, but considering this is only for testing, that would like be a mistake.
Third, you have a bit of a circular dependency here, as DecoratedBarService itself takes a dependency on IBarService. Again, a more advanced DI container can handle this sort of thing; Microsoft.Extensions.DependencyInjection cannot.
Your best bet here is to use an inherited TestStartup class and factor out this dependency registration into a protected virtual method you can override. In your Startup class:
protected virtual void AddBarService(IServiceCollection services)
{
services.AddScoped<IBarService, BarService>();
}
Then, where you were doing the registration, call this method instead:
AddBarService(services);
Next, in your test project create a TestStartup and inherit from your SUT project's Startup. Override this method there:
public class TestStartup : Startup
{
protected override void AddBarService(IServiceCollection services)
{
services.AddScoped(_ => new DecoratedBarService(new BarService()));
}
}
If you need to get dependencies in order to new up any of these classes, then you can use the passed in IServiceProvider instance:
services.AddScoped(p =>
{
var dep = p.GetRequiredService<Dependency>();
return new DecoratedBarService(new BarService(dep));
}
Finally, tell your WebApplicationFactory to use this TestStartup class. This will need to be done via the UseStartup method of the builder, not the generic type param of WebApplicationFactory. That generic type param corresponds to the entry point of the application (i.e. your SUT), not which startup class is actually used.
builder.UseStartup<TestStartup>();
All the other answers were very helpful:
#ChrisPratt clearly explains the underlying problem, and offers a solution where Startup makes the service registration virtual and then overrides that in a TestStartup that is forced upon the IWebHostBuilder
#huysentruitw answers as well that this is a limitation of the underlying default DI container
#KirkLarkin offers a pragmatic solution where you register BarService itself in Startup and then use that to overwrite the IBarService registration completely
And still, I'd like to offer yet another answer.
The other answers helped me find the right terms to Google for. Turns out, there is the "Scrutor" NuGet package which adds the needed decorator support to the default DI container. You can test this solution yourself as it simply requires:
builder.ConfigureTestServices(servicesConfiguration =>
{
// Requires "Scrutor" from NuGet:
servicesConfiguration.Decorate<IBarService, DecoratedBarService>();
});
Mentioned package is open source (MIT), and you can also just adapt only the needed features yourself, thus answering the original question as it stood, without external dependencies or changes to anything except the test project:
public class IntegrationTestsFixture : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(servicesConfiguration =>
{
// The chosen solution here is adapted from the "Scrutor" NuGet package, which
// is MIT licensed, and can be found at: https://github.com/khellang/Scrutor
// This solution might need further adaptation for things like open generics...
var descriptor = servicesConfiguration.Single(s => s.ServiceType == typeof(IBarService));
servicesConfiguration.AddScoped<IBarService>(di
=> new DecoratedBarService(GetInstance<IBarService>(di, descriptor)));
});
}
// Method loosely based on Scrutor, MIT licensed: https://github.com/khellang/Scrutor/blob/68787e28376c640589100f974a5b759444d955b3/src/Scrutor/ServiceCollectionExtensions.Decoration.cs#L319
private static T GetInstance<T>(IServiceProvider provider, ServiceDescriptor descriptor)
{
if (descriptor.ImplementationInstance != null)
{
return (T)descriptor.ImplementationInstance;
}
if (descriptor.ImplementationType != null)
{
return (T)ActivatorUtilities.CreateInstance(provider, descriptor.ImplementationType);
}
if (descriptor.ImplementationFactory != null)
{
return (T)descriptor.ImplementationFactory(provider);
}
throw new InvalidOperationException($"Could not create instance for {descriptor.ServiceType}");
}
}
There's a simple alternative to this that just requires registering BarService with the DI container and then resolving that when performing the decoration. All it takes is updating ConfigureTestServices to first register BarService and then use the instance of IServiceProvider that's passed into ConfigureTestServices to resolve it. Here's the complete example:
builder.ConfigureTestServices(servicesConfiguration =>
{
servicesConfiguration.AddScoped<BarService>();
servicesConfiguration.AddScoped<IBarService>(di =>
new DecoratedBarService(di.GetRequiredService<BarService>()));
});
Note that this doesn't require any changes to the SUT project. The call to AddScoped<IBarService> here effectively overrides the one provided in the Startup class.
I generate a singleton at runtime
public void ConfigureServices(IServiceCollection services)
{
var applications = Utils.generateApplications()
services.AddSingleton<ApplicationModel[]>(applications);
services.AddMvc();
}
How can I later update this dependency injected ApplicationModel[] with a completely new ApplicationModel[]. I have a feature in my application that the user can use to trigger a data refresh on the applications, but how can I update the underlying injected Singleton so all future injections will use the updated applications? I have a function Utils.generateApplications() that gets an up to date list of applications and returns a ApplicationModel[]. How do I overwrite the old injected Singleton with a new object to be injected into future calls if that makes sense?
I have some code :
public void UpdateData()
{
var applications = Utils.generateApplications()
//How do I set applications to replace the injected singleton for all future injections?
}
You should use an additional layer of indirection. I think the simplest way is to use an abstract factory. Define an interface something like this:
interface IApplicationModelFactory
{
public ApplicationModel[] GetModel();
}
Define a second interface with the method (or methods) needed to update the model:
interface IApplicationModelUpdate
{
void UpdateModel();
}
You can then change your ApplicationModel[] registration from single instance to scoped and delegate to the factory:
var modelFactory = new ApplicationModelFactory();
services.AddSingleton<IApplicationModelFactory>(modelFactory);
services.AddSingleton<IApplicationModelUpdate>(modelFactory);
services.AddScoped<ApplicationModel[]>(provider =>
provider.GetRequiredService<IApplicationModelFactory>().GetModel());
Inject IApplicationModelUpdate into the types that update the model and ApplicationModel[] into the types that use it. This has the advantage that all types resolved for the same request will get a consistent view of the model, even if it changes in the middle of processing that request.
You could also inject IApplicationModelFactory into the consumer code, but I think injecting the model directly is better. Using the factory can lead to different bits of code seeing different models during the same request. The mutability of the model is also an implementation detail that consumer code shouldn't have to worry about.
I wouldn't monkey with dependency injection that way. Instead, inject a factory, and write whatever logic you need to return the proper instance.
Simple factory:
interface IApplicationModelFactory
{
ApplicationModel[] Model { get; }
}
class ApplicationModelFactory : IApplicationModelFactory
{
public ApplicationModel[] Model { get; set; }
}
Registration:
services.AddSingleton<IApplicationModelFactory>
(
new ApplicationModelFactory[] { Model = util.generateApplications() }
)
class receiving the injection:
class Foo
{
protected readonly IApplicationModelFactory _factory;
public Foo(IApplicationModelFactory injected)
{
_factory = injected;
}
protected ApplicationModel[] => _factory.Model;
public void Bar()
{
DoSomethingWithModel(this.ApplicationModel);
}
}
Within my Web API I have linked Autofac as IoC container, and I do it like this:
Domain level
public class Autofac
{
protected ContainerBuilder Builder { get; set; }
public Autofac()
{
this.Builder = new ContainerBuilder();
}
public virtual IContainer Register()
{
// Register dependencies
SetUpRegistration(this.Builder);
// Build registration.
var container = this.Builder.Build();
// End
return container;
}
private static void SetUpRegistration(ContainerBuilder builder)
{
// === DATALAYER === //
// MyRepository
builder.RegisterType<MyRepository>()
.As<IMyRepository>()
.InstancePerLifetimeScope();
// === DOMAIN === //
// MyManager
builder.RegisterType<MyManager>()
.As<IMyManager>()
.InstancePerLifetimeScope();
}
}
Web API
public class Autofac : Domain.IoC.Autofac
{
public IContainer Register(HttpConfiguration config)
{
// Register your Web API controllers.
base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
// Complete registration and get container instance.
var container = base.Register();
// Set the dependency resolver to be Autofac.
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Done.
return container;
}
}
As you see it inherits from the base class from Domain and sets up Web API specific config.
Usage
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}
Which is at global.asax, as you know.
The question
This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.
The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).
Any pointers?
Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test.
As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.
Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.
I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.
interface IDBSetWrapper
{
object Add(object entity);
}
interface IDBContextWrapper
{
...
IDBSet Set(Type entityType);
...
}
class DBContextWrapper : IDBContextWrapper
{
private readonly DBContext context;
public DBContextWrapper()
{
context = new DBContext();
}
...
public IDBSet Set(Type entityType)
{
var dbSet = context.Set(entityType);
return new DBSetWrapper(dbSet);
}
...
}
It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything.
I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.
You can add this new wrapper into your IoC a little better now as it provides an interface.
One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.
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.
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.