In my project I register many ISerializers
implementations with the assembly scanner. FWIW this is the code that registers my ISerializers
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.
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(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().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(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.