Take a look at Prism’s Prism Full App (.NET Core) template

7 minute read

A project template named Prism Full App (.NET Core) has been added to the Prism project template for some time.

Prism Full App (.NET Core)

Using this project template, I think that the person who develops Prism should create a project separately like this if there are no particular restrictions, so look at this and structure Let’s understand and use it as a reference when creating a project.
If you’re happy with this configuration, it’s a good idea to use this Prism Full App project template as a starting point for your own development.
Let’s take a look.

Now let’s create a new project based on Prism Full App (.NET Core). This time I created a project with the name WpfSampleFullApp.

Just by creating this project, 6 projects will be created as follows.

image.png

If you run WpfSampleFullApp as a startup project, an application that looks just like Hello world will start up as shown below.

image.png

Generated project

Let’s take a look at each project.

WpfSampleFullApp project

This project will be the entry point. It’s very simple, it just has an App class and a MainWindow and MainWindowViewModel.
There is nothing special to say here.

Let’s see what the WpfSampleFullApp project refers to. I am referring to the following project.

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

To put it simply, it’s all like that. Now let’s see how this App class adds modules and registers the class in the DI container. App.xaml.cs looks like this:

csharp:App.xaml.cs


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>();
        }
    }
}

Let’s first look at the CreateShell method. This is self-explanatory, but because MainWindow is the shell of the application, it simply instantiates MainWindow.

Next, let’s take a look at the ConfigureModuleCatalog method. You’re simply adding the only module project generated by this project template with AddModule.

Finally, let’s look at RegisterTypes.
Here, it is the association between the service interface defined in WpfSampleFullApp.Services.Interfaces and the implementation class defined in WpfSampleFullApp.Services. It is simply registered in the DI container.

So, in summary, this WpfSampleFullApp project roughly does the following:

–Module registration
–Linking service interface and implementation
–Creating the main window

WpfSampleFullApp.Core project

This project is a module class that defines a common class that everyone uses. It is a project that does not depend on other projects. And the following classes are defined.

–RegionNames class
–Mvvm / ViewModelBase class
–Mvvm / RegionViewModelBase class

RegionNames class

This is simply a constant that defines the name of the Region defined in MainWindow. The name of Region is specified when the screen changes, but it is defined as a constant here so as not to be hard-coded, and it is referenced by other projects.
After defining a new Region, add the name here as a constant.

Mvvm / ViewModelBase class

It’s an almost empty class that just inherits from BindableBase and implements the IDestructible interface.

Mvvm / RegionViewModelBase class

As the name implies, this is the base class of ViewModel that you set in Region.
It inherits ViewModelBase and implements two interfaces, INavigationAware and IConfirmNavigationRequest.

Since ViewModel set in Region often writes screen transition related processing, INavigationAware that provides callbacks before and after screen transition and IConfirmNavigationRequest interface that writes confirmation processing before screen transition are often implemented. It feels like we are implementing it here and providing an empty implementation.

Other things I think I’ll add here

For example, you’ll probably add a base class for the ViewModel of the View that you want to display as a dialog.
After that, the inheritance class of PubSubEvent exchanged by EventAggregator will be added here.

WpfSampleFullApp.Services.Interfaces project

It feels like the services in the model layer of the application are defined here. There are no projects that depend on it. In other words, Prism does not depend on potatoes.
In short, it’s the definition of the interface used from the ViewModel layer of various modules. So it feels like only the interface that the model layer provides to the ViewModel is defined.

If your model layer provides a use case! If it looks like this, it would be something like WpfSampleFullApp.UseCases.Interfaces.
The rest is a delicate line whether to mix or define in another project, but if you make a project that defines the interface provided by the model layer other than the model layer, the Repository system for implementing by the class that accesses the DB or WebAPI Interface will also be defined here. As for the context, it is not the interface of the context seen from the DB such as GetAll, GetById, Update, Save, but the interface named in the context that the model wants from the external resource.

By default, an IMessageService interface is defined that just serves a message.

WpfSampleFullApp.Services project

This defines the implementation class for the interface of the service defined in the WpfSampleFullApp.Services.Interfaces project. This also does not depend on Prism. There is only a MessageService.

The referenced project is the WpfSampleFullApp.Services.Interfaces project.
It feels like the core logic of the model layer comes here.

WpfSampleFullApp.Modules.ModuleName project

This is the module’s project, which defines the View and ViewModel. It’s a disappointing project name, ModuleName, so you’ll probably want to erase it and recreate it based on the Prism Module (.NET Core) project template.

The point is that it only depends on the WpfSampleApp.Modules.Services.Interfaces project, not the WpfSampleApp.Modules.Services project. The point is that the dependency is an interface, not an implementation.

Only the WpfSampleFullApp project knows the link between the implementation and the interface.

Let’s put it together

So it has a dependency like this.

図1.png

Let’s call the Web API

It’s not as big as a Web API, but I’d like to get messages via the Web.
Specifically, the JSON message property returned from the following URL should be displayed instead of the Hello from the Message Service currently displayed.

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

JSON returned

message.json


{
  "message": "Hello from GitHub"
}

You can write the process using HttpClient directly in MessageService, but it is not good for testing, so let’s create a Repository layer for external resource access.
Define the following repository interfaces in the WpfSampleFullApp.SErvices.Interfaces project.

Repositories/IMessageRepostory.cs


using System.Threading.Tasks;

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

IMessageService will also be asynchronous, so let’s change it as well.

IMessageService.cs


using System.Threading.Tasks;

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

I rewrote the ViewAViewModel class in the module’s project to do the following: I don’t think that data is read in the constructor so much, but this time it was the case, so I made the following with the minimum code changes.

ViewAViewModel.cs


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; //I don't think about errors for the time being
            });
        }

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

Then create a project called WpfSampleFullApp.Repositories and add a reference to the WpfSampleFullApp.Services.Interfaces project. Now let’s implement the IMessageRepository. Let’s add a reference to System.Text.Json and implement it quickly.

MessageRepository.cs


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; }
    }
}

Add processing to add the repository class implementation to the DI container in App.xaml.cs. Don’t forget to add a reference to the WpfSampleFullApp.Repositories project to your WpfSampleFullApp project.

csharp:App.xaml.cs


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)
        {
            //Add the following two lines
            containerRegistry.RegisterSingleton<HttpClient>();
            containerRegistry.RegisterSingleton<IMessageRepository, MessageRepository>();
            containerRegistry.RegisterSingleton<IMessageService, MessageService>();
        }

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

If you can do it so far, let’s execute it. You should see something like this:

image.png

it is a good feeling. The project dependencies are as follows.

図2.png

Unit test

By default, unit test projects are created for modules. If you also want to test Services projects, you may want to add unit test projects for the model layer as needed.

Summary

I looked at the Prism Full App (.NET Core) project template and tried to add some processing, but I personally thought that it would be ant to add various things based on this one.

I just added some processing to the code that the project template spits out, but for the time being, I’ll post this code on GitHub.

https://github.com/runceel/WpfSampleFullApp