c# - Blazor项目结构/最佳实践

标签 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....
}

第二个项目是服务器Blazor项目,它引用了项目DAL。项目划分如下:
  • 模型-这些是当前正在进行的项目专用的模型。例如,一个模型可能是多个表(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;}
    }
    
  • Pages-带有页面引用的Razor页面/组件。
  • 共享- Razor 组件-在多个页面上使用的东西。一个例子就是模态。
  • 服务-这是我想的业务层。到目前为止,“模型”文件夹中的每个模型/类都有一项服务,但共享组件也有一项。来自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通过依赖项注入(inject)生成的。页面类将通过以下几行加载数据:
    
    @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类如下所示:
    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();
        }
    }
    

    看到我使用遗留代码的方式已经存在了这么长时间,没有其他人告诉我,我只是想知道自己做得对。这是特别正确的,因为这应该是我们业务发展的核心,并且我不想结束意粉代码或进行彻底的重构。

    我想我的问题如下:
  • 我目前如何实现DAL?可以使实际属性与CRUD操作保持一致吗?就像有一个带有DBContext的构造函数而没有一个呢?我已经看到一些项目有一个单独的库,仅针对没有CRUD操作的类,而我没有看到其中的值(value)。这种方式背后的逻辑是我们的大多数应用程序只是CRUD操作,因此我希望能够在以后的每个应用程序中重用该项目。从在线上看,此实现是DAL/BLL的混合
  • 我的Blazor当前实现是否“有效”?还是我可以遵循其他更好的设计规范?我喜欢MVVM,但到目前为止,我真的看不到任何实现的值(value)。让页面在ViewModel上调用函数,而只是在另一个具有相同名称/参数的类中调用函数的意义是什么?可以这么说,裁掉中间人不是很有意义吗?
  • 是否可以跟踪任何示例企业项目以更好地了解在这里做什么?正如我已经说过的那样,我公司中没有任何一位高级人士可以就此进行讨论。我只是想使其尽可能适应变化/专业。

  • 在此先感谢您的帮助!

    最佳答案

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

    NetLearner:https://github.com/shahedc/NetLearnerApp

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

    共享库包括:

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

  • 这是相应的博客文章,随后是A-Z每周系列,它将在接下来的6个月中探讨26个不同的主题。
  • 博客文章:https://wakeupandcode.com/netlearner-on-asp-net-core-3-1/

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

    关于c# - Blazor项目结构/最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59538859/

    相关文章:

    c# - 当 observablecollection 中的字段更改时列表框不更新

    .net - 在 .NET Core 中缓存 API 响应

    .net-core - 未为 dotnet 核心项目生成 Coverlet lcov.info 文件

    c# - 如何获取 Amazon S3 Blob 的创建日期?

    c# - 无法在 64 位操作系统上的 Visual Studio 中从 catch 设置下一条语句

    c# - 在 C# 中将两个列表相交

    c# - WPF 项目,使用按钮的侧边菜单导航

    windows-phone-7 - WP7 PanoramaItem 绑定(bind)第二个 PanoramaItem

    c# - 是C#编译器还是CLR禁止多重继承

    templates - 如何显示已安装的 dotnet 新模板版本号