MVVM ViewModel 到模型通信和参数

MVVM ViewModel to Model communication and parameters

背景

我想我已经找到了这个问题的答案,但我正在努力尽可能地坚持 MVVM,并在很长一段时间内学到很多东西。

我目前已经设置好视图、视图模型和模型。我的模型正在使用 entity framework.

查询数据库

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

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

问题

我的模型公开了一个函数,该函数 returns 将查询结果作为某种 IEnumerable。我现在遇到的问题是 "search criteria" 我让用户设置的数量。我现在有 9 模型函数的参数。不知道能不能接受至少,它是丑陋的。很丑陋。但是这样一来,我的 viewModel 只需要保存模型的一个实例,然后只需要知道那个函数及其签名。

问题

我应该在模型中设置属性,然后在视图模型中设置这些属性吗?这样单个函数会更清晰,但视图模型必须更多 "aware" 模型具有的属性。我知道创建一些 public 属性没什么大不了的,但我想知道哪个更适合 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();

我会说,"It depends",这些参数之间的联系有多紧密。

能不能分成几组,功能多一点就好了。 例如按时间范围、严重程度、文本搜索等过滤。

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

想想其他人 'consumers' can/will 将来如何使用你的模型,并据此做出决定/

根据我的理解,您的用例要求实施 Command Query Separation. About CQS details you can see my answer 。现在我们有了这个作为我们想法基础的据点,我们将检查下一个重构步骤。

参数泛洪的重构

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

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

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

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() 调用并且你的模型具有属性 "filled".

将模型映射到视图模型

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

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

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

然后得到映射视图模型的结果,映射操作可以这样完成:

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

其中model参数为填充模型。

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

The 4.2.0 release of AutoMapper marked the entire static configuration and mapping API as obsolete

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

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