StructureMap通过注入而不是服务位置来解析依赖性

在我的项目中,我使用汇编扫描程序注册了许多ISerializers实现。 FWIW这是注册我的ISerializers的代码

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

然后正确注册

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

等等。

目前,我能够弄清楚如何解决我想要的串行器的唯一方法是使用硬编码服务位置调用

 jsonSerializer = ObjectFactory.GetNamedInstance("JsonSerializer"); 

现在我在class上知道我特别想要jsonSerializer,所以有没有办法配置一个规则或类似的东西,说ISerializer根据属性名连接命名实例? 所以我可以

 MySomeClass(ISerializer jsonSerializer, ....) 

StructureMap正确解决了这种情况? 或者我正在接近这个错误,也许我应该只注册实现ISerializer的具体类型,然后专门使用

 MySomeClass(JsonSerializer jsonSerializer, ....) 

对于具体类的这些方面的东西?

当您执行dependency injection并且需要能够创建给定接口的特殊类型实例时,建议的解决方案是创建专用工厂类。 这允许您在不实际注入容器的情况下使用命名参数。

这是您要注入的抽象类型:

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

这是具体类型,它使用您的容器(StructureMap):

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

然后你的课将如下所示:

 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); } } 

我写过这个传递“Json”而不是“JsonSerializer”,它不会自动运行。 但我认为您应该更改您的注册名称以消除冗余的“Serializer”后缀(我们已经知道它是一个序列化器,因为我们要求ISerializer )。 换句话说,创建一个这样的方法:

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

并注册如下:

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

然后你可以使用字符串“Json”来创建它而不是“JsonSerializer”,它看起来会有点不那么难看,感觉不那么耦合。

如果您不喜欢硬编码的字符串,那么您可以做的另一件事是为您的工厂创建一个枚举:

 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()); } } 

所以不要写这个:

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

你可以写这个:

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

从长远来看,这将更不容易出错。

从长远来看,这可能更容易维护,因为如果你开始更改序列化程序的类名和/或名称不一致,那么你可以用switch语句替换简单的ToString()并实际将枚举值映射到您正在注册的class级名称。

我可能会把所有这些代码 – 包括你的问题中的自动注册代码 – 放在同一个命名空间,甚至是相同的代码文件中,以清楚地表明这些代码都是相互依赖的。

据我所知,这并不是组装扫描function的意义所在。 当单个程序集具有许多不同接口的实现时(例如, IRepositoryIRepository等),它会更有用。 因此,例如,当您引用测试程序集时,您将注入测试存储库,而当您正在生产时,您将注入entity framework存储库。

在您的情况下,它看起来不像您的任何示例完全注入依赖项。 换句话说,当你写作

 ObjectFactory.GetNamedInstance("JsonSerializer"); 

由于对字符串进行了硬编码,你仍然依赖于Json序列化程序,而且从那个调用中返回一些其他类型的序列化程序也没有意义。

我无法准确地告诉您使用StructureMap完成什么,但是如果您需要根据特定的运行时条件返回特定的序列化程序,则可以查看条件构造 。

另一方面,它听起来并不像这样的开关就是你要去的地方,所以你一定要考虑摆脱它。 毕竟,上面的代码确实没有什么不同

 new JsonSerializer(); 

StructureMap是一个很棒的工具,但并不是每个项目都需要。

祝好运!

由于您的代码假定它正在获取JsonSerializer,因此请创建一个只有JsonSerializer实现的新IJsonSerializer接口。 任何需要JsonSerializer的类都应该接受IJsonSerializer。 如果仍需要ISerializer接口在所有序列化器中都是通用的,则IJsonSerializer可以用作标记接口。

或者,当您在StructureMap中注册类时,可以将特定的ISerializer实现绑定到您的类。

 x.For().Use(c => new MySomeClass(c.GetInstance())); 

我好奇。 ISerializer对它自己有什么价值呢? 让我们从特定的实现转到运行时选择的一个或多个。

如果您的类型依赖于特定类型的序列化程序,则依赖它(IJsonSerializer)。 这要求在容器中注册该类型的默认实例。

但是,如果您更多地考虑将ISerializers作为策略,那么您将注册所有ISerializers,然后依赖它们的数组,StructureMap将推入所有已注册ISerializers的数组。 然后,使用这些序列化器的类负责选择使用哪一个。

在策略方案中,您可能需要序列化程序上的一些元数据供协调类用于区分它们。 恕我直言,这应该不是容器配置,如注册类型上的名称,而是实现本身的元数据。