Build your first Blazor server-side application
If you are tracking the progress of ASP.NET Core 3, you are probably aware
that Blazor is getting a lot of attention and feature improvements. If you
haven't developed Blazor applications yet it's worthwhile to take a quick look
at the overall development process. To that end this article discusses a simple
database update page build using server-side Blazor.
Blazor is a framework for building web applications that use C# for user
interface development. That means instead of using JavaScript you use C# for
dealing with HTML elements and other markup. Of course, you can also interact
with JavaScript code whenever necessary. Blazor comes with
two hosting models - client-side and server-side. The client-side hosting
model uses
WebAssembly whereas server-side hosting model uses a SignalR connection for
UI updates.
Now that you have some idea about what Blazor is, let's go straight to code
level details and develop a simple data entry form as shown below:
As you can see, the page displays a list of CustomerIDs from Customers table
of Northwind database. Selecting a CustomerID and clicking on the Show Data
button displays CompanyName, ContactName, and Country values of that customer.
You can modify these values and click on Update button to save the changes back
to the database.
Begin by creating a new Blazor server-side application in Visual Studio.
Once the project gets created. Add NuGet package for Entity Framework Core -
Microsoft.EntityFrameworkCore.SqlServer.
Then add two classes that represent DbContext and Customer entity. These
classes are added to the Data folder and are shown below:
public class Customer
{
[Key]
[Required]
[StringLength(5, MinimumLength = 5)]
public string CustomerID { get; set; }
[Required]
[StringLength(30)]
public string CompanyName { get; set; }
[Required]
[StringLength(30)]
public string ContactName { get; set; }
[Required]
[StringLength(15)]
public string Country { get; set; }
}
public class AppDbContext:DbContext
{
public AppDbContext(
DbContextOptions<AppDbContext> options) :
base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
These classes are quite straightforward. Notice that the Customer class uses
data annotations for the sake of model validation.
To perform database operations you need one more class - CustomerManager.
This class is shown below:
public class CustomerManager
{
private AppDbContext db;
public CustomerManager(AppDbContext db)
{
this.db = db;
}
public List<Customer> SelectAll()
{
return db.Customers.ToList();
}
public Customer SelectByID(string id)
{
return db.Customers.Find(id);
}
public string Update(Customer obj)
{
db.Update(obj);
db.SaveChanges();
return "Success!";
}
}
The CustomerManager class contains three methods - SelectAll(), SelectByID(),
and Update(). These methods perform the respective operations. Once developed
the AppDbContext and CustomerManager need to be registered with the DI
container. This is done in the ConfigureServices() of the Startup class.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("data source=.;
initial catalog=Northwind;integrated security=true;"));
services.AddScoped<CustomerManager>();
}
You register AppDbContext with the DI container using AddDbContext() method
whereas CustomerManager is registered using AddScoped() method.
Next, add a new Razor Component named Customers.razor to the Pages folder.
Open Customers.razor file and following code at the top:
@page "/customers"
@using BlazorDemo.Data
@inject BlazorDemo.Data.CustomerManager customerManager
The @page directive controls the route at which this component can be
accessed. In this case the component is accessible at this URL :
http://localhost:1234/customers. The @using directives uses Data namespace so
that model classes can be referenced in the code. Notice the @inject directive
that injects an instance of CustomerManager into the component (you registered
it in ConfigureServices()).
Now, add the following code to the @code block.
@code {
Customer cust = new Customer();
List<Customer> customers = null;
string Message { get; set; }
}
As you might have guessed the @code block is used to write C# code (members,
properties, methods) that interacts with the user interface. Here, the code
declares a member of type Customer. This member is used during data binding with
the form (discussed shortly). The List<Customer> stores a list of customers
returned from SelectAll() method and is also used to fill the dropdown list. The
Message property is used to notify user of the result of database update
operation.
As soon as the component is initialized you need to call the SelectAll()
method so that the CustomerID dropdown list can be populated. To do this, you
write OnInit() method in @code block as shown below:
protected override void OnInit()
{
customers = customerManager.SelectAll();
}
You are basically overriding OnInit() method of ComponentBase class (Blazor
components inherit from ComponentBase class). Inside, you call SelectAll()
method and store the return value into customers member.
If you observe the user interface shown at the beginning of this article, you
will find that there are two buttons - Show Data and Update. You need click
event handlers for these buttons.
void OnShowData()
{
cust = customerManager.SelectByID(cust.CustomerID);
}
void OnUpdate()
{
Message = customerManager.Update(cust);
}
You will be binding cust member with the data entry form. The data binding
features of Blazor allow you to do one-way and two-way data binding. When you
select a CustomerID the CustomerID property of cust member is automatically
changed to reflect the selection. So, when you pass cust.CustomerID in the
SelectByID() call you are passing the selected value. The returned Customer
object's values are reflected in the other textboxes. The OnUpdate() event
handler simply calls the Update() method of CustomerManager to save the changes
to the database.
Below the @code block write the following Blazor markup.
<h2>Customer Manager</h2>
<EditForm Model="cust">
<DataAnnotationsValidator>
</DataAnnotationsValidator>
<table border="0" cellpadding="20">
<tr>
<td>Customer ID :</td>
<td>
<InputSelect id="customerid"
@bind-Value="@cust.CustomerID">
@if (customers != null)
{
foreach (var c in customers)
{
<option value="@c.CustomerID">
@c.CustomerID</option>
}
}
</InputSelect>
<button type="button"
@onclick="@OnShowData">Show Data</button>
</td>
</tr>
<tr>
<td>Company Name :</td>
<td>
<InputText id="companyname"
@bind-Value="@cust.CompanyName" />
<ValidationMessage
For="@(() => cust.CompanyName)" />
</td>
</tr>
<tr>
<td>Contact Name :</td>
<td>
<InputText id="contactname"
@bind-Value="@cust.CompanyName" />
<ValidationMessage
For="@(() => cust.ContactName)" />
</td>
</tr>
<tr>
<td>Country :</td>
<td>
<InputText id="country"
@bind-Value="@cust.Country" />
<ValidationMessage
For="@(() => cust.Country)" />
</td>
</tr>
<tr>
<td colspan="2">
<button type="button"
@onclick="@OnUpdate">Update</button>
</td>
</tr>
</table>
<ValidationSummary></ValidationSummary>
</EditForm>
<h5>@Message</h5>
The above markup shows several Blazor features including data binding,
components, data annotations and validations.
The <EditForm> component is used to render a data entry <form>. The Model
property sets the model for the form to an object (Customer object in this
case). The <DataAnnotationsValidator> component enables form validations based
on data annotation attributes such as [Required] and [StringLength].
The CustomerID dropdown list is rendered using an <InputSelect> component
(same as <select> HTML element). To bind the dropdown list with model's property
@bind-Value attribute performs two-way data binding (from cust to dropdown and
from dropdown to cust). To populate all the CustomerIDs into the dropdown list
C# foreach loop is used. The onclick event handler of Show Data button is wired
to OnShowData() method written in the @code block.
To display CompanyName, ContactName, and Country <InputText> component is
used. This component renders HTML <input> element. Two-way data binding is done
as before. To display field level validation errors <ValidationMessage>
component is used. The onclick event of Update button is handled by
OnUpdate() method. At the bottom, there is <ValidationSummary> component that
displays a collective list of error messages.
Finally, the Message property is displayed so that use known the outcome of
update operation.
Compile the project and run the application by pressing F5. Once the browser
window loads the application, go to the address bar and change the URL to
http://localhost:1234/customers. You should now see the UI of the Customers
component. Check whether data can be updated or not.
That's it for now! Keep coding!!