c# - StructureMap 通过注入(inject)而不是服务定位来解决依赖

标签 c# dependency-injection inversion-of-control structuremap service-locator

在我的项目中,我使用程序集扫描器注册了许多 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, ....)

对于与具体类类似的事情?

最佳答案

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

例子

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

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 语句并将枚举值实际映射到您正在注册的类名。

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

关于c# - StructureMap 通过注入(inject)而不是服务定位来解决依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2531612/

相关文章:

asp.net-mvc - 如何在 ASP.NET MVC 的 RegisterGlobalFilters 方法中进行依赖项注入(inject)

c# - 如何在 MVC .Net Core 的依赖注入(inject)框架中利用完全相同的对象实例

c# - 如何使用 CaSTLe Windsor 的 CollectionResolver 避免循环行为?

c# - 在 XBAP 应用程序中包含外部文件

c# - 使用 Xamarin.Forms.Maps 的 Android 6.0 以上的位置权限

用于编译时的 Scala 依赖注入(inject),具有单独的配置

c# - 是否可以在不传递容器的情况下在 CaSTLe Windsor 中使用范围限定的生活方式?

c# - 模拟 'System.Console' 行为

c# - FirstOrDefault() 无法与 ??运算符(operator)

c# - 对 C# Nest 的简单匹配查询