Отображение динамического объекта в интерфейс и регистрация в IoC

Я пытаюсь зарегистрировать динамические реализации для интерфейсов, которые будут внедрены в объекты, созданные моим контейнером IoC (в данном случае Unity).

Вот подход высокого уровня, который я использую:

  1. Динамически загружать список свойств из файла JSON. Я использую JSON.NET для этого в настоящее время.
  2. Сопоставьте этот динамический объект с интерфейсом. В настоящее время я использую экспромт для этого.
  3. Зарегистрируйте этот динамический объект в моем контейнере IoC для типа интерфейса

Вот код, который должен "теоретически" работать:

var configJson = File.ReadAllText(".\\Configuration\\DataCollector.json");
dynamic expando = JsonConvert.DeserializeObject(configJson);
var container = new UnityContainer();
var interfaceType = Type.GetType("Manufacturing.Framework.Configuration.IDataCollectorConfiguration", true);
var interfaceInstance = Impromptu.ActLike(expando, interfaceType);

container.RegisterInstance(interfaceType, "IDataCollectorConfiguration", interfaceInstance, new ContainerControlledLifetimeManager());

Все хорошо до последней строчки. Unity не нравится тот факт, что я не даю ему фактический экземпляр интерфейса, а просто экземпляр типа duck.

The type ImpromptuInterface.ActLikeCaster cannot be assigned to variables of type Manufacturing.Framework.Configuration.IDataCollectorConfiguration

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

2 ответа

Решение

Если вам не нужно использовать интерфейсы, вы можете использовать конкретные типы:

using System;
using Microsoft.Practices.Unity;
using Newtonsoft.Json;

namespace TestGrounds
{
    public class TestClass
    {
        #region Properties

        public int TestIntegerProperty { get; set; }

        public string TestStringProperty { get; set; }

        #endregion
    }

    internal class Program
    {
        #region Static Methods

        private static void Main(string[] args)
        {
            const string json =
                @"{ TestIntegerProperty: 1, TestStringProperty: 'Hello', AnotherTestPropertyToIgnore: 'Sup' }";

            registerDependencyFromJson<TestClass>(json);

            Console.ReadKey();
        }

        private static void registerDependencyFromJson<T>(string json) where T: class, new()
        {
            var deserializedObject = JsonConvert.DeserializeObject<T>(json);
            var type = deserializedObject.GetType();
            var container = new UnityContainer();

            container.RegisterInstance(type, type.Name, deserializedObject, new ContainerControlledLifetimeManager());
        }

        #endregion
    }
}

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

Обновить:

Вот пример создания типа из строкового имени, а также показ неверного типа:

using System;
using Microsoft.Practices.Unity;
using Newtonsoft.Json;

namespace TestGrounds
{
    public class TestClass
    {
        #region Properties

        public int TestIntegerProperty { get; set; }

        public string TestStringProperty { get; set; }

        #endregion
    }

    public class BadTestClass : TestClass
    {
        #region Properties

        public double TestDoubleProperty { get; set; }

        #endregion

        #region Constructors

        public BadTestClass(double testDouble)
        {
            TestDoubleProperty = testDouble;
        }

        #endregion
    }

    internal class Program
    {
        #region Static Methods

        private static void Main(string[] args)
        {
            const string json =
                @"{ TestIntegerProperty: 1, TestStringProperty: 'Hello', AnotherTestPropertyToIgnore: 'Sup' }";
            var type = Type.GetType("TestGrounds.TestClass", true);
            var badType = Type.GetType("TestGrounds.BadTestClass", true);

            registerDependencyFromJson(type, json);
            try
            {
                registerDependencyFromJson(badType, json);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadKey();
        }

        private static void registerDependencyFromJson(Type type, string json)
        {
            // type requires a default constructor for this to work
            var constructor = type.GetConstructor(Type.EmptyTypes);
            if(constructor == null)
            {
                throw new ArgumentException("Type must have a parameterless constructor.");
            }

            var deserializedObject = JsonConvert.DeserializeObject(json, type);
            var container = new UnityContainer();

            container.RegisterInstance(type, type.Name, deserializedObject, new ContainerControlledLifetimeManager());
        }

        #endregion
    }
}

ActLike предназначен для статической типизации динамических объектов, он требует как минимум неявного приведения к интерфейсу. Вместо этого используйте DynamicActLike и он вернет финальный экземпляр без предварительного статического приведения.

var interfaceInstance = Impromptu.DynamicActLike(expando, interfaceType);
Другие вопросы по тегам