介绍
传统上,在单应用程序中,我们对整个应用程序使用单个数据库服务器。但是,我将 SQL 数据库和 MongoDB 结合在同一个应用程序中。此应用程序将是 RDBMS 和 No SQL 数据库的组合。我们将从头开始创建一个 Blazor 应用程序,并使用数据迁移创建一个 SQL 数据库和一个表。我们将使用此 SQL DB 来保存员工数据。我们将使用 MongoDB 数据库来保存城市数据。我们可以一步一步地看到所有操作。
在 Visual Studio 中创建 Blazor 应用程序
我们可以使用 Blazor 服务器模板在 Visual Studio 中创建 Blazor 应用程序。
我们必须将以下库安装到我们的项目中。我们可以使用 NuGet 来安装这些库。
- “Microsoft.EntityFrameworkCore.SqlServer”
- “Microsoft.EntityFrameworkCore.Tools”
- “MongoDB.Driver”
我们可以在“Data”文件夹内创建一个具有以下属性的“Employee”类。
Employee.cs
namespace BlazorSQLAndMongoDB.Data
{
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Designation { get; set; }
public string Company { get; set; }
public string City { get; set; }
}
}
我们可以在Data文件夹内创建一个“SqlDbContext”类,用于实体框架相关的操作。
SqlDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace BlazorSQLAndMongoDB.Data
{
public class SqlDbContext : DbContext
{
public SqlDbContext(DbContextOptions<SqlDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees { get; set; }
}
}
我们可以在“appsettings.json”中为 SQL 数据库创建一个连接字符串。
我们使用了本地 SQL 服务器,该服务器可通过 Visual Studio 创建数据库和表。您可以根据需要使用任何 SQL 服务器。
我们必须在 Startup 类中的“ConfigureServices”方法中注册 SqlDbContext 类。
使用实体框架和数据迁移创建 SQL 数据库和表
我们可以使用工具菜单中的 NuGet 包管理器控制台,并使用以下迁移命令来创建 SQL 数据库和表。
add-migration Initial
上述命令将在“Migrations”文件夹内创建一个以当前时间戳为后缀的迁移脚本类。
我们可以使用以下命令来创建数据库和表。
update-database
数据库和表将很快创建。
我们可以创建一个“IEmployeeService”接口并在接口内声明以下方法。
IEmployeeService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlazorSQLAndMongoDB.Data
{
public interface IEmployeeService
{
Task<List<Employee>> GetEmployees();
Task<bool> CreateEmployee(Employee employee);
Task<bool> EditEmployee(string id, Employee employee);
Task<Employee> SingleEmployee(string id);
Task<bool> DeleteEmployee(string id);
}
}
我们可以从另一个类“EmployeeService”的接口中实现上述方法
EmployeeService.cs
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlazorSQLAndMongoDB.Data
{
public class EmployeeService : IEmployeeService
{
private readonly SqlDbContext _dbContext;
public EmployeeService(SqlDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<Employee>> GetEmployees()
{
return await _dbContext.Employees.ToListAsync();
}
public async Task<bool> CreateEmployee(Employee employee)
{
employee.Id = Guid.NewGuid().ToString();
_dbContext.Add(employee);
try
{
await _dbContext.SaveChangesAsync();
return true;
}
catch (DbUpdateException)
{
return false;
}
}
public async Task<Employee> SingleEmployee(string id)
{
return await _dbContext.Employees.FindAsync(id);
}
public async Task<bool> EditEmployee(string id, Employee employee)
{
if (id != employee.Id)
{
return false;
}
_dbContext.Entry(employee).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
return true;
}
public async Task<bool> DeleteEmployee(string id)
{
var employee = await _dbContext.Employees.FindAsync(id);
if (employee == null)
{
return false;
}
_dbContext.Employees.Remove(employee);
await _dbContext.SaveChangesAsync();
return true;
}
}
}
我们在服务类中添加了所有 CRUD 操作的逻辑。请注意,您甚至可以在没有接口的情况下创建此服务。
我们可以创建一个具有以下属性的 City 类。City 数据将存储在 MongoDB 中。
City.cs
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace BlazorSQLAndMongoDB.Data
{
public class City
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public string State { get; set; }
}
}
我们可以将 MongoDB 连接字符串、数据库名称和集合名称保存在 appsettings.json 文件中,而不是对值进行硬编码。
我们必须创建一个接口和类来从 appsettings.json 文件中读取值。
我们可以创建“IMongoDbSettings”接口并声明以下属性。
IMongoDbSettings.cs
namespace BlazorSQLAndMongoDB.Data
{
public interface IMongoDbSettings
{
string CollectionName { get; set; }
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
}
我们可以创建“MongoDbSettings”类并在类内部继承IMongoDbSettings接口。
MongoDbSettings.cs
namespace BlazorSQLAndMongoDB.Data
{
public class MongoDbSettings : IMongoDbSettings
{
public string CollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
}
我们可以在Startup类中的ConfigureServices类里面注册这个接口和类。
我们可以创建一个“ICityService”接口并在接口内声明以下方法。
ICityService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlazorSQLAndMongoDB.Data
{
public interface ICityService
{
Task<List<City>> GetCities();
Task<bool> CreateCity(City city);
Task<bool> EditCity(string id, City city);
Task<City> SingleCity(string id);
Task<bool> DeleteCity(string id);
}
}
我们可以从另一个类“CityService”的接口中实现上述方法
ICityService.cs
using MongoDB.Driver;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlazorSQLAndMongoDB.Data
{
public class CityService : ICityService
{
private readonly IMongoCollection<City> _cities;
public CityService(IMongoDbSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_cities = database.GetCollection<City>(settings.CollectionName);
}
public async Task<bool> CreateCity(City city)
{
try
{
await _cities.InsertOneAsync(city);
return true;
}
catch
{
return false;
}
}
public async Task<bool> DeleteCity(string id)
{
try
{
await _cities.DeleteOneAsync(city => city.Id == id);
return true;
}
catch
{
return false;
}
}
public async Task<bool> EditCity(string id, City city)
{
try
{
await _cities.ReplaceOneAsync(book => book.Id == id, city);
return true;
}
catch
{
return false;
}
}
public async Task<List<City>> GetCities()
{
try
{
return await _cities.Find(city => true).ToListAsync();
}
catch
{
return null;
}
}
public async Task<City> SingleCity(string id)
{
try
{
return await _cities.Find<City>(city => city.Id == id).FirstOrDefaultAsync();
}
catch
{
return null;
}
}
}
}
我们已经在上述类中注入了 IMongoDbSettings 接口,并从 appsettings.json 文件中获取了 MongoDB 配置值。
我们已经在上述服务类中添加了针对 City 实体的 CRUD 操作的所有逻辑。
我们可以在 Startup 类中注册 Employee 服务和 City 服务。
我们还可以在启动类中为 Blazor 服务器应用程序启用详细的电路错误。
配置服务(方法)
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddDbContext<SqlDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlDbContext")));
services.Configure<MongoDbSettings>(Configuration.GetSection(nameof(MongoDbSettings)));
services.AddSingleton<IMongoDbSettings>(sp => sp.GetRequiredService<IOptions<MongoDbSettings>>().Value);
services.AddScoped<IEmployeeService, EmployeeService>();
services.AddScoped<ICityService, CityService>();
services.AddServerSideBlazor().AddCircuitOptions(o => o.DetailedErrors = true);
}
我们已经完成了 Blazor 应用程序的后端部分。我们可以在“Pages”文件夹中创建所有用于 CRUD 操作的 Razor 组件。
我们可以先为City创建组件。
ListCities.razor
@using BlazorSQLAndMongoDB.Data
@page "/listcities"
@inject ICityService CityService
<h2>City Details</h2>
<p>
<a href="/addcity">Create New City</a>
</p>
@if (cities == null)
{
<img src="./basicloader.gif" />
}
else
{
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>State</th>
</tr>
</thead>
<tbody>
@foreach (var city in cities)
{
<tr>
<td>@city.Name</td>
<td>@city.State</td>
<td>
<a href='/editcity/@city.Id'>Edit</a>
<a href='/deletecity/@city.Id'>Delete</a>
</td>
</tr>
}
</tbody>
</table>
}
@code {
List<City> cities;
protected override async Task OnInitializedAsync()
{
cities = await CityService.GetCities();
}
}
AddCity.razor
@using BlazorSQLAndMongoDB.Data
@page "/addcity"
@inject NavigationManager NavigationManager
@inject ICityService CityService
<h2>Create City</h2>
<hr />
<form>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="Name" class="control-label">Name</label>
<input for="Name" class="form-control" @bind="@city.Name" />
</div>
<div class="form-group">
<label for="State" class="control-label">State</label>
<input for="State" class="form-control" @bind="@city.State" />
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<input type="button" class="btn btn-primary" @onclick="@CreateCity" value="Save"/>
<input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
</div>
</div>
</div>
</form>
@code {
City city = new City();
protected async Task CreateCity()
{
await CityService.CreateCity(city);
NavigationManager.NavigateTo("listcities");
}
void Cancel()
{
NavigationManager.NavigateTo("listcities");
}
}
EditCity.razor
@using BlazorSQLAndMongoDB.Data
@page "/editcity/{id}"
@inject NavigationManager NavigationManager
@inject ICityService CityService
<h2>Edit City</h2>
<hr />
<form>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="Name" class="control-label">Name</label>
<input for="Name" class="form-control" @bind="@city.Name" />
</div>
<div class="form-group">
<label for="State" class="control-label">State</label>
<input for="State" class="form-control" @bind="@city.State" />
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<input type="button" class="btn btn-primary" @onclick="@UpdateCity" value="Update" />
<input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
</div>
</div>
</form>
@code {
[Parameter]
public string id { get; set; }
City city = new City();
protected override async Task OnInitializedAsync()
{
city = await CityService.SingleCity(id);
}
protected async Task UpdateCity()
{
await CityService.EditCity(id, city);
NavigationManager.NavigateTo("listcities");
}
void Cancel()
{
NavigationManager.NavigateTo("listcities");
}
}
DeleteCity.razor
@using BlazorSQLAndMongoDB.Data
@page "/deletecity/{id}"
@inject NavigationManager NavigationManager
@inject ICityService CityService
<h2>Confirm Delete</h2>
<p>Are you sure you want to delete this City with Id: <b>@id</b></p>
<br />
<div class="col-md-4">
<table class="table">
<tr>
<td>Name</td>
<td>@city.Name</td>
</tr>
<tr>
<td>State</td>
<td>@city.State</td>
</tr>
</table>
<div class="form-group">
<input type="button" value="Delete" @onclick="@Delete" class="btn btn-primary" />
<input type="button" value="Cancel" @onclick="@Cancel" class="btn" />
</div>
</div>
@code {
[Parameter]
public string id { get; set; }
City city = new City();
protected override async Task OnInitializedAsync()
{
city = await CityService.SingleCity(id);
}
protected async Task Delete()
{
await CityService.DeleteCity(id);
NavigationManager.NavigateTo("listcities");
}
void Cancel()
{
NavigationManager.NavigateTo("listcities");
}
}
我们现在可以为员工创建组件。
ListEmployees.razor
@using BlazorSQLAndMongoDB.Data
@page "/listemployees"
@inject IEmployeeService EmployeeService
<h2>Employee Details</h2>
<p>
<a href="/addemployee">Create New Employee</a>
</p>
@if (employees == null)
{
<img src="./basicloader.gif" />
}
else
{
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>Department</th>
<th>Designation</th>
<th>Company</th>
<th>City</th>
</tr>
</thead>
<tbody>
@foreach (var employee in employees)
{
<tr>
<td>@employee.Name</td>
<td>@employee.Department</td>
<td>@employee.Designation</td>
<td>@employee.Company</td>
<td>@employee.City</td>
<td>
<a href='/editemployee/@employee.Id'>Edit</a>
<a href='/deleteemployee/@employee.Id'>Delete</a>
</td>
</tr>
}
</tbody>
</table>
}
@code {
List<Employee> employees;
protected override async Task OnInitializedAsync()
{
employees = await EmployeeService.GetEmployees();
}
}
AddEmployee.razor
@using BlazorSQLAndMongoDB.Data
@page "/addemployee"
@inject NavigationManager NavigationManager
@inject IEmployeeService EmployeeService
@inject ICityService CityService
<h2>Create Employee</h2>
<hr />
<form>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="Name" class="control-label">Name</label>
<input for="Name" class="form-control" @bind="@employee.Name" />
</div>
<div class="form-group">
<label for="Department" class="control-label">Department</label>
<input for="Department" class="form-control" @bind="@employee.Department" />
</div>
<div class="form-group">
<label for="Designation" class="control-label">Designation</label>
<input for="Designation" class="form-control" @bind="@employee.Designation" />
</div>
<div class="form-group">
<label for="Company" class="control-label">Company</label>
<input for="Company" class="form-control" @bind="@employee.Company" />
</div>
<div class="form-group">
<label asp-for="City" class="control-label">City</label>
<select asp-for="City" class="form-control" @bind="@employee.City">
<option value="">-- Select City --</option>
@foreach (var city in cities)
{
<option value="@city.Name">@city.Name</option>
}
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<input type="button" class="btn btn-primary" @onclick="@CreateEmployee" value="Save" />
<input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
</div>
</div>
</div>
</form>
@code {
Employee employee = new Employee();
List<City> cities = new List<City>();
protected override async Task OnInitializedAsync()
{
cities = await CityService.GetCities();
}
protected async Task CreateEmployee()
{
await EmployeeService.CreateEmployee(employee);
NavigationManager.NavigateTo("listemployees");
}
void Cancel()
{
NavigationManager.NavigateTo("listemployees");
}
}
请注意,我已在上述组件中注入了城市服务和员工服务。城市组件用于从 MongoDB 获取城市名称,员工服务用于将所有员工数据保存在 SQL 数据库中。
EditEmployee.razor
@using BlazorSQLAndMongoDB.Data
@page "/editemployee/{id}"
@inject NavigationManager NavigationManager
@inject IEmployeeService EmployeeService
@inject ICityService CityService
<h2>Edit Employee</h2>
<hr />
<form>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="Name" class="control-label">Name</label>
<input for="Name" class="form-control" @bind="@employee.Name" />
</div>
<div class="form-group">
<label for="Department" class="control-label">Department</label>
<input for="Department" class="form-control" @bind="@employee.Department" />
</div>
<div class="form-group">
<label for="Designation" class="control-label">Designation</label>
<input for="Designation" class="form-control" @bind="@employee.Designation" />
</div>
<div class="form-group">
<label for="Company" class="control-label">Company</label>
<input for="Company" class="form-control" @bind="@employee.Company" />
</div>
<div class="form-group">
<label asp-for="City" class="control-label">City</label>
<select asp-for="City" class="form-control" @bind="@employee.City">
<option value="">-- Select City --</option>
@foreach (var city in cities)
{
<option value="@city.Name">@city.Name</option>
}
</select>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<input type="button" class="btn btn-primary" @onclick="@UpdateEmployee" value="Update" />
<input type="button" class="btn" @onclick="@Cancel" value="Cancel" />
</div>
</div>
</form>
@code {
[Parameter]
public string id { get; set; }
Employee employee = new Employee();
List<City> cities = new List<City>();
protected override async Task OnInitializedAsync()
{
cities = await CityService.GetCities();
employee = await EmployeeService.SingleEmployee(id);
}
protected async Task UpdateEmployee()
{
await EmployeeService.EditEmployee(id, employee);
NavigationManager.NavigateTo("listemployees");
}
void Cancel()
{
NavigationManager.NavigateTo("listemployees");
}
}
该组件也被注入到City服务和Employee服务中。
DeleteEmployee.razor
@using BlazorSQLAndMongoDB.Data
@page "/deleteemployee/{id}"
@inject NavigationManager NavigationManager
@inject IEmployeeService EmployeeService
<h2>Confirm Delete</h2>
<p>Are you sure you want to delete this Employee with Id: <b>@id</b></p>
<br />
<div class="col-md-4">
<table class="table">
<tr>
<td>Name</td>
<td>@employee.Name</td>
</tr>
<tr>
<td>Department</td>
<td>@employee.Department</td>
</tr>
<tr>
<td>Designation</td>
<td>@employee.Designation</td>
</tr>
<tr>
<td>Company</td>
<td>@employee.Company</td>
</tr>
<tr>
<td>City</td>
<td>@employee.City</td>
</tr>
</table>
<div class="form-group">
<input type="button" value="Delete" @onclick="@Delete" class="btn btn-primary" />
<input type="button" value="Cancel" @onclick="@Cancel" class="btn" />
</div>
</div>
@code {
[Parameter]
public string id { get; set; }
Employee employee = new Employee();
protected override async Task OnInitializedAsync()
{
employee = await EmployeeService.SingleEmployee(id);
}
protected async Task Delete()
{
await EmployeeService.DeleteEmployee(id);
NavigationManager.NavigateTo("listemployees");
}
void Cancel()
{
NavigationManager.NavigateTo("listemployees");
}
}
NavMenu.razor
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">Blazor with SQL and Mongo</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="listcities">
<span class="oi oi-plus" aria-hidden="true"></span> City details
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="listemployees">
<span class="oi oi-list-rich" aria-hidden="true"></span> Employee details
</NavLink>
</li>
</ul>
</div>
@code {
bool collapseNavMenu = true;
string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
我们已经删除了默认创建的计数器和天气数据的路由。
我们已经完成了整个编码部分。我们可以运行该应用程序了。请确保您的 MongoDB Windows 服务正在运行。
我们可以创建一个新的城市数据。
保存数据后,您可以使用 MongoDB 指南针软件检查 MongoDB 数据。
我们可以在那里看到新添加的城市数据。
我们现在可以创建新的员工数据。
我们可以再添加一个员工详细信息,并在网格中列出两个员工详细信息。
我们已成功通过申请添加了两个城市和两名员工。
结论
在这篇文章中,我们了解了如何在同一个 Blazor 应用程序中结合 SQL DB 和 MongoDB。我们已成功在单个应用程序中集成了 RDBMS 和 No SQL 功能。