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?
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.
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.
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.
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<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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With