-->

In which class should I load my data when using MV

2020-07-15 02:55发布

问题:

I'm currently learning C#, and recently I've learned about the MVVM design pattern for WPF. I am writing a simple program as a way to practice this, but I'm not sure where I should write the method that loads the data.

I have a SalesSheet class, as shown below. This holds the data that I load from a .xls file.

class SalesSheet
{
    public List<Row> Rows { get; set; }

    public SalesSheet()
    {
        Rows = new List<Row>();
    }

    public class Row
    {
        public string CallType { get; set; }
        public string HistoryText { get; set; }
    }

}

My question is, where should I write the method that loads the data? Is it bad practice to write a method like:

private void LoadData(string filePath)

in the model, and call it the constructor?

Should I load it from the ViewModel?

回答1:

In general, a small WPF project should have the following approximate folder structure:

  • ProjectName
    • Converters
    • DataAccess
    • DataTypes
    • Images
    • ViewModels
    • Views

DataAccess is the folder where you should store your data access classes. It is good practice to separate the various aspects of the application; the views, the view models and the data access classes. This is known as Separation of Concerns and is good practice because (among other things) it enables you to switch out layers... this means that you could later add a web interface (or change your database) while still keeping the majority of your code the same, and it also makes testing your code easier.

You might only have one class in this folder, let's call it DataProvider. In this DataProvider class, you put all of your data access methods. You now have one point of entry to your data access and you can add a reference to it in a base view model:

protected DataProvider DataProvider
{
    get { return new DataProvider(); }
}

Now your view models all have access to the project data source and then you can do something like this:

SomeObject someObject = DataProvider.LoadData(filePath);

Of course, there are many different ways of implementing this pattern, but hopefully now, you get the idea.



回答2:

In my understanding of MVVM your LoadData method belongs in the model. The viewmodels can then access the loaded data through a property or method from the model. The important point here is, that the viewmodels don't know about the concrete file access logic, it is abstracted by the model.



回答3:

Create a view model class. Instantiate the view model class within the xaml of the view by creating it inside the DataContext property.

Implement a method to load the data in your view model, e.g. LoadData. Set up the view, so that this method is called when the view loads. You can do that by implementing the Loaded event in an event handler of the view, or alternatively you can you an interaction trigger in your view and link it to the method in the view model.

View (xaml):

<Window x:Class="MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Test" 
        xmlns:viewModel="clr-namespace:ViewModels" Loaded="Window_Loaded">
    <Window.DataContext>
        <viewModel:ExampleViewModel/>
    </Window.DataContext>

View (code behind):

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        ((ExampleViewModel)this.DataContext).LoadData();
    }

If you do not like setting up the Loaded event in the code behind, you can also do it in xaml (references to "Microsoft.Expression.Interactions" and "System.Windows.Interactivity" are needed):

<Window x:Class="MyWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test" 
    xmlns:viewModel="clr-namespace:ViewModels"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"            
    >
<Window.DataContext>
    <viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
    </i:EventTrigger>
</i:Interaction.Triggers>   

In each case, you call the LoadData method in the ViewModel. Here, you should make a call to a separate class which provides the data. This class is usually called repository.

public class ExampleViewModel
{
    /// <summary>
    /// Constructor.
    /// </summary>
    public ExampleViewModel()
    {
        // Do NOT do complex stuff here
    }


    public void LoadData()
    {
        // Make a call to the repository class here
        // to set properties of your view model
    }

If the method in the repository is an async method, you can make the LoadData method async too, but this is not needed in each case.

Generally I would not load data in the constructor of the view model. In the example above the (parameter less) constructor of the view model is called when the designer shows your view. Doing complex things here can cause errors in the designer when showing your view (for the same reason I would not make complex things in the views constructor).

In some scenarios code in the view models constructor can even cause issues at runtime, when the view models constructors executes, set properties of the view model which are bound to elements in the view, while the view object is not completely finished creating.



回答4:

You have many option depending on your project specifications.

But I found DataService approach simple and flexible.

  1. Create a class in Core (or business layer) and name it SalesDataService.
  2. Initialize the variables and stuff (if any) in its constructor
  3. Add your methods such as GetRows() or GetSingleById(int id) ... to the DataService
  4. Add a protected SalesDataService _salesDataService field in your view model
  5. Instantiate _salesDataService in the constructor of your view model
  6. Call GetRows() when necessary (if nobody calls for loading data, then put it in the constructor)


标签: c# wpf mvvm model xls