在Blazor中构建数据库应用程序——第1部分——项目结构和框架

目录

介绍

储存库和数据库

解决方案结构

设计哲学

UI结构

页面

ViewManager

Views

布局

表单

控件

CEC.Blazor.WASM.Client项目

index.html

CSS

Program.cs

ServiceCollectionExtensions.cs

CEC.Blazor.WASM.Server项目

WeatherForecastController.cs

ServiceCollectionExtensions.cs

CEC.Blazor.Server项目

Pages

Startup.cs

ServiceCollectionExtensions.cs

总结


介绍

本系列文章着眼于如何在Blazor中构建和构造一个真正的数据库应用程序。有6篇文章:

  1. 项目结构与框架
  2. 服务——构建CRUD数据层
  3. View组件——UI中的CRUD编辑和查看操作
  4. UI组件——构建HTML / CSS控件
  5. View组件-UI中的CRUD列表操作
  6. 逐步详细介绍如何向应用程序添加气象站和气象站数据

他们记录了我当前用于开发Blazor应用程序的框架。

他们不是:

  1. 尝试定义最佳做法。
  2. 成品。

根据您的需要或多或少的使用,请提供建议。

我确实提出了一些建议,主要是为了使Blazor的新手摆脱黑洞。例如,我建议停用Page这个词。路由组件不是页面。将其标记为页面,甚至是在下意识的情况下,它也会获得根本不适用的网页属性。我在显示它们的组件——RouteView之后使用术语视图

第一部分描述了我的一些激进的开发方法,并逐步介绍了GitHub存储库上的两个项目——Blazor ServerWASM/Backend API项目——解释了结构。

储存库和数据库

CEC.Blazor GitHub存储库

存储库中的一个SQL脚本位于/SQL中,用于构建数据库。

您可以在此处查看运行的项目的服务器版本

您可以在此处查看该项目的WASM版本

解决方案结构

我使用Visual Studio,因此Github存储库由一个包含五个项目的解决方案组成。这些是:

  1. CEC.Blazor——核心库,包含可以在任何项目中重复使用和重用的所有内容。
  2. CEC.Weather——这是服务器和WASM专家共享的库。几乎所有项目代码都位于此处。示例包括EF DB上下文、模型类、特定于模型的CRUD组件、Bootstrap SCSS、视图、表单...
  3. CEC.Blazor.Server——服务器项目。
  4. CEC.Blazor.WASM.Server——WASM后端服务器项目。具有支持代码的API控制器。
  5. CEC.Blazor.WASM.Client——WASM客户端项目。

设计哲学

该项目的数据端是按照常规的结构构建的,大致基于三层模型(数据、逻辑层和表示层)。公开的数据类均作为服务运行,并且可用于依赖项注入。第二篇文章将详细介绍。

用户界面更为激进:

  1. 自定义Component类用作基本UI组件——ControlBase仅由开箱即用的数据控件使用。
  2. 路由被丢弃。有一个管理用户界面的新ViewManager

UI结构

页面

页面是充当应用程序主机的网页。通常每个应用程序一个。

ViewManager

ViewManager是在 RenderTree中的子根组件,由应用程序载入。目的是管理和加载视图。支持ViewData类用于存储View配置数据。使用的主要方法是LoadViewAsync。有多种版本,但所有版本均加载ViewData中定义的ViewViewManager通过级联值将自身暴露给所有其他组件。

Views

视图是由ViewManager加载到页面中的组件。他们必须实现IView并可以定义布局。

public interface IView : IComponent
{
    /// provides a unique reference for the instance of the view
    public Guid GUID => Guid.NewGuid();

    /// The cascaded ViewManager Instance
    [CascadingParameter] public ViewManager ViewManager { get; set; }
}

布局

布局是Blazor的即用型布局。ViewManager 会以作为子内容的View呈现Layout

表单

表单是控件的逻辑集合,这些控件显示在视图或模式对话框中。列表、视图表单、编辑表单都是经典表单。表单包含的控件不是HTML

控件

控件是显示某些内容的组件:它们触发HTML代码。例如,编辑框、下拉菜单、按钮...窗体是控件的集合。

CEC.Blazor.WASM.Client项目

该项目几乎是空的。控件和服务都在库中。

index.html

index.html 几乎是标准的问题:

  1. 添加了样式表引用。请注意,您使用虚拟目录_content/Assembly_Name访问依赖程序集wwwroot文件夹中公开的内容。脚本的访问方式相同。
  2. app的基本内容是HTML块,在应用程序初始化时显示微调框。  

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, 
          maximum-scale=1.0, user-scalable=no" />
    <title>CEC.Blazor.WASM</title>
    <base href="/" />
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,
     300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
    <link rel="stylesheet" href="_content/CEC.Blazor/cec.blazor.min.css" />
    <link  rel="stylesheet" href="_content/CEC.Weather/css/site.min.css" />
</head>

<body>
    <app>
        <div class="mt-4" style="margin-right:auto; margin-left:auto; width:100%;" >
            <div class="loader"></div>
            <div style="width:100%; text-align:center;"><h4>Web Application Loading</h4></div>
        </div>
    </app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="_content/CEC.Blazor/site.js"></script>
</body>

</html>

CSS

所有CSS都是共享的,因此位于CEC.Weather中。我使用Bootstrap,并通过SASS对其进行了一些自定义。我在Visual Studio中安装了WEB COMPILER扩展程序,可以即时编译SASS文件。

Program.cs

CEC.Blazor和本地应用程序服务一起加载。请注意,此处定义了Blazor根组件。您不必使用App

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    // Full class name used here to make it a little more obvious. 
    // Not required if the assemble is referenced.
    builder.RootComponents.Add<CEC.Weather.Components.App>("app");

    // Added here as we don't have access to builder in AddApplicationServices
    builder.Services.AddScoped(sp => new HttpClient 
            { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
    // the Services for the CEC.Blazor Library
    builder.Services.AddCECBlazor();
    // the local application Services defined in ServiceCollectionExtensions.cs
    builder.Services.AddApplicationServices();

    await builder.Build().RunAsync();
}

ServiceCollectionExtensions.cs

加载的特定于站点的服务是控制器服务WeatherForecastControllerService和作为IWeatherForecastDataService接口加载的数据服务WeatherForecastWASMDataService。最终的临时服务是编辑表单的Fluent验证程序。

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        // Scoped service for the WASM Client version of WeatherForecast Data Service 
        services.AddScoped<IWeatherForecastDataService, WeatherForecastWASMDataService>();
        // Scoped service for the WeatherForecast Controller Service
        services.AddScoped<WeatherForecastControllerService>();
        // Transient service for the Fluent Validator for the WeatherForecast record
        services.AddTransient<IValidator<DbWeatherForecast>, WeatherForecastValidator>();
        return services;
    }
}

CEC.Blazor.WASM.Server项目

服务器项目中的唯一文件,除了对试图导航到该站点的任何人进行错误处理外,都是WeatherForecast Controller和启动/程序文件。

WeatherForecastController.cs

这是一个标准的API类型控制器。它使用已注册IWeatherForecastDataService的数据层通过IWeatherForecastDataService接口进行异步调用。

[ApiController]
public class WeatherForecastController : ControllerBase
{
    protected IWeatherForecastDataService DataService { get; set; }

    private readonly ILogger<WeatherForecastController> logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger,
           IWeatherForecastDataService weatherForecastDataService)
    {
        this.DataService = weatherForecastDataService;
        this.logger = logger;
    }

    [MVC.Route("weatherforecast/list")]
    [HttpGet]
    public async Task<List<DbWeatherForecast>> GetList() =>
                                               await DataService.GetRecordListAsync();

    [MVC.Route("weatherforecast/count")]
    [HttpGet]
    public async Task<int> Count() => await DataService.GetRecordListCountAsync();

    [MVC.Route("weatherforecast/get")]
    [HttpGet]
    public async Task<DbWeatherForecast>
           GetRec(int id) => await DataService.GetRecordAsync(id);

    [MVC.Route("weatherforecast/read")]
    [HttpPost]
    public async Task<DbWeatherForecast>
           Read([FromBody]int id) => await DataService.GetRecordAsync(id);

    [MVC.Route("weatherforecast/update")]
    [HttpPost]
    public async Task<DbTaskResult> Update([FromBody]DbWeatherForecast record) =>
                                    await DataService.UpdateRecordAsync(record);

    [MVC.Route("weatherforecast/create")]
    [HttpPost]
    public async Task<DbTaskResult> Create([FromBody]DbWeatherForecast record) =>
                                    await DataService.CreateRecordAsync(record);

    [MVC.Route("weatherforecast/delete")]
    [HttpPost]
    public async Task<DbTaskResult> Delete([FromBody] DbWeatherForecast record) =>
                                    await DataService.DeleteRecordAsync(record);
}

ServiceCollectionExtensions.cs

特定于站点的服务是加载WeatherForecastServerDataServiceWeatherForecastDummyDataService的单例IWeatherForecastDataService接口。WeatherForecastDummyDataService用于演示目的,不需要后端SQL数据库。它按其说的创建了应用程序内数据集。  

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        // You have a choice of data sources.
        // services.AddSingleton<IWeatherForecastDataService, 
        // WeatherForecastServerDataService>();
        services.AddSingleton<IWeatherForecastDataService, WeatherForecastDummyDataService>();
        // Factory for building the DBContext 
        var dbContext = configuration.GetValue<string>("Configuration:DBContext");
        services.AddDbContextFactory<WeatherForecastDbContext>
                 (options => options.UseSqlServer(dbContext), ServiceLifetime.Singleton);
        return services;
    }
}

CEC.Blazor.Server项目

这个项目几乎与CEC.Blazor.WASM.Client相同。

Pages

我们有一个真实的页面——标准版_Host.cshtml。当我们在服务器上运行时,这是一个asp.net core页面。

  1. 添加了样式表引用。请注意,您使用虚拟目录_content/Assembly_Name访问依赖程序集wwwroot文件夹中公开的内容。脚本的访问方式相同。
  2. 在这个例子中,app的基本内容使用TagHelper加载根组件CEC.Weather.Components.App。同样,您不依赖于App,只需指定其他组件类即可。  
@page "/"
@namespace CEC.Blazor.Server.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>CEC.Blazor.Server</title>
    <base href="~/" />
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,
     300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
    <link rel="stylesheet" href="_content/CEC.Blazor/cec.blazor.min.css" />
    <link rel="stylesheet" href="_content/CEC.Weather/css/site.min.css" />
</head>
<body>
    <app>
        <component type="typeof(CEC.Weather.Components.App)" render-mode="Server" />
    </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>
    <script src="_content/CEC.Blazor/site.js"></script>
</body>
</html>

Startup.cs

添加了本地服务和CEC.Blazor库服务。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddCECBlazor();
    services.AddApplicationServices();
}

ServiceCollectionExtensions.cs

特定于站点的服务是一个单例IWeatherForecastDataService接口,它加载WeatherForecastServerDataServiceWeatherForecastDummyDataServiceWeatherForecastControllerService为编辑器提供范围和瞬时Fluent Validator服务。

public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
    //services.AddSingleton<IWeatherForecastDataService, WeatherForecastServerDataService>();
    services.AddSingleton<IWeatherForecastDataService, WeatherForecastDummyDataService>();
    services.AddScoped<WeatherForecastControllerService>();
    services.AddTransient<IValidator<DbWeatherForecast>, WeatherForecastValidator>();
    return services;
}

总结

到此结束。这只是一个概述,后面还会有更多详细信息。希望它可以演示您可以使用Blazor项目实现的抽象级别。下一节将介绍服务和实现数据层。

需要注意的一些关键点:

  1. Blazor ServerBlazor WASM项目中,几乎所有代码都是通用的。您可以小心地编写可以以任何一种方式部署的应用程序。
  2. 对术语要非常小心。我们的应用程序中没有页面
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值