Nov 12

Written by: Michael Washington
11/12/2018 12:03 PM  RssIcon

The primary benefit we have when using server-side Blazor is that we do not have to make web http calls from the client code to the server code. This reduces the code we need to write and eliminates many security concerns.

In this article, we will demonstrate how a list of Weather forecasts can be added to the database by each user. A user will only have the ability to see their own forecasts.

image

We will start with the code from the article: A Demonstration of Simple Server-side Blazor Cookie Authentication.

NOTE: This sample code does not check to see if a person is using a legitimate username and password! You would need to add the proper code to check. This code is just a demonstration of how the process of authorizing a user works.

Create The Database

image

Open the project from A Demonstration of Simple Server-side Blazor Cookie Authentication in Visual Studio 2017 (or higher).

In Visual Studio, select Tools then Connect to Database.

image

Connect to any database you wish. However, if you want to use the SQL Server Express LocalDB for development…

  1. Click the Change button to switch providers
  2. Select the Microsoft SQL Server Database File provider
  3. Enter BlazorCookieAuthDB for Database file name
  4. Click OK

 

image

A box will pop up and ask if you want to create the database.

Click Yes.

image

If you look on your file system, at the location indicated in the popup, you will see the database files have been created.

image

In Visual Studio, select View then Server Explorer.

image

The database will display in the window (if it doesn’t, right-click on the Data Connections node and select Add connection and navigate to the database .mdf file).

Right-click on the Tables node and select Add New Table.

image

Paste the following script in the T-SQL window and then click the Update button:

 

CREATE TABLE [dbo].[WeatherForecast] (
    [Id]           INT           IDENTITY (1, 1) NOT NULL,
    [Date]         DATETIME      NULL,
    [TemperatureC] INT           NULL,
    [TemperatureF] INT           NULL,
    [Summary]      NVARCHAR (50) NULL,
    [UserName]     NVARCHAR (50) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

image

The script will prepare.

Click the Update Database button.

image

Back in the Server Explorer window, right-click on Tables and select Refresh.

image

The WeatherForecast table will display.

Right-click on the table and select Show Table Data.

image

We will enter some sample data for UserName Test@Test.com so that we will be able to test the data connection in the next steps.

Create Data Context

image

We will need to use Entity Framework Core to connect to the database from our code.

To do this, right-click on the BlazorCookieAuth.App node in the Solution Explorer and select Manage NuGet Packages.

image

Add a reference to Microsoft.EntityFrameworkCore.SqlServer

image

If you do not already have it installed, install EF Core Power Tools from:

https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools

(Note: Before installing, close Visual Studio)

(Note: Please give this project a 5 star review!)

image

Right-click on the BlazorCookieAuth.App node in the Solution Explorer and select EF Core Power Tools then Reverse Engineer.

image

Select the database file and click OK.

image

Select the WeatherForecast table and click OK.

image

Set the values and click OK.

image

Click OK.

image

In the Solution Explorer, you will see the Data Context has been created.

image

Select Build, then Rebuild Solution.

Set Run-Time Database Connection

image

We now desire to allow the database connection to be set in the application settings file.

This allows us to easily change the database connection when the application is deployed to different environments, such as production.

Create a file, in the BlazorCookieAuth.Server project called appsettings.json using the following code:

image

Replace {Path To Your database file} with the exact location of the of the .mdf database file you created earlier.

For example, on my computer the full path to the file is: C:\\Users\\Michael\\Documents\\

Alternatively, if you have used a connection to SQL Server, or another database, you would put that connection string here.

image

Open the Startup.cs file in the BlazorCookieAuth.Server project.

Add the following method:

 

        public IConfigurationRoot Configuration { get; }
        // This allows the appsettings.json file to be read
        // this allows the database connection string to be passed to the 
        // Blazor Client app
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
           .SetBasePath(env.ContentRootPath)
           .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
           .AddJsonFile($"appsettings{env.EnvironmentName}.json", optional: true)
           .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

 

Next, add the following code to the end of the existing ConfigureServices method:

 

            // Allows: Configuration.GetConnectionString("DefaultConnection")
            services.AddSingleton(Configuration);

 

Read From The Database

image

Delete the WeatherForecast.cs file in the BlazorCookieAuth.App project.

We will use the WeatherForcast class file created by the EF Core Tools.

image

Open the WeatherForecastService.cs file in the BlazorCookieAuth.App project.

Replace all the code with the following code:

 

using BlazorCookieAuth.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorCookieAuth.App.Services
{
    public class WeatherForecastService
    {
        private IHttpContextAccessor _httpContextAccessor;
        private IConfigurationRoot _configRoot { get; set; }
        public WeatherForecastService(
            IHttpContextAccessor httpContextAccessor,
            IConfigurationRoot configRoot)
        {
            // Allows us to determine who the logged in user is
            _httpContextAccessor = httpContextAccessor;
            // Allows us to call: 
            // Configuration.GetConnectionString("DefaultConnection")
            // to get the connection to the database
            _configRoot = configRoot;
        }
        public Task<List<WeatherForecast>> GetForecastAsync()
        {
            List<WeatherForecast> colWeatherForcasts = new List<WeatherForecast>();
            // Get the name of the current user
            string strCurrentUser = _httpContextAccessor.HttpContext.User.Identity.Name;
            // Get the connection string that was set in the appsettings.json file
            string strConnectionString = _configRoot.GetConnectionString("DefaultConnection");
            // Create database options to connect to the database
            var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
            optionsBuilder.UseSqlServer(strConnectionString);
            // Connect to the database
            using (var context = new DatabaseContext(optionsBuilder.Options))
            {
                // Get Weather Forecasts  
                colWeatherForcasts = (from weatherForecast in context.WeatherForecast
                                      // only get entries for the current logged in user
                                      where weatherForecast.UserName == strCurrentUser
                                      select weatherForecast).ToList();
            }
            return Task.FromResult(colWeatherForcasts);
        }
    }
}

 

image

Finally, open the FetchData.cshtml file in the BlazorCookieAuth.App project.

Replace all the code with the following code:

 

@using BlazorCookieAuth.App.Services
@using BlazorCookieAuth.Model
@page "/fetchdata"
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.Value.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>    
}
@functions {
    List<WeatherForecast> forecasts;
    protected override async Task OnInitAsync()
    {
        forecasts = await ForecastService.GetForecastAsync();
    }
}

 

This code simply calls the GetForecastAsync method we created in the previous step.

In a normal web application we would have to make a http web call from this client code to the code that connects to the database.

With server-side Blazor we don’t have to do that, yet the call is still secure because the pages are rendered on the server.

 

image

Build and run the project.

Note: If the build gets stuck or hangs. First cancel the build then select Clean build then try running the project again.

Click the Login button.

image

Log in as Test@Test.com (because we created data for that user earlier).

(Note: The password you enter does not matter because this sample code does not actually validate the password)

image

After you are logged in, switch to the Fetch data page and you will see the data for the test@test.com user we entered earlier.

Inserting Data Into The Database

image

Open the WeatherForecastService.cs file and add the following method:

 

      public void AddForecast(WeatherForecast objNewWeatherForecast)
        {
            // Get the name of the current user
            string strCurrentUser = _httpContextAccessor.HttpContext.User.Identity.Name;
            // Get the connection string that was set in the appsettings.json file
            string strConnectionString = _configRoot.GetConnectionString("DefaultConnection");
            // Create database options to conect to the database
            var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
            optionsBuilder.UseSqlServer(strConnectionString);
            // Connect to the database
            using (var context = new DatabaseContext(optionsBuilder.Options))
            {
                // Set this forecast as the current user 
                // Convert Fahrenheit to Celsius
                // Set the date to today
                objNewWeatherForecast.UserName = strCurrentUser;
                objNewWeatherForecast.TemperatureC = 
                    (objNewWeatherForecast.TemperatureF.Value - 32) * 5 / 9;
                objNewWeatherForecast.Date = System.DateTime.Now;
                // Save the forecast
                context.WeatherForecast.Add(objNewWeatherForecast);
                context.SaveChanges();
            }
        }

 

Note that we are computing and setting values, such as the current logged in user, rather than trusting user input.

 

image

Open the FetchData.cshtml file.

Add the following HTML markup directly under the existing table element:

 

    <p>
        <button class="btn btn-primary" onclick="@OpenPopup">Add New Forecast</button>
    </p>
    @if (ShowAdd)
    {
        <div class="modal" tabindex="-1" style="display:block" role="dialog">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h3 class="modal-title">Add Forecast</h3>
                        <button type="button" class="close" onclick="@ClosePopup">
                            <span aria-hidden="true">X</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <input class="form-control" type="text" 
                               placeholder="Fahrenheit forecast" bind="@NewFahrenheit" />
                        <input class="form-control" type="text" 
                               placeholder="Summary" bind="@NewSummary" />
                        <br/>
                        <button class="btn btn-primary" 
                                onclick="@AddForecast">Add</button>                        
                    </div>
                </div>
            </div>
        </div>
    }

 

Finally add the following code to the @functions section:

 

    // Add Forcast
    bool ShowAdd = false;
    string NewFahrenheit;
    string NewSummary;
    void OpenPopup()
    {
        NewFahrenheit = "";
        NewSummary = "";
        ShowAdd = true;
    }
    void ClosePopup()
    {
        ShowAdd = false;
    }
    async Task AddForecast()
    {
        // Close the Popup
        ShowAdd = false;
        // Create as new forcast
        // Only set the Fahrenheit and Summary values for now
        // All other values will be set in the service method
        WeatherForecast objNewWeatherForecast = new WeatherForecast();
        objNewWeatherForecast.TemperatureF = Convert.ToInt32(NewFahrenheit);
        objNewWeatherForecast.Summary = NewSummary;
        ForecastService.AddForecast(objNewWeatherForecast);
        // Update the forecasts
        forecasts = await ForecastService.GetForecastAsync();
    }

 

 

image

When you run the project, you can click the Add New Forecast button to add an entry.

image

The form only requires a Fahrenheit value and a summary, because all other values will be computed and set by the service method.

image

After clicking the Add button, the entry is saved to the database and displayed.

 

Links

Blazor.net

Blazor: Single Page Application Using Server-Side Blazor

Work with SQL Server LocalDB and ASP.NET Core

EF Core Power Tools

 

Download

The project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

You must have Visual Studio 2017 (or higher) installed to run the code.


Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation