【问题标题】:MVVM with webservice带有网络服务的 MVVM
【发布时间】:2011-03-08 14:10:32
【问题描述】:

我将使用 MVVM 做一个 wpf 应用程序(它基于 http://www.codeproject.com/KB/WPF/MVVMQuickTutorial.aspx )。

此应用程序将每月连接一个网络服务。

在网络服务上我有合同

public class Student
{
        public string Name {get; set;}
        public int Score {get; set;}
        public DateTime TimeAdded {get; set;}
        public string Comment {get; set;}
}

在 WPF 应用程序中添加和删除学生将保存到 xml 文件。

所以在 wpf 应用程序中,学生会是这样的:

public class Student
{
    public string Name {get; set;}
    public int Score {get; set;}
    public DateTime TimeAdded {get; set;}
    public string Comment {get; set;}

    public Student(string Name, int Score,
        DateTime TimeAdded, string Comment) {
        this.Name = Name;
        this.Score = Score;
        this.TimeAdded = TimeAdded;
        this.Comment = Comment;
    }
}

public class StudentsModel: ObservableCollection<Student>
{
    private static object _threadLock = new Object();
    private static StudentsModel current = null;

    public static StudentsModel Current {
        get {
            lock (_threadLock)
            if (current == null)
                current = new StudentsModel();

            return current;
        }
    }

    private StudentsModel() 
    {

        // Getting student s from xml
        }
    }

    public void AddAStudent(String Name,
        int Score, DateTime TimeAdded, string Comment) {
        Student aNewStudent = new Student(Name, Score,
            TimeAdded, Comment);
        Add(aNewStudent);
    }
}

如何连接这两个类?

我猜最糟糕的是,来自 webservice 的合同 Student 将在这个 wpf 应用程序中用于从 xml 获取学生,而在其他应用程序中,学生的集合将从数据库中获取。

我是设计模式的新手,所以对我来说很难:/

示例:我单击 AddUser,在应用程序 A 中调用 webservice 方法将用户添加到数据库,在应用程序 B 中将用户添加到 XML 文件,在应用程序中。 基类是 webservice 中的合约。

下一个解释:

第一个应用程序使用 web 服务将数据保存在数据库中。第二个应用程序从不将数据保存在 xmls 中,并且一个 perm 月将此 xmls 发送到 web 服务并将其转换为学生的实例并将其保存在数据库中

【问题讨论】:

  • 几件事。您拥有 Web 服务这一事实与您的其他大部分问题完全无关。如果您需要将同一个对象同时存储到 XML 文件和 Web 服务中,请查看 Java 中的 DTO/DAO 设计模式。
  • MVVM 设计模式旨在将您的 UI 与业务逻辑/数据分开。您的逻辑位于模型(DataModel)类中,而 UI 实现位于 ViewModel 类中。理想情况下,您可以丢弃 UI 和 DataModel,构建另一个视图,其余逻辑可以正常工作,无需更改。 UI 通过 WPF 数据绑定和命令与 ViewModel 对话,而 ViewModel 通过代码直接与 DataModel 对话。这就是 MVVM 设计模式。
  • 另外,问题“如何连接这两个类?”真的不是问题。它可以重新形成为“请为我实现这个”。

标签: c# wpf mvvm web-services


【解决方案1】:

从您的问题中不清楚实际问题是什么。但是,我想我可以解决一些问题并向您展示解决它们的方法。

1) 我在您的项目中看到的主要问题是您有两个Student 类的定义。您可以轻松地将它们合并到一个定义中。 (我只会告诉你如何......)

2) 您是否希望 WPF 客户端将数据保存到 Data Source (XML?) 还是您的 Web Service 应该这样做非常不清楚。如果WPF 客户端应该保存Students,那么Web Service 是干什么用的?

3) 您没有在任何地方为Student 类定义ViewModel,在本例中为Model

我创建了一个包含 3 个项目的示例。

1) WebService - WCF 服务项目

2) StudentLib - 类库项目(其中定义了 Student 类)

3) DesktopClient - 一个 WPF 应用程序项目

这里是源代码:

WebService.IStudentService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using StudentLib;

namespace WebService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IStudentService" in both code and config file together.
    [ServiceContract]
    public interface IStudentService
    {
        [OperationContract]
        StudentLib.Student GetStudentById(Int32 id);

        [OperationContract]
        void AddStudent(StudentLib.Student student);
    }
}

WebService.StudentService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using StudentLib;

namespace WebService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "StudentService" in code, svc and config file together.
    public class StudentService : IStudentService
    {
        public StudentLib.Student GetStudentById(int id)
        {
            return new StudentLib.Student() { Name = "John Doe", Score = 80, TimeAdded = DateTime.Now, Comment = "Average" };
        }

        public void AddStudent(StudentLib.Student student)
        {
            // Code to add student
        }
    }
}

WebService's Web.Config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <bindings />
    <client />
    <services>
      <service name="WebService.StudentService" behaviorConfiguration="metaDataBehavior">
        <endpoint address="basic" binding="basicHttpBinding" contract="WebService.IStudentService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metaDataBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

StudentLib.Student.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;

namespace StudentLib
{
    [DataContract]
    public class Student
    {
        [DataMember]
        public String Name { get; set; }

        [DataMember]
        public Int32 Score { get; set; }

        [DataMember]
        public DateTime TimeAdded { get; set; }

        [DataMember]
        public String Comment { get; set; }
    }
}

DesktopClient.StudentViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesktopClient
{
    class StudentViewModel
    {
        protected StudentLib.Student Student { get; set; }

        public StudentViewModel(StudentLib.Student student)
        {
            this.Student = student;
        }

        public String Name { get { return Student.Name; } }
        public Int32 Score { get { return Student.Score; } }
        public DateTime TimeAdded { get { return Student.TimeAdded; } }
        public String Comment { get { return Student.Comment; } }
    }
}

DesktopClient.MainWindow.xaml

<Window x:Class="DesktopClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="400"
        Height="300"
        Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Column="0"
                   Grid.Row="0">Name :</TextBlock>
        <TextBlock Grid.Column="1"
                   Grid.Row="0"
                   Text="{Binding Name}"></TextBlock>
        <TextBlock Grid.Column="0"
                   Grid.Row="1">Score :</TextBlock>
        <TextBlock Grid.Column="1"
                   Grid.Row="1"
                   Text="{Binding Score}"></TextBlock>
        <TextBlock Grid.Column="0"
                   Grid.Row="2">Time Added :</TextBlock>
        <TextBlock Grid.Column="1"
                   Grid.Row="2"
                   Text="{Binding TimeAdded}"></TextBlock>
        <TextBlock Grid.Column="0"
                   Grid.Row="3">Comment :</TextBlock>
        <TextBlock Grid.Column="1"
                   Grid.Row="3"
                   Text="{Binding Comment}"></TextBlock>
    </Grid>
</Window>

DesktopClient.MainWindow.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DesktopClient.StudentService;
using StudentLib;

namespace DesktopClient
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            IStudentService client = new StudentServiceClient();

            Student student = client.GetStudentById(1);
            DataContext = new StudentViewModel(student);

            client.AddStudent(new StudentLib.Student() { Name = "Jane Doe", Score = 70, TimeAdded = DateTime.Now, Comment = "Average" });
        }
    }
}

到这里所有的问题都解决了:

1) Student 类在WebService 项目和DesktopClient 项目引用的公共程序集(StudentLib) 中定义。因此,在添加 Service Reference 时,代码生成器会重用该类。

2) 我建议所有与存储相关的操作都在Web Service 中,Client 应用程序应该只使用Web Service 来存储数据。

3) 使用StudentViewModel 类代替Student 类来显示MainWindow 中的数据。

【讨论】:

  • 第一个应用程序使用 web 服务将数据保存在数据库中。第二个应用程序从不将数据保存在 xmls 中,并且一个 perm 月将此 xmls 发送到 web 服务并将其转换为学生的实例并将其保存在数据库中
  • 好的,我明白了。 How connect this two classes ? 到底是什么意思?
【解决方案2】:

您可以使用以下架构。

1) 带有 app.config 的 WPF 应用程序(应用程序 A 和 B),其中包含 Web 服务 URL 或 XML 文件路径

2) 业务/数据层 为 Web 服务 URL 创建一个代理类,并创建您在帖子中提到的 Student Model 类。 修改您的 Add 方法以支持这两种功能。如果 app.config 有 webservice url (App A),则在 Webservice 中调用 Add 方法;如果 appsetting 有 XML 文件路径 (App B),则调用 add to XML 方法

【讨论】:

    【解决方案3】:

    幸运的是,这些类已经“链接”了,至少在形式上是这样:因为 ViewModel 只是一堆模型。

    在 MVVM 模式中,您需要在 ViewModel 中处理数据绑定函数。这包括以下内容:

    • StudentModel 构造函数 (private StudentsModel() { ...) 应加载所有 Student 实例。 (或者,您可以使用单独的 Load() 函数——如果您也有 Save() 方法,这似乎最合乎逻辑。)在这里,您可能会读取 XML 文件,使用 XmlSerializer 来deserialize XML 数据进入学生集合(可能是列表),然后使用base constructor(用于整个列表)或Add() method(一次一个,例如循环)将它们添加到自身。
    • 您需要将学生添加到集合中的函数,如教程示例中所示。在桌面应用程序中,您要在此处调用 Web 服务上的 Add 函数,如果成功,则将其添加到您自己的内存集合中。然后,您必须决定是要立即将(序列化)数据保存到 XML 文件中,还是稍后一起完成(例如,在卸载对象或调用 Save() 方法时)。
    • 您可能还想包含一个从集合中删除 Student 对象的方法。 ObservableCollection 的公共方法在这里会有所帮助,但在选择要删除的对象时,您可能需要/需要更多的逻辑。同样,您必须在此处通知网络服务有关已删除项目的信息,并知道您是要立即保存更改还是等待单独的事件。
    • ObservableCollection 继承是明智的,因为这会为您带来很多使绑定动态化的魔力——我的意思是,UI 会随着数据的变化而更新。只要您使用 Add() 和 Remove() 等基本方法,您就可以获得IEnumerableINotifyPropertyChangedINotifyCollectionChanged 的好处。

    您可能注意到我在上面的解释中主要提到了 XML 序列化,但是为了制作具有类似功能的 Web 服务,您需要将“序列化 XML 并保存到文件”之类的想法换成“将更改保存到数据库中” ”。模式是一样的,只是实际的动作(实现)不同。

    简而言之,这里的 ViewModel 是您要实现加载和处理内存中数据并将其保存到文件、数据库或 Web 服务的所有内容的地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      • 2019-10-30
      相关资源
      最近更新 更多