如何使用Blazor和Google Cloud Firestore执行CRUD操作

李浩邈
2023-12-01

介绍 (Introduction)

In this article, we will create a Blazor application using Google Firstore as database provider. We will create a Single Page Application (SPA) and perform CRUD operations on it. We will use Bootstrap 4 to display a modal popup for handling user inputs. The form also has a dropdown list, which will bind to a collection in our database. We will also implement a client-side search functionality to search the employee list by employee name.

在本文中,我们将使用Google Firstore作为数据库提供程序来创建Blazor应用程序。 我们将创建一个单页应用程序(SPA)并对其执行CRUD操作。 我们将使用Bootstrap 4显示用于处理用户输入的模式弹出窗口。 该表单还具有一个下拉列表,该列表将绑定到我们数据库中的集合。 我们还将实现客户端搜索功能,以按员工姓名搜索员工列表。

Take a look at the final application.

看一下最终的应用程序。

先决条件 (Prerequisites)

  • Install the .NET Core 2.1 or above SDK from here

    此处安装.NET Core 2.1或更高版本的SDK

  • Install latest version of Visual Studio 2017 from here

    此处安装最新版本的Visual Studio 2017

  • Install ASP.NET Core Blazor Language Services extension from here

    此处安装ASP.NET Core Blazor语言服务扩展

源代码 (Source Code)

Get the source code from GitHub.

GitHub获取源代码。

配置Cloud Firestore (Configuring Cloud Firestore)

The first step is to create a project in google Firebase console. Navigate to https://console.firebase.google.com and sign-in with your google account. Click on Add Project link. A pop up window will open as shown in the image below. Provide your project name and click on Create project button at the bottom.

第一步是在Google Firebase控制台中创建一个项目。 导航到https://console.firebase.google.com并使用您的Google帐户登录。 单击添加项目链接。 如下图所示,将弹出一个窗口。 提供您的项目名称,然后单击底部的“创建项目”按钮。

Note the project id here. Firebase project ids are globally unique. You can edit your project id while creating a new project. Once the project is created you cannot change your project id. We will use this project id in next section while initializing our application.

在此处记录项目ID。 Firebase项目ID在全球范围内是唯一的。 您可以在创建新项目时编辑项目ID。 创建项目后,您将无法更改项目ID。 在初始化应用程序时,我们将在下一部分中使用此项目ID。

Click on the project you just created. A project overview page will open. Select “Database” from left menu. Then click on “Create database” button. A popup window will open asking you to select the “Security rules for Cloud Firestore”. Select “Start in locked mode” and click on enable.

单击刚刚创建的项目。 将打开一个项目概述页面。 从左侧菜单中选择“数据库”。 然后点击“创建数据库”按钮。 将打开一个弹出窗口,要求您选择“ Cloud Firestore的安全规则”。 选择“以锁定模式启动”,然后单击启用。

Refer to the image below:

请参考下图:

This will enable the database for your project. Firebase project have two options for database — Realtime Database and Cloud Firestore. For this application, we will use “Cloud Firestore” database. Click on “Database” dropdown at the top of the page and select “Cloud Firestore”.

这将为您的项目启用数据库。 Firebase项目有两个数据库选项-实时数据库和Cloud Firestore。 对于此应用程序,我们将使用“ Cloud Firestore”数据库。 单击页面顶部的“数据库”下拉列表,然后选择“ Cloud Firestore”。

Refer to the image below:

请参考下图:

We will create cities collection to store the city name for employees. We will also bind this collection to a dropdown list in our web application from which the user will select the desired city. Click on “Add collection”. Set the collection ID as “cities”. Click on “Next”. Refer to the image below:

我们将创建城市集合以存储员工的城市名称。 我们还将将此集合绑定到Web应用程序中的下拉列表,用户将从中选择所需的城市。 点击“添加收藏”。 将集合ID设置为“城市”。 点击“下一步”。 请参考下图:

Put the Field value as “CityName”, Select string from the Type dropdown and fill the value with city name as “Mumbai”. Click on Save. Refer to the image below:

将字段值设置为“ CityName”,从“类型”下拉列表中选择字符串,然后用城市名称填写为“ Mumbai”。 点击保存。 请参考下图:

This will create the “cities” collection and insert the first document in it. Similarly, create four more documents inside this collection and put the “CityName” value as Chennai, New Delhi, Bengaluru and Hyderabad.

这将创建“城市”集合并将第一个文档插入其中。 同样,在该集合中再创建四个文档,并将“ CityName”值分别设为Chennai,New Delhi,Bengaluru和Hyderabad。

We will use “employees” collection to store employee data, but we will not create it manually. We will create “employees” collection while adding the first employee data from the application.

我们将使用“员工”集合来存储员工数据,但是我们不会手动创建它。 我们将创建“员工”集合,同时从应用程序中添加第一个员工数据。

配置Google应用程序凭据 (Configuring Google Application Credentials)

To access database from our project, we need to set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to a JSON service account key file. This will set an authentication pipeline from our application to cloud Firestore.

要从我们的项目访问数据库,我们需要将GOOGLE_APPLICATION_CREDENTIALS环境变量设置为指向JSON服务帐户密钥文件。 这将设置从我们的应用程序到Cloud Firestore的身份验证管道。

To generate the service account key file follow the steps mentioned below:

要生成服务帐户密钥文件,请遵循以下步骤:

Step 1: Navigate to https://console.cloud.google.com/iam-admin/. Login with the same google account, you have used to create Firestore DB.

步骤1 :浏览至https://console.cloud.google.com/iam-admin/ 。 使用相同的Google帐户登录,您已用来创建Firestore数据库。

Step 2: Select Project from the drop down in menu bar at the top.

步骤2 :从顶部菜单栏中的下拉菜单中选择Project。

Step 3: Select “Service accounts” from the left menu. Select the service account for which you want to create the key. Click on more button in the “Actions” column in that row, and then click Create key.

步骤3 :从左侧菜单中选择“服务帐户”。 选择您要为其创建密钥的服务帐户。 单击该行“操作”列中的更多按钮,然后单击创建密钥。

Refer to the image below:

请参考下图:

Step 4: A popup modal will open asking you to select the key type. Select “JSON” and click on create button. This will create private key for accessing your Firestore account and downloads a JSON key file to your machine. We will use this file to set GOOGLE_APPLICATION_CREDENTIALS environment variable in later part of this article.

步骤4 :将打开一个弹出模式,要求您​​选择密钥类型。 选择“ JSON”,然后单击创建按钮。 这将创建用于访问您的Firestore帐户的私钥,并将JSON密钥文件下载到您的计算机。 我们将在本文后面的部分中使用此文件来设置GOOGLE_APPLICATION_CREDENTIALS环境变量。

创建Blazor Web应用程序 (Create Blazor Web Application)

Open Visual Studio and select File >> New >> Project. After selecting the project, a “New Project” dialog will open. Select .NET Core inside Visual C# menu from the left panel. Then, select “ASP.NET Core Web Application” from available project types. Put the name of the project as BlazorWithFirestore and press OK.

打开Visual Studio,然后选择“文件>>新建>>项目”。 选择项目后,将打开“新建项目”对话框。 从左侧面板的Visual C#菜单中选择.NET Core。 然后,从可用的项目类型中选择“ ASP.NET Core Web应用程序”。 将project as BlazorW名称命名project as BlazorW ithFirestore,然后按OK。

After clicking on OK, a new dialog will open asking you to select the project template. You can observe two drop-down menus at the top left of the template window. Select “.NET Core” and “ASP.NET Core 2.1” from these dropdowns. Then, select “Blazor (ASP .NET Core hosted)” template and press OK.

单击确定后,将打开一个新对话框,要求您选择项目模板。 您可以在模板窗口的左上方观察两个下拉菜单。 从这些下拉列表中选择“ .NET Core”和“ ASP.NET Core 2.1”。 然后,选择“ Blazor(ASP.NET Core托管)”模板,然后按OK。

Now, our Blazor solution will be created. You can observe that we have three project files created in this solution.

现在,将创建我们的Blazor解决方案。 您可以看到我们在此解决方案中创建了三个项目文件。

  1. BlazorWithFirestore.Client — It has the client side code and contains the pages that will be rendered on the browser.

    BlazorWithFirestore.Client —它具有客户端代码,并包含将在浏览器中呈现的页面。
  2. BlazorWithFirestore.Server — It has the server side codes such as data access layer and web API.

    BlazorWithFirestore.Server —它具有服务器端代码,例如数据访问层和Web API。
  3. BlazorWithFirestore.Shared — It contains the shared code that can be accessed by both client and server. It contains our Model classes.

    BlazorWithFirestore.Shared —它包含可以由客户端和服务器访问的共享代码。 它包含我们的Model类。

为Firestore添加软件包参考 (Adding Package reference for Firestore)

We need to add the package reference for Google cloud Firestore, which will allow us to access our DB from the Blazor application. Right click on BlazorWithFirestore.Shared project.

我们需要添加Google Cloud Firestore的软件包参考,这将使我们能够从Blazor应用程序访问数据库。 右键单击BlazorWithFirestore.Shared项目。

Select “Edit BlazorWithFirestore.Shared.csproj”. It will open the BlazorWithFirestore.Shared.csproj file. Add the following lines inside it.

选择“编辑BlazorWithFirestore.Shared.csproj”。 它将打开BlazorWithFirestore.Shared.csproj文件。 在其中添加以下行。

<ItemGroup>  <PackageReference Include="Google.Cloud.Firestore" Version="1.0.0-beta14" /></ItemGroup>

Similarly add these lines to BlazorWithFirestore.Server.csproj file also.

同样,也将这些行添加到BlazorWithFirestore.Server.csproj文件中。

创建模型 (Creating the Model)

We will create our model class in BlazorWithFirestore.Shared project. Right click on BlazorWithFirestore.Shared and select Add >> New Folder. Name the folder as Models. Again, right click on Models folder and select Add >> Class to add a new class file. Put the name of you class as Employee.cs and click Add.

我们将在BlazorWithFirestore.Shared项目中创建模型类。 右键单击BlazorWithFirestore.Shared然后选择添加>>新建文件夹。 将该文件夹命名为Models。 再次右键单击Models文件夹,然后选择Add >> Class添加新的类文件。 将您的类的名称输入Employee.cs,然后单击“添加”。

Open the Employee.cs class and put the following code into it.

打开Employee.cs类,并将以下代码放入其中。

using System;using Google.Cloud.Firestore;namespace BlazorWithFirestore.Shared.Models{    [FirestoreData]    public class Employee    {        public string EmployeeId { get; set; }        public DateTime date { get; set; }        [FirestoreProperty]        public string EmployeeName { get; set; }        [FirestoreProperty]        public string CityName { get; set; }        [FirestoreProperty]        public string Designation { get; set; }        [FirestoreProperty]        public string Gender { get; set; }    }}

We have decorated the class with [FirestoreData] attribute. This will allow us to map this class object to Firestore collection. Only those class properties, which are marked with [FirestoreProperty] attribute, are considered when we are saving the document to our collection. We do not need to save EmployeeId to our database as it is generated automatically.

我们已经用[FirestoreData]属性装饰了该类。 这将使我们能够将此类对象映射到Firestore集合。 将文档保存到集合时,仅考虑那些标记有[FirestoreProperty]属性的类属性。 我们不需要将EmployeeId保存到我们的数据库中,因为它是自动生成的。

While fetching the data, we will bind the auto generated document id to the EmployeeId property. Similarly, we will use date property to bind the created date of collection while fetching the record. We will use this date property to sort the list of employees by created date. Hence, we have not applied [FirestoreProperty] attribute to these two properties.

在获取数据时,我们会将自动生成的文档ID绑定到EmployeeId属性。 同样,我们将在获取记录时使用date属性绑定创建的日期集合。 我们将使用此date属性按创建日期对员工列表进行排序。 因此,我们尚未将[FirestoreProperty]属性应用于这两个属性。

Similarly, create a class file Cities.cs and put the following code into it.

同样,创建一个类文件Cities.cs并将以下代码放入其中。

using System;using Google.Cloud.Firestore;namespace BlazorWithFirestore.Shared.Models{    [FirestoreData]    public class Cities    {        public string CityName { get; set; }    }}

为应用程序创建数据访问层 (Creating Data Access Layer for the Application)

Right-click on BlazorWithFirestore.Server project and then select Add >> New Folder and name the folder as DataAccess. We will be adding our class to handle database related operations inside this folder only. Right click on DataAccess folder and select Add >> Class. Name your class EmployeeDataAccessLayer.cs.

右键单击BlazorWithFirestore.Server项目,然后选择添加>>新建文件夹,并将文件夹命名er as Data访问。 我们将添加我们的类以仅处理此文件夹内的数据库相关操作。 右键单击DataAccess文件夹,然后选择添加>>类。 your class EmployeeDataAc命名为your class EmployeeDataAc cessLayer.cs。

Put the following code inside this class.

将以下代码放入此类。

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using BlazorWithFirestore.Shared.Models;using Google.Cloud.Firestore;using Newtonsoft.Json;namespace BlazorWithFirestore.Server.DataAccess{    public class EmployeeDataAccessLayer    {        string projectId;        FirestoreDb fireStoreDb;        public EmployeeDataAccessLayer()        {            string filepath = "C:\\FirestoreAPIKey\\blazorwithfirestore-6d0a096b0174.json";            Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", filepath);            projectId = "blazorwithfirestore";            fireStoreDb = FirestoreDb.Create(projectId);        }        public async Task<List<Employee>> GetAllEmployees()        {            try            {                Query employeeQuery = fireStoreDb.Collection("employees");                QuerySnapshot employeeQuerySnapshot = await employeeQuery.GetSnapshotAsync();                List<Employee> lstEmployee = new List<Employee>();                foreach (DocumentSnapshot documentSnapshot in employeeQuerySnapshot.Documents)                {                    if (documentSnapshot.Exists)                    {                        Dictionary<string, object> city = documentSnapshot.ToDictionary();                        string json = JsonConvert.SerializeObject(city);                        Employee newuser = JsonConvert.DeserializeObject<Employee>(json);                        newuser.EmployeeId = documentSnapshot.Id;                        newuser.date = documentSnapshot.CreateTime.Value.ToDateTime();                        lstEmployee.Add(newuser);                    }                }                List<Employee> sortedEmployeeList = lstEmployee.OrderBy(x => x.date).ToList();                return sortedEmployeeList;            }            catch            {                throw;            }        }        public async void AddEmployee(Employee employee)        {            try            {                CollectionReference colRef = fireStoreDb.Collection("employees");                await colRef.AddAsync(employee);            }            catch            {                throw;            }        }        public async void UpdateEmployee(Employee employee)        {            try            {                DocumentReference empRef = fireStoreDb.Collection("employees").Document(employee.EmployeeId);                await empRef.SetAsync(employee, SetOptions.Overwrite);            }            catch            {                throw;            }        }        public async Task<Employee> GetEmployeeData(string id)        {            try            {                DocumentReference docRef = fireStoreDb.Collection("employees").Document(id);                DocumentSnapshot snapshot = await docRef.GetSnapshotAsync();                if (snapshot.Exists)                {                    Employee emp = snapshot.ConvertTo<Employee>();                    emp.EmployeeId = snapshot.Id;                    return emp;                }                else                {                    return new Employee();                }            }            catch            {                throw;            }        }        public async void DeleteEmployee(string id)        {            try            {                DocumentReference empRef = fireStoreDb.Collection("employees").Document(id);                await empRef.DeleteAsync();            }            catch            {                throw;            }        }        public async Task<List<Cities>> GetCityData()        {            try            {                Query citiesQuery = fireStoreDb.Collection("cities");                QuerySnapshot citiesQuerySnapshot = await citiesQuery.GetSnapshotAsync();                List<Cities> lstCity = new List<Cities>();                foreach (DocumentSnapshot documentSnapshot in citiesQuerySnapshot.Documents)                {                    if (documentSnapshot.Exists)                    {                        Dictionary<string, object> city = documentSnapshot.ToDictionary();                        string json = JsonConvert.SerializeObject(city);                        Cities newCity = JsonConvert.DeserializeObject<Cities>(json);                        lstCity.Add(newCity);                    }                }                return lstCity;            }            catch            {                throw;            }        }    }}

In the constructor of this class we are setting the GOOGLE_APPLICATION_CREDENTIALS environment variable. You need to set the value of filepath variable to the path where the JSON service account key file is located in your machine. Remember we downloaded this file in the previous section. The projectId variable should be set to the project id of your Firebase project.

在此类的构造函数中,我们将设置GOOGLE_APPLICATION_CREDENTIALS环境变量。 您需要将filepath变量的值设置为JSON服务帐户密钥文件在您的计算机中的路径。 请记住,我们在上一节中下载了此文件。 应将projectId变量设置为Firebase项目的项目ID。

We have also defined the methods for performing CRUD operations. The GetAllEmployees method will fetch the list of all employee document from our “employees” collection. It will return the employee list sorted by document creation date.

我们还定义了执行CRUD操作的方法。 GetAllEmployees方法将从“员工”集合中获取所有员工文档的列表。 它将返回按文档创建日期排序的员工列表。

The AddEmployee method will add a new employee document to our “employees” collection. If the collection does not exist, it will create the collection first then insert a new document in it.

AddEmployee方法会将新的员工文档添加到我们的“员工”集合中。 如果该集合不存在,它将首先创建该集合,然后在其中插入一个新文档。

The UpdateEmployee method will update the field values of an already existing employee document, based on the employee id passed to it. We are binding the document id to employeeId property, hence we can easily manipulate the documents.

UpdateEmployee方法将根据传递给它的员工ID更新现有员工文档的字段值。 我们将文档ID绑定到employeeId属性,因此我们可以轻松地操作文档。

The GetEmployeeData method will fetch a single employee document from our “employees” collection based on the employee id.

GetEmployeeData方法将根据员工ID从我们的“员工”集合中获取单个员工文档。

DeleteEmployee method will delete the document for a particular employee from the “employees” collection.

DeleteEmployee方法将从“员工”集合中删除特定员工的文档。

GetCityData method will return the list of cities from “cities” collection.

GetCityData方法将从“城市”集合中返回城市列表。

将Web API控制器添加到应用程序 (Adding the web API Controller to the Application)

Right-click on BlazorWithFirestore.Server/Controllers folder and select Add >> New Item. An “Add New Item” dialog box will open. Select Web from the left panel, then select “API Controller Class” from templates panel and put the name as EmployeeController.cs. Click Add.

右键单击BlazorWithFirestore.Server/Controllers文件夹,然后选择添加>>新建项。 “添加新项”对话框将打开。 从左侧面板中选择Web,然后从模板面板中选择“ API控制器类”,并将名称命名me as EmployeeControl 。 单击添加。

This will create our API EmployeeController class. We will call the methods of EmployeeDataAccessLayer class to fetch data and pass on the data to the client side.

这将创建我们的API EmployeeController类。 我们将调用EmployeeDataAccessLayer类的方法来获取数据并将数据传递给客户端。

Open EmployeeController.cs file and put the following code into it.

打开EmployeeController.cs文件,并将以下代码放入其中。

using System;using System.Collections.Generic;using System.Threading.Tasks;using BlazorWithFirestore.Server.DataAccess;using BlazorWithFirestore.Shared.Models;using Microsoft.AspNetCore.Mvc;namespace BlazorWithFirestore.Server.Controllers{    [Route("api/[controller]")]    public class EmployeeController : Controller    {        EmployeeDataAccessLayer objemployee = new EmployeeDataAccessLayer();        [HttpGet]        public Task<List<Employee>> Get()        {            return objemployee.GetAllEmployees();        }        [HttpGet("{id}")]        public Task<Employee> Get(string id)        {            return objemployee.GetEmployeeData(id);        }        [HttpPost]        public void Post([FromBody] Employee employee)        {            objemployee.AddEmployee(employee);        }        [HttpPut]        public void Put([FromBody]Employee employee)        {            objemployee.UpdateEmployee(employee);        }        [HttpDelete("{id}")]        public void Delete(string id)        {            objemployee.DeleteEmployee(id);        }        [HttpGet("GetCities")]        public Task<List<Cities>> GetCities()        {            return objemployee.GetCityData();        }    }}

创建Blazor组件 (Creating the Blazor component)

We will create the component in the BlazorWithFirestore.Client/Pages folder. The application template provides the Counter and Fetch Data files by default in this folder. Before adding our own component file, we will delete these two default files to make our solution cleaner. Right-click on BlazorWithFirestore.Client/Pages folder and then select Add >> New Item. An “Add New Item” dialog box will open, select “ASP.NET Core” from the left panel, then select “Razor Page” from templates panel and name it EmployeeData.cshtml. Click Add. Refer to the image below:

我们将在BlazorWithFirestore.Client/Pages文件夹中创建组件。 默认情况下,应用程序模板在此文件夹中提供“计数器”和“获取数据”文件。 在添加我们自己的组件文件之前,我们将删除这两个默认文件以使我们的解决方案更加简洁。 右键单击BlazorWithFirestore.Client/Pages文件夹,然后选择添加>>新建项目。 将打开“添加新项”对话框,从左侧面板中选择“ ASP.NET Core”,然后从模板面板中选择“剃刀页面”,并命名me it EmployeeData. cshtml。 单击添加。 请参考下图:

This will add an EmployeeData.cshtml page to our BlazorSPA.Client/Pages folder. This razor page will have two files – EmployeeData.cshtml and EmployeeData.cshtml.cs.

这会将EmployeeData.cshtml页面添加到我们的BlazorSPA.Client / Pages文件夹中。 此剃刀页面将包含两个文件-EmployeeData.cshtml和EmployeeData.cshtml.cs。

为JS Interop添加参考 (Adding references for JS Interop)

We will be using a bootstrap modal dialog in our application. We will also include a few Font Awesome icons for styling in the application. To be able to use these two libraries, we need to add the CDN references to allow the JS interop.

我们将在应用程序中使用引导模式对话框。 我们还将包括一些Font Awesome图标,用于在应用程序中进行样式设置。 为了能够使用这两个库,我们需要添加CDN引用以允许JS互操作。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

Here, we have included the CDN references, which will allow us to use the bootstrap modal dialog and Font Awesome icons in our applications. Now, we will add codes to our view files.

在这里,我们包括了CDN参考,这将使我们能够在应用程序中使用引导模式对话框和Font Awesome图标。 现在,我们将代码添加到视图文件中。

EmployeeData.cshtml.cs (EmployeeData.cshtml.cs)

Open EmployeeData.cshtml.cs and put the following code into it.

打开EmployeeData.cshtml.cs并将以下代码放入其中。

using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Threading.Tasks;using BlazorWithFirestore.Shared.Models;using Microsoft.AspNetCore.Blazor;using Microsoft.AspNetCore.Blazor.Components;namespace BlazorWithFirestore.Client.Pages{    public class EmployeeDataModel : BlazorComponent    {        [Inject]        protected HttpClient Http { get; set; }        protected List<Employee> empList = new List<Employee>();        protected List<Cities> cityList = new List<Cities>();        protected Employee emp = new Employee();        protected string modalTitle { get; set; }        protected string searchString { get; set; }        protected override async Task OnInitAsync()        {            await GetCityList();            await GetEmployeeList();        }        protected async Task GetCityList()        {            cityList = await Http.GetJsonAsync<List<Cities>>("api/Employee/GetCities");        }        protected async Task GetEmployeeList()        {            empList = await Http.GetJsonAsync<List<Employee>>("api/Employee");        }        protected void AddEmployee()        {            emp = new Employee();            modalTitle = "Add Employee";        }        protected async Task EditEmployee(string empID)        {            emp = await Http.GetJsonAsync<Employee>("/api/Employee/" + empID);            modalTitle = "Edit Employee";        }        protected async Task SaveEmployee()        {            if (emp.EmployeeId != null)            {                await Http.SendJsonAsync(HttpMethod.Put, "api/Employee/", emp);            }            else            {                await Http.SendJsonAsync(HttpMethod.Post, "/api/Employee/", emp);            }            await GetEmployeeList();        }        protected async Task DeleteConfirm(string empID)        {            emp = await Http.GetJsonAsync<Employee>("/api/Employee/" + empID);        }        protected async Task DeleteEmployee(string empID)        {            Console.WriteLine(empID);            await Http.DeleteAsync("api/Employee/" + empID);            await GetEmployeeList();        }        protected async Task SearchEmployee()        {            await GetEmployeeList();            if (searchString != "")            {                empList = empList.Where(                x => x.EmployeeName.IndexOf(searchString,                StringComparison.OrdinalIgnoreCase) != -1).ToList();            }        }    }}

Here, we have defined the EmployeeDataModel class, which is inheriting from BlazorComponent. This allows the EmployeeDataModel class to act as a Blazor component.

在这里,我们定义了EmployeeDataModel类,该类是从BlazorComponent继承的。 这允许EmployeeDataModel类充当Blazor组件。

We are also injecting the HttpClient service to enable the web API calls to our EmployeeController API.

我们还将注入HttpClient服务,以启用对EmployeeController API的Web API调用。

We will use the two variables — empList and cityList — to hold the data of our Employee and Cities collections respectively. The modalTitle property, which is of type string, is used to hold the title that will be displayed in the modal dialog. The value provided in the search box is stored in the searchString property which is also of type string.

我们将使用两个变量empList和cityList分别保存Employee和Cities集合的数据。 modalTitle属性为字符串类型,用于保存将在模式对话框中显示的标题。 搜索框中提供的值存储在searchString属性中,该属性也是string类型。

The GetCityList method will make a call to our web API GetCities method to fetch the list of city data from the cities collection. The GetEmployeeList method will send a GET request to our web API to fetch the list of Employee Data from the Employee table.

GetCityList方法将调用我们的Web API GetCities方法以从城市集合中获取城市数据列表。 GetEmployeeList方法将向我们的Web API发送GET请求,以从Employee表中获取Employee数据列表。

We are invoking these two methods inside the OnInitAsync method, to ensure that the Employee Data and the cities data will be available as the page loads.

我们在OnInitAsync方法中调用这两个方法,以确保在页面加载时可以使用Employee数据和城市数据。

The AddEmployee method will initialize an empty instance of the Employee object and set the modalTitle property, which will display the title message on the Add modal popup.

AddEmployee方法将初始化Employee对象的空实例,并设置modalTitle属性,该属性将在Add modal弹出窗口上显示标题消息。

The EditEmployee method will accept the employee ID as the parameter. It will send a GET request to our web API to fetch the record of the employee corresponding to the employee ID supplied to it.

EditEmployee方法将接受员工ID作为参数。 它将向我们的Web API发送GET请求,以获取与其提供的员工ID对应的员工记录。

We will use the SaveEmployee method to save the record of the employee for both the Add request and Edit request. To differentiate between the Add and the Edit requests, we will use the EmployeeId property of the Employee object. If an Edit request is made, then the EmployeeId property contains a string value, and we will send a PUT request to our web API, which will update the record of the employee. Otherwise, if we make an Add request, then the EmployeeId property is not initialized, and hence it will be null. In this case, we need to send a POST request to our web API, which will create a new employee record.

我们将使用SaveEmployee方法来保存Add请求和Edit请求的员工记录。 为了区分Add和Edit请求,我们将使用Employee对象的EmployeeId属性。 如果发出了Edit请求,则EmployeeId属性包含一个字符串值,我们将向我们的Web API发送一个PUT请求,这将更新员工的记录。 否则,如果我们发出添加请求,则EmployeeId属性不会初始化,因此它将为null。 在这种情况下,我们需要将POST请求发送到我们的Web API,这将创建一个新的员工记录。

The DeleteConfirm method will accept the employee ID as the parameter. It will fetch the Employee Data corresponding to the employee ID supplied to it.

DeleteConfirm方法将接受员工ID作为参数。 它将获取与提供给它的员工ID对应的员工数据。

The DeleteEmployee method will send a delete request to our API and pass the employee ID as the parameter. It will then call the GetEmployeeList method to refresh the view with the updated list of Employee Data.

DeleteEmployee方法将向我们的API发送删除请求,并将员工ID作为参数传递。 然后,它将调用GetEmployeeList方法以使用更新的Employee Data列表刷新视图。

The SearchEmployee method is used to implement the search by the employee name functionality. We will return all the records of the employee, which will match the search criteria either fully or partially. To make the search more effective, we will ignore the text case of the search string. This means the search result will be same whether the search text is in uppercase or in lowercase.

SearchEmployee方法用于通过员工姓名功能实现搜索。 我们将返回员工的所有记录,这些记录将完全或部分匹配搜索条件。 为了使搜索更有效,我们将忽略搜索字符串的文本大小写。 这意味着无论搜索文本是大写还是小写,搜索结果都将相同。

EmployeeData.cshtml (EmployeeData.cshtml)

Open EmployeeData.cshtml page and put the following code into it.

打开EmployeeData.cshtml页面,并将以下代码放入其中。

@page "/employeerecords"@inherits EmployeeDataModel<h1>Employee Data</h1><div class="container">    <div class="row">        <div class="col-xs-3">            <button class="btn btn-primary" data-toggle="modal" data-target="#AddEditEmpModal" onclick="@AddEmployee">                <i class="fa fa-user-plus"></i>                Add Employee            </button>        </div>        <div class="input-group col-md-4 offset-md-5">            <input type="text" class="form-control" placeholder="Search Employee" bind="@searchString" />            <div class="input-group-append">                <button class="btn btn-info" onclick="@SearchEmployee">                    <i class="fa fa-search"></i>                </button>            </div>        </div>    </div></div><br />@if (empList == null){    <p><em>Loading...</em></p>}else{    <table class='table'>        <thead>            <tr>                <th>Name</th>                <th>Gender</th>                <th>Designation</th>                <th>City</th>            </tr>        </thead>        <tbody>            @foreach (var emp in empList)            {                <tr>                    <td>@emp.EmployeeName</td>                    <td>@emp.Gender</td>                    <td>@emp.Designation</td>                    <td>@emp.CityName</td>                    <td>                        <button class="btn btn-outline-dark" data-toggle="modal" data-target="#AddEditEmpModal"                                onclick="@(async () => await EditEmployee(@emp.EmployeeId))">                            <i class="fa fa-pencil-square-o"></i>                            Edit                        </button>                        <button class="btn btn-outline-danger" data-toggle="modal" data-target="#deleteEmpModal"                                onclick="@(async () => await DeleteConfirm(@emp.EmployeeId))">                            <i class="fa fa-trash-o"></i>                            Delete                        </button>                    </td>                </tr>            }        </tbody>    </table>}<div class="modal fade" id="AddEditEmpModal">    <div class="modal-dialog">        <div class="modal-content">            <div class="modal-header">                <h3 class="modal-title">@modalTitle</h3>                <button type="button" class="close" data-dismiss="modal">                    <span aria-hidden="true">X</span>                </button>            </div>            <div class="modal-body">                <form>                    <div class="form-group">                        <label class="control-label">Name</label>                        <input class="form-control" bind="@emp.EmployeeName" />                    </div>                    <div class="form-group">                        <label class="control-label">Gender</label>                        <select class="form-control" bind="@emp.Gender">                            <option value="">-- Select Gender --</option>                            <option value="Male">Male</option>                            <option value="Female">Female</option>                        </select>                    </div>                    <div class="form-group">                        <label class="control-label">Designation</label>                        <input class="form-control" bind="@emp.Designation" />                    </div>                    <div class="form-group">                        <label class="control-label">City</label>                        <select class="form-control" bind="@emp.CityName">                            <option value="-- Select City --">-- Select City --</option>                            @foreach (var city in cityList)                            {                                <option value="@city.CityName">@city.CityName</option>                            }                        </select>                    </div>                </form>            </div>            <div class="modal-footer">                <button class="btn btn-block btn-success"                        onclick="@(async () => await SaveEmployee())" data-dismiss="modal">                    Save                </button>            </div>        </div>    </div></div><div class="modal fade" id="deleteEmpModal">    <div class="modal-dialog">        <div class="modal-content">            <div class="modal-header">                <h3 class="modal-title">Confirm Delete !!!</h3>                <button type="button" class="close" data-dismiss="modal">                    <span aria-hidden="true">X</span>                </button>            </div>            <div class="modal-body">                <table class="table">                    <tr>                        <td>Name</td>                        <td>@emp.EmployeeName</td>                    </tr>                    <tr>                        <td>Gender</td>                        <td>@emp.Gender</td>                    </tr>                    <tr>                        <td>Designation</td>                        <td>@emp.Designation</td>                    </tr>                    <tr>                        <td>City</td>                        <td>@emp.CityName</td>                    </tr>                </table>            </div>            <div class="modal-footer">                <button class="btn btn-danger" data-dismiss="modal"                        onclick="@(async () => await DeleteEmployee(@emp.EmployeeId))">                    Delete                </button>                <button data-dismiss="modal" class="btn">Cancel</button>            </div>        </div>    </div></div>

The route for our component is defined at the top as “/employeerecords”. To use the methods defined in the EmployeeDataModel class, we will inherit it using the @inherits directive.

我们组件的路由在顶部定义为“ / employeerecords”。 要使用EmployeeDataModel类中定义的方法,我们将使用@inherits指令继承它。

We have defined an Add Employee button. Upon clicking, this button will invoke the AddEmployee method and open a modal dialog, which allows the user to fill out the new Employee Data in a form.

我们定义了一个添加员工按钮。 单击后,此按钮将调用AddEmployee方法并打开一个模式对话框,该对话框允许用户在表单中填写新的Employee数据。

We have also defined our search box and a corresponding search button. The search box will bind the value to searchString property. On clicking the search button, SearchEmployee method will be invoked, which will return the filtered list of data as per the search text. If the empList property is not null, we will bind the Employee Data to a table to display it on the web page. Each employee record has the following two action buttons corresponding to it:

我们还定义了搜索框和相应的搜索按钮。 搜索框会将值绑定到searchString属性。 单击搜索按钮时,将调用SearchEmployee方法,该方法将根据搜索文本返回过滤后的数据列表。 如果empList属性不为null,我们将把Employee Data绑定到一个表以在网页上显示它。 每个员工记录具有以下两个与之对应的操作按钮:

  • Edit: This button will perform two tasks. It will invoke the EditEmployee method and open the edit employee modal dialog for editing the employee record.

    编辑:此按钮将执行两个任务。 它将调用EditEmployee方法并打开用于编辑员工记录的编辑员工模式对话框。
  • Delete: This button will also perform two tasks. It will invoke the DeleteConfirm method and open a delete confirm modal dialog, asking the user to confirm the deletion of the employee’s record .

    删除:此按钮还将执行两项任务。 它将调用DeleteConfirm方法并打开一个删除确认模式对话框,要求用户确认雇员记录的删除。

We have defined a form inside the bootstrap modal to accept user inputs for the employee records. The input fields of this form will bind to the properties of the employee class. The City field is a drop-down list, which will bind to the cities collection of the database with the help of the cityList variable. When we click on the save button, the SaveEmployee method will be invoked and the modal dialog will be closed.

我们在引导程序模式中定义了一个表单,以接受用户输入的员工记录。 此表单的输入字段将绑定到employee类的属性。 City字段是一个下拉列表,它将在cityList变量的帮助下绑定到数据库的citys集合。 当我们单击保存按钮时,将调用SaveEmployee方法,并且将关闭模式对话框。

When user click on the Delete button corresponding to an employee record, another bootstrap modal dialog will be displayed. This modal will show the Employee Data in a table and ask the user to confirm the deletion. Clicking on the Delete button inside this modal dialog will invoke the DeleteEmployee method and close the modal. Clicking on the Cancel button will close the modal without performing any action on the data.

当用户单击与员工记录对应的“删除”按钮时,将显示另一个引导模式对话框。 此模式将在表中显示“员工数据”,并要求用户确认删除。 单击此模式对话框内的Delete按钮将调用DeleteEmployee方法并关闭模式。 单击“取消”按钮将关闭模式,而不对数据执行任何操作。

Before executing the application, we will add the navigation link to our component in the navigation menu.

在执行应用程序之前,我们将导航链接添加到导航菜单中的组件。

Open the BlazorWithFirestore.Client/Shared/NavMenu.cshtml page and add the following navigation link:

打开BlazorWithFirestore.Client/Shared/NavMenu.cshtml页面并添加以下导航链接:

<li class="nav-item px-3">  <NavLink class="nav-link" href="employeerecords">    <span class="oi oi-list-rich" aria-hidden="true"></span> Employee Data  </NavLink></li>

Hence, we have successfully created a Single Page Application (SPA) using Blazor with the help of cloud Firestore as database provider.

因此,我们已在Cloud Firestore作为数据库提供者的帮助下使用Blazor成功创建了单页应用程序(SPA)。

执行演示 (Execution Demo)

Press F5 to launch the application.

按F5启动应用程序。

A web page will open as shown in the image below. The navigation menu on the left is showing navigation link for Employee data page.

如下图所示,将打开一个网页。 左侧的导航菜单显示“员工数据”页面的导航链接。

You can perform the CRUD operations on this application as shown in the GIF image at the start of this article.

您可以按照本文开头的GIF图像所示在此应用程序上执行CRUD操作。

结论 (Conclusion)

We have created a Single Page Application (SPA) using Blazor with the help of Google cloud Firestore as database provider. We have created a sample employee record management system and performed CRUD operations on it. Firestore is a NoSQL database, which allows us to store data in form of collections and documents. We have also used a bootstrap modal popup to handle user inputs. We have also implemented a search box to search the employee list by employee name.

我们已经使用Blazor在Google Cloud Firestore作为数据库提供者的帮助下创建了一个单页应用程序(SPA)。 我们创建了样本员工记录管理系统,并在其上执行了CRUD操作。 Firestore是一个NoSQL数据库,它使我们能够以集合和文档的形式存储数据。 我们还使用了引导模式弹出窗口来处理用户输入。 我们还实现了一个搜索框,用于按员工姓名搜索员工列表。

Please get the source code from GitHub and play around to get a better understanding.

请从GitHub获取源代码并进行尝试以获得更好的理解。

Get my book Blazor Quick Start Guide to learn more about Blazor.

获取我的书《 Blazor快速入门指南》,以了解有关Blazor的更多信息。

Preparing for interviews? Read my article on C# Coding Questions For Technical Interviews

准备面试吗? 阅读有关技术面试的C#编码问题的文章

也可以看看 (See Also)

Originally published at https://ankitsharmablogs.com/

最初发布在https://ankitsharmablogs.com/

翻译自: https://www.freecodecamp.org/news/how-to-perform-crud-operations-using-blazor-and-google-cloud-firestore-52890b06e2f8/

 类似资料: