在WPF智能客户端应用程序中使用WCF DTO时理解MVVM使用的问题

los*_*wpf 14 wpf wcf prism smartclient mvvm

我在该领域的经验非常少,我正在编写一个使用MVVM与WCF后端通信的WPF智能客户端应用程序,并且我真的很难从所有信息中做出正确的决策.这引出了一系列问题,我希望在这方面有更多经验的人可以解决这些问题.

例如,其中一个屏幕将允许输入订单并向订单添加订单行.

什么用作模型?

在WCF服务上,我有以下简化的DTO:

public OrderDTO
{
   string orderDetails { get; set; }
   List<OrderLineDTO> OrderLines { get; set; }
}

public OrderLineDTO
{
   int customerId { get; set; }
   int productId { get; set; }
   double quantity { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和WCF服务有以下方法:

public OrderService Order
{
    CreateOrderResponse CreateOrder(OrderDTO order) 
}
Run Code Online (Sandbox Code Playgroud)

在我的WPF智能客户端中,我有一个对DTO的引用,但显然它没有实现,INotifyPropertyChanged因为它纯粹是为了传输.

问题

建议的方法是将这些DTO转换为INotifyPropertyChanged使用Automapper或类似方法实现的模型吗?或者DTO应该直接在ViewModel中用作模型?

在视图模型之间进行通信

目前,我有一个带有ViewModels 和2的标签(OrderOrderLines)的订单视图.在订单选项卡上,我有一个包含客户ID和名称.当我选择客户时,我需要告知客户已被选中,以便仅显示属于该客户的产品.OrderViewModelOrderLineViewModelComboBoxOrderViewOrderLineViewComboBox

问题

在这种情况下如何OrderViewModel沟通OrderLineViewModel

添加订单行并应用逻辑/业务规则

由于服务器级应用程序将由多个客户端使用,例如PC,移动设备..我想确保所有业务规则都应用于服务器级应用程序.例如,添加订单行时.如果是某种产品类型,只有在客户有特定认证的情况下才能添加.

但是,我所读到的关于MVVM的所有内容都表明该模型适用于业务规则和行为 - 所有这些示例都在客户端实现了该模型.理想情况下,我不想在客户端和服务器上复制相同的检查,所以我想知道如何确保不会发生这种情况.

问题

您是否允许用户添加无效行,将请求发送到服务器,让服务器应用相关规则并返回响应?或者,在将请求发送到服务器之前,您是否以某种方式在智能客户端应用程序中应用逻辑?

我真的希望在这里概述的所有领域都做得更好,我提前感谢你的任何回复.

谢谢

亚历克斯

编辑: 感谢大家的贡献,因为它帮助我在最佳前进方面变得更加清晰.所有答案都很好,但我决定接受Uri的回答,因为它最符合我现阶段的想法.但是,我仍然不确定处理从DTO的Id到ItemsSource中的SelectedItem的转换的最佳方法,ItemsSource是ViewModel的列表.我可以看到转换器可能有效,但我将尝试找到另一种解决方案.谢谢Alex

Uri*_*Uri 5

以下是我对你的问题的思考:

问题: 推荐的方法是将这些 DTO 转换为使用 Automapper 或类似工具实现 INotifyPropertyChanged 的​​模型吗?或者应该直接在视图模型中使用 DTO 作为模型?

答:我最喜欢的方法是遏制。我同意你的观点,DTO 除了 getter 和 setter 之外不应该有任何东西。尽可能保持干净,因此它不应该触发 INotifyPropertyChanged。我也不认为视图应该直接访问对象模型(如果没有其他原因,您没有属性更改的好处)。我的方法的缺点是 ViewModel 中有一些额外的代码,但我认为这是值得的。

public class VmBase : INotifyPropertyChanged {

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void raise( string propName )
    {
        if( PropertyChanged ) {
            PropertyChanged( this, new PropertyChangedEventArgs(propName) );
        }
    }
}

public class OrderLineVm : VmBase {
    private OrderLineDTO orderLine;

    public OrderLineVm( OrderLineDTO ol ) {
        orderLine = ol;
    }

    public OrderLineVm( ) {
        orderLine = new OrderLineDTO();
    }

    int customerId {
        get { return orderLine.customerId; }
        set { orderLine.customerId=value; raise("customerId"); }
    }

    int productId {
        get { return orderLine.productId; }
        set { orderLine.productId=value; raise("productId"); }
    }

    double quantity {
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

通过垃圾收集的奇迹,OrderLineDTO 将仅创建一次(当它来自服务器时),并且只要需要它就一直存在。有两种公共构造函数:一种带有 DTO(通常,当对象来自服务器时),另一种在客户端上创建。

对于 OrderVm 来说,这有点复杂,因为您希望有一个 OrderLineVm(相对于 OrderLineDTO)的 ObservableCollection(相对于 List),所以包含不起作用。另请注意,orderLines 只有一个 getter(您可以从中添加和删除订单行,但不会更改整个列表。在构造期间分配一次)。

public class OrderVm : VmBase {
    private string _orderDetails;
    public string orderDetails {
        get { return _orderDetails;
        set { _orderDetails=value; raise("orderDetails"); }
    }

    private ObservableCollection<OrderLineVm> _orderLines;
    public ObservableCollection<OrderLineVm> orderLines { 
        get { return _orderLines; }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题: 在这种情况下,OrderViewModel 将如何与 OrderLineViewModel 通信?

答:如果需要沟通,确实应该以最简单的方式进行。两个视图模型类位于同一层。OrderVm 引用了 OrderLineVm 的列表,如果您需要从 OrderLineVm 类到订单的通信,只需保留一个引用即可。

然而,我强烈认为不需要沟通。一旦视图被适当地绑定,我认为没有理由进行这种通信。绑定的 Mode 属性应该是“双向”,因此 UI 中更改的所有内容都将在视图模型中更改。由于 ObservableCollection 发送的通知,对订单行列表的添加、删除将自动反映在视图上。

问题:是否允许用户添加无效行,将请求发送到服务器,让服务器应用相关规则并返回响应?或者,在将请求发送到服务器之前,您是否以某种方式应用智能客户端应用程序中的逻辑?

答:除了服务器之外,在客户端进行数据验证并没有什么问题。避免重复代码 - 使用单个程序集(可能是定义 DTO 的程序集)来执行验证,并在客户端中部署该程序集。这样您的应用程序的响应速度将会更快,并且您将减少服务器上的工作负载。

显然,您需要在服务器上进行数据验证(出于安全原因和竞争冲突)。即使客户端上的验证已通过,您也必须处理服务器返回错误的情况。

编辑:(跟进亚历克斯的评论):

显示下拉列表:我认为您困惑的根源在于实际上有两个独立的 ItemsSource(因此有两个独立的数据上下文):有一个订单行列表,每个订单行中嵌入的是 ProductID 列表,其中是填充组合框的项目。只有 SelectedItem 是 ProductLine 的属性。通常,可能的 ProductID 列表对于应用程序(或订单)应该是全局的。您将拥有整个表单的 ProductID 属性,并为其指定一个名称(例如 x:Key 或 x:Name)。然后,在 ComboBox 元素中只需引用此列表:

<ComboBox ItemsSource="{Binding Source={StaticResource ProductIDs}}"
          SelectedItem="{Binding Path=productId}"
          />
Run Code Online (Sandbox Code Playgroud)