【问题标题】:Pass information from one layer to another将信息从一层传递到另一层
【发布时间】:2016-09-09 21:24:19
【问题描述】:

抽象视图:我想将信息从一层传递到另一层(注意:如果该线程有更好的标题,请告诉我)。

我有一个 ViewModel,它与我的视图和我的服务层通信。 而且我与我的持久层有一个服务层通信。

假设我有以下课程:

public class EmployeeViewModel()
{
    // The following properties are binded to my View (bidirectional communication)
    public Firstname ...
    public Lastname ...
    public Email ...

    public void PerformingSearch()
    {
        ...
        EmployeeService.Search(...);
        ...
    }
}

public class EmployeeService()
{
    public List<Employee> Search(...)
    {
        // Searching in db
    }
}

将数据从我的 ViewModel 移交给我的服务层(例如,用于执行搜索)的最佳做法是什么?

我看到了一些选项(ViewModel 视角):

  1. EmployeeService.Search(Firstname, Lastname, Email);
  2. EmployeeService.Search(employeeSearchModel); // 在这种情况下,我需要另一个模型。模型应该如何实例化?
  3. EmployeeService.Search(this); // 转换必须在某个地方完成

这个问题有什么设计模式吗?怎么称呼?什么选择是最好的?我错过了什么吗?

【问题讨论】:

  • 您的视图模型是否包含用例所需的业务逻辑?我假设您没有其他(域/持久性)模型以保持您的架构简单......?问这个是因为目前我们没有关于您的架构的详细概述,而且对我来说,很难向您推荐一个简洁的答案。
  • ViewModel 不应包含业务逻辑。业务逻辑放置在服务层内。服务层和数据库之间是我的数据访问层。到目前为止,我主要使用模型将数据从/向数据库转换。

标签: c# mvvm


【解决方案1】:

描述您的问题空间

您的特定示例告诉我,您当前的架构包含一个服务层,该服务层充当数据访问层的代理。如果对您的架构没有更深入的了解,我会在您的环境允许的范围内建议keep it simple 的可能解决方案。

现在让我们尝试选择一个策略来获得一个可能的解决方案模型。 您的用户故事听起来像:“用户提交信息以获取员工列表”

您当前的用例已简化:

  1. UI:提交一些您需要服务的信息;
  2. VM:接收搜索词并将其传递到服务层旁边;
  3. SL:将接收到的数据发送到数据访问层(并可能将响应值更新为 VM 属性);
  4. DAL:在持久化存储中查找信息并返回获得的值。

重构的用例示例:

  1. VM:调用封装了所需值的查询,并设置要在 UI 中显示的属性。

看起来更容易吧?

输入:Command Query Separation

简而言之CQS:

声明每个方法都应该是执行的命令 向调用者返回数据的操作或查询,但不能同时返回两者。

在您的特定情况下,我们需要关注queries,其中:

查询:返回结果且不改变系统的可观察状态(无副作用)。

但这对您有什么帮助?让我们看看。 CQS 查询端的一个非常好的和详细的解释可以在Steven 的“Meanwhile... on the query side of my architecture”博客文章中完整阅读。

查询概念应用于您的问题

为查询对象定义接口

public interface IQuery<TResult> {}

查询处理程序定义:

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

现在这里是您的 "search" 查询对象的实现。这实际上是您“如何传递信息”问题的答案:

public class FindEmployeeBySearchTextQuery : IQuery<Employee>
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Email { get; set; }
}

最后是您将在查询对象中传递的查询处理程序:

public class FindEmployeeBySearchTextQueryHandler
    : IQueryHandler<FindEmployeeBySearchTextQuery, List<Employee>>
{
    private readonly IDbContext db;

    public FindEmployeeBySearchTextQueryHandler(IDbContext db)
    {
        this.db = db;
    }

    public List<Employee> Handle(FindEmployeeBySearchTextQuery query)
    {
        return (
            from employee in this.db.employees
            where employee.FirstName.Contains(query.FirstName) ||
                  employee.LastName.Contains(query.LastName) ||
                  employee.Email == query.Email
            select employee )
            .ToList();
    }
}

注意:此Handle() 示例实现使用实体框架的IDbContext,您必须根据您的需要(ADO.NET、NHibernate 等)重新设计/修改它。

最后在您的视图模型中:

public class EmployeeViewModel()
{
    private readonly IQueryHandler _queryHandler;
    public EmployeeViewModel(IQueryHandler queryHandler) 
    {
        _queryHandler = queryHandler;
    }

    public void PerformingSearch()
    {
        var query = new FindEmployeeBySearchTextQuery
        {
            FirstName = "John", 
            LastName = "Doe",
            Email = "stack@has.been.over.flowed.com"
        };

        List<Employee> employees = _queryHandler.Handle(query);

        // .. Do further processing of the obtained data
    }
}

此示例假设您正在使用依赖注入。
您将 IQueryHandler 实现注入到您的视图模型构造函数中,然后使用接收到的实现。

使用这种方法,您的代码会变得更简洁、更受用例驱动,并且具有更好的职责隔离,您可以轻松地测试和装饰进一步的横切关注点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 2015-03-24
    • 1970-01-01
    • 2011-07-20
    相关资源
    最近更新 更多