将动态对象映射到接口并在IoC中注册

3
我正在尝试为接口注册动态实现,这些实现将被注入到由我的IoC容器(在本例中是Unity)创建的对象中。
以下是我采取的高级方法:
1. 动态从JSON文件加载属性列表。我目前正在使用JSON.NET进行此操作。 2. 将该动态对象映射到一个接口。我目前正在使用Impromptu进行此操作。 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不喜欢我没有给它一个实际的接口实例,而只是一个鸭子类型的实例。

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

我为什么要这样做?我试图通过将设置存储为JSON,定义映射到该JSON的接口,然后让我的IoC容器自动将适当的配置注入到任何请求它的类中来简化我的复杂应用程序配置。


我现在已经把这个全部搞定了。实际上,它的配置非常优雅。我很快就会写一篇博客介绍它。 - Jason Young
2个回答

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
    }
}

可能更好的方式是使用具体类型,因为接口上可能有要求实现的方法,而任何代理都不能很好地处理(尽管我认为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
    }
}

你作弊了。:-) 在你的主方法中,你指定了类型(TestClass)。你能在没有它的情况下让你的代码工作吗?如果我用相同的方式使用接口,我可以让我的代码工作。 - Jason Young
更新允许发送字符串。 - Brandon Martinez

1

ActLike 适用于静态类型的动态对象,它需要至少一个隐式转换到接口。相反,使用 DynamicActLike,它将返回最终实例,而无需先进行静态转换。

var interfaceInstance = Impromptu.DynamicActLike(expando, interfaceType);

那绝对有帮助,我成功地将其注册了。但是,当我尝试解决该类型时,Unity会抛出一个异常,指出没有可访问的构造函数。我会给你点赞,但我想我会采用Brandon的解决方案并切换到具体类型。 - Jason Young
哎呀,我不确定什么会是无法访问的,接口代理是公共的并且拥有一个无参构造函数。 - jbtule

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接