使用Unity仅根据名称获取对象

时间:2016-08-18 20:57:42

标签: c# dependency-injection unity-container

我有一个带有处理程序方法的应用程序。 handler方法获取一个json字符串,其中包含需要处理请求的对象的名称和请求的参数。基本上,这样的东西(我会保持简单):

public interface IJob
{
    bool Execute();
    bool Hydrate(string source);
}

public class JobBase 
{
    public int Id { get; set; }
    public JobType JobType { get; set; }
    public CronExpression CronExpression { get; set; }
}

public class JobSubmitClone : JobBase, IJob
{
    public string[] Tokens { get; set; }

    public bool Hydrate(string source)
    {
        // code omitted...
        return true;
    }
    public bool Execute()
    {
        // code omitted...
        return true;
    }
}

IJob和JobBase都保存在Common类项目中。所有应用程序都引用此DLL。

在我的主应用程序中,我安装了Unity,其中一个加载容器的步骤如下:

// Scan assemblies for Job definitions...
_container.RegisterTypes(AllClasses.FromAssembliesInBasePath().
    Where(type => typeof(IJob).IsAssignableFrom(type)),
    WithMappings.FromAllInterfaces,
    WithName.TypeName,
    WithLifetime.Transient);

每个“作业”都在其自己的课程项目中定义,主应用程序引用 NOT 。每个“作业”必须继承自JobBaseIJob

主应用程序公开了一个简单的REST服务。您可以发布类似的内容:

{ jobName : JobSubmitClone, Id : 1, JobType : 2, CronExpression : '' }

在主应用程序中,我试图根据JobName从容器中提取对象。我试过这个(是的,我知道它违反了IoC模式):

var container = UnityHelpers.GetConfiguredContainer();
var job = container.Resolve<IJob>(myParams.jobName);  // "JobSubmitClone" //
var hydrated = job.Hydrate(myParams);
if(hydrated)
    var result = job.Execute();

我收到以下错误:

  

异常是:InvalidOperationException - 当前类型IJob是   一个接口,无法构造。你错过了一个类型吗?   映射?

我错过了什么?

2 个答案:

答案 0 :(得分:0)

  

每个&#34;工作&#34;在其自己的类项目中定义,不被引用   由主应用程序。每个&#34; Job&#34;必须从JobBase和IJob继承。

你看过MEF了吗?它能够通过元数据查询和加载类。我倾向于将Unity用于已知的编译时依赖项,并将MEF用于在运行时加载的动态程序集。 (没有理由你不能在同一个项目中同时使用它们。)

我认为,我们会做一些类似于您正在寻找的事情。我们根据类名加载工作流。

使用System.ComponentModel.Composition.MetadataAttribute ....装饰作业

[MetadataAttribute]
public class WorkflowMetadataAttribute : Attribute, IWorkflowMetadata 
{
    public WorkflowMetadataAttribute(string typeName) {
        TypeName = typename;
    }
    public string TypeName { get; private set; }
}

你把它放在想要出口的东西上....

public interface IWorkflow // aka IJob
{
    void Execute();
}

[Export(typeof(IWorkflow))]
[WorkflowMetadata("WhateverWorkflow")]
public class WhateverWorkflow : IWorkflow 
{
    public void Execute() { }
}

导出的类可以与运行它的项目分开构建。如果将其作为库构建到单独的程序集中,则可以在导入程序类中加载程序集(或程序集目录)。

public class WorkflowCatalog : IPartImportsSatisfiedNotification
{
    [ImportMany]
    public IEnumerable<Lazy<IWorkflow, IWorkflowMetadata>> Workflows { get; private set; }

    public void Compose() {
        var path = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location );
        var catalog = new DirectoryCatalog( path );
        var compositionContainer = new CompositionContainer( catalog );
        compositionContainer.ComposeParts(this);
    }

    public void OnImportsSatisfied() {
        var workflow = Workflows.Single(w => w.Metadata.TypeName == "WhateverWorkflow").Value;
        workflow.Execute();
    }
}

IJob,IJobMetadata和JobBase生活在核心。工作班住在他们自己的图书馆(或者他们也可以住在主程序中)。

答案 1 :(得分:0)

事实证明,有很多方法可以操纵Unity。到目前为止,这就是最终的工作:

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies().Where(type => typeof(IJob).IsAssignableFrom(type) && type.IsClass),
    WithMappings.FromAllInterfaces,
    t => t.IsNested ? t.DeclaringType.Name + "." + t.Name : t.Name,
    WithLifetime.Transient);

我还构建了一个扩展方法:

public static IJob Job(this string src)
{
    var container = UnityConfig.GetConfiguredContainer();
    var job = container.Resolve<IJob>(src);
    return job;
}

我为最小有效负载创建了一个小模型:

public class MinimumCommandModel : IRequest<MinimumResultModel>
{
    public MinimumCommandModel(string json)
    {
        FullPayloadString = json;
        MinimumPayload = JsonConvert.DeserializeObject<MinimumPayload>(json);
    }

    public string MinimumPayloadString => JsonConvert.SerializeObject(MinimumPayload); 
    public string FullPayloadString { get; set; }
    public MinimumPayload MinimumPayload { get; set; }
}

然后我可以直接从(JSON)sting有效负载中获取作业:

var command = new MinimumCommandModel(Request.Content.ReadAsStringAsync().Result);
var job = command.MinimumPayload.JobName.Job();