如何在MVP中的演示者和数据服务之间传输数据

时间:2014-05-10 12:38:12

标签: c# design-patterns mvp

我是MVP模式的初学者,只想了解以下案例的最佳做法。

为了更好地理解,我将通过一个例子提出问题。假设我们有一个表单EmployeeViewEmployeePresenterEmployeeModel和一个DataService类,它封装了GetEmployeeByID()方法。在本演示中,我使用了具体的类。

让我们说,现在在win表单应用中,我们希望按ID搜索员工,因此我们在视图中输入ID并按“搜索”按钮。在这种情况下,Presenter可能会使用反射来更新EmployeeModel。 (目前只有' EmployeeModel.ID'属性有数据)。然后Presenter将与DataService对话。这可以通过两种方式完成

  1. 在此,我们将Model传递给DataService,然后它会更新相同的模型并返回Presenter
  2. 类EmployeePresenter {

    private void SearchEmployee (Object sender, EventArgs e)
    {
        SearchEmployee();
    }
    
    private void SearchEmployee()
    {
        var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode);
        base.SetViewPropertiesFromModel(EmployeeModel);
    }
    

    }

    类DataService {

    public EmployeeModel GetEmployeeByID(EmployeeModel employee)
    {
        //Database code here
    
        employee.Name= (string) dataReader["name"];
        . 
        .
        .
        return employee;
    
    }
    

    }

    1. 此处我们仅传递Model的属性值,然后DataService将创建Model并返回Presenter
    2. 类EmployeePresenter {

      private void SearchEmployee (Object sender, EventArgs e)
      {
          SearchEmployee();
      }
      
      private void SearchEmployee()
      {
          var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode.ID);
          Base.SetViewPropertiesFromModel(EmployeeModel);
      }
      

      }

      类DataService {

      public EmployeeModel GetEmployeeByID (string employeeID)
      {
          //Database code here
      
          return new BankAccount
          {
           EmployeeName = (string) dataReader["name"],
           .
           .
           . };
      }
      

      }

      1. 上述哪一项是可以接受的?
      2. 例如,如果在一个视图上显示两个实体(例如,员工和工资实体)的详细信息,我们是将这些实体视为两个模型还是单个模型,如EmployeeSalary?如果它的两个型号我们需要两个演示者吗?
      3. DataService是否应始终返回商业模式? cant DataService返回stingsDataSets为ex?
      4. 演示者可以在_view.EmployeeName=EmployeeModel.Name;
      5. 等视图上设置值

2 个答案:

答案 0 :(得分:2)

  1. 第二个选项会更好,因为您的方法被称为GetEmployeeByID,期望Id的参数不是整个模型更合乎逻辑。

  2. 与ASP.NET MVC不同,您不需要将模型作为传递给视图的单个类,因此您可以保留2个模型以获得更好的语义结构。

  3. 这取决于您要实现的目标。同样,如果您的方法被调用GetEmployeeByID,则应该返回类型为Employee的业务模型。您的服务可以返回“字符串或数据集”,但这意味着您需要在Presenter中使用其他映射来将数据集映射到模型。

  4. 是的,演示者可以设置像_view.EmployeeName=EmployeeModel.Name这样的值来实现set的{​​{1}}访问修饰符,以便在某些控件中呈现文字,例如

    _view.EmployeeName
  5. 在某些情况下,虽然通过演示者设置值只会使其更复杂而没有任何特别的好处。在这些情况下,您可以使用监督控制器,它是MVP的子类型,其中一些与业务逻辑无关的基本渲染留在视图中,更复杂的逻辑在演示者/控制器中完成。你可以在这里找到相关的信息:

    http://martinfowler.com/eaaDev/SupervisingPresenter.html

    还有另一个名为Passive View的子类型,其中视图仅包含Controls,Presenter负责将值传递给View。你可以在这里阅读:

    http://martinfowler.com/eaaDev/PassiveScreen.html

    编辑:您还可以查看此答案,以便简要了解这两种子类型: What are MVP-Passive View and MVP-Supervising controller

答案 1 :(得分:0)

编辑: 这是被动视图代码。 我们希望能够从一种表单类型(Windows.Forms,Gtk.Forms等)切换到另一种表单类型,并希望能够在将来轻松地从hibernate切换到ado.net或其他类型。

我更喜欢采用固定参数的混合方法和采用搜索参数列表的通用参数。

例如,GetSomethingByID只会将一个int作为参数并返回一个Model。

但是当我想搜索一个地址时(至少有两个表被调用)。一个保存地址数据,如addressno。,searchname等。还有另一个包含name1,name2等的表。然后我会得到一个带有大量参数的方法。 在这一点上,我没有扩展这两个表中的任何一个。(!)

我们不喜欢超过4个参数的方法。所以我们创建了一个" QueryMethodParameter"我们从视图中使用的对象。 我将举一个例子,我表现出来比解释我们做的更容易。

当您在我们的视图中搜索地址时,会执行此操作。

p.Items.Add(new QueryMethodParameterItem("Address", "AddressNumber", addressNumber));
p.Items.Add(new QueryMethodParameterItem("Address", "Searchname", searchName));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name1", name1));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name2", name2));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name3", name3));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Street", street));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Zipcode", zipcode));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Location", location));
((AddressSearchPresenter)this.Presenter).DoAddressSearch(p);

AddressSearchPresenter

public void DoAddressSearch(QueryMethodParameter p)
{
  IAddressService addrService = (IAddressService)ApplicationController.GetInstance().ServiceFactory.GetService(typeof(Model.Address), this.ServiceContext);
  IListEx<Model.Address> erg = addrService.LoadAddresses(p);
  this.SetModel(erg);
  _viewItems = new AddressSearchViewItems(erg);
  this.ModelToView();
}

AddressService

public IListEx<Model.Address> LoadAddresses(QueryMethodParameter p)
{
  ICriteria ca = this.ServiceFactory.CreateCriteria(this.Context, typeof(Model.Address));
  ICriteria ma = null;

  for (int i = 0; i < p.Items.Count; i++)
  {
    object val = p.Items[i].Value;
    if (val == null)
    {
      throw new NullReferenceException();
    }

    if (val.GetType() == typeof(string))
    {
      if (!val.ToString().EndsWith("%"))
      {
        val = val.ToString() + "%";
      }

      if (!val.ToString().StartsWith("%"))
      {
        val = "%" + val.ToString();
      }
    }

    if (p.Items[i].ModelName == "Address")
    {
      ca.Add(Expression.Like(p.Items[i].PropertyName, val));
    }
    else if (p.Items[i].ModelName == "MailingAddress")
    {
      if (ma == null)
      {
        ma = ca.CreateCriteria("MailingAddress", "MailingAddress");
      }

      ma.Add(Restrictions.Like(p.Items[i].ModelName + "." + p.Items[i].PropertyName, val));
    }
    else
    {
      throw new NotImplementedException();
    }
  }

  ca.Add(Expression.Gt("AddressID", 0));

  return ca.ListEx<Model.Address>();
}

还有一些我们不喜欢的东西。例如,我们在服务中对模型名称进行硬编码。 - &GT;坏 我们将来可能会使用静态字符串或枚举来更改此类,以便在更改字段名称时获得编译器错误。

所以第二种方法总体上看起来更好。但是您仍然需要针对许多搜索参数的解决方案。简单的ID就是最简单的例子。