初次接触Blazor项目,很喜欢这个框架,使我等C#程序员不必再花时间和精力去学习各种前端UI框架。手头的项目正好要做一个界面丰富的前端,于是决定用Blazor Server来做。
项目分为前端和后台管理,后台管理已经用ASP.NET Core MVC完成。前端和后台共享一个数据库,前端主要是查询和展示,有少量提交和更新。
微软在github中的AspNetCore源码中提供了Blazor Server使用EF Core的源码:下载
在源码目录aspnetcore\blazor\common\samples\版本号\BlazorServerEFCoreSample\BlazorServerDbContextExample\Data中复制以下三个文件到自己的项目中:
DbContextFactory.cs
FactoryExtensions.cs
IDbContextFactory.cs
FactoryExtensions中包含一个扩展方法AddDbContextFactory,用来在StartUp中注册工厂方法。DbContextFactory则包含了创建EF上下文的工厂方法。
首先在StartUp中注册工厂方法
//以下代码添加到StartUp的ConfigureServices方法中
services.AddDbContextFactory<CloudContext>(options =>
options.UseMySql(Configuration.GetConnectionString("CloudContextConnection")));
//DeviceService是访问数据的类,需要在这里注册
services.AddSingleton<DeviceService>();
接下来创建DeviceService,用来访问数据库中的Device表
public class DeviceService
{
private readonly IServiceProvider provider;
public DeviceService(IServiceProvider provider)
{
this.provider = provider;
}
public Task<Service[]> GetAllDevices()
{
using var context = new DbContextFactory<CloudContext>(provider).CreateDbContext();
return Task.FromResult(context.Devices.ToArray());
}
}
blazor的组件Device.razor的代码:
@inject DeviceService deviceService
<div>
<ul>
@foreach (var device in devices)
{
<li>
@device.deviceName
</li>
}
</ul>
</div>
@code {
private Device[] devices;
protected override async Task OnInitializedAsync()
{
devices= await deviceService.GetAllDevices();
}
}
解决了数据库问题,还要处理用户授权验证问题。本项目的前端是需要用户注册登录,并根据是否登录和用户身份来展现不同内容。
Blazor支持ASP.NET Core的Identity基架,而且AuthorizeView组件可以根据是否授权来呈现不同内容。
按照微软官方文档的教程,向Blazor项目添加Identity基架却总是不能成功,出现各种服务未注册、不能解析DB上下文等异常,经过反复尝试,最后总算成功了。
首先,重新创建Blazor项目,创建时,点击右边的身份验证下的“更改”,然后选择“个人用户账户”。
项目创建成功后,Ctrl+F5运行,可以发现Identity基架没有任何问题,注册登录等功能都可以使用。但项目模板使用的是默认的SqlExpress数据库,登录注册等页面也是完全被封装而无法更改,所以我们还要加以改造。
右键单击Blazor项目,在弹出菜单中选择:添加->新搭建基架的项目,在模板对话框中选择“标识”,最后按图所示点“确定”。
完成后查看项目
这时我们可以自由修改注册登录的页面内容了。
不过项目现在还不能运行,会有一些错误。
打开Register.cshtml.cs,把和IEmailSender相关的代码都注释掉。这是一个验证Email的服务,但是我们并没有实现它,如果需要这个服务,你必须得自己实现发送及验证Email的代码,并在StartUp中注册你的服务:
services.AddTransient<IEmailSender,YourEmailSender>();
。如果使用了自定义的用户类来代替IdentityUser,那么再打开Pages\Shared_Layout.cshtml,把嵌入_LoginPartial.cshtml的代码
await Html.RenderPartialAsync("_LoginPartial");
也注释掉,因为这里始终会报一个无法解析IdentityUser的错误,没有找到原因。好在我们并不需要_LoginPartial.cshtml。
接下来修改StartUp的ConfigureServices方法,修改后的代码类似如下:
public void ConfigureServices(IServiceCollection services)
{
//注册访问EF上下文的工厂方法,这行和下一行的顺序一定不能错
services.AddDbContextFactory<CloudContext>(options =>
options.UseMySql(Configuration.GetConnectionString("CloudContextConnection")));
//添加Identity基架的数据库支持
services.AddDbContext<CloudContext>(options =>
options.UseMySql(Configuration.GetConnectionString("CloudContextConnection")));
//如果使用默认的IdentityUser类进行身份验证,则取消这一行的注释,注释掉下面一行
//services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
// .AddEntityFrameworkStores<CloudContext>();
//使用自定义的User和Role类进行身份验证
services.AddIdentity<User, Role>().AddEntityFrameworkStores<CloudContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
//如果使用IdentityUser类,把User改为IdentityUser
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<User>>();
services.AddSingleton<DeviceService>();
}
至此大功告成。注册登录等页面可以根据项目的实际需求重写。