StructureMap resolves dependency via injection instead of service location

In my project, I am registering many ISerializers implementations with an assembly scanner. FWIW is the code that my ISerializers registers

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

What then registers correctly

 ISerializer (...ISerializer) Scoped as: Transient JsonSerializer Configured Instance of ...JsonSerializer BsonSerializer Configured Instance of ...BsonSerializer 

And so on.

Currently, the only way I have been able to figure out how to enable the serializer that I want is to hardcode the service call using

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

Now I know in my class that I specifically want jsonSerializer, so is there a way to configure a rule or the like that says to ISerializer about connecting a named instance based on the name of the property? So i can have

 MySomeClass(ISerializer jsonSerializer, ....) 

And does StructureMap correctly resolve this scenario? Or am I approaching this incorrectly, and maybe I should just register a specific type that implements ISerializer, and then just use

 MySomeClass(JsonSerializer jsonSerializer, ....) 

for something along these lines with a specific class?

+6
c # dependency-injection inversion-of-control structuremap service-locator
source share
4 answers

When you do dependency injection and should be able to create specially typed instances of this interface, the recommended solution is to create specialized factory classes. This allows a named argument to be used without actually entering the container.

Example

This is the abstract type that you will enter:

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

Here is the specific type your container uses (StructureMap):

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

Then your class will look like this:

 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 wrote this passage "Json" instead of "JsonSerializer", which will not automatically work. But I think you should change your login names to eliminate the excessive suffix Serializer (we already know this serializer, because we ask for 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 as follows:

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

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

If you don't like 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 can write this instead:

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

In the long run, this will be less error prone.

It will probably be more convenient to maintain in the long run, because if you start changing the class names of your serializers and / or the names are incompatible, you can replace the simple ToString() with a switch and actually display the enum values โ€‹โ€‹for the class names. which you register.

I would probably put all this code, including the automatic registration code in your question, in the same namespace or even in the same code file to clearly indicate that these parts are interdependent.

+5
source share

As far as I know, this is not quite what the assembly scan functions are for. This is more useful when one assembly has many implementations of different interfaces (for example, IRepository<File> , IRepository<Folder> , etc.). So, for example, when you reference your test build, you enter test repositories, and when you are in production, you enter the Entity Framework repositories.

In your case, it does not look like any of your examples completely add dependencies. In other words, when you write

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

you still have a dependency on the Json serializer due to hard-coding strings, and it would not make sense for StructureMap to return any other serializer from this call.

I canโ€™t say exactly what you want to do with StructureMap, but if you need to return a specific serializer depending on a specific set of execution conditions, you can see the conditional construction .

On the other hand, in fact, it doesnโ€™t seem like the way of switching that you are going here, so you will definitely need to get rid of it. After all, the code above is really no different from

 new JsonSerializer(); 

StructureMap is a great tool, but it is not needed for every project.

Good luck

+2
source share

Since your code assumes that it receives a JsonSerializer, create a new IJsonSerializer interface that implements only JsonSerializer. Any class that needs a JsonSerializer should accept an IJsonSerializer. If you still need an ISerializer interface that will be common to all serializers, IJsonSerializer can be used as a marker interface.

Alternatively, you can associate a specific implementation of ISerializer with your class when registering your class with StructureMap.

 x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>())); 
+1
source share

I am curious. What does own ISerializer matter? Move from a specific implementation to one or more selected at runtime.

If your type depends on a specific type of serializer, depend on it (IJsonSerializer). This requires that a default instance of this type be registered in the container.

However, if you think ISerializers have Strategies , you must register all your ISerializers, and then depend on the array they and StructureMap will insert an array of all registered ISerializers. Then the class using these serializers is responsible for deciding which one to use.

In a strategic scenario, you will most likely need some metadata in the serializer for use by your coordination class to distinguish between them. IMHO, this really should not be the configuration of the container, for example, the names of registered types, but the metadata of the implementation itself.

+1
source share

All Articles