Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StructureMap resolve dependency through injection instead of service location

In my project I register many ISerializers implementations with the assembly scanner. FWIW this is the code that registers my ISerializers

Scan(scanner =>
{
    scanner.AssemblyContainingType<ISerializer>();
    scanner.AddAllTypesOf<ISerializer>().NameBy(type => type.Name);
    scanner.WithDefaultConventions();
});

Which then correctly registers

ISerializer (...ISerializer)
Scoped as:  Transient

JsonSerializer    Configured Instance of ...JsonSerializer
BsonSerializer    Configured Instance of ...BsonSerializer

And so forth.

Currently the only way I've been able to figure out how to resolve the serializer I want is to hardcode a service location call with

jsonSerializer = ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");

Now I know in my class that I specifically want the jsonSerializer so is there a way to configure a rule or similar that says for ISerializer's to connect the named instance based on the property name? So that I could have

MySomeClass(ISerializer jsonSerializer, ....)

And StructureMap correctly resolve this scenario? Or am I approaching this wrong and perhaps I should just register the concrete type that implements ISerializer and then just specifically use

MySomeClass(JsonSerializer jsonSerializer, ....)

for something along these lines with the concrete class?

like image 507
Chris Marisic Avatar asked Mar 28 '10 01:03

Chris Marisic


People also ask

How does dependency Injection work?

Dependency Injection (DI) is a design pattern used to implement IoC. It allows the creation of dependent objects outside of a class and provides those objects to a class through different ways. Using DI, we move the creation and binding of the dependent objects outside of the class that depends on them.

What is dependency injection resolve?

Resolution: this feature allows the IoC Container to resolve a dependency by creating an object and injecting it into the requesting class. Thanks to this feature, you don't have to instantiate objects manually to manage dependencies.

What is dependency Injection in java?

Dependency injection enables you to turn regular Java classes into managed objects and to inject them into any other managed object. Using dependency injection, your code can declare dependencies on any managed object.


1 Answers

When you're doing Dependency Injection and need to be able to create specially-typed instances of a given interface, the recommended solution is to create specialized factory classes. This allows you to use a named argument without actually injecting the container.

Example

This is the abstract type that you'll be injecting:

public interface ISerializerFactory
{
    ISerializer GetSerializer(string name);
}

Here is the concrete type, which makes use of your container (StructureMap):

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(string name)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(name);
    }
}

Then your class would look like the following:

public class MyClass
{
    private readonly ISerializerFactory serializerFactory;

    public MyClass(ISerializerFactory serializerFactory)
    {
        if (serializerFactory == null)
            throw new ArgumentNullException("serializerFactory");
        this.serializerFactory = serializerFactory;
    }

    public string SerializeSomeData(MyData data)
    {
        ISerializer serializer = serializerFactory.GetSerializer("Json");
        return serializer.Serialize(data);
    }
}

I've written this passing "Json" instead of "JsonSerializer" which won't automatically work. But I think you should change your registration names to eliminate the redundant "Serializer" suffix (we already know it's a serializer because we're asking for an ISerializer). In other words create a method like this:

private static string ExtractSerializerName(Type serializerType)
{
    string typeName = serializerType.Name;
    int suffixIndex = typeName.IndexOf("Serializer");
    return (suffixIndex >= 0) ?
        typeName.Substring(0, suffixIndex - 1) : typeName;
}

And register it like this:

scanner.AddAllTypesOf<ISerializer>().NameBy(type => ExtractSerializerName(type));

Then you can just use the string "Json" to create it instead of "JsonSerializer", which will look a little less ugly and feel less coupled.

If you don't like the hard-coded strings, then another thing you can do is create an enumeration for your factory:

public enum SerializationFormat { Json, Bson, Xml };

public interface ISerializerFactory
{
    ISerializer GetSerializer(SerializationFormat format);
}

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(SerializationFormat format)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(format.ToString());
    }
}

So instead of writing this:

ISerializer serializer = serializerFactory.GetSerializer("Json");

You get to write this instead:

ISerializer serializer =
    serializerFactory.GetSerializer(SerializationFormat.Json);

Which is going to be less error-prone in the long run.

This will probably be more maintainable in the long run because if you start changing the class names of your serializers and/or the names are inconsistent, then you can replace the simple ToString() with a switch statement and actually map the enum values to the class names you're registering.

I'd probably put all of this code - including the auto-registration code in your question - in the same namespace, or even the same code file, to clearly indicate that these pieces are all interdependent.

like image 136
Aaronaught Avatar answered Sep 19 '22 13:09

Aaronaught