![](https://habrastorage.org/getpro/habr/upload_files/241/85d/b47/24185db475d142904827a37c4d49dc43.png)
В этой статье мы создадим веб-приложение, используя Blazor, .Net 6.0 и Entity Framework Core для выполнения CRUD операций на базе Asp.Net Core.
В этом руководстве мы будем использовать Visual Studio 2022 и SQL Server 2014.
Создание таблицы
Для выполнения CRUD операций мы будем использовать таблицу “userdetails”. Откройте SQL Server и создайте таблицу “userdetails”, используя запрос, приведенный ниже.
CREATE TABLE [dbo].[userdetails](
[userid] [int] IDENTITY(1,1) NOT NULL,
[username] [nvarchar](100) NULL,
[address] [nvarchar](500) NULL,
[cellnumber] [nvarchar](50) NULL,
[emailid] [nvarchar](50) NULL,
CONSTRAINT [PK_userdetails] PRIMARY KEY CLUSTERED
(
[userid] ASC
)
)
Создание веб-приложения Blazor
Сейчас мы перейдем к созданию нового проекта, используя приложение Blazor WebAssembly и .Net 6.0. Откройте Visual Studio 2022 и следуйте этой пошаговой инструкции.
Шаг 1
![](https://habrastorage.org/getpro/habr/upload_files/0b3/6d4/c40/0b36d4c40356f09d82960533bb92c402.png)
Шаг 2
![](https://habrastorage.org/getpro/habr/upload_files/83d/9e6/5b6/83d9e65b6b254c232c6abb967a8153d7.png)
На этом шаге выбираем тип проекта “Blazor WebAssembly App”.
Шаг 3
![](https://habrastorage.org/getpro/habr/upload_files/fbd/3bc/b63/fbd3bcb63ba3b969da91e98f6e562bdd.png)
Шаг 4
![](https://habrastorage.org/getpro/habr/upload_files/a96/e8e/124/a96e8e1242d76c7f5e03abce92049cd4.png)
Выберите тип Framework’а - .NET 6.0, и вариант развертки - ASP.NET Core.
Таким образом будет создано приложение Blazor, и структура папок в Solution Explorer будет выглядеть, как показано на изображении ниже.
![](https://habrastorage.org/getpro/habr/upload_files/ccf/430/1a2/ccf4301a2672e8c264fc21808f3c4ba0.png)
На изображении мы также видим 3 файла проекта, которые созданы внутри солюшена “BlazorApp”.
BlazorApp.Client — содержит клиентский код и страницы, которые будут отображаться в браузере.
BlazorApp.Server — содержит серверный код, такой как подключение к базе данных, операции и веб-API.
BlazorApp.Shared — содержит общий код, к которому есть доступ как у клиента, так и у сервера.
Если запустить приложение на данном этапе, нажав F5, мы увидим главную страницу приложения, похожую на изображенную ниже.
![](https://habrastorage.org/getpro/habr/upload_files/ab2/ccf/4b4/ab2ccf4b44057f063a564e91bf73c26b.png)
Установка необходимых Nuget-пакетов
Перейдите в пункт меню “Tools”, выберите NuGet Package Manager > Package Manager Console
а затем выполните приведенные ниже команды, чтобы добавить поставщика базы данных и инструменты Entity Framework.
=> Install-Package Microsoft.EntityFrameworkCore.SqlServer
=> Install-Package Microsoft.EntityFrameworkCore.Tools
Добавление модели в приложение
Теперь мы создадим класс Model, который будет содержать свойства модели User.
Для этого кликните правой кнопкой мыши на проект “BlazorApp.Shared” и добавьте новую папку с названием “Models”.
Затем кликните правой кнопкой мыши на папку “Models” и добавьте класс “User.cs”.
Откройте файл “User.cs” и вставьте в него код, приведенный ниже:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BlazorApp.Shared.Models
{
public class User
{
public int Userid { get; set; }
public string Username { get; set; } = null!;
public string Address { get; set; } = null!;
public string Cellnumber { get; set; } = null!;
public string Emailid { get; set; } = null!;
}
}
Добавление уровня доступа к данным в приложение
А теперь мы создадим класс “DatabaseContext.cs”, в котором определим подключение к базе данных. Для этого кликните правой кнопкой мыши проект “BlazorApp.Server” и добавьте папку “Models”. Добавьте файл “DatabaseContext.cs” в папку “Models” и перенесите в него приведенный ниже код:
using BlazorApp.Shared.Models;
using Microsoft.EntityFrameworkCore;
namespace BlazorApp.Server.Models
{
public partial class DatabaseContext : DbContext
{
public DatabaseContext()
{
}
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
}
public virtual DbSet<User> Users { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("userdetails");
entity.Property(e => e.Userid).HasColumnName("Userid");
entity.Property(e => e.Username)
.HasMaxLength(100)
.IsUnicode(false);
entity.Property(e => e.Address)
.HasMaxLength(500)
.IsUnicode(false);
entity.Property(e => e.Cellnumber)
.HasMaxLength(50)
.IsUnicode(false);
entity.Property(e => e.Emailid)
.HasMaxLength(50)
.IsUnicode(false);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
Далее, необходимо будет создать еще две папки “Interfaces” и “Services” для обработки операций, связанных с базой данных.
Для этого кликните правой кнопкой мыши проект “BlazorApp.Server” и добавьте две новые папки “Interfaces” и “Services”.
Добавьте интерфейс в папку “Interfaces”, назовите его “IUser.cs” и перенесите в него приведенный ниже код:
using BlazorApp.Shared.Models;
namespace BlazorApp.Server.Interfaces
{
public interface IUser
{
public List<User> GetUserDetails();
public void AddUser(User user);
public void UpdateUserDetails(User user);
public User GetUserData(int id);
public void DeleteUser(int id);
}
}
Добавьте класс “UserManager.cs” в папку “Services”, который реализует интерфейс “IUser”, и перенесите в него приведенный ниже код:
using BlazorApp.Server.Interfaces;
using BlazorApp.Server.Models;
using BlazorApp.Shared.Models;
using Microsoft.EntityFrameworkCore;
namespace BlazorApp.Server.Services
{
public class UserManager : IUser
{
readonly DatabaseContext _dbContext = new();
public UserManager(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
//Для получения всех данных о пользователе
public List<User> GetUserDetails()
{
try
{
return _dbContext.Users.ToList();
}
catch
{
throw;
}
}
//Для добавления новой записи пользователя
public void AddUser(User user)
{
try
{
_dbContext.Users.Add(user);
_dbContext.SaveChanges();
}
catch
{
throw;
}
}
//Для обновления записи конкретного пользователя
public void UpdateUserDetails(User user)
{
try
{
_dbContext.Entry(user).State = EntityState.Modified;
_dbContext.SaveChanges();
}
catch
{
throw;
}
}
//Для получения информации о конкретном пользователе
public User GetUserData(int id)
{
try
{
User? user = _dbContext.Users.Find(id);
if (user != null)
{
return user;
}
else
{
throw new ArgumentNullException();
}
}
catch
{
throw;
}
}
//Для удаления записи конкретного пользователя
public void DeleteUser(int id)
{
try
{
User? user = _dbContext.Users.Find(id);
if (user != null)
{
_dbContext.Users.Remove(user);
_dbContext.SaveChanges();
}
else
{
throw new ArgumentNullException();
}
}
catch
{
throw;
}
}
}
}
Теперь необходимо добавить ссылки на “DatabaseContext”, “IUser” и “UserManager” в файл Program.cs проекта “BlazorApp.Server”.
Откройте файл “Program.cs” и перенесите в него приведенный ниже код:
using BlazorApp.Server.Interfaces;
using BlazorApp.Server.Models;
using BlazorApp.Server.Services;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Добавляем сервисы в контейнер.
//Не забудьте добавить ConnectionStrings как
"DefaultConnection" в файл appsetting.json
builder.Services.AddDbContext<DatabaseContext>
(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddTransient<IUser, UserManager>();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// Дефолтное значение HSTS — 30 дней. Вы можете изменить это, см. https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
Добавление веб-API контроллера в приложение
Кликните правой кнопкой мыши по папке “BlazorApp.Server/Controllers” и выберите “Add”, затем “New Item”. Откроется диалоговое окно “Add New Item”. Выберите “ASP.NET” на левой панели, затем выберите “API Controller - Empty” из шаблонов и назовите класс контроллера “UserController.cs”. Нажмите Add, чтобы создать контроллер.
Откройте файл “UserController.cs” и перенесите в него приведенный ниже код:
using BlazorApp.Server.Interfaces;
using BlazorApp.Shared.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace BlazorApp.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUser _IUser;
public UserController(IUser iUser)
{
_IUser = iUser;
}
[HttpGet]
public async Task<List<User>> Get()
{
return await Task.FromResult(_IUser.GetUserDetails());
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
User user = _IUser.GetUserData(id);
if (user != null)
{
return Ok(user);
}
return NotFound();
}
[HttpPost]
public void Post(User user)
{
_IUser.AddUser(user);
}
[HttpPut]
public void Put(User user)
{
_IUser.UpdateUserDetails(user);
}
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
_IUser.DeleteUser(id);
return Ok();
}
}
}
Добавление Razor View в приложение
Добавим три страницы в проект “BlazorApp.Client”. Для просмотра записей пользователей добавляем страницу “UserDetails.razor”, страницу “AddUser.razor” для добавления и редактирования сведений о пользователе и страницу “DeleteUser.razor” для удаления пользователя.
Кликните правой кнопкой мыши по папке “Pages” проекта “BlazorApp.Client” и выберите “Add”, затем “New Item”. Откроется диалоговое окно “Add New Item”. Выберите “Web” на левой панели, затем выберите “Razor Component” из шаблонов и введите имя “UserDetails.razor”. По этому же принципу добавляем еще две страницы “AddUser.razor” и “DeleteUser.razor” в проект “BlazorApp.Client”.
Откройте файл “UserDetails.razor” и перенесите в него приведенный ниже код:
@page "/fetchuserdetails"
@using BlazorApp.Shared.Models
@inject HttpClient Http
<h1>User Data</h1>
<p>Blazor CRUD operation</p>
<div class="row">
<div class="col-md-6">
<a href='/user/add' class="btn btn-primary" role="button">
<i class="fas fa-user-plus"></i>
Add User
</a>
</div>
<div class="input-group col">
<input type="text" class="form-control" placeholder="Search user by name"
@bind="SearchString" @bind:event="oninput" @onkeyup="FilterUser" />
@if (SearchString.Length > 0)
{
<div class="input-group-append">
<button class="btn btn-danger" @onclick="ResetSearch">
<i class="fas fa-times"></i>
</button>
</div>
}
</div>
</div>
<br />
@if (userList == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table table-striped align-middle table-bordered">
<thead class="table-success">
<tr>
<th>ID</th>
<th>Name</th>
<th>Address</th>
<th>Department</th>
<th>Cell No</th>
<th>E-mail</th>
</tr>
</thead>
<tbody>
@foreach (var user in userList)
{
<tr>
<td>@user.Userid</td>
<td>@user.Username</td>
<td>@user.Address</td>
<td>@user.Cellnumber</td>
<td>@user.Emailid</td>
<td>
<a href='/user/edit/@user.Userid' class="btn btn-outline-dark" role="button">
Edit
</a>
<a href='/user/delete/@user.Userid' class="btn btn-outline-danger" role="button">
Delete
</a>
</td>
</tr>
}
</tbody>
</table>
}
@code {
protected List<User> userList = new();
protected List<User> searchUserData = new();
protected User user = new();
protected string SearchString { get; set; } = string.Empty;
protected override async Task OnInitializedAsync()
{
await GetUser();
}
protected async Task GetUser()
{
userList = await Http.GetFromJsonAsync<List<User>>("api/User");
searchUserData = userList;
}
protected void FilterUser()
{
if (!string.IsNullOrEmpty(SearchString))
{
userList = searchUserData
.Where(x => x.Username.IndexOf(SearchString, StringComparison.OrdinalIgnoreCase) != -1)
.ToList();
}
else
{
userList = searchUserData;
}
}
protected void DeleteConfirm(int userID)
{
user = userList.FirstOrDefault(x => x.Userid == userID);
}
public void ResetSearch()
{
SearchString = string.Empty;
userList = searchUserData;
}
}
Откройте страницу “AddUser.razor” и перенесите приведенный ниже код, где мы можем добавить нового пользователя, а также изменить о нем сведения.
@page "/user/add"
@page "/user/edit/{userId:int}"
@using BlazorApp.Shared.Models
@inject HttpClient Http
@inject NavigationManager NavigationManager
<h1>@Title User</h1>
<hr />
<EditForm Model="@user" OnValidSubmit="SaveUser">
<DataAnnotationsValidator />
<div class="mb-3">
<label for="Name" class="form-label">Name</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="user.Username" />
</div>
<ValidationMessage For="@(() => user.Username)" />
</div>
<div class="mb-3">
<label for="Address" class="form-label">Address</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="user.Address" />
</div>
<ValidationMessage For="@(() => user.Address)" />
</div>
<div class="mb-3">
<label for="Cellnumber" class="form-label">Cell No</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="user.Cellnumber" />
</div>
<ValidationMessage For="@(() => user.Cellnumber)" />
</div>
<div class="mb-3">
<label for="Emailid" class="form-label">E-mail</label>
<div class="col-md-4">
<InputText class="form-control" @bind-Value="user.Emailid" />
</div>
<ValidationMessage For="@(() => user.Emailid)" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Save</button>
<button class="btn btn-light" @onclick="Cancel">Cancel</button>
</div>
</EditForm>
@code {
[Parameter]
public int userId { get; set; }
protected string Title = "Add";
protected User user = new();
protected override async Task OnParametersSetAsync()
{
if (userId != 0)
{
Title = "Edit";
user = await Http.GetFromJsonAsync<User>("api/User/" + userId);
}
}
protected async Task SaveUser()
{
if (user.Userid != 0)
{
await Http.PutAsJsonAsync("api/User", user);
}
else
{
await Http.PostAsJsonAsync("api/User", user);
}
Cancel();
}
public void Cancel()
{
NavigationManager.NavigateTo("/fetchuserdetails");
}
}
Откройте страницу “DeleteUser.razor” и вставьте приведенный ниже код.
@page "/user/delete/{userId:int}"
@using BlazorApp.Shared.Models
@inject HttpClient Http
@inject NavigationManager NavigationManager
<h2>Delete User</h2>
<br />
<div class="form-group">
<h4>Do you want to delete this user?</h4>
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>@user.Username</td>
</tr>
<tr>
<td>Address</td>
<td>@user.Address</td>
</tr>
<tr>
<td>Cell No</td>
<td>@user.Cellnumber</td>
</tr>
<tr>
<td>E-mail</td>
<td>@user.Emailid</td>
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Delete" @onclick="(async () => await RemoveUser(user.Userid))" class="btn btn-danger" />
<input type="submit" value="Cancel" @onclick="(() => Cancel())" class="btn btn-warning" />
</div>
@code {
[Parameter]
public int userId { get; set; }
User user = new User();
protected override async Task OnInitializedAsync()
{
user = await Http.GetFromJsonAsync<User>("/api/User/" + Convert.ToInt32(userId));
}
protected async Task RemoveUser(int userID)
{
await Http.DeleteAsync("api/User/" + userID);
NavigationManager.NavigateTo("/fetchuserdetails");
}
void Cancel()
{
NavigationManager.NavigateTo("/fetchuserdetails");
}
}
Добавьте ссылку “User Details” в меню навигации. Для этого откройте файл “BlazorApp.Client/Shared/ NavMenu.razor” и перенесите в него приведенный ниже код:
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">BlazorApp</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchuserdetails">
<span class="oi oi-list-rich" aria-hidden="true"></span> User Details
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
Давайте запустим приложение. После запуска приложения, мы увидим навигационную ссылку “User Details” под ссылкой “Fetch Data” с левой стороны, как показано на изображении ниже.
![](https://habrastorage.org/getpro/habr/upload_files/4b0/912/644/4b0912644417cb1ae939e8c603109174.png)
Когда мы кликаем “User Details” в меню навигации, оно перенаправляет нас на страницу “User Details” и отображает все данные пользователя.
![](https://habrastorage.org/getpro/habr/upload_files/a9a/e4a/2da/a9ae4a2da625ad19a321500beecb441d.png)
На странице “User Details” мы можем найти кнопку “Add User”. Нажав ее, мы будем перенаправлены на страницу “Add User”, где мы можем добавить нового пользователя.
![](https://habrastorage.org/getpro/habr/upload_files/62c/124/da6/62c124da65f3ae87d2393e749eb0ed05.png)
Если мы хотим отредактировать или удалить существующую запись пользователя, нажмите ссылку “Edit” или “Delete” этой текущей записи. Откроется соответствующее представление (редактирование/удаление), как показано ниже, где мы можем редактировать или удалять пользовательские данные.
![](https://habrastorage.org/getpro/habr/upload_files/894/4d5/471/8944d5471b7312285c1360503ec0d778.png)
![](https://habrastorage.org/getpro/habr/upload_files/d93/c48/571/d93c485710d9ca7b603ffc53e5aa426c.png)
Вот и все. В этой статье мы показали создание приложения ASP.NET Core с использованием Blazor Entity Framework и .NET 6.0, а также выполнили CRUD операцию.
Перевод материала подготовлен в рамках набора учащихся на новый поток курса «C# ASP.NET Core разработчик». Скоро в рамках курса пройдет открытый demo-урок на тему «Пишем свой API: SignalR Core». На занятии разберем написание своего API: используем библиотеку SignalR для реализациии асинхронного обмена данными между сервером и клиентом на примере простого чата. Если интересно — записывайтесь.
Комментарии (5)
navferty
03.02.2022 18:44+3Рассказывать про Entity Framework, и ни разу не упомянуть про миграции и code-first подход - очень странно для туториала. Также при вставке кода рекомендуется выбирать язык для подсветки синтаксиса.
Зачем пересказывать в очередной раз то, что уже отлично расписано на docs.microsoft.com, в частности:
P.S. Немного саморекламы. Собрал в одном месте материалы для обучения C# и ASP.NET Core с сопутстсвующими темами: https://github.com/navferty/dotnet-learning
Korobei
03.02.2022 20:33Я думал что статья как для клиента blazor сделали EF.
Также как на стороне сервера c# expression tree преобразуется в SQL, также на стороне клиента, c# expression tree преобразуется в набор правильных api вызовов спрятанных от пользователя.
Типа клиент и сервер имеют набор поддерживаемых операций, может даже сверху OData и прозрачно прокинуть это до самого клиента.
Хотя наверное поддержать expression tree на стороне клиента нереалистично с точки зрения необходимого объёма клиентского кода.
А то что статья про EF только на стороне сервера, это не так уж и интересно, какого-то особого волшебства в этом нету.
slepmog
03.02.2022 23:14+3Вот это:
[HttpGet] public async Task<List<User>> Get() { return await Task.FromResult(_IUser.GetUserDetails()); }
очень плохо и часто свидетельствует о непонимании базовых вещей про
async
.При синхронном
GetUserDetails
у вас должно было быть[HttpGet] public List<User> Get() { return _IUser.GetUserDetails(); }
Если очень хотелось
Task<List<User>>
, то[HttpGet] public Task<List<User>> Get() { return Task.FromResult(_IUser.GetUserDetails()); }
, но это уже [само]обман.
FreeBa
Какой прекрасный пересказ MSDN.