七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
前言
上一章节我们引入BootstrapBlazor UI组件完成了EasySQLite后台界面的基本架子的搭建,本章节的主要内容是Blazor班级管理页面编写和接口对接。
七天.NET 8 操作 SQLite 入门到实战详细教程
- 第一天 SQLite 简介
- 第二天 在 Windows 上配置 SQLite 环境
- 第三天 SQLite 快速入门
- 第四天 EasySQLite 前后端项目框架搭建
- 第五天引入 SQLite-net ORM 并封装常用方法
- 第六天后端班级管理相关接口完善和Swagger自定义配置
- 第七天BootstrapBlazor UI组件库引入(1)
EasySQLite 项目源码地址
Blazor简介和快速入门
不熟悉Blazor的同学可以先看这篇文章大概了解一下。
前端Table页面和接口对接代码
主要是常见Table的数据展示、数据添加、数据删除、数据修改等操作。




@page "/SchoolClass"
@using Entity
@using WebUI.Common
@inject HttpClient _httpClient;
<Table TItem="SchoolClass"
AutoGenerateColumns="true"
ShowToolbar="true"
IsMultipleSelect="true"
OnSaveAsync="@OnSaveAsync"
OnQueryAsync="@OnQueryAsync"
OnDeleteAsync="@OnDeleteAsync"
IsStriped="true"
IsBordered="true"
ShowSearch="true"
IsPagination="true"
ShowSearchText="true">
<TableColumns>
<TableColumn Sortable="true" Filterable="true" Searchable="true" @bind-Field="@context.ClassName" PlaceHolder="请输入班级名称" />
<TableColumn @bind-Field="@context.ClassID" IsVisibleWhenAdd="false" IsVisibleWhenEdit="false" />
<TableColumn @bind-Field="@context.CreateTime" IsVisibleWhenAdd="false" IsVisibleWhenEdit="false" />
</TableColumns>
<SearchTemplate>
<GroupBox Title="搜索条件">
<div class="row g-3 form-inline">
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="@context.ClassName" PlaceHolder="请输入班级名称" maxlength="50" ShowLabel="true" DisplayText="姓名" />
</div>
</div>
</GroupBox>
</SearchTemplate>
</Table>
@code {
/// <summary>
/// 数据查询
/// </summary>
/// <param name="options">options</param>
/// <returns></returns>
private async Task<QueryData<SchoolClass>> OnQueryAsync(QueryPageOptions options)
{
var getClass = new List<SchoolClass>();
var getResults = await _httpClient.GetFromJsonAsync<ApiResponse<List<SchoolClass>>>("api/SchoolClass/GetClass").ConfigureAwait(false);
if (getResults.Success)
{
// 数据模糊过滤筛选
if (!string.IsNullOrWhiteSpace(options.SearchText))
{
getClass = getResults.Data.Where(x => x.ClassName.Contains(options.SearchText)).ToList();
}
else
{
getClass = getResults.Data.ToList();
}
}
//假分页
return await Task.FromResult(new QueryData<SchoolClass>()
{
Items = getClass.Skip((options.PageIndex - 1) * options.PageItems).Take(options.PageItems).ToList(),
TotalCount = getClass.Count()
});
}
/// <summary>
/// 模拟数据增加和修改操作
/// </summary>
/// <param name="studentInfo">studentInfo</param>
/// <param name="changedType">changedType</param>
/// <returns></returns>
public async Task<bool> OnSaveAsync(SchoolClass studentInfo, ItemChangedType changedType)
{
if (changedType.ToString() == "Update")
{
var addResult = await _httpClient.PutAsJsonAsync($"api/SchoolClass/UpdateClass/{studentInfo.ClassID}", studentInfo).ConfigureAwait(false);
if (UtilityBusiness.CheckResponse(addResult))
{
return await Task.FromResult(true);
}
else
{
return await Task.FromResult(false);
}
}
else if (changedType.ToString() == "Add")
{
var addResult = await _httpClient.PostAsJsonAsync("api/SchoolClass/CreateClass", studentInfo).ConfigureAwait(false);
if (UtilityBusiness.CheckResponse(addResult))
{
return await Task.FromResult(true);
}
else
{
return await Task.FromResult(false);
}
}
return await Task.FromResult(true);
}
/// <summary>
/// 数据删除
/// </summary>
/// <param name="items">items</param>
/// <returns></returns>
private async Task<bool> OnDeleteAsync(IEnumerable<SchoolClass> items)
{
var deleteSuccessNum = 0;
var schoolClassList = items.ToList();
foreach (var item in schoolClassList)
{
var delResult = await _httpClient.DeleteAsync($"api/SchoolClass/DeleteClass/{item.ClassID}").ConfigureAwait(false);
if (UtilityBusiness.CheckResponse(delResult))
{
deleteSuccessNum++;
}
}
if (deleteSuccessNum > 0)
{
return await Task.FromResult(true);
}
else
{
return await Task.FromResult(false);
}
}
}
后端API接口

using Entity;
using Microsoft.AspNetCore.Mvc;
using Utility;
namespace WebApi.Controllers
{
/// <summary>
/// 学校班级管理
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class SchoolClassController : ControllerBase
{
private readonly SQLiteAsyncHelper<SchoolClass> _schoolClassHelper;
/// <summary>
/// 依赖注入
/// </summary>
/// <param name="schoolClassHelper">schoolClassHelper</param>
public SchoolClassController(SQLiteAsyncHelper<SchoolClass> schoolClassHelper)
{
_schoolClassHelper = schoolClassHelper;
}
/// <summary>
/// 班级创建
/// </summary>
/// <param name="schoolClass">创建班级信息</param>
/// <returns></returns>
[HttpPost]
public async Task<ApiResponse<int>> CreateClass([FromBody] SchoolClass schoolClass)
{
try
{
var querySchoolClass = await _schoolClassHelper.QuerySingleAsync(c => c.ClassName == schoolClass.ClassName).ConfigureAwait(false);
if (querySchoolClass != null)
{
return new ApiResponse<int>
{
Success = false,
Message = $"创建班级失败,班级{schoolClass.ClassName}已存在"
};
}
schoolClass.CreateTime = DateTime.Now;
int insertNumbers = await _schoolClassHelper.InsertAsync(schoolClass);
if (insertNumbers > 0)
{
return new ApiResponse<int>
{
Success = true,
Message = "创建班级成功"
};
}
else
{
return new ApiResponse<int>
{
Success = false,
Message = "创建班级失败"
};
}
}
catch (Exception ex)
{
return new ApiResponse<int>
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 获取所有班级信息
/// </summary>
[HttpGet]
public async Task<ApiResponse<List<SchoolClass>>> GetClass()
{
try
{
var classes = await _schoolClassHelper.QueryAllAsync().ConfigureAwait(false);
return new ApiResponse<List<SchoolClass>>
{
Success = true,
Data = classes
};
}
catch (Exception ex)
{
return new ApiResponse<List<SchoolClass>>
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 根据班级ID获取班级信息
/// </summary>
/// <param name="classId">班级ID</param>
/// <returns></returns>
[HttpGet("{classId}")]
public async Task<ApiResponse<SchoolClass>> GetClass(int classId)
{
try
{
var schoolClass = await _schoolClassHelper.QuerySingleAsync(c => c.ClassID == classId).ConfigureAwait(false);
if (schoolClass != null)
{
return new ApiResponse<SchoolClass>
{
Success = true,
Data = schoolClass
};
}
else
{
return new ApiResponse<SchoolClass>
{
Success = false,
Message = "班级不存在"
};
}
}
catch (Exception ex)
{
return new ApiResponse<SchoolClass>
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 更新班级信息
/// </summary>
/// <param name="classId">班级ID</param>
/// <param name="updatedClass">更新的班级信息</param>
/// <returns></returns>
[HttpPut("{classId}")]
public async Task<ApiResponse<int>> UpdateClass(int classId, [FromBody] SchoolClass updatedClass)
{
try
{
var existingClass = await _schoolClassHelper.QuerySingleAsync(c => c.ClassID == classId).ConfigureAwait(false);
if (existingClass != null)
{
existingClass.ClassName = updatedClass.ClassName;
var updateResult = await _schoolClassHelper.UpdateAsync(existingClass).ConfigureAwait(false);
if (updateResult > 0)
{
return new ApiResponse<int>
{
Success = true,
Message = "班级信息更新成功"
};
}
else
{
return new ApiResponse<int>
{
Success = false,
Message = "班级信息更新失败"
};
}
}
else
{
return new ApiResponse<int>
{
Success = false,
Message = "班级不存在"
};
}
}
catch (Exception ex)
{
return new ApiResponse<int>
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 班级删除
/// </summary>
/// <param name="classId">班级ID</param>
/// <returns></returns>
[HttpDelete("{classId}")]
public async Task<ApiResponse<int>> DeleteClass(int classId)
{
try
{
var deleteResult = await _schoolClassHelper.DeleteAsync(classId).ConfigureAwait(false);
if (deleteResult > 0)
{
return new ApiResponse<int>
{
Success = true,
Message = "班级删除成功"
};
}
else
{
return new ApiResponse<int>
{
Success = true,
Message = "班级删除失败"
};
}
}
catch (Exception ex)
{
return new ApiResponse<int>
{
Success = false,
Message = ex.Message
};
}
}
}
}
接口对接所遇问题及其解决方案
跨源请求 (CORS)问题

在API服务端启用跨源请求 (CORS):
调用 UseCors 扩展方法并指定 PolicyCorsName CORS 策略。UseCors 添加 CORS 中间件。对 UseCors 的调用必须放在 UseRouting 之后,但在 UseAuthorization 之前。
Program.cs添加如下代码(注意中间件顺序)
var builder = WebApplication.CreateBuilder(args);
var PolicyCorsName = "EasySQLitePolicy";
builder.Services.AddCors(option =>
{
option.AddPolicy(PolicyCorsName, builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
app.UseCors(PolicyCorsName);
app.UseAuthorization();
app.MapControllers();
app.Run();
System.Text.Json 反序列化时间异常问题
异常:
Microsoft.AspNetCore.Components.Web.ErrorBoundary[0]
System.Text.Json.JsonException: The JSON value could not be converted to System.DateTime. Path: $.Data[0].CreateTime | LineNumber: 0 | BytePositionInLine: 113.
---> System.FormatException: The JSON value is not in a supported DateTime format.
at System.Text.Json.ThrowHelper.ThrowFormatException(DataType dataType)
at System.Text.Json.Utf8JsonReader.GetDateTime()
at System.Text.Json.Serialization.Converters.DateTimeConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
异常原因:
System.Text.Json 时间是认标准的. yyyy-MM-ddTHH:mm:ss 中间得有个T
解决方案:
注释掉服务端对时间日期类型默认格式化处理!

DotNetGuide技术社区交流群
- DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目框架推荐、求职和招聘资讯、以及解决问题的平台。
- 在DotNetGuide技术社区中,开发者们可以分享自己的技术文章、项目经验、学习心得、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。
- 我们致力于构建一个积极向上、和谐友善的.NET技术交流平台。无论您是初学者还是有丰富经验的开发者,我们都希望能为您提供更多的价值和成长机会。
参考文章
- 在 ASP.NET Core 中启用跨源请求 (CORS):https://learn.microsoft.com/zh-cn/aspnet/core/security/cors?view=aspnetcore-8.0
七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接的更多相关文章
- Docker技术入门与实战 第二版-学习笔记-7-数据管理(volume)
Docker 数据管理 为什么要进行数据管理呢?因为当我们在使用container时,可能会在里面创建一些数据或文件,但是当我们停掉或删除这个容器时,这些数据或文件也会同样被删除,这是我们并不想看见的 ...
- C#操作Sqlite快速入门及相关工具收集
Sqlite不需要安装即可使用.Sqlite是不是那个System.Data.SQLite.DLL临时创建了数据库引擎? 1.新建一个WinForm项目,引用System.Data.SQLite.DL ...
- 【图像处理】OpenCV+Python图像处理入门教程(七)图像形态学操作
图像形态学主要从图像内提取分量信息,该分量信息通常对表达图像的特征具有重要意义.例如,在车牌号码识别中,能够使用形态学计算其重要特征信息,在进行识别时,只需对这些特征信息运算即可.图像形态学在目标视觉 ...
- 七、Android学习第六天——SQLite与文件下载(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 七.Android学习第六天——SQLite与文件下载 SQLite SQ ...
- SQLite入门与分析(二)---设计与概念(续)
SQLite入门与分析(二)---设计与概念(续) 写在前面:本节讨论事务,事务是DBMS最核心的技术之一.在计算机科学史上,有三位科学家因在数据库领域的成就而获ACM图灵奖,而其中之一Jim G ...
- SQLite 入门教程(三)好多约束 Constraints(转)
转于: SQLite 入门教程(三)好多约束 Constraints 一.约束 Constraints 在上一篇随笔的结尾,我提到了约束, 但是在那里我把它翻译成了限定符,不太准确,这里先更正一下,应 ...
- SQLite 入门教程(四)增删改查,有讲究 (转)
转于: SQLite 入门教程(四)增删改查,有讲究 一.插入数据 INSERT INTO 表(列...) VALUES(值...) 根据前面几篇的内容,我们可以很轻送的创建一个数据表,并向其中插入一 ...
- C#操作SQLite方法实例详解
用 C# 访问 SQLite 入门(1) CC++C#SQLiteFirefox 用 C# 访问 SQLite 入门 (1) SQLite 在 VS C# 环境下的开发,网上已经有很多教程.我也是从 ...
- Java操作Sqlite数据库-jdbc连接
Java操作Sqlite数据库步骤: 1. 导入Sqlite jdbc 本文使用sqlite-jdbc-3.7.2.jar,下载地址 http://pan.baidu.com/s/1kVHAGdD 2 ...
- SQLite入门语句之HAVING和DISTINCT
一.SQLite入门语句之HAVING HAVING 子句允许指定条件来过滤将出现在最终结果中的分组结果. WHERE 子句在所选列上设置条件,而 HAVING 子句则在由 GROUP BY 子句创建 ...
随机推荐
- 06.Android之消息机制问题
目录介绍 6.0.0.1 谈谈消息机制Hander作用?有哪些要素?流程是怎样的? 6.0.0.2 为什么一个线程只有一个Looper.只有一个MessageQueue,可以有多个Handler? 6 ...
- 介绍几款WPF应用的UI库
在WPF中对于前端页面的书写,我们有现成的UI类库,不需要我们自己再去写 我这里介绍几款 1.MahApps 官网 https://mahapps.com/ 使用,在App.xaml中添加 <A ...
- 记录--ECharts — 饼图相关功能点(内环、外环、环形间隔、环形文字、轮播动画)
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 记录一下在公司遇到的一些功能,以及相关实现 以上的内容我花了一周时间去实现的,自己也觉得时间很长,但主要因为很少使用ECharts,导致使 ...
- OpenLayers绘制热力图 代码记录
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 做地图开发,往往需要掌握专题地图制作的技能.今天用OpenLayers6来做一个热力图的效果. 页面效果: 代码部分: <!DOCT ...
- TP6框架--CRMEB学习笔记:项目初始化+环境配置
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近在研究一个基于TP6的框架CRMEB,这里分享下我的开发心得 首先要获取原始项目文件 这里是git地址 https://gitee.c ...
- 第145篇:js设计模式注册模式及相应实践
好家伙, 0.索引 在阿里的低开项目中,使用这种形式去注册组件,我不禁好奇,这到底是个什么玩意 1.概念 在 JavaScript 中,注册模式(Registry Pattern)是一种设计模式 ...
- date_histogram,es按照时间分组统计
日期直方图聚合(date_histogram) 与histogram相似,es中内部将日期表示为一个long值,所以有时候可以用histogram来达到相同的目的,但往往没有date_histogra ...
- KingbaseES V8R6集群运维案例之---securecmdd工具usePAM配置
案例说明: 默认在部署securecmdd工具后,在配置文件securecmdd_config中配置参数usePAM=yes,在有的系统主机环境,会导致securecmd客户端连接失败. 适用版本: ...
- archlinux xfce未中文化 goldendict不能显示中文
下载个中文字体包就好了 https://wiki.archlinuxcn.org/wiki/简体中文本地化
- Java面试题【4】
28)Java 栈和堆的区别 1 栈:为编译器自动分配和释放,如函数参数.局部变量.临时变量等等 2 堆:为成员分配和释放,由程序员自己申请.自己释放.否则发生内存泄露.典型为使用new申请的堆内容. ...