如何在不使用MEF导入的情况下将参数传递给构造函数?

时间:2012-08-11 04:56:23

标签: c# export prism mef factory

我正和MEF合作。我正在观看来自PRISM的名为MVVM RI的演示,该程序的一部分有以下代码:

    /// <summary>
    /// Factory class to create a question view model for a given question object.
    /// </summary>
    private static class QuestionViewModelFactory
    {
        private static Dictionary<Type, Func<Question, QuestionViewModel>> maps = new Dictionary<Type, Func<Question, QuestionViewModel>>()
        {
            { typeof(OpenQuestion), (q) => new OpenQuestionViewModel((OpenQuestion)q) },
            { typeof(MultipleSelectionQuestion), (q) => new MultipleSelectionQuestionViewModel((MultipleSelectionQuestion)q) },
            { typeof(NumericQuestion), (q) => new NumericQuestionViewModel((NumericQuestion)q) }
        };

        public static QuestionViewModel GetViewModelForQuestion(Question question)
        {
            Func<Question, QuestionViewModel> viewModelInstanceFactory = null;
            if (maps.TryGetValue(question.GetType(), out viewModelInstanceFactory))
            {
                return viewModelInstanceFactory(question);
            }
            else
            {
                throw new ArgumentOutOfRangeException("Could not locate a view model for question type");
            }
        }
    }

    // Note that each class derived QuestionViewModel needs a constructor parameter to be created.
public abstract class QuestionViewModel : NotificationObject 
{ 
    protected QuestionViewModel() { ... } 
} 

public abstract class QuestionViewModel<T> : QuestionViewModel 
    where T : Question 
{ 
    protected QuestionViewModel(T question) { ... } 
} 

在我的软件中,我需要这个功能,但现在我想通过发现来做。

一开始,我想创建一个自定义导出,仅存储QuestionViewModel并将contractName存储为问题类型模型。检查一下。

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportViewModelForProblemAttribute : ExportAttribute
{
    public ExportViewModelForProblemAttribute(Type viewModelType, Type questionType)
        : base(questionType.ToString(), typeof(QuestionViewModel))
    {
    }
}

然后我说,我怎么能通过构造函数传递对象?我们的想法是在不使用Import的情况下传递对象q。但我迷失在这一部分。

public class ProblemViewModelFactory
{
    private readonly CompositionContainer container;

    [ImportingConstructor]
    public ProblemViewModelFactory(CompositionContainer container)
    {
        this.container = container;
    }

    public QuestionViewModelFactory GetQuestionViewModelFactory(Question question)
    {
        // what can I do to return the correspond view model with the question inside?
    }
}

如何实现此映射并传递参数? 提前谢谢。

1 个答案:

答案 0 :(得分:0)

使用MEF的Silverlight变体,我们可以包含一个名为ExportFactory<T, TMetadata>的类型。这种类型的作用是,每当我们在其上调用CreateExport()时,就会启动该类型的新实例,但它还包含有关该部分的一些附加信息(元数据)。

现在,您目前正在使用问题名称作为合同名称导出问题视图模型。这不会让您轻松获取所有QuestionViewModel类型的实例,因此您应该继续导出为QuestionViewModel并将一些元数据附加到该类型,因此在这种情况下,您需要name属性,因此我们可以将元数据合约定义为:

public interface INameMetadata
{
  string Name { get; }
}

现在,让我们修改导出属性:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public class ExportQuestionAttribute : ExportAttribute, INameMetadata
{
  public ExportQuestionAttribute(string name)
    : base(typeof(QuestionViewModel))
  {
    this.Name = name;
  }

  public string Name { get; private set; }
}

我已经更改了导出属性类型以仅使用type参数调用基础构造函数,而是将name的值存储在属性Name中。您实际上不需要使用我们的元数据合约INameMetadata来装饰您的导出属性,但我更喜欢这样,因为它确保在导出时,我会在编译时检查我是否提供了所有必需的元数据。 / p>

接下来,我们可以对我们的消费者类型进行修改:

[Export]
public class ProblemViewModelFactory
{
  private readonly IEnumerable<ExportFactory<Question, INameMetadata>> _questionFactories;

  [ImportingConstructor]
  public ProblemViewModelFactory(
    [ImportMany] IEnumerable<ExportFactory<Question, INameMetadata>> questionFactories)
  {
    if (questionFactories == null)
      throw new ArgumentNullException("questionFactories");

    _questionFactories = questionFactories;
  }

  public QuestionViewModel GetQuestionViewModel(string name)
  {
    return _questionFactories
      // Get matching question factories
      .Where(q => q.Metadata.Name == name)
      // Select the exported value
      .Select(q => q.CreateExport().Value)
      // First or default - what if the question doesn't exist?
      .FirstOrDefault();
  }
}

现在,我们已经通过几种方式对这部分进行了修改。

首先,我们只接受IEnumerable<ExportFactory<QuestionViewModel, INameMetata>>的实例,这是我们的部分工厂的集合。这应包含已导出的每种问题类型的工厂。导入部分是GetQuestionViewModel方法(我假设您想要返回QuestionViewModel,而不是QuestionViewModelFactory)。 ExportFactory类型执行启动新实例的工作,并且因为它是ExportFactory<QuestionViewModel, INameMetadata>类型,它具有类型为Metadata的{​​{1}}属性我们可以在创建我们的部分之前进行查询。

最后一个方法将查询可用工厂集,检查每个INameMetadata实例的Name属性,并返回匹配的问题,或INameMetadata如果找不到。它也会忽略具有相同名称的多个问题,并仅选择第一个问题(此设计决定将取决于您)。

我希望指出你正确的方向。