【问题标题】:MVVM ViewModel to Model communication and parametersMVVM ViewModel 到模型通信和参数
【发布时间】:2016-08-23 09:24:35
【问题描述】:

背景

我想我已经有了这个问题的答案,但我正在努力尽可能地坚持 MVVM,并在漫长的道路上学到很多东西。

我目前已设置好 View、ViewModel 和 Model。我的模型正在使用实体框架查询数据库。

My View 有一堆控件,允许用户设置查询的参数(基本上是构建一个大的 where 子句)。ViewModel 存储这些通过控件设置的选项。

所以我的视图-视图模型交互看起来很不错,我认为这是可以接受的。

问题

我的模型公开了一个函数,该函数将查询结果作为某种 IEnumerable 返回。我现在遇到的问题是我让用户设置的“搜索条件”的数量。我现在有模型函数的 9 个参数。不知道能不能接受至少,它是丑陋的。非常难看。但是这样一来,我的 viewModel 只需要保存模型的一个实例,然后只需要知道一个函数及其签名。

问题

我应该在模型中设置属性,然后在视图模型中设置这些属性吗?这样,单个函数会更清晰,但视图模型必须更加“了解”模型具有哪些属性。我知道仅仅创建一些公共属性并不是什么大不了的事,但我想知道哪个更适合 MVVM。我们当前的代码库没有关注点分离。所以我独自一人。

问题中的代码

当前模型功能:

public IEnumerable<> GetResults(string id, string inputName, DateTime? fromDate,
    DateTime? toDate, bool option1, int selectCount, bool exactMatch = true, bool showFailed = false)
{
    //QUERY HERE, returns results
}

相关 ViewModel 调用:

var queryResults = MyModel.GetResults(id, inputname, FromDate, ToDate, Option1, selectCount, ExactMatch, ShowFailed);
Results = queryResults.ToList();

建议模型:

public string Id          {get;set}
public string InputName   {get;set}
public DateTime? FromDate {get;set}
public DateTime? ToDate   {get;set}
public bool Option1       {get;set}
public int SelectCount    {get;set}
public bool ExactMatch    {get;set}
public bool ShowFailed    {get;set}

public IEnumerable<> GetResults()
{
    //Query here, return results
}

相关提议的 ViewModel 调用:

MyModel.Id = this.Id;
MyModel.InputName = this.InputName;
MyModel.FromDate = this.FromDate;
//...etc (I put the this. to clarify the view model also has those properties).
var results= MyModel.GetResults();

【问题讨论】:

  • 有几件事我会像你一样做:1. 引入强类型参数对象以摆脱当前的 9 个参数,2. 创建一个外观,其关注点是创建你的模型,传递 GetResults() 调用, 3. 通过它自己的抽象将结果映射到视图模型。
  • +1 到@kayess。另见自动映射器lostechies.com/jimmybogard/2009/01/23/…

标签: c# wpf mvvm model viewmodel


【解决方案1】:

我会说,“这取决于”这些参数的关联程度。

能不能分组,最好有多个功能。 例如按时间范围、严重性、文本搜索等过滤。

当它们明显属于一起时,创建一个类或结构来对它们进行分组并传递一个参数。

想想其他“消费者”将来如何/将如何使用您的模型,并据此做出决定/

【讨论】:

  • 我现在问这个问题真的很糟糕。 “当它们明显属于一起时,创建一个类或结构来对它们进行分组并传递一个参数。”这似乎是一个非常好的和简单的解决方案。谢谢你的提醒。我不知道为什么我没有想到这一点。
【解决方案2】:

据我了解,您的用例要求实现Command Query Separation。关于 CQS 的详细信息你可以看我的回答here。现在我们已经将这个据点作为我们想法的基础,我们将检查下一个重构步骤。

参数洪水的重构

看到你的方法签名,我们可以清楚地看到你这里有很多个参数:

public IEnumerable<T> GetResults(string id, string inputName, DateTime? fromDate,
                                 DateTime? toDate, bool option1, int selectCount, 
                                 bool exactMatch = true, bool showFailed = false)

现在,如果我们引入 parameter object 而不是当前的 9 个参数,您的代码将如下所示:

public IEnumerable<T> GetResults(FilterObject filterObject)

看起来好多了,对吧?现在示例参数对象只是一个POCO,它看起来像:

public class FilterObject 
{
    public string id { get; set; }
    public string inputName { get; set; }
    ...
}

分离获得结果的关注

分离关注点并摆脱紧密耦合的模型和视图模型。可以在here 找到关于查询的非常好的和简短的介绍。我们创建一个示例查询处理程序:

public class GetResultsQueryHandler
: IQueryHandler<FilterObject, YourModel>
{
    public GetResultsQueryHandler([pass your needed dependencies here])
    {
        //set them to local variables
    }

    public YourModel Handle(FilterObject filterObject)
    {
       // Logic to call GetResults(filterObject) and return the filled model
    }
}

现在您已经很好地分离了以前的 GetResults() 调用,并让您的模型具有“填充”属性。

将模型映射到视图模型

最后我们需要做的是如何将模型映射到视图模型实例。有一堆对象映射器,一个流行的是AutoMapper。在像您这样的情况下,它让您的生活更加轻松,实际上您需要做的就是设置地图并致电Mapper.Map()

在您在问题中显示的示例中,模型和视图模型属性名称相同,映射定义应该很简单:

public static void Configure()
{
    Mapper.CreateMap<YourModel, YourViewModel>();
}

然后得到结果映射的视图模型,映射操作可以如下:

var viewModel = Mapper.Map<YourModel, YourViewModel>(model);

其中model 参数是填充模型。

引用我的 AutoMapper 示例中显示的静态用例方法:

AutoMapper 4.2.0 版本将整个静态配置和映射 API 标记为过时

这意味着使用 AutoMapper >= v4.2 创建配置已更改为:

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<YourModel, YourViewModel>();
});
var mapper = config.CreateMapper();

【讨论】:

  • 大家都推荐了对象而不是荒谬的参数数量,这似乎是一个不错的选择。我将此标记为深入解释和对其他来源的引用的正确答案。谢谢。
猜你喜欢
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-09
  • 2016-07-17
  • 2015-08-24
  • 2011-11-17
相关资源
最近更新 更多