Blazor 项目结构/最佳实践

Blazor Project structure / best practices

我的公司正在从遗留代码库迁移到更现代的平台,我们正在迁移到 Blazor。我们目前刚刚参与 ORM 和最佳实践,似乎有很多关于项目设置的相互矛盾的想法(至少从我收集到的)。我目前的结构如下:

首先是一个名为 DAL 的 class 库 - 这是我们的 "data layer"。我们正在使用 Dapper,它相对简单。示例 class 如下所示:

public class Person
{
      public string Id {get; set;}
      public string FirstName {get; set;}
      public string LastName {get; set;}

      public Person() {}
      public Person(DbContext context) {}

      public void GetPerson(int id) {}
      public void DeletePerson(int id) {}


      etc....
}

第二个项目是引用项目 DAL 的服务器 Blazor 项目。项目划分如下:

  1. 模型 - 这些是当前正在处理的项目的特定模型。例如,一个模型可能是几个表的组合(来自 DAL class 的模型)或只是用于网页上表单的字段。

一个例子可能是这样的:

public class EmployeeModel
{
    public int Id {get; set;}
    public int Department{get; set;}
    public DateTime HireDate {get; set;}
    public decimal Salary {get; set;}
    public Person {get; set;}
}
  1. 页面 - Razor pages/components 带有页面引用。
  2. Shared - Razor 组件 - 在多个页面上使用的东西。一个例子是模态。
  3. 服务 - 我想这是业务层。截至目前,文件夹 "Models" 中的每个 model/class 都有一项服务,但也有一些用于共享组件。 Models 文件夹中的 EmployeeModel 示例可能如下所示:
public class EmployeeService
{
    private DbContext _dbContext = dbContext;
    public EmployeeService(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public Task<EmployeeModel> Get(int id)
    {
        var personRepository = new Person(_dbContext);
        var person = personRepository.Get(id);
        Id = id;
        if (id > 10)
            Department = "Accounting"
        etc...
    }

    public Task<int>CalculateBonus(DateTime hireDate, string department, decimal salary)
    {
         //logic here...
    }
}

服务和 dbcontext 都是通过 startup.cs 通过依赖注入生成的。页面 class 将使用以下内容加载数据:


@code{

    [Parameter]
    int EmployeeId;

    public Employee employee;
    public decimal bonus;

    protected override OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender)
            return;

        employee = EmployeeService.Get(EmployeeId);
    }

    public void GetBonus()
    {
        if (employee != null)
            bonus = EmployeeService.CalculateBonus(employee.HireDate, employee.Department, employee.Salary) 
    }
}

到目前为止这似乎还不错,但有很多不同的解释。例如,我喜欢使用 MVVM 模式的想法。我最初关注的一个例子如下:https://itnext.io/a-simple-mvvm-implementation-in-client-side-blazor-8c875c365435

但是,我没有看到将 Model/ViewModel 与他们在该示例中的表现分开的目的,因为他们似乎在做同样的事情,但只是在应用程序的不同区域。我也无法在网上找到此实现的任何其他示例,所以我认为我走错了路,最初放弃了它,但在这个过程中还处于足够早的阶段,也可以尝试一下该方法。例如,EmployeeService class 在这个方法中看起来像这样:

public class EmployeeService
{
    private EmployeeModel _employeeModel;
    public EmployeeService(EmployeeModel employeeModel)
    {
        _employeeModel = employeeModel;
    }

    private EmployeeModel currentEmployee;
    public EmployeeModel CurrentEmployee
    {
        get { return currentEmployee}
    }
    {
        set {currentEmployee = value; }
    }

    public Task<EmployeeModel> Get(int id)
    {
         currentEmployee = EmployeeModel.Get(id);
    }

    public Task<int>CalculateBonus()
    {
         //logic implemented here with properties instead of parameters... 
    }
}

然后在页面上是这样的:


@code{

    [Parameter]
    int EmployeeId;
    public decimal bonus;

    protected override OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender)
            return;

        EmployeeService.Get(EmployeeId); //reference Employee on page with EmployeeService.CurrentEmployee
    }

    public void GetBonus()
    {
        bonus = EmployeeService.CalculateBonus();
    }
}

鉴于我使用遗留代码工作了这么长时间,而且没有一位资深人士告诉我其他情况,我只想知道我做对了。这一点尤其正确,因为这应该是我们业务向前发展的核心,我不想以意大利面条式代码结束,也不想进行完整的重构。

我想我的问题如下:

  1. 我目前的 DAL 实施情况如何?实际属性符合CRUD操作可以吗?就像有一个带 DBContext 的构造函数和一个不带 DBContext 的构造函数一样?我看到一些项目有一个单独的库,只用于 class 而没有 CRUD 操作,我没有看到其中的价值。这种方式背后的逻辑是我们的大多数应用程序只是 CRUD 操作,所以我希望能够在未来的每个应用程序中重用该项目。从网上看,这个实现是 DAL/BLL

    的混合体
    1. 我当前的 Blazor 实现是否 "work"?或者我可以遵循其他更好的设计实践吗?我喜欢 MVVM,但我真的没有看到到目前为止我所看到的任何实现的价值。让一个页面调用 ViewModel 上的一个函数有什么意义,它只是调用另一个 class 中具有相同 name/parameters 的函数?可以这么说,切断中间人是不是有意义?

    2. 有没有我可以遵循的示例企业项目,以便更好地了解在这里要做什么?正如我所说的,我公司没有任何高级人员可以就此进行任何讨论。我只是想让它尽可能适应 change/professional。

在此先感谢您的帮助!

我刚刚创建了一个新的 ASP .NET Core 3.1 项目,其中包含 3 个 Web 应用程序:MVC、Razor Pages 和 Blazor。

网络学习者:https://github.com/shahedc/NetLearnerApp

我正在并行开发所有 3 个,以便您可以在所有这些中看到相似的功能。我已将常用项目提取到共享库中,以便于共享。

共享库包括:

  • 核心项目(模型和服务)
  • 基础结构项(数据库上下文和迁移)

这是相应的博客文章,随后是 A-Z 每周系列,将在接下来的 6 个月内探讨 26 个不同的主题。

希望当前版本对您的要求有用。请继续关注并随时就项目结构提出建议或提供反馈。

因此,我正在深入寻找更多示例项目,并遇到了一个 SPA 服务器端 Dapper 应用程序 (https://www.c-sharpcorner.com/article/create-a-blazor-server-spa-with-dapper/)。从我在那里、这里和我看到的任何其他地方可以收集到的信息来看,添加一个单独的项目来处理仅 CRUD 操作似乎比它的价值更麻烦。

我将按照 link 中的内容实施一些内容,然后看看进展如何。如果其他人正在寻找一些灵感,这里有一些很好的例子:

https://github.com/AdrienTorris/awesome-blazor#sample-projects

FWIW,每个示例似乎都遵循这条路径,只是似乎以略有不同的方式进行(ORM 使用、文件夹名称等)。这意味着我将需要添加更多服务(总共至少 20 个),但如果它是一个复杂的业务应用程序,我只是想这就是野兽的本性。

编码愉快!

在使用三层架构时,我看到业务层会增长,并且某些时间点会变得非常混乱和紧密耦合。

我建议尝试 Onion Architecture, its very popular and very similar to Clean Architecture

我相信您正在寻找一些示例应用程序,以了解如何将每一层分开,什么是最佳实践和最佳库。

尝试以下 link,您将在 youtube 上获得大量视频。

Asp.net boilerplate

ABP Framework

由于 Blazor Web Assembly 仍处于预览阶段,我开始将我的个人网站从 asp.netcore 2.0 迁移到 Blazor 客户端和服务器端的 Azure 函数。

这是我的申请结构

希望对您有所帮助!

来自现有的 WPF-project,我想尝试为我的 Blazor-project 实施 MVVM。

我的目标是能够通过代码控制服务器中的所有内容,而完全不知道演示文稿使用了它。像 menu-entries,显示弹出窗口或导航。 还能够监视来自 Azure and/or MS-Message 队列的事件,因为它们没有任何 user-interaction.

为了获得更高的灵活性和更好的测试,我决定通过将 razor-page 中的常见代码作为视图模型注入到 code-behind 中来解耦。 viewmodel 也得到了它的 dataservice/model 注入。 数据作为数据服务的一部分直接使用 CQRS 进行处理,但这将在未来转移到 grpc-server。

目前为止效果很好。需要做更多的工作 - 但值得付出努力。