使用 Blazor 来实现前后端统一使用 C# 开发,用 C# 写前端,想想就刺激😃

# 什么是 Blazor

Blazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架:

  • 使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI。
  • 共享使用 .NET 编写的服务器端和客户端应用逻辑。
  • 将 UI 呈现为 HTML 和 CSS,以支持众多浏览器,其中包括移动浏览器。
  • 与新式托管平台(如 Docker)集成。

使用 .NET 进行客户端 Web 开发可提供以下优势:

  • 使用 C# 代替 JavaScript 来编写代码。
  • 利用现有的 .NET 库生态系统。
  • 在服务器和客户端之间共享应用逻辑。
  • 受益于 .NET 的性能、可靠性和安全性。
  • 在 Windows、Linux 和 macOS 上使用 Visual Studio 保持高效工作。
  • 以一组稳定、功能丰富且易用的通用语言、框架和工具为基础来进行生成

Blazor 应用基于组件。 Blazor 中的组件是指 UI 元素,例如页面、对话框或数据输入窗体。

组件是内置到 .NET 程序集的 .NET C# 类,它们用于:

  • 定义灵活的 UI 呈现逻辑。
  • 处理用户事件。
  • 可以嵌套和重用。
  • 可作为 Razor 类库NuGet 包共享和分发。

# 创建项目

此文使用 vs2019 .net core 3.1

这里我从空项目开始创建。

image-20210406092424290

# 配置项目

# 配置 Starup

ConfigureServices
public void ConfigureServices(IServiceCollection services)
 {
     // 启用 RazorPage
     services.AddRazorPages();
     // 启用服务端 Blazor
     services.AddServerSideBlazor();
 }
Configure
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            // 启用静态文件
            app.UseStaticFiles();
            // 启用路由
            app.UseRouting();
            // 启用终端节点
            app.UseEndpoints(endpoints =>
            {
                // 配置实时连接 SinglR
                endpoints.MapBlazorHub();
                // 配置 Blazor 的主页
                endpoints.MapFallbackToPage("_Host");
            });
        }

# 配置通用依赖项

创建_Imports.razor 文件加入内容

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using BlazorAppGo
@using BlazorAppGo.Shared

# 书写_Host 主页

@page "/"
@namespace BlazorAppGo.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BlazorAppGo</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
</head>
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>
    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>

# Razor 页面

在 Blazor 中,每一个页面以.cshtml 结尾,页面可以绑定一个模型类用户数据操作。

# Rezor 布局

在 Blazor 中,在 Shared 文件夹下书写布局和通用组件。创建.razor 结尾的文件写入如下文件。

@inherits LayoutComponentBase
<div class="sidebar">
    <NavMenu />
</div>
<div class="main">
    <div class="top-row px-4">
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>
    <div class="content px-4">
        @Body
    </div>
</div>

在布局文件中,使用 @inherits LayoutComponentBase 来继承自 LayoutComponentBase 标注为布局文件

# App.razor

创建 App.razor 文件用于配置路由情况和默认布局

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

# Razor 组件

创建.razor 结尾的文件作为 Razaor 组件,组件可以在其他页面或组件中复用

直接使用 <组件名称> 当作 html 元素使用即可。

# CURD 小例子

# EF 连接 Sqlite

在数据库连接上,这里我使用比较简单的 sqlite

image-20210406154848381

安装相关包

# 实体配置

using Microsoft.EntityFrameworkCore;
namespace BlazorAppGo.Models
{
    public class AppDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }
}

# 注入

services.AddDbContext<AppDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("sqlite")));

# appsetting

// 连接字符串
  "ConnectionStrings": {
    "sqlite": "Data Source=./myData.db"
  },

# service

using BlazorAppGo.Models;
using System.Collections.Generic;
using System.Linq;
namespace BlazorAppGo.Services
{
    public class UserService
    {
        private readonly AppDbContext dbContext;
        public UserService(AppDbContext dbContext)
        {
            this.dbContext = dbContext;
        }
        public void Add(User user)
        {
            dbContext.AddAsync(user);
            dbContext.SaveChanges();
        }
        public void Update(int id, User data)
        {
            var user = dbContext.Users.FirstOrDefault(x => x.Id == id);
            user.Name = data.Name;
            dbContext.Update(user);
            dbContext.SaveChanges();
        }
        public void Remove(int id)
        {
            dbContext.Remove(dbContext.Users.FirstOrDefault(x => x.Id == id));
            dbContext.SaveChanges();
        }
        public List<User> List()
        {
            return dbContext.Users.ToList();
        }
        public User FindOne(int id)
        {
            return dbContext.Users.FirstOrDefault(x => x.Id == id);
        }
    }
}
注入
/*
             * AddSingleton 添加唯一注入
             * AddScoped 添加会话注入,每次会话不同
             * AddTransient 添加不同实例,每次请求都不同
             */
            services.AddScoped<AppDbContext>();
            services.AddScoped<UserService>();

# Razor 界面

# userList

@page "/userList"
@using BlazorAppGo.Models
@using BlazorAppGo.Services
@inject BlazorAppGo.Services.UserService userService
@inject NavigationManager NavigationManager
<button class="btn btn-primary" @onclick="Add">新增</button>
<div class="row">
    <div class="col-1">ID</div>
    <div class="col-2"><input type="text" class="form-control" @bind-value="id"></div>
    <div class="col-1">Name</div>
    <div class="col-2"><input type="text" class="form-control" @bind-value="name"></div>
</div>
<table class="table" style="margin-top:15px">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Options</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var user in users)
        {
            <tr>
                <td>@user.Id</td>
                <td>@user.Name</td>
                <td>
                    <button class="btn btn-info" @onclick="@(e=>Info(user.Id))">详细</button>
                    <a href="/userUpdate/@user.Id" class="btn btn-warning">修改</a>
                    <button class="btn btn-danger" @onclick="@(e=>Del(user.Id))">删除</button>
                </td>
            </tr>
        }
    </tbody>
</table>
<h3>@cid</h3>
@code {
    private List<User> users;
    private int id;
    private string name;
    private int cid;
    protected override void OnInitialized()
    {
        users = userService.List();
    }
    private void Add()
    {
        userService.Add(new User { Id = id, Name = name });
        users = userService.List();
        // 重新绑定 刷新页面
        //StateHasChanged();
        // 刷新
        ShouldRender();
    }
    private void Info(int id)
    {
        NavigationManager.NavigateTo($"/userInfo/{id}");
    }
    private void Del(int id)
    {
        userService.Remove(id);
        users = userService.List();
        ShouldRender();
    }
}

# userUpdate

@page "/userUpdate/{Id}"
@inject BlazorAppGo.Services.UserService userService
<button class="btn btn-primary" @onclick="Save">保存</button>
<div class="row">
    <div class="col-1">ID</div>
    <div class="col-2"><input type="text" class="form-control" @bind-value="Id"></div>
    <div class="col-1">Name</div>
    <div class="col-2"><input type="text" class="form-control" @bind-value="name"></div>
</div>
@if (show)
{
    <h4>修改成功</h4>
}
<a href="/userList">返回</a>
@code {
    [Parameter]
    public string Id { get; set; }
    string name;
    bool show;
    protected override void OnInitialized()
    {
        var user = userService.FindOne(int.Parse(Id));
        name = user.Name;
    }
    void Save()
    {
        userService.Update(int.Parse(Id), new Models.User { Id = int.Parse(Id), Name = name });
        show = true;
    }
}

# userInfo

@page "/userInfo/{Id}"
@inject BlazorAppGo.Services.UserService userService
<h1>UserInfo @Id</h1>
<h2>Id:@Id</h2>
<h2>Name:@Name</h2>
<a href="/userList" class="btn btn-primary">返回</a>
@code{
    [Parameter]
    public string Id { get; set; }
    private string Name;
    protected override void OnInitialized()
    {
        var user = userService.FindOne(int.Parse(Id));
        Name = user.Name;
    }
}

# 效果

效果

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Fasty 微信支付

微信支付

Fasty 支付宝

支付宝

Fasty 贝宝

贝宝