看看Prism的Prism Full App(.NET Core)模板


一段时间以来,一个名为Prism Full App(.NET Core)的项目模板已添加到Prism的项目模板中。

Prism Full App (.NET Core)

使用这个项目模板,我认为开发Prism的人应该以这种方式创建项目,除非有任何限制,所以我将使用它。让我们查看并了解结构,并在创建项目时将其用作参考。
如果您对此配置感到满意,则最好使用此Prism Full App项目模板作为自己开发的起点。
让我们来看看。

现在,在

中基于Prism Full App(.NET Core)创建一个新项目。这次我创建了一个名为WpfSampleFullApp的项目。

只需创建此项目,就会创建6个项目,如下所示。

image.png

如果将

WpfSampleFullApp作为启动项目运行,则如下图所示,将启动一个外观类似于Hello world的应用程序。

image.png

生成的项目

中,我们将逐个查看每个项目。

WpfSampleFullApp项目

该项目将成为切入点。这非常简单,它只有一个App类以及一个MainWindow和MainWindowViewModel。
这里没有什么特别要说的。

让我们看看WpfSampleFullApp项目指的是什么。我指的是以下项目。

  • WpfSampleFullApp.Core
  • WpfSampleFullApp.modules.ModuleName
  • WpfSampleFullApp.Services
  • WpfsampleFullApp.Services.Interfaces

简而言之,就是那样。现在,让我们看看这个App类如何在DI容器中添加模块并注册类。 App.xaml.cs看起来像这样:

App.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using Prism.Ioc;
using WpfSampleFullApp.Views;
using System.Windows;
using Prism.Modularity;
using WpfSampleFullApp.Modules.ModuleName;
using WpfSampleFullApp.Services.Interfaces;
using WpfSampleFullApp.Services;

namespace WpfSampleFullApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<IMessageService, MessageService>();
        }

        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<ModuleNameModule>();
        }
    }
}

首先,让我们看一下CreateShell方法。这是不言而喻的,但是因为MainWindow是应用程序的外壳,所以它只是创建MainWindow的实例。

接下来,让我们看一下ConfigureModuleCatalog方法。您只需使用AddModule添加此项目模板生成的唯一模块项目。

最后,让我们看一下RegisterTypes。
在这里,它是WpfSampleFullApp.Services.Interfaces中定义的服务接口与WpfSampleFullApp.Services中定义的实现类之间的关联。它只是在DI容器中注册。

总而言之,此WpfSampleFullApp项目大致执行以下操作。

  • 模块注册
  • 链接服务接口和实现
  • 创建主窗口

WpfSampleFullApp.Core项目

该项目是一个模块类,定义了每个人都使用的通用类。这是一个不依赖其他项目的项目。并且定义了以下类。

  • RegionNames类
  • Mvvm / ViewModelBase类
  • Mvvm / RegionViewModelBase类

RegionNames类

这只是一个常量,用于定义MainWindow中定义的Region的名称。我在过渡到屏幕时指定Region的名称,但此处将其定义为常量,以便不进行硬编码,并由其他项目引用。
定义新的区域后,在此处添加名称作为常量。

Mvvm / ViewModelBase类

从BindableBase继承并实现IDestructible接口的几乎为空的类。

Mvvm / RegionViewModelBase类

顾名思义,这是设置为Region的ViewModel的基类。
它继承了ViewModelBase并实现了两个接口INavigationAware和IConfirmNavigationRequest。

由于在

区域中设置的ViewModel通常会编写与屏幕过渡有关的处理,因此通常会实现在屏幕过渡之前和之后提供回调的INavigationAware以及在屏幕过渡之前编写确认处理的IConfirmNavigationRequest接口。这是一个家伙,所以感觉就像我们在这里实现它并提供一个空的实现。

我想在此添加

例如,您将添加要显示为对话框的View的ViewModel基类。
之后,将在此处添加由EventAggregator交换的PubSubEvent的继承类。

WpfSampleFullApp.Services.Interfaces项目

感觉在这里定义了应用程序模型层中的服务。没有依赖它的项目。换句话说,它不依赖于棱镜。
简而言之,它是各种模块的ViewModel层中使用的接口的定义。因此,感觉就像仅定义了模型层提供给ViewModel的接口。

如果您的模型层提供了用例!如果看起来像这样,它将类似于WpfSampleFullApp.UseCases.Interfaces。
其余的是在其他项目中进行混合还是定义的一条细线,但是如果它是一个定义由模型层(而不是模型层)提供的接口的项目,则由访问DB或WebAPI的类实现的Repository系统接口也将在此处定义。至于上下文,它不是从数据库中看到的上下文的接口,例如GetAll,GetById,Update,Save,而是定义了模型需要从外部资源在上下文中命名的接口的位置。

默认情况下,定义了仅提供消息的IMessageService接口。

WpfSampleFullApp.Services项目

这定义了WpfSampleFullApp.Services.Interfaces项目中定义的服务接口的实现类。这也不取决于棱镜。只有一个MessageService。

引用的项目是WpfSampleFullApp.Services.Interfaces项目。
感觉就像模型层的核心逻辑就在这里。

WpfSampleFullApp.Modules.ModuleName项目

这是定义View和ViewModel的模块项目。这是一个令人失望的项目名称ModuleName,因此您可能要删除它并根据Prism Module(.NET Core)项目模板重新创建它。

点在于,它仅取决于WpfSampleApp.Modules.Services.Interfaces项目,而不取决于WpfSampleApp.Modules.Services项目。关键是依赖性是接口,而不是实现。

只有WpfSampleFullApp项目知道

实现和接口关联。

让我们放在一起

表示依赖性是这样的。

図1.png

让我们调用Web API

它不如Web API大,但我想通过Web获取消息。
具体来说,应该显示从以下URL返回的JSON的message属性,而不是当前显示的Message Service中的Hello。

https://raw.githubusercontent.com/runceel/mockapi/master/message.json

返回的JSON

message.json

1
2
3
{
  "message": "Hello from GitHub"
}

您可以直接在MessageService中使用HttpClient编写该过程,但是它不利于测试,因此让我们为外部资源访问创建一个Repository层。
在WpfSampleFullApp.SErvices.Interfaces项目中为存储库定义以下接口。

储存库/IMessageRepostory.cs

1
2
3
4
5
6
7
8
9
using System.Threading.Tasks;

namespace WpfSampleFullApp.Services.Interfaces.Repositories
{
    public interface IMessageRepository
    {
        ValueTask<string> GetMessageAsync();
    }
}

IMessageService也将是异步的,因此我们也对其进行更改。

IMessageService.cs

1
2
3
4
5
6
7
8
9
using System.Threading.Tasks;

namespace WpfSampleFullApp.Services.Interfaces
{
    public interface IMessageService
    {
        ValueTask<string> GetMessageAsync();
    }
}

我重写了

模块项目的ViewAViewModel类,以执行以下操作:我认为我不会在构造函数中读取很多数据,但是这次是这样,因此我在进行最少代码更改的情况下进行了以下工作。

ViewAViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using WpfSampleFullApp.Core.Mvvm;
using WpfSampleFullApp.Services.Interfaces;
using Prism.Regions;
using System.Threading.Tasks;

namespace WpfSampleFullApp.Modules.ModuleName.ViewModels
{
    public class ViewAViewModel : RegionViewModelBase
    {
        private string _message;
        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }

        public ViewAViewModel(IRegionManager regionManager, IMessageService messageService) :
            base(regionManager)
        {
            Message = "Loading...";
            messageService.GetMessageAsync().AsTask().ContinueWith(x =>
            {
                Message = x.Result; // エラーはとりあえず考えない
            });
        }

        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            //do something
        }
    }
}

然后创建一个名为WpfSampleFullApp.Repositories的项目,并添加对WpfSampleFullApp.Services.Interfaces项目的引用。现在让我们实现IMessageRepository。让我们添加对System.Text.Json的引用并快速实现它。

MessageRepository.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WpfSampleFullApp.Services.Interfaces.Repositories;

namespace WpfSampleFullApp.Repositories
{
    public class MessageRepository : IMessageRepository
    {
        private readonly HttpClient _httpClient;

        public MessageRepository(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }
        public async ValueTask<string> GetMessageAsync()
        {
            using var jsonStream = await _httpClient.GetStreamAsync(
                "https://raw.githubusercontent.com/runceel/mockapi/master/message.json");
            var result = await JsonSerializer.DeserializeAsync<MessageResult>(jsonStream,
                new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                }
            );
            return result.Message;
        }
    }

    public class MessageResult
    {
        public string Message { get; set; }
    }
}

添加处理以将存储库类实现添加到App.xaml.cs中的DI容器中。不要忘记在WpfSampleFullApp项目中添加对WpfSampleFullApp.Repositories项目的引用。

App.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using Prism.Ioc;
using WpfSampleFullApp.Views;
using System.Windows;
using Prism.Modularity;
using WpfSampleFullApp.Modules.ModuleName;
using WpfSampleFullApp.Services.Interfaces;
using WpfSampleFullApp.Services;
using System.Net.Http;
using WpfSampleFullApp.Services.Interfaces.Repositories;
using WpfSampleFullApp.Repositories;

namespace WpfSampleFullApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // 以下 2 行を追加
            containerRegistry.RegisterSingleton<HttpClient>();
            containerRegistry.RegisterSingleton<IMessageRepository, MessageRepository>();
            containerRegistry.RegisterSingleton<IMessageService, MessageService>();
        }

        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<ModuleNameModule>();
        }
    }
}

如果到目前为止可以执行,请执行它。您应该会看到以下内容:

image.png

感觉很好。项目依赖项如下。

図2.png

单元测试

默认情况下,将为模块创建单元测试项目。如果您还想测试与服务相关的项目,则最好根据需要为模型层添加一个单元测试项目。

摘要

Prism Full App(.NET Core)我查看了项目模板并添加了一些处理程序,但是我个人认为基于此模板添加各种内容很容易。是的

我刚刚对项目模板所吐出的代码进行了一些处理,但是目前,此代码已发布在GitHub上。

https://github.com/runceel/WpfSampleFullApp