【问题标题】:How to pass a parameter into the constructor without import using MEF?如何在不使用 MEF 导入的情况下将参数传递给构造函数?
【发布时间】:2012-08-11 04:56:22
【问题描述】:

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

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

【问题讨论】:

    标签: c# export prism mef factory


    【解决方案1】:

    使用 MEF 的 Silverlight 变体,我们可以包含一个称为 ExportFactory&lt;T, TMetadata&gt; 的类型。这种类型的作用是,每次我们在其上调用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 来装饰您的导出属性,但我更喜欢这样做,因为它可以确保在我导出时,我会在编译时检查我是否提供了所有必需的元数据。

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

    [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&lt;ExportFactory&lt;QuestionViewModel, INameMetata&gt;&gt; 的实例,这是我们的零件工厂集合。这应该包含已导出的每种类型问题的工厂。导入部分是GetQuestionViewModel 方法(我假设您想返回QuestionViewModel,而不是QuestionViewModelFactory)。 ExportFactory 类型负责启动一个新实例,并且因为它属于ExportFactory&lt;QuestionViewModel, INameMetadata&gt; 类型,所以它有一个Metadata 类型的INameMetadata 属性,我们可以在创建零件之前对其进行查询。

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

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

    【讨论】:

    • 感谢您的回答,但我想我不明白,QuestionViewModel 将如何具有 Question 对象参数? ExportFactory 和 Metadata 标签让我有点困惑。
    猜你喜欢
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-27
    • 2019-07-13
    • 1970-01-01
    • 2023-02-23
    相关资源
    最近更新 更多