6

在我的项目中,我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, ....)

对于与具体类类似的东西?

4

4 回答 4

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”,它不会自动工作。但是我认为您应该更改注册名称以消除多余的“序列化程序”后缀(我们已经知道它是序列化程序,因为我们要求使用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语句并将枚举值实际映射到您的类名重新注册。

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

于 2010-04-07T15:44:39.307 回答
2

据我所知,这并不是程序集扫描功能的真正用途。当单个程序集具有不同接口的多个实现(例如IRepository<File>IRepository<Folder>等)时,它会更有用。因此,例如,当您引用您的测试程序集时,您正在注入测试存储库,而当您在生产中时,您正在注入实体框架存储库。

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

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

由于对字符串进行硬编码,您仍然依赖于 Json 序列化程序,并且 StructureMap 从该调用返回某种其他类型的序列化程序是没有意义的。

我不能确切地告诉你用 StructureMap 完成什么,但如果你需要根据一组运行时条件返回一个特定的序列化程序,你可以查看条件构造

另一方面,这听起来不像是你想要的那种开关,所以你绝对应该考虑摆脱它。毕竟上面的代码真的和

new JsonSerializer();

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

祝你好运!

于 2010-03-28T09:33:22.597 回答
1

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

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

x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>()));
于 2010-03-29T13:36:55.470 回答
1

我好奇。ISerializer 本身增加了什么价值?让我们从特定的实现转到运行时选择的一个或多个。

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

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

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

于 2010-04-07T15:24:50.923 回答