Отображение динамического объекта в интерфейс и регистрация в IoC
Я пытаюсь зарегистрировать динамические реализации для интерфейсов, которые будут внедрены в объекты, созданные моим контейнером IoC (в данном случае Unity).
Вот подход высокого уровня, который я использую:
- Динамически загружать список свойств из файла JSON. Я использую JSON.NET для этого в настоящее время.
- Сопоставьте этот динамический объект с интерфейсом. В настоящее время я использую экспромт для этого.
- Зарегистрируйте этот динамический объект в моем контейнере 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);