菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
作者:陈超超
Ant Design Blazor 项目贡献者,拥有十多年从业履历,历久基于.Net手艺栈举行架构与开发产物的事情,现就职于正泰团体。
邮箱:timchen@live.com
迎接列位读者有任何问题联系我,我们共同进步。
上一次课程我们完成了ToDo应用的界面制作,这次我们要将客户端的数据写入数据库,并从数据库中读取我们需要的数据。
数据交互历程
我们先看一下从客户端到数据库的流程
Blazor
Blazor客户端就是我们上节课做的ToDo程序。
HttpClient
HttpClient就是我们完成网络通讯用的组件,对于这类组件我们希望在一个应用中只组织一次,这样制止重复分配资源,因此我们在 Program.cs 中举行注册。
{
{
builder.Services.AddScoped(sp => HttpClient { BaseAddress = Uri(builder.HostEnvironment.BaseAddress) });
}
}
BaseAddress 为基地址,这样我们使用时,Url只需要传入相对地址即可,此处默以为当前主机的地址。
DefaultRequestHeaders 默认HTTP请求头参数
Timeout 毗邻超时参数
上面通过服务注入的方式实现了 HttpClient 全局共享(单例),那么若何使用服务?这里我们就需要引入一下“依赖关系注入 (DI)”的观点。
DI是一种手艺,基本原理是把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入。应用可通过将内置服务注入组件来使用这些服务。应用还可界说和注册自界说服务,并通过 DI 使其在整个应用中可用。
该手艺在 Blazor 应用中常用于以下两个方面:
服务生存期决议了服务何时建立,何时销毁,有三种模式:
Scoped : Blazor WebAssembly 应用当前没有 DI 局限的观点。已注册 Scoped 的服务的行为与 Singleton 服务类似。然则,Blazor Server 托管模子支持 Scoped 生存期。在 Blazor Server 应用中, Scoped 服务注册的局限为“毗邻”。因此,纵然当前意图是在浏览器中运行客户端,对于局限应限定为当前用户的服务来说,首选使用 Scoped 服务。
Singleton :DI 建立服务的单个实例。需要 Singleton 服务的所有组件都市吸收统一服务的实例。
Transient :每当组件从服务容器获取 Transient 服务的实例时,它都市吸收该服务的新实例。
这里的 HttpClient 使用了 AddScoped 方式,那么就是当前局限内使用统一个实例,由于项目是 Blazor WebAssembly 模式,以是相当于单例服务。
ASP.Net Core
我用ASP.Net Core项目给Blazor应用提供WebAPI接口
官方文档:https://docs.microsoft.com/zh-cn/aspnet/core
项目结构如下
这里设置了我们调试的方式,端口等,相对于通俗的Web项目多了 inspectUri 属性,具有以下作用:
- 使 IDE 能够检测到该应用为 Blazor WebAssembly 应用。
- 指示剧本调试基础结构通过 Blazor 的调试署理毗邻到浏览器。
- 已启动的浏览器 (browserInspectUri) 上 WebSocket 协议 (wsProtocol)、主机 (url.hostname)、端口 (url.port) 和检查器 URI 的占位符值由框架提供。
: {
: {
: ,
: ,
: ,
: {
:
}
},
}
- Controllers
控制器( Controller )放在这里,站点的路由表是通过遍历项目中带有 ApiControllerAttribute (基类 ControllerAttribute )的类,然后寻找内里的方式实现,他和Blazor的路由表建立方式上有点相似。
[ ]
[ ]
:
Route 界说了路由花样,上例中 [controller]/[action] 意为使用 Controller 和 action 的名称作为路由地址,这样写可以省去每个 action 上符号路由名字的贫苦。
- Pages
存放页面文件的位置,由于我们的项目页面所有使用Blazor构建,以是用不到此文件夹,因此这里就不做先容了。
- appsettings.json
站点的设置文件,我们的项目就用到了数据库链接字符串设置
- Program.cs
应用的 Main 函数在这里,这里完成了 Host 的建立与启动
- Startup.cs
启动类,项目启动时的服务注册,设置等事情都在此处完成
ConfigureServices 使用此方式将服务添加到容器。
Configure 使用此方式来设置HTTP请求管道。
{
app.UseBlazorFrameworkFiles;
app.UseStaticFiles;
app.UseRouting;
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages;
endpoints.MapControllers;
endpoints.MapFallbackToFile( );
});
}
app.UseBlazorFrameworkFiles; 设置应用程序提供Blazor WebAssembly框架文件,默认根路径为 / ,也可以自界说路径前缀
endpoints.MapControllers; 添加控制器( Controller )的路由。
endpoints.MapFallbackToFile("index.html"); 添加默认路由地址是 index.html 。
EF Code
所有的数据我们需要保留入数据库,这里我选择使用EF Core作为我们的数据接见手艺
官方文档:https://docs.microsoft.com/zh-cn/ef/
EF Core部门特点
- Entity Framework (EF) Core 是轻量化、可扩展、开源和跨平台版的数据接见手艺。
- EF Core可用作工具关系映射程序 (O/RM),能让我们用工具来处置数据库,使用Linq举行查询,这样我们就可以不用编写大量SQL代码了。
- EF Core 支持多个数据库引擎,好比MySQL、SQLite等。
他支持接纳Code Firs或者Database First两种模式
- Code Firs 用代码编写工具关系,然后通过它建立数据库。
- Database First 可以提供现有数据库,反向天生工具映射 。
Database
数据库我选择SQL Server,使用全套微软手艺栈工具链使用体验比较好,固然我们也可以选择其他数据库。
SQL Server产物家族中有一个SQL Server LocalDB的器械,它是SQL Server的一个超级精简版本,安装包只有几十MB(安装好后200+MB),它包含了数据库的基础功效,然则不支持联网,只能本机毗邻,对于小我私家开发资源占用少,强烈推荐,VS安装Web开发组件会默认安装此数据库。
毗邻时服务器名称默认是 (localdb)MSSQLLocalDB ,也可以使用 C:Program FilesMicrosoft SQL Server130ToolsBinnSqlLocalDB.exe 举行设置数据库实例
我们可以使用VS的SQL Server工具资源管理器来查看我们的数据库,不外我这里强烈推荐使用SQL Server Management Studio (SSMS) 的“数据库关系图”功效来维护数据库,可视化编辑表,主外键关系等,保留即更新数据库,这对于数据库优先的模式下开发异常友好,效率极高。
下图是我们ToDo应用使用的表结构。
代码实战
上面先容了数据交互的流程观点,接下来我们革新上回制作的ToDo项目。
引入和设置EF Code
我们先建立一个 ToDo.Entity 项目用于存储ORM映射以及EF的Context。
使用EF Core Power Tools工具建立代码注重:现在VS 16.8.4版本建立类库会默认使用.net core 3.1,需要手动修改成.net 5
由于我们上面已经把数据库设计完成了,以是我们接纳 Database First 模式建立EF相关的代码。
此处推荐一个从数据库到EF实体的代码天生扩展 EF Core Power Tools
扩展下载地址:https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools
选摘要毗邻的数据库。
选摘要添加的数据库工具。
设置 Context 的名称和命名空间等,下图是我常用设置。
EF Core Power Tools 天生的代码文件如下
appsettings.json中添加链接字符串
打开 ToDo.Serverappsettings.json 添加数据库毗邻字符串
: {
:
},
ConfigureServices中添加服务注册使用 EF Core Power Tools 天生的 TodoContext.cs 文件中就有默认的毗邻字符串,开发时想偷懒可以直接从这里复制。
打开 ToDo.ServerStartup.cs ,把 TodoContext 注册到 DbContext 中为,并设置毗邻字符串
services.AddDbContext<TodoContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString( ));
});
有时候我们需要在输出EF执行的SQL语句,这便于我们调试以及优化数据库,下面的设置就把EF日志输出到控制台
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole; });
{
services.AddDbContext<TodoContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString( )).UseLoggerFactory(loggerFactory);
});
}
功效实现
首先建立 ToDo.ServerControllersTaskController.cs 文件用于编写WebAPI接口,代码如下:
{
[ ]
[ ]
:
{
TodoContext Context;
{
Context = context;
}
}
}
通过依赖注入将 TodoContext 注入到当前类中。
1. 列出当天的所有代庖事情
ToDo.Server
TaskController.cs 中添加 GetToDayTask 方式用于返回当前待办数据。
[ ]
{
result = Context.Task.Where(x => x.PlanTime == DateTime.Now.Date);
QueryToDto(result).ToList;
}
[ ]
{
query.Select(x => TaskDto
{
TaskId = x.TaskId,
Title = x.Title,
Deion = x.Deion,
PlanTime = x.PlanTime,
Deadline = x.Deadline,
IsImportant = x.IsImportant,
IsFinish = x.IsFinish,
});
}
ToDo.Client
添加 PagesToDay.razor.cs 类文件,VS会自动将 ToDay.razor 与 ToDay.razor.cs 折叠到一起。
在类界说中增添 partial 关键字,声明类为局部类,你可以明白成 ToDay.razor 与 ToDay.razor.cs 中的代码都属于统一个类,只是放在差别文件中,编译器编译时会将他们合并到一起后举行编译。
接着做一下代码迁徙
- 将 PagesToDay.razor 文件 @code{} 中的代码剪切到 ToDay.razor.cs
- 将 PagesToDay.razor 文件 @inject 代码接纳 [Inject] public MessageService MsgSrv { get; set; } 这样的花样等价的迁徙到 ToDay.razor.cs
这样做我们可以实现界面代码与营业代码分开在差别的文件中,利便整理代码,提高代码可读性。
后续其他页面我默认完成了建立局部类的操作,不再赘述。
ToDay.razor.cs 中添加 HttpClient 的依赖注入,用于向服务端提议Http请求
[ ] HttpClient Http { ; ; }
项目中其他类若是使用到 HttpClient ,我默认完成了依赖注入,不再赘述。
广告时间
Blazor 和 Ant Design Blazor 中有许多服务,我们经常在差别的地方需要注入,为了编码利便,我们提供了一个VS扩展快速插入常用服务代码段,安装地址:https://marketplace.visualstudio.com/items?itemName=TimChen44.AntDesignBlazorSnippets
修改 OnInitializedAsync 方式的代码
= List<TaskDto>;
isLoading = ;
{
isLoading = ;
taskDtos = Http.GetFromJsonAsync<List<TaskDto>>( );
isLoading = ;
.OnInitializedAsync;
}
Http.GetFromJsonAsync<List<TaskDto>> 使用 HttpGet 模式请求数据,这里使用 await 举行异步守候,充分利用 await 可以极大的简化代码量。
isLoading 是载入状态,网络通讯肯定有延迟,制止白屏,我们在载入前后划分改变载入状态,同时修改 ToDay.razor 代码添加 Spin 组件用于显示载入效果。
@foreach (var item in taskDtos)
2. 添加代庖
ToDo.Server
TaskController.cs 中添加 SaveTask 方式用于保留新的待办内容
[ ]
{
Entity.Task entity;
(dto.TaskId == Guid.Empty)
{
entity = Entity.Task;
entity.TaskId = Guid.NewGuid;
Context.Add(entity);
}
{
entity = Context.Task.FirstOrDefault(x => x.TaskId == dto.TaskId);
}
entity.Title = dto.Title;
entity.Deion = dto.Deion;
entity.PlanTime = dto.PlanTime;
entity.Deadline = dto.Deadline;
entity.IsImportant = dto.IsImportant;
entity.IsFinish = dto.IsFinish;
Context.SaveChanges;
entity.TaskId;
}
我通过判断 dto.TaskId 的值,直接将新增与更新写在一个接口中,这样可以复用代码。
此处可以使用 AutoMapper 库来简化赋值历程,这将在未来的章节中详细先容
ToDo.Client
ToDay.razor.cs 文件修改 OnInsert 方式相关的代码
TaskDto newTask = TaskDto { PlanTime = DateTime.Now.Date };
[ ] MessageService MsgSrv { ; ; }
isNewLoading = ;
{
(e.Code == )
{
( .IsNullOrWhiteSpace(newTask.Title))
{
MsgSrv.Error( );
;
}
isNewLoading = ;
result = Http.PostAsJsonAsync<TaskDto>( , newTask);
(result.IsSuccessStatusCode)
{
newTask.TaskId = result.Content.ReadFromJsonAsync<Guid>;
taskDtos.Add(newTask);
newTask = TaskDto { PlanTime = DateTime.Now.Date };
}
{
MsgSrv.Error( );
}
isNewLoading = ;
StateHasChanged;
}
}
ToDay.razor 文件增添保留时守候组件
通过 Http.PostAsJsonAsync 挪用 api/Task/SaveTask 将 newTask 内容提交到后端并保留,返回的 HttpResponseMessage 包含了状态编码等,若是乐成就在界面上显示新的待办,失败就提醒错误
MessageService 全局展示操作反馈信息。
3. 编辑待办组件文档地址:https://ant-design-blazor.github.io/zh-CN/components/message
ToDo.Server
TaskController.cs 中添加 GetTaskDto 方式用于获取待办信息
{
result = Context.Task.Where(x => x.TaskId == taskId);
QueryToDto(result).FirstOrDefault;
}
ToDo.Client
TaskInfo.razor 文件中增添 Spin 与 @if 代码
@if (taskDto != null)
{
,,菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
保留
作废
}
TaskInfo.razor.cs 添加下面代码
: < , >
{
[ ]
HttpClient Http { ; ; }
[ ]
MessageService MsgSvr { ; ; }
TaskDto taskDto;
isLoading = ;
{
taskDto = Http.GetFromJsonAsync<TaskDto>( );
.OnInitializedAsync;
}
{
result = Http.PostAsJsonAsync<TaskDto>( , taskDto);
(result.StatusCode == System.Net.HttpStatusCode.OK)
{
.CloseAsync(taskDto);
}
{
MsgSvr.Error( );
}
}
{
.CloseAsync( );
}
}
ToDay.razor.cs 中的 OnCardClick 方式更新
[ ] DrawerService DrawerSrv { ; ; }
{
result = DrawerSrv.CreateDialogAsync<TaskInfo, TaskDto, TaskDto>(task, title: task.Title, width: );
(result == ) ;
index = taskDtos.FindIndex(x => x.TaskId == result.TaskId);
taskDtos[index] = result;
InvokeAsync(StateHasChanged);
}
DrawerSrv.CreateDialogAsync 相对于 DrawerSrv.CreateAsync 简化了挪用方式,默认将抽屉的 CloseAsync 参数返回,这就简化了每次使用抽屉时需要注册 CloseAsync 事宜的贫苦,也让代码加倍清晰。
title: task.Title, width: 450 使用可选参数简化对抽屉的参数设置。
4. 修改主要水平DrawerService组件辅助文档:https://ant-design-blazor.github.io/zh-CN/components/drawer
ToDo.Server
TaskController.cs 中添加 SetImportant 方式用于修改 IsImportant 字段的值
[ ]
{
entity = Context.Task.FirstOrDefault(x => x.TaskId == req.TaskId);
entity.IsImportant = req.IsImportant;
Context.SaveChanges;
}
ToDo.Shared
添加 SetImportantReq 类用于 SetImportant 接口请求参数
{
Guid TaskId { ; ; }
IsImportant { ; ; }
}
ToDo.Client
ToDay.razor.cs 中的 OnStar 方式更新
{
req = SetImportantReq
{
TaskId = task.TaskId,
IsImportant = !task.IsImportant,
};
result = Http.PostAsJsonAsync<SetImportantReq>( , req);
(result.IsSuccessStatusCode)
{
task.IsImportant = req.IsImportant;
StateHasChanged;
}
}
5. 修改完成状态
ToDo.Server
TaskController.cs 中添加 SetFinish 方式用于修改 IsFinish 字段的值
[ ]
{
entity = Context.Task.FirstOrDefault(x => x.TaskId == req.TaskId);
entity.IsFinish = req.IsFinish;
Context.SaveChanges;
}
ToDo.Shared
添加 SetFinishReq 类用于 SetFinish 接口请求参数
{
Guid TaskId { ; ; }
IsFinish { ; ; }
}
ToDo.Client
ToDay.razor.cs 中的 OnFinish 方式更新
{
req = SetFinishReq
{
TaskId = task.TaskId,
IsFinish = !task.IsFinish,
};
result = Http.PostAsJsonAsync<SetFinishReq>( , req);
(result.IsSuccessStatusCode)
{
task.IsFinish = req.IsFinish;
StateHasChanged;
}
}
6. 删除代庖
ToDo.Server
TaskController.cs 中添加 DelTask 方式用于删除待办。
[ ]
{
Context.Task.Remove(Context.Task.Find(taskId));
Context.SaveChanges;
}
ToDo.Client
ToDay.razor.cs 中的 OnFinish 方式更新
[ ] ConfirmService ConfirmSrv { ; ; }
{
( ConfirmSrv.Show( , , ConfirmButtons.YesNo, ConfirmIcon.Info) == ConfirmResult.Yes)
{
taskDtos.Remove(task);
}
}
ConfirmService 可以快捷地弹出一个内置的确认框,类似于 Windows MessageBox。
7. 查询代庖ConfirmService组件辅助文档:https://ant-design-blazor.github.io/zh-CN/components/modal
ToDo.Server
TaskController.cs 中添加 GetSearch 方式用于修改 SetFinish 字段的值
[ ]
{
(req.PageIndex == ) req.PageIndex = ;
query = Context.Task.Where(x => x.Title.Contains(req.QueryTitle ?? ));
( sort req.Sorts)
{
(sort.SortOrder == )
query = query.OrderBy(sort.SortField + );
query = query.OrderBy(sort.SortField);
}
result = GetSearchRsp
{
Data = QueryToDto(query.Skip(--req.PageIndex * req.PageSize).Take(req.PageSize)).ToList,
Total = query.Count,
};
result;
}
if (req.PageIndex == 0) req.PageIndex = 1 吐槽最先:险些所有的UI框架页码都是从0最先,然则AntDesign规范的页码是从1最先的,然而没有载入数据时又是返回0,以是要特别注重。
OrderBy 使用了 System.Linq.Dynamic.Core 扩展包,它提供了一些动态的Linq支持,好比此处排序我传入的参数不是一个表达式,而是一个字符串,这样可以让代码灵活性大增。
ToDo.Shared
{
QueryTitle { ; ; }
PageIndex { ; ; }
PageSize { ; ; }
List<SortFieldName> Sorts { ; ; }
}
{
SortField { ; ; }
SortOrder { ; ; }
}
{
List<TaskDto> Data { ; ; }
Total { ; ; }
}
ToDo.Client
TaskSearch.razor 文件的代码
@page "/search"
@context.Title
@if (context.IsImportant)
{
主要
}
@if (context.IsFinish)
{
}
TaskSearch.razor.cs 文件的代码
[ ] HttpClient Http { ; ; }
isLoading = ;
List<TaskDto> datas = List<TaskDto>;
queryTitle;
total = ;
{
OnQuery( , , List<SortFieldName>);
}
{
OnQuery(
queryModel.PageIndex,
queryModel.PageSize,
queryModel.SortModel.Where(x => .IsNullOrEmpty(x.SortType.Name) == ).OrderBy(x => x.Priority)
.Select(x => SortFieldName { SortField = x.FieldName, SortOrder = x.SortType.Name }).ToList
);
}
{
isLoading = ;
req = GetSearchReq
{
QueryTitle = queryTitle,
PageIndex = pageIndex,
PageSize = pageSize,
Sorts = sort,
};
httpRsp = Http.PostAsJsonAsync<GetSearchReq>( , req);
result = httpRsp.Content.ReadFromJsonAsync<GetSearchRsp>;
datas = result.Data;
total = result.Total;
isLoading = ;
}
Search 带有查询按钮的文本框框
查看详细服务Search组件文档地址:https://ant-design-blazor.github.io/zh-CN/components/input
在 我的一天 与 所有 页面上均存在打开待办详情的功效需求,这时我们就可以自己做一个服务将双方的功效合并到一起。
添加 TaskDetailServices.cs 文件,加入以下代码
{
{
DrawerService DrawerSvr { ; ; }
{
DrawerSvr = drawerSvr;
}
{
taskItem = DrawerSvr.CreateDialogAsync<TaskInfo, TaskDto, TaskDto>(taskDto, title: taskDto.Title, width: );
(taskItem == ) ;
index = datas.FindIndex(x => x.TaskId == taskItem.TaskId);
datas[index] = taskItem;
}
}
}
TaskDetailServices(DrawerService drawerSvr) 只有 razor 文件可以使用 [Inject] 符号属性举行注入服务,通俗得类需要在组织函数中界说才气注入服务。
Program.cs 文件中注册 TaskDetailServices
builder.Services.AddScoped<TaskDetailServices>;
TaskSearch.razor 文件中添加详情按钮
详情
TaskSearch.razor.cs 插入以下代码,我们注入自界说的服务,使用服务中的方式打开编辑界面。
[ ] TaskDetailServices TaskSrv { ; ; }
{
TaskSrv.EditTask(taskDto, datas);
}
次回预告
网友评论
7条评论usdt无需实名买入卖出
回复他示意,高市辖内有744座工业锅炉,提早在去年底改善完成,相符排放尺度,污染物共削减6103公吨;国营企业及前20大企业配合互助,削减污染排放,2020年减量394公吨、4年减量约4855公吨,占高雄工厂总排放量9.14%;相较于去年,今年投入金额增添121亿,消减量增1155公吨。有课代表讲讲吗
皇冠会员线路(www.22223388.com)
回复ug环球官网(www.ugbet.us)
回复日本共同社22日报道称,卡特在由新美国安全中心于19日举办的一场会议上表达了该见解,并发布了视频。彼时卡特被问及日本等不在框架内的国家如何与AUKUS互动,他回答称AUKUS“并没有被设计成排他性的框架”,暗示了日本以及由英语圈5国构成的机密情报共享框架“五眼联盟”成员国加拿大、新西兰均有可能参加AUKUS。可以提建议不
皇冠正网(www.huangguan.us)
回复不错,太实在了
aLLbet代理开户(www.aLLbetgame.us)
回复2018年6月11日,允许馨被中国药科大学教务处公示拟推荐美国德雷赛尔大学实习生项目。很亲切呢
新2投注平台出租(www.hg9988.vip)
回复不是我吹,我能看三天
ug官方网站(www.ugbet.us)
回复我和同学都很喜欢呢