在我的项目中,我使用汇编扫描程序注册了许多ISerializers
实现。 FWIW这是注册我的ISerializers
Scan(scanner =>
{
scanner.AssemblyContainingType<ISerializer>();
scanner.AddAllTypesOf<ISerializer>().NameBy(type => type.Name);
scanner.WithDefaultConventions();
});
然后正确注册
ISerializer (...ISerializer)
Scoped as: Transient
JsonSerializer Configured Instance of ...JsonSerializer
BsonSerializer Configured Instance of ...BsonSerializer
等等。
目前,我能够弄清楚如何解决我想要的串行器的唯一方法是使用
对服务位置调用进行硬编码jsonSerializer = ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");
现在我在课堂上知道我特别想要jsonSerializer所以有没有办法配置一个规则或类似的规则,让ISerializer根据属性名称连接命名实例?所以我可以
MySomeClass(ISerializer jsonSerializer, ....)
StructureMap正确解决了这种情况?或者我正在接近这个错误,也许我应该注册实现ISerializer的具体类型然后只是专门使用
MySomeClass(JsonSerializer jsonSerializer, ....)
对于这些具有特定类的东西?
答案 0 :(得分:5)
当您执行依赖注入并且需要能够创建给定接口的特殊类型实例时,建议的解决方案是创建专用工厂类。这允许您在不实际注入容器的情况下使用命名参数。
这是你要注入的抽象类型:
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);
从长远来看,哪个不易出错。
从长远来看,这可能更易于维护,因为如果您开始更改序列化程序的类名称和/或名称不一致,那么您可以将ToString()
替换为switch
声明并实际将枚举值映射到您正在注册的类名。
我可能会将所有这些代码 - 包括你的问题中的自动注册代码 - 放在同一个命名空间,甚至是相同的代码文件中,以清楚地表明这些代码都是相互依赖的。
答案 1 :(得分:2)
据我所知,这并不是装配扫描功能的用途。当单个程序集具有大量不同接口的实现时(例如IRepository<File>
,IRepository<Folder>
等),它会更有用。因此,例如,当您引用测试程序集时,您将注入测试存储库,而当您正在生产时,您将注入实体框架存储库。
在您的情况下,看起来您的任何示例都不会完全注入依赖项。换句话说,当你写
ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");
由于对字符串进行了硬编码,你仍然依赖于Json序列化程序,而且这个调用从这个调用返回其他类型的序列化程序也没有意义。
我无法确切地告诉您使用StructureMap完成什么,但如果您需要根据某些运行时条件返回特定的序列化程序,则可以查看conditional construction。
另一方面,它听起来并不像这样的开关就是你要去的地方,所以你一定要考虑摆脱它。毕竟,上面的代码与
没什么不同new JsonSerializer();
StructureMap是一个很棒的工具,但并不是每个项目都需要。
祝你好运!答案 2 :(得分:1)
由于您的代码假定它正在获取JsonSerializer,因此请创建一个只有JsonSerializer实现的新IJsonSerializer接口。任何需要JsonSerializer的类都应该接受IJsonSerializer。如果仍需要ISerializer接口在所有序列化器中都是通用的,则IJsonSerializer可以用作标记接口。
或者,当您在StructureMap中注册类时,可以将特定的ISerializer实现绑定到您的类。
x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>()));
答案 3 :(得分:1)
我很好奇。 ISerializer对它自己有什么价值呢?让我们从特定的实现转到在运行时选择的一个或多个。
如果您的类型依赖于特定类型的序列化程序,则依赖它(IJsonSerializer)。这要求在容器中注册该类型的默认实例。
但是,如果您更多地考虑将ISerializers作为Strategies,那么您将注册所有ISerializers,然后依赖它们的数组,StructureMap将推入所有已注册的ISerializer的数组。然后,使用这些序列化器的类负责选择使用哪一个。
在策略方案中,您可能需要序列化程序上的一些元数据供协调类用于区分它们。恕我直言,这应该不是容器配置,如注册类型上的名称,但实现本身的元数据。