在同一个 Blazor 应用中结合 SQL-DB 和 MongoDB

介绍

传统上,在单应用程序中,我们对整个应用程序使用单个数据库服务器。但是,我将 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 功能。

  • 31
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢.锋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值