Building a Software Survey using Blazor - Part 12 - Putting it all together
Tags: blazor, csharp, azure
Published: 2020-12-21 15:35:32

I've decided to write a small survey site using Blazor.  Part of this is an excuse to learn Blazor.

As I learn with Blazor I will blog about it as a series of articles.

This series of articles is not intended to be a training course for Blazor - rather my thought process as I go through learning to use the product.

All articles in this series:

* [Part 1 - State](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-1-state)
* [Part 2 - Better State](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-2-better-state)
* [Part 3 - Components](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-3-components)
* [Part 4 - Render Mode](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-4-render-mode)
* [Part 5 - Client IP](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-5-client-ip)
* [Part 6 - Azure Cosmos DB](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-6-azure-cosmos-db)
* [Part 7 - Azure SignalR Service](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-7-azure-signalr-service)
* [Part 8 - bUnit](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-8-bunit)
* [Part 9 - End to End Tests](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-9-end-to-end-tests)
* [Part 10 - Bicep](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-10-bicep)
* [Part 11 - Azure DevOps](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-11-azure-devops)
* Part 12 - Putting it all together - this article

---

In this article I wanted to recap the previous articles in the series and how they fit together to produce the final Software Survey application.

## Starting the survey
A number of things occur simply by the user visiting https://software-survey.red-folder.com/

Firstly, the static HTML is provided by the application.
  The `PreRenderLoadingMessage` component I described in [part 4](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-4-render-mode) allows me to provide a loading page while the Blazor Server app starts up:

![Pre-Render Page](https://dev-to-uploads.s3.amazonaws.com/i/0gylwzez8xlg5ugvypiy.png)

During the Server app start up, it will:

* Get the IP address of the user - as discussed in [part 5](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-5-client-ip)
* Create the users state - as discussed in [part 2](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-2-better-state)
* Sets up the SignalR connection - as discussed in [part 7](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-7-azure-signalr-service)

The app is then ready to use and the `PreRenderLoadingMessage` will swap over from the loading page to the starting page.

## The survey pages
As the user works through the survey, each page will use Dependency Injection to get the relevant part of the state.

### Demographics page:
![Demographics Page](https://dev-to-uploads.s3.amazonaws.com/i/7h949peedth3oy3dknvg.png)

State defined as:

```
namespace SoftwareSurvey.Models
{
    public class Demographic
    {
        [Required(ErrorMessage = "Please provide company size")]
        [DisplayName("Company Size")]
        [JsonProperty(PropertyName = "companySize")]
        public string CompanySize { get; set; }

        [Required(ErrorMessage = "Please provide your job seniority")]
        [DisplayName("Job Seniority")]
        [JsonProperty(PropertyName = "jobSeniority")]
        public string JobSeniority { get; set; }

        [DisplayName("Job Title (optional)")]
        [JsonProperty(PropertyName = "jobTitle")]
        public string JobTitle { get; set; }

        [Required(ErrorMessage = "Please provide if your business is UK Based")]
        [DisplayName("Is your business UK based?")]
        [JsonProperty(PropertyName = "ukBased")]
        public string UKBased { get; set; }
    }
}
```

Injected as:

```
    [Inject]
    private Models.Demographic Model { get; set; }
```

### Software Types page:
![Software Types Page](https://dev-to-uploads.s3.amazonaws.com/i/0ekt6u2xfltfe8cr1gbb.png)

State defined as:

```
namespace SoftwareSurvey.Models
{
    public class SoftwareTypes
    {
        [DisplayName("eCommerce")]
        [Description("Website(s) selling products or services")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "eCommerce")]
        public int? ECommerce { get; set; }

        [DisplayName("Information Website")]
        [Description("Website(s) providing information (portfolio, blog, etc)")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "informationWebsite")]
        public int? InformationWebsite { get; set; }

        [DisplayName("Mobile App")]
        [Description("Application(s) for install on mobile or tablet")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "mobileApps")]
        public int? MobileApps { get; set; }

        [DisplayName("Line Of Business")]
        [Description("Application(s) to support specific business process")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "lineOfBusiness")]
        public int? LineOfBusiness { get; set; }

        [DisplayName("SaaS")]
        [Description("Software as a Service - sold as a product (normally subscription)")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "softwareAsAService")]
        public int? SoftwareAsAService { get; set; }

        [DisplayName("Other")]
        [Description("Any other type of software not listed above")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "other")]
        public int? Other { get; set; }
    }
}
```

Injected as:

```
    [Inject]
    private Models.SoftwareTypes Model { get; set; }
```

### Your Experiences page:
![Your Experiences Page](https://dev-to-uploads.s3.amazonaws.com/i/hk2f9c0l82g5h456tqsl.png)

State defined as:

```
namespace SoftwareSurvey.Models
{
    public class Experiences
    {
        [DisplayName("ROI")]
        [Description("Is it providing Return On Investment?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "returnOnInvestment")]
        public int? ReturnOnInvestment { get; set; }

        [DisplayName("Keeping pace")]
        [Description("Is it keeping pace with the business needs?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "keepingPace")]
        public int? KeepingPace { get; set; }

        [DisplayName("Recruitment")]
        [Description("It is easy to recruit the developers you want?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "recruitment")]
        public int? Recruitment { get; set; }

        [DisplayName("Retention")]
        [Description("Is it easy to retain your developers?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "retention")]
        public int? Retention { get; set; }

        [DisplayName("Quality")]
        [Description("Is the software of a high quality?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "quality")]
        public int? Quality { get; set; }

        [DisplayName("Predicability")]
        [Description("Is the software development predicatable?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "predicability")]
        public int? Predicability { get; set; }
    }
}
```

Injected as:

```
    [Inject]
    private Models.Experiences Model { get; set; }
```

### One Change page:
![One Change Page](https://dev-to-uploads.s3.amazonaws.com/i/zz98fvinlkylwu1mlpp3.png)

State defined as:

```
namespace SoftwareSurvey.Models
{
    public class OneChange
    {
        [JsonProperty(PropertyName = "text")]
        public string Text { get; set; }
    }
}
```

Injected as:

```
    [Inject]
    private Models.OneChange Model { get; set; }
```

### Further Contact page:
![Further Contact Page](https://dev-to-uploads.s3.amazonaws.com/i/b5walvybexmozclsrr6s.png)

State defined as:

```
namespace SoftwareSurvey.Models
{
    public class Contact : IValidatableObject
    {
        [DisplayName("With the results of the survery?")]
        [JsonProperty(PropertyName = "resultsOfTheSurvey")]
        public bool SurveyResults { get; set; }

        [DisplayName("For any follow up questions?")]
        [JsonProperty(PropertyName = "followUpQuestions")]
        public bool FollowUpQuestions { get; set; }

        [DisplayName("For future surveys?")]
        [JsonProperty(PropertyName = "furtherSuvey")]
        public bool FurtherSurveys { get; set; }

        [DisplayName("Email Address")]
        [EmailAddress]
        [JsonProperty(PropertyName = "email")]
        public string Email { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            return IsEmailValid ? null : new ValidationResult[] { new ValidationResult("Please provided email", new string[] { "Email" }) };
        }

        private bool IsEmailValid => !RequiresEmailAddress || !string.IsNullOrEmpty(Email);
        private bool RequiresEmailAddress => SurveyResults || FollowUpQuestions || FurtherSurveys;
    }
}
```

Injected as:

```
    [Inject]
    private Models.Contact Model { get; set; }
```

## Saving the results
On hitting the final page, the application saves the full state object for the user to Cosmos DB - as discussed in [part 6](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-6-azure-cosmos-db)

The survey is then complete for the user.

## Supporting cast
While not directly related to the application itself, its obvious right to draw attention to the supporting functions.

The following allowed me work quickly by providing a safety net (mainly to avoid regression):

* bUnit for Unit testing - see [Part 8](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-8-bunit)
* End to End testing - see [Part 9](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-9-end-to-end-tests)
* Continous Integration, Delivery and Deployment - see [Part 11](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-11-azure-devops)

## And that's it
The full source code (including tests, Bicep/ ARM templates and Azure DevOps pipeline YAML) can be found in [this repo](https://github.com/Red-Folder/software-survey).

There is however one further article to the series.  I take a look at the different upgrading to .Net 5 makes to my Blazor Server application.