C# · 12月 24, 2021

c# – StructureMap通过注入而不是服务位置来解析依赖关系

在我的项目中,我使用汇编扫描程序注册了许多ISerializers实现. FWIW这是注册我的ISerializers的代码 Scan(scanner =>{ scanner.AssemblyContainingType<ISerializer>(); scanner.AddAllTypesOf<ISerializer>().NameBy(type => type.Name); scanner.WithDefaultConventions();});

然后正确注册

ISerializer (…ISerializer)Scoped as: TransientJsonSerializer Configured Instance of …JsonSerializerBsonSerializer Configured Instance of …BsonSerializer

等等.

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

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

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

MySomeClass(ISerializer jsonSerializer,….)

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

MySomeClass(JsonSerializer jsonSerializer,….)

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

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

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

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

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

public class StructureMapSerializerFactory : ISerializerFactory{ public ISerializer GetSerializer(string name) { return ObjectFactory.GetNamedInstance<ISerializer>(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<ISerializer>().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<ISerializer>(format.ToString()); }}

所以不要写这个:

ISerializer serializer = serializerFactory.GetSerializer(“Json”);

你可以这样写:

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

从长远来看,哪个更容易出错.

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

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