StructureMap разрешают зависимость посредством инжекции вместо места предоставления услуг

В моем проекте я регистрирую многих 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

И т.д.

В настоящее время единственным способом, которым я смог выяснить, как разрешить сериализатор, который я хочу, является к hardcode вызов места предоставления услуг с

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

Теперь я знаю в своем классе, что конкретно хочу jsonSerializer так там способ настроить правило или подобный, который говорит, чтобы ISerializer's соединил именованный экземпляр на основе имени свойства? Так, чтобы я мог иметь

MySomeClass(ISerializer jsonSerializer, ....)

И StructureMap правильно разрешают этот сценарий? Или я приближаюсь к этой несправедливости, и возможно я должен просто зарегистрировать конкретный тип, который реализует ISerializer, и затем просто конкретно используйте

MySomeClass(JsonSerializer jsonSerializer, ....)

для чего-то вдоль этих строк с реальным классом?

6
задан Chris Marisic 28 March 2010 в 01:47
поделиться

4 ответа

Когда вы выполняете внедрение зависимостей и вам нужно иметь возможность создавать специально типизированные экземпляры данного интерфейса, рекомендуемым решением является создание специализированных фабричных классов. Это позволяет использовать именованный аргумент без фактического внедрения контейнера.

Пример

Это абстрактный тип, который вы будете вводить:

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 и фактически сопоставляет значения перечисления с именами классов, которые вы регистрируете.

Я бы, вероятно, поместил весь этот код - включая код авторегистрации в вашем вопросе - в одно и то же пространство имен или даже в один и тот же файл кода, чтобы четко указать, что все эти части взаимозависимы.

5
ответ дан 10 December 2019 в 02:45
поделиться

Мне любопытно. Какую ценность добавляет ISerializer сам по себе? Давайте перейдем от конкретной реализации к одной или нескольким, выбранным во время выполнения.

Если ваш тип зависит от определенного типа сериализатора, выберите зависимость от него (IJsonSerializer).Для этого требуется, чтобы экземпляр этого типа по умолчанию был зарегистрирован в контейнере.

Однако, если вы больше думаете об использовании ISerializer как Strategies , вы должны зарегистрировать все свои ISerializer, а затем принять зависимость от их массива, и StructureMap вставит массив всех зарегистрированных ISerializer. Затем класс, использующий эти сериализаторы, отвечает за выбор того, какой из них использовать.

В сценарии стратегии вам, вероятно, потребуются некоторые метаданные сериализатора, которые будут использоваться вашим координирующим классом для их различения. IMHO, на самом деле это должна быть не конфигурация контейнера, например имена зарегистрированных типов, а метаданные самой реализации.

1
ответ дан 10 December 2019 в 02:45
поделиться

Насколько я знаю, функция сканирования сборок предназначена не для этого. Это более полезно, когда одна сборка имеет множество реализаций различных интерфейсов (например, IRepository , IRepository и т. Д.). Так, например, когда вы ссылаетесь на свою тестовую сборку, вы вводите тестовые репозитории, а когда вы находитесь в производстве, вы вводите репозитории Entity Framework.

В вашем случае не похоже, что ни один из ваших примеров полностью внедряет зависимости. Другими словами, когда вы пишете

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

, у вас все еще есть зависимость от сериализатора Json в силу жесткого кодирования строки, и для StructureMap не имеет смысла возвращать какой-либо другой сериализатор из этого вызова.

Я не могу точно сказать, чего вы хотите достичь с помощью StructureMap, но если вам нужно вернуть определенный сериализатор в зависимости от определенного набора условий выполнения, вы можете изучить условную конструкцию .

С другой стороны, на самом деле это не похоже на то, что вы собираетесь использовать такой переключатель, поэтому вам определенно стоит подумать о том, чтобы от него избавиться. В конце концов, приведенный выше код действительно ничем не отличается от

new JsonSerializer();

. StructureMap - замечательный инструмент, но он не нужен для каждого проекта.

Удачи!

2
ответ дан 10 December 2019 в 02:45
поделиться

Поскольку ваш код предполагает, что он получает JsonSerializer, создайте новый интерфейс IJsonSerializer, который реализует только JsonSerializer. Любой класс, которому требуется JsonSerializer, должен принимать IJsonSerializer. Если вам по-прежнему требуется, чтобы интерфейс ISerializer был общим для всех сериализаторов, IJsonSerializer можно использовать просто как интерфейс маркера.

В качестве альтернативы вы можете привязать конкретную реализацию ISerializer к вашему классу, когда вы регистрируете свой класс в StructureMap.

x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>()));
1
ответ дан 10 December 2019 в 02:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: