Blazor 项目结构/最佳实践

Dan*_*Dan 20 c# mvvm .net-core blazor blazor-server-side

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

首先是一个名为 DAL 的类库——这是我们的“数据层”。我们正在使用 Dapper,它相对简单。示例类如下所示:

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....
}
Run Code Online (Sandbox Code Playgroud)

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

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

一个例子可能是这样的:

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;}
}
Run Code Online (Sandbox Code Playgroud)
  1. 页面 - 带有页面引用的 Razor 页面/组件。
  2. 共享 - Razor 组件 - 在多个页面上使用的东西。一个例子是模态。
  3. 服务 - 这就是我想的业务层。截至目前,“模型”文件夹中的每个模型/类都有一个服务,但也有一些用于共享组件。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...
    }
}
Run Code Online (Sandbox Code Playgroud)

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


@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) 
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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... 
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在页面上它会是这样的:


@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

看到我如何处理遗留代码这么长时间并且没有一位前辈告诉我其他情况,我只想知道我做得对。这一点尤其正确,因为这应该是我们业务向前发展的核心,我不想以意大利面条式代码结束,也不想做一个完整的重构。

我想我的问题如下:

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

    1. 我当前的 Blazor 实现“有效”吗?或者还有其他更好的设计实践可以遵循吗?我喜欢 MVVM,但我真的没有看到迄今为止我看到的任何实现的价值。让页面调用 ViewModel 上的函数有什么意义,它只是调用另一个具有相同名称/参数的类中的函数?可以这么说,去掉中间人难道没有意义吗?

    2. 是否有任何示例企业项目我可以遵循以更好地了解在这里做什么?正如我所说的,我公司没有任何一位资深人士可以就此事进行咨询。我只是想让它尽可能适应变化/专业。

在此先感谢您的帮助!

小智 5

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

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

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

共享库包括:

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

这是相应的博客文章,接下来是 AZ 每周系列,将在接下来的 6 个月内探索 26 个不同的主题。

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

NetLearner 架构

  • @weblar83 查看我在此处发布的答案,这就是我最终要做的事情。数据文件夹是具有业务逻辑的对象,服务是数据层。我还有一个用于授权、页面和组件的文件夹。效果非常好 (2认同)

Dan*_*Dan 3

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

我将按照该链接中的内容实施一些操作,看看效果如何。如果其他人正在寻找一些灵感,这里有一些很好的例子:

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

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

快乐编码!