0

我正在与 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))
    {
    }
}

但是后来我说,我怎样才能通过构造函数传递对象?q这个想法是在不使用 Import的情况下传递对象。但我在这部分迷路了。

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

我能做些什么来实现这个映射并传递参数?提前致谢。

4

1 回答 1

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在 propertyName中。您实际上不需要使用我们的元数据合约来装饰您的导出属性INameMetadata,但我更喜欢这样做,因为它可以确保在我导出时,我会在编译时检查我是否提供了所有必需的元数据。

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

[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();
  }
}

现在,我们以几种方式修改了这部分。

首先,我们只接受 an 的一个实例IEnumerable<ExportFactory<QuestionViewModel, INameMetata>>,它是我们的零件工厂集合。这应该包含已导出的每种类型问题的工厂。导入部分是GetQuestionViewModel方法(我假设您想要返回QuestionViewModel,而不是QuestionViewModelFactory)。该ExportFactory类型负责启动一个新实例,并且因为它属于 type ExportFactory<QuestionViewModel, INameMetadata>,所以它有一个属于 type 的Metadata属性,INameMetadata我们可以在创建零件之前查询该属性。

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

我希望这能为您指明正确的方向。

于 2012-08-11T08:17:30.337 回答