Как изменить конфиги в Spring.Net

Преимущество контейнера IoC заключается в том, что вы можете заменить имитацию службы в нижней части графа вашего объекта. Однако в Spring.Net это кажется намного сложнее, чем в других контейнерах IoC. Вот код, который делает это в Unity и имеет код Spring.Net;

namespace IocSpringDemo
{
    using Microsoft.Practices.Unity;
    using NUnit.Framework;

    using Spring.Context;
    using Spring.Context.Support;

    public interface ISomeService
    {
        string DoSomething();
    }

    public class ServiceImplementationA : ISomeService
    {
        public string DoSomething()
        {
            return "Hello A";
        }
    }

    public class ServiceImplementationB : ISomeService
    {
        public string DoSomething()
        {
            return "Hello B";
        }
    }

    public class RootObject
    {
        public ISomeService SomeService { get; private set; }

        public RootObject(ISomeService service)
        {
            SomeService = service;
        }
    }

    [TestFixture]
    public class UnityAndSpringDemo
    {
        [Test]
        public void UnityResolveA()
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<ISomeService, ServiceImplementationA>();
            RootObject rootObject = container.Resolve<RootObject>();
            Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void UnityResolveB()
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<ISomeService, ServiceImplementationB>();
            RootObject rootObject = container.Resolve<RootObject>();
            Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void SpringResolveA()
        {
            IApplicationContext container = ContextRegistry.GetContext();
            RootObject rootObject = (RootObject)container.GetObject("RootObject");
            Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void SpringResolveB()
        {
            // does not work - what to do to make this pass?
            IApplicationContext container = ContextRegistry.GetContext();
            RootObject rootObject = (RootObject)container.GetObject("RootObject");
            Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
        }
    }
}

В интересах Spring в файле App.config должно быть следующее. Ясно, что это служит только первому тесту пружины, а не второму. Можете ли вы поместить несколько конфигураций пружин в файл конфигурации? Если да, то каков синтаксис и как получить к ним доступ? Или есть другой способ сделать это?

  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="RootObject" type="IocSpringDemo.RootObject, IocDemo" autowire="constructor" />
      <object name="service" type="IocSpringDemo.ServiceImplementationA, IocDemo" autowire="constructor" />
    </objects>
  </spring>

Обновлять

Вот частичный ответ на основе кода на ссылки, которые Марко Лахма дал на блог Марка Поллака. Я прохожу вышеуказанные тесты со следующим кодом:

public static class SpringHelper
{
    public static T Resolve<T>(this IApplicationContext context, string name)
    {
        return (T)context.GetObject(name);
    }

    public static void RegisterType<T>(this GenericApplicationContext context, string name)
    {
        context.RegisterType(name, typeof(T));
    }

    public static void RegisterType(this GenericApplicationContext context, string name, Type type)
    {
        IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory();
        ObjectDefinitionBuilder builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, type);
        builder.SetAutowireMode(AutoWiringMode.AutoDetect);

        context.RegisterObjectDefinition(name, builder.ObjectDefinition);
    }
}

...

    [Test]
    public void SpringResolveA()
    {
        GenericApplicationContext container = new GenericApplicationContext();
        container.RegisterType<RootObject>("RootObject");
        container.RegisterType<ServiceImplementationA>("service");

        RootObject rootObject = container.Resolve<RootObject>("RootObject");
        Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
    }

    [Test]
    public void SpringResolveB()
    {
        GenericApplicationContext container = new GenericApplicationContext();
        container.RegisterType<RootObject>("RootObject");
        container.RegisterType<ServiceImplementationB>("service");

        RootObject rootObject = container.Resolve<RootObject>("RootObject");
        Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
    }

Это вызывает ко мне несколько вопросов:

  • Я хочу интегрировать эту технику в существующий код, который использует обычный контейнер. Почему мне нужно использовать контейнер другого типа, в данном случае GenericApplicationContext? Что, если я хочу считывать данные в этот объект из существующей конфигурации Spring в app.config или web.config? Будет ли это работать в обычном контексте? Могу ли я затем записать данные поверх этих регистраций с помощью кода?

  • Как я могу указать, что ISomeService должен быть создан как синглтон? Я имею в виду не предоставление экземпляра синглтона контейнеру, а контейнер для создания экземпляра, разрешения его конструктора и использования его, когда этот тип необходим.

  • как я могу сделать эквивалент container.RegisterType<ISomeService, ServiceImplementationA>();? Я хочу зарегистрировать сопоставления типов для использования во всех случаях, когда этот тип нужен конструктору.

  • Что именно делает container.RegisterType<ServiceImplementationA>("service");? Кажется, что ServiceImplementationA регистрируется как реализация ISomeService, но ISomeService никогда не упоминается, поэтому может возникнуть двусмысленность. например что, если ServiceImplementationA реализовано более одного интерфейса.

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

Я пытаюсь использовать Spring так, что это просто не работает? Я пытаюсь использовать его, как другие контейнеры IoC, но он не совсем работает.


person Anthony    schedule 23.12.2009    source источник


Ответы (2)


Добавление в качестве нового ответа попытка решить открытые вопросы ...

Я хочу интегрировать эту технику в существующий код, который использует обычный контейнер. Почему в этом случае я должен использовать другой тип контейнера, GenericApplicationContext? Что, если я хочу считывать данные в этот объект из существующей конфигурации Spring в app.config или web.config? Будет ли это работать в обычном контексте? Могу ли я затем записать данные поверх этих регистраций с помощью кода?

Spring имеет конкретные реализации контекста приложения для различных тактик инициализации. Наиболее распространенными из них являются GenericApplicationContext (руководство), XmlApplicationContext (файлы XML) и WebApplicationContext (очень похоже на XmlApplicationContext, но адаптированный для использования в Интернете). Все они реализуют общий интерфейс: IApplicationContext, который является предпочтительным способом доступа к этим контейнерам.

К сожалению, изменение регистраций с помощью кода обычно означает, что вам нужно напрямую использовать конкретный подкласс. С GenericApplicationContext и StaticApplicationContext это вполне естественно, но XmlApplicationContext обычно считается только XML и таким образом "фиксируется" в определении XML.

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

Ваш SpringHelper делает именно это, по умолчанию все объекты в Spring являются одиночными. Вы можете изменить это поведение, вызвав метод SetSingleton ObjectDefinitionBuilder с false.

как я могу сделать эквивалент container.RegisterType (); ? Я хочу зарегистрировать сопоставления типов для использования во всех случаях, когда этот тип нужен конструктору.

Spring использует имена объектов (идентификаторы), чтобы различать разные реализации. Поэтому, если вы хотите получить конкретный тип для обслуживания определенного экземпляра, если есть много альтернатив, вам следует обращаться к этому конкретному экземпляру по имени. Если вы используете автоматическое подключение, и ваш объект зависит от интерфейса ISomeService, и есть только один зарегистрированный объект, который его реализует, автоматическое подключение может установить его без двусмысленности.

Что именно делает container.RegisterType ("service"); делать? Кажется, что ServiceImplementationA регистрируется как реализация ISomeService, но ISomeService никогда не упоминается, поэтому может быть двусмысленность. например что, если ServiceImplementationA реализовал более одного интерфейса.

Продолжая предыдущий ответ, это регистрирует синглтон типа ServiceImplementationA с именем «service». Этот объект поставляется со всеми реализованными интерфейсами (и, конечно, с конкретным типом).

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

Как объяснялось ранее, это очень важно. Имя является уникальным идентификатором в этом контексте (родительский контекст может иметь объект с таким же именем) и может использоваться для доступа к определенным регистрациям объекта. Короче говоря, там, где другие фреймворки могут связывать тип как ключ к регистрации объекта, Spring использует имя.

person Marko Lahma    schedule 13.01.2010
comment
Смешивание XML и конфигурации кода важно, жаль, что это непросто. С чем совпадает регистрационное имя? Предположим, что у меня зарегистрированы две реализации ISomeService, одна из которых подключена к конструктору с автоматическим подключением. Это та, в которой имя регистрации совпадает с именем параметра? - person Anthony; 13.01.2010
comment
Связано, вы можете сделать новый GenericApplicationContext (родительский IApplicationContext). Позволит ли это вам поместить XmlApplicationContext в качестве родительского и эффективно разрешать классы с использованием обоих - конфигурации кода, если она есть, вернуться к родительской конфигурации XML? - person Anthony; 13.01.2010
comment
Обновление: простой тест с новым GenericApplicationContext (ContextRegistry.GetContext ()) предполагает, что вы не можете разрешать классы, используя регистрацию в обоих. Для чего тогда нужен родительский контекст? - person Anthony; 14.01.2010
comment
Spring.NET имеет различные режимы автоматического подключения, описанные здесь: springframework .net / doc / reference / html /. Таким образом, 0 или более 1 - это состояние ошибки. - person Marko Lahma; 15.01.2010
comment
Родительский контекст позволяет, например, разделить уровень фреймворка и уровень модулей. Вы можете разместить свою общую общую службу в корневом контексте и создавать подконтексты для чего-то вроде плагинов. Затем плагины найдут свои зависимости в контейнере, в котором они указаны, или в родительском контейнере. Определения дочерних контейнеров могут перезаписывать определения родителей, используя их определения для большей видимости. - person Marko Lahma; 15.01.2010
comment
Возможно, теперь это требует нового вопроса, но найти зависимости от контейнера, в котором они указаны, или от родительского контейнера - это именно то, что мы хотим, но для меня это не работает. Он находит зависимости в родительском контейнере, но не в корне. - person Anthony; 15.01.2010
comment
Это звучит так, что у вас три контейнера? убедитесь, что все они связаны друг с другом .. Вы можете искать объекты от дочерних к корневым, проходя по всем родительским объектам в цепочке. - person Marko Lahma; 15.01.2010
comment
Всего 2 контейнера. Если корневой объект, который вы пытаетесь разрешить, не зарегистрирован в дочернем контейнере, но находится в родительском, он не будет найден в дочернем контейнере, даже если зависимости корневого объекта найдены таким образом. - person Anthony; 16.01.2010

Это небольшое сравнение яблок и апельсинов, поскольку модульный тест использует конфигурацию кода для Unity и конфигурацию XML (app.config) для Spring.NET.

Если вы пойдете по XML-маршруту, вы можете либо закомментировать старую реализацию A, либо определить реализацию B как ту, которую нужно использовать - какая конфигурация - это правильно? Другой вариант - иметь выделенные файлы XML для каждого сценария (настройка конфигурации) и включать их через определения ресурсов контекста (теперь у вас есть встроенный ресурс). Другие варианты включают файловую систему и сборку, см. Раздел веб-конфигурации в руководстве по Spring.NET для хорошего примера.

Если вы выберете путь конфигурации кода, я бы посоветовал проверить Spring.NET Recoil и предстоящий CodeConfig.

person Marko Lahma    schedule 24.12.2009
comment
Что касается сравнения яблок и апельсинов, но я попытался использовать каждый инструмент в соответствии с его обычной практикой и доступными примерами. Таким образом, он использует конфигурацию в коде для единства и конфигурацию в app.config для весны. Действительно ли единство и весна охватывают одни и те же сценарии? Или это яблоки против апельсинов? Предлагать закомментировать конфигурацию A для работы B - не лучшая идея. Это, например, имитирует тестовые случаи с помощью моков. Для одного теста могут потребоваться разные макеты, и все тесты должны проходить за один раз. Этот сценарий является для нас ключевым для многих других. - person Anthony; 28.12.2009
comment
Меня заставили поверить, что Spring вообще не умеет конфигурировать в коде. Спасибо за ссылки, которые говорят об обратном. Являются ли эти надстройки зрелыми? Я также хотел бы увидеть рабочий пример упомянутого вами метода настройки конфигурации. Хотя это казалось возможным, в документации, которую я нашел, не совсем ясно, что именно здесь делать. Это однострочный вариант в большинстве других контейнеров IoC, на что он похож в Spring? - person Anthony; 28.12.2009
comment
Этот авторитетный ответ: stackoverflow.com/questions/411660/ говорит, что Spring.Net настроен только в XML. - person Anthony; 30.12.2009
comment
Да, XML - это обычный способ настройки Spring.NET. Всегда существует метамодель базовой конфигурации, которая состоит из обычных классов .NET. У Марка Поллака есть хорошая запись в блоге об этом здесь: blog.springsource.com/2008/01/04/. Spring.NET Recoil является разработкой сторонних разработчиков и является расширением модели конфигурации (как и XML). CodeConfig находится в стадии разработки и разработан разработчиками ядра Spring.NET. - person Marko Lahma; 31.12.2009
comment
Яблоки и апельсины использовали для обозначения основной проблемы с помощью различных подходов. Конфигурация кода проще для тестовых случаев, и XML лучше подходит, когда есть достаточно статическая модель (или файлы сценария XML, которые можно комбинировать как есть без изменений). - person Marko Lahma; 31.12.2009
comment
Я попробовал код и добавил его к исходному вопросу. - person Anthony; 05.01.2010