我正在尝试为接口注册动态实现,这些接口将被注入到由我的IoC容器(本例中为Unity)创建的对象中。
以下是我正在采取的高级方法:
- 从JSON文件中动态加载属性列表。我目前正在为此使用JSON.NET
- 将该动态对象映射到接口。我目前正在使用Impromptu
- 将该动态对象注册到接口类型的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不喜欢我没有给它一个实际的接口实例,只是一个鸭子类型的实例。
The type ImpromptuInterface.ActLikeCaster cannot be assigned to variables of type Manufacturing.Framework.Configuration.IDataCollectorConfiguration
我为什么要这么做?我试图通过将我的设置存储为JSON来简化我的复杂应用程序配置,定义映射到该JSON的接口,然后让我的IoC容器自动将正确的配置注入任何需要它的类。
如果不需要使用接口,可以使用具体类型:
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
}
}
无论如何,这可能会更好,因为接口可能需要任何类型的代理都无法很好地处理的方法实现(不过,我认为Castle有某种方法拦截器)。混凝土类型在那里摆脱了任何类型的假设;唯一真正的需求是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);