Dependency Injection Type-Selection

Dependency injection type-selection

I would suggest that you combine your IEmail and ISms interfaces into an IMessageService (provided that doesn't violate the Liskov Substitution Principal) and to use a strategy pattern to enable your notification service to be able to select the type (or types) of IMessageService that are used.

Refactor to IMessageService

public interface IMessageService
{
void Send(string subject, string body);
bool AppliesTo(IEnumerable<string> providers);
}

public class EmailMessageService : IMessageService
{
public EmailMessageService(/* inject dependencies (and configuration) here */)
{
// Set dependencies to private (class level) variables
}

public void Send(string subject, string body)
{
// Implementation - use dependencies as appropriate
}

public bool AppliesTo(IEnumerable<string> providers)
{
return providers.Contains("email");
}
}

public class SmsMessageService : IMessageService
{
public SmsMessageService(/* inject dependencies (and configuration) here */)
{
// Set dependencies to private (class level) variables
}

public void Send(string subject, string body)
{
// Implementation - use dependencies as appropriate
}

public bool AppliesTo(IEnumerable<string> providers)
{
return providers.Contains("sms");
}
}

Implement the Strategy Pattern

public interface IMessageStrategy
{
void Send(string message, string body, string provider);
void Send(string message, string body, IEnumerable<string> providers);
}

public class MessageStrategy : IMessageStrategy
{
private readonly IMessageService[] messageServices;

public MessageStrategy(IMessageService[] messageServices)
{
if (messageServices == null)
throw new ArgumentNullException("messageServices");
this.messageServices = messageServices;
}

public void Send(string message, string body, string provider)
{
string[] providers = provider.Split(';').Select(p => p.ToLower().Trim()).ToArray();
this.Send(message, body, providers);
}

public void Send(string message, string body, IEnumerable<string> providers)
{
foreach (IMessageService messageService in messageServices)
{
if (messageService.AppliesTo(providers))
{
messageService.Send(message, body);
}
}
}
}

Usage

In your DI container, register all types that match IMessageService to be resolved as an array. For example, in StructureMap:

container.For<IMessageService>().Use<EmailMessageService>();
container.For<IMessageService>().Use<SmsService>();

Or alternatively you can use Scan to pickup new types automatically that are added after the fact.

var container = new Container(x => x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.AddAllTypesOf<IMessageService>();
}));

Either way, registering the types with the container is all you need to satisfy the IMessageService[] dependency.

Then it is just a matter of injecting IMessageStrategy into a class that requires messaging and passing the magic string to select which types of message services to use.

public class SomeService : ISomeService
{
private readonly IMessageStrategy messageStrategy;

public SomeService(IMessageStrategy messageStrategy)
{
if (messageStrategy == null)
throw new ArgumentNullException("messageStrategy");
this.messageStrategy = messageStrategy;
}

public void DoSomething()
{
// Send a message via email
this.messageStrategy.Send("This is a test", "Hello", "email");

// Send a message via SMS
this.messageStrategy.Send("This is a test", "Hello", "sms");

// Send a message via email and SMS
this.messageStrategy.Send("This is a test", "Hello", "email;sms");
}
}

Note that if you take this approach, your EmailStrategy class won't need to change if you decide later to add or remove a IMessageService - you only need to change the DI configuration.

Constructing a List of Interface that requires Type Argument via Dependency Injection in C#

Unless IProcessor<T> implements a non-generic version IProcessor, it is impossible to inject them all into the constructor. But even if you do so, it forces you to iterate all processors, while it only makes sense to call the versions that implement IProcessor<T> where the T is the required type.

There are several ways to solve this problem. Here are two:

1. Make DataRepository generic:

For instance:

public class DataRepository<TReportSection>
: IParticularReportTypeRepository<TReportSection>
{
private IProcessor<TReportSection>[] _processors;

public DataRepository(IProcessor<TReportSection>[] processors)
{
_processors = processors;
}

public async Task<TReportSection> Process(IncomingData evt)
{
TReportSection reportSection = ...;

foreach (var processor in _processors)
{
processor.Process(evt, reportSection);
}

...
}
}

With this approach you would inject a specific IParticularReportTypeRepository<TReportSection> (e.g. IParticularReportTypeRepository<PageViewSection> into the constructor of a consumer.

This design is only possible when each consumer has only one (or a few types of report sections) that it passes on. Otherwise, consumers get many dependencies of type IParticularReportTypeRepository<T> in their constructors.

If you need a more dynamic approach, you can start using Reflection inside the DataRepository and (optionally) call back into the used DI Container. This is demonstrated next.

2. Resolve collections from inside DataRepository:

For instance:

public class DataRepository : IParticularReportTypeRepository
{
private readonly Container _container;

public DataRepository(Container container)
{
_container = container;
}

public async Task<IStateSection> Process(IncomingData evt)
{
IStateSection reportSection =
await GetReportSectionAsync(evt.EventTime, evt.ContextId);

Type processorType =
typeof(IProcessor<>).MakeGenericType(reportSection.GetType())

IEnumerable<object> processors =
_container.GetAllInstances(processorType);

// NOTE the dynamic keywords here.
foreach (dynamic processor in processors)
{
processor.Process(evt, (dynamic)reportSection);
}

...
}
}

With this implementation, DataRepository only knows during the call to its Process method what type of processor it needs. It does so by constructing a closed generic version of IProcessor<T> and asks the DI Container to return a list of them. Note that the API calls might look different depending on the DI Container you use. You should review your container's documentation to find out how to resolve collections. If you don't use a DI Container, you might want to inject an Func<Type, IEnumerable<object>> factory delegate instead.

IMPORTANT: Because of the dependency on the DI Container, this class should now be part of your Composition Root.

The DataRepository.Process method makes use of Reflection and calls the processor using C#'s dynamic keyword. This isn't per se the best approach (there are some downsides to it), but it produces the least amount of code, which is useful for demonstration purposes. Jimmy Bogard recently did an article that demonstrated two different approaches concerning the invocation of generic types. You might want to take a look at that article.

How to configure dependency injection container with FuncT, Result?

What you're trying to do is possible, but it's not a common thing and isn't something magic you'll get out of the box. You'll have to write code to implement it.

Before I get to that... from a future perspective, you might get help faster and more eyes on your question if your repro is far more minimal. The whole BusinessAction<T> isn't really needed; the RequestHandler isn't needed... honestly, all you need to repro what you're doing is:

public interface IActionRegistry
{
}

public class ActionRegistry<T> : IActionRegistry
{
}

If the other stuff is relevant to the question, definitely include it... but in this case, it's not, so adding it in here just makes the question harder to read through and answer. I know I, personally, will sometimes just skip questions where there's a lot of extra stuff because there are only so many hours in the day, you know?

Anyway, here's how you'd do it, in working example form:

var builder = new ContainerBuilder();

// Register the action registry generic but not AS the interface.
// You can't register an open generic as a non-generic interface.
builder.RegisterGeneric(typeof(ActionRegistry<>));

// Manually build the factory method. Going from reflection
// System.Type to a generic ActionRegistry<Type> is not common and
// not directly supported.
builder.Register((context, parameters) => {
// Capture the lifetime scope or you'll get an exception about
// the resolve operation already being over.
var scope = context.Resolve<ILifetimeScope>();

// Here's the factory method. You can add whatever additional
// enhancements you need, like better error handling.
return (Type type) => {
var closedGeneric = typeof(ActionRegistry<>).MakeGenericType(type);
return scope.Resolve(closedGeneric) as IActionRegistry;
};
});

var container = builder.Build();

// Now you can resolve it and use it.
var factory = container.Resolve<Func<Type, IActionRegistry>>();
var instance = factory(typeof(DivideByZeroException));
Assert.Equal("ActionRegistry`1", instance.GetType().Name);
Assert.Equal("DivideByZeroException", instance.GetType().GenericTypeArguments[0].Name);

What is dependency injection?

Dependency Injection is passing dependency to other objects or framework( dependency injector).

Dependency injection makes testing easier. The injection can be done through constructor.

SomeClass() has its constructor as following:

public SomeClass() {
myObject = Factory.getObject();
}

Problem:
If myObject involves complex tasks such as disk access or network access, it is hard to do unit test on SomeClass(). Programmers have to mock myObject and might intercept the factory call.

Alternative solution:

  • Passing myObject in as an argument to the constructor
public SomeClass (MyClass myObject) {
this.myObject = myObject;
}

myObject can be passed directly which makes testing easier.

  • One common alternative is defining a do-nothing constructor. Dependency injection can be done through setters. (h/t @MikeVella).
  • Martin Fowler documents a third alternative (h/t @MarcDix), where classes explicitly implement an interface for the dependencies programmers wish injected.

It is harder to isolate components in unit testing without dependency injection.

In 2013, when I wrote this answer, this was a major theme on the Google Testing Blog. It remains the biggest advantage to me, as programmers not always need the extra flexibility in their run-time design (for instance, for service locator or similar patterns). Programmers often need to isolate the classes during testing.

How can one use an existing instance to select a type to create in an IoC container

I think what you are looking for is the ForObject() method in StructureMap. It can close an open generic type based on a given object instance. The key change you need to make to your design is to introduce the generic type:

public interface IProcessor { }
public interface IProcessor<TSettings> : IProcessor{}

All of the important stuff is still declared on IProcessor, the generic IProcessor<TSettings> is really just a marker interface. Each of your processors will then implement the generic interface, to declare which settings type they expect:

public class FooProcessor : IProcessor<FooSettings>
{
public FooProcessor(FooSettings settings) { }
}

public class BarProcessor : IProcessor<BarSettings>
{
public BarProcessor(BarSettings settings) { }
}

public class DohProcessor : IProcessor<DohSettings>
{
public DohProcessor(DohSettings settings) { }
}

Now, given an instance of a settings object, you can retrieve the correct IProcessor:

IProcessor processor = container.ForObject(settings).
GetClosedTypeOf(typeof(IProcessor<>)).
As<IProcessor>();

Now you can tell StructureMap to use this logic whenever it resolves an IProcessor:

var container = new Container(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IProcessor<>));
});

x.For<IProcessor>().Use(context =>
{
// Get the settings object somehow - I'll assume an ISettingsSource
var settings = context.GetInstance<ISettingsSource>().GetSettings();
// Need access to full container, since context interface does not expose ForObject
var me = context.GetInstance<IContainer>();
// Get the correct IProcessor based on the settings object
return me.ForObject(settings).
GetClosedTypeOf(typeof (IProcessor<>)).
As<IProcessor>();
});

});

Is dependency injection only for service type objects and singletons? (and NOT for gui?)

From what I see in your sample code, I am not sure passing Product to EditProductDialog is the best design, this means that you cannot reuse the same dialog for 2 different products.

In general, I would rather add a setProduct(Product) to EditProductDialog in order to:

  • enable reuse of the same dialog
  • allow constructor injection of the dialog itself

Besides, you can also, if you want to keep the current constructor arguments, take a look at Guice Assisted Injection, which would allow you to have all dependencies injected into the constructor, while still providing the product explicitly.

Finally, you may want to take a look at Guts-GUI, an open source framework for creating Swing GUIs with Guice (still under work, but ready to use as of today). It has a sample application that contains examples of reusable dialogs.

pass type to dependency injection container

You could make use of factory pattern:

interface IUserQueryFactory {
get(): IUserQueries;
get(type: string): IUserQueries;
}

class UserQueryFactory implements IUserQueryFactory {
get(): IUserQueries {
const defaultValue = config.database; // or whatever returns your "mssql" from config
return this.get(defaultValue);
}

get(type: string): IUserQueries {
switch (type) {
case "mssql":
return new MSSQLUserQueries();
case "postgresql":
return new PostgresUserQueries();
default:
return null;
}
}
}

const container: Container = new Container();
container.bind<IUserQueryFactory>(IoCTypes.IUserQueryFactory).to(UserQueryFactory);
container.bind<IUserQueries>(IoCTypes.IUserQueries).toDynamicValue((context: interfaces.Context) => { return context.container.get<IUserQueryFactory>(IoCTypes.IUserQueryFactory).get(); });
export { container };

I'm not sure I've got the whole syntax correctly since this is not tested, but I guess you get the idea:

  1. IUserQueryFactory returns IUserQueries based on input type or returns default one (one defined in configuration) if type is not provider
  2. Default implementation for IUserQueries is basically implemented through IUserQueryFactory which means that whole source of creating IUserQueries is at single place and easily maintanable.


Related Topics



Leave a reply



Submit