znlgis 博客

GIS开发与技术分享

第三章:项目架构与核心设计

目录

  1. 分层架构设计
  2. Furion内核架构解析
  3. 启动配置详解
  4. 配置选项系统
  5. 模块化与插件化设计
  6. 应用生命周期管理
  7. 多环境配置
  8. 项目引用关系与依赖管理

1. 分层架构设计

1.1 Furion推荐的分层架构

Furion推荐采用经典的四层架构(也可以扩展为五层),每一层有明确的职责划分:

┌─────────────────────────────────────────┐
│           Web.Entry(入口层)            │
│   程序入口、运行配置、静态资源            │
├─────────────────────────────────────────┤
│           Web.Core(Web核心层)          │
│   启动配置、过滤器、中间件、权限处理      │
├─────────────────────────────────────────┤
│           Application(应用层)           │
│   业务服务、DTO、动态WebAPI              │
├─────────────────────────────────────────┤
│     EntityFramework.Core(数据层)       │
│   DbContext、数据库迁移、种子数据        │
├─────────────────────────────────────────┤
│            Core(核心层)                │
│   实体模型、接口定义、枚举、常量          │
└─────────────────────────────────────────┘

1.2 Core层(核心层)

Core层是整个应用的基础层,不依赖其他任何项目层,定义了应用程序的核心概念。

// Core/Entities/User.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MyApp.Core.Entities;

/// <summary>
/// 用户实体
/// </summary>
[Table("sys_user")]
public class User
{
    /// <summary>
    /// 主键Id
    /// </summary>
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    /// <summary>
    /// 用户名
    /// </summary>
    [Required, MaxLength(64)]
    public string UserName { get; set; }

    /// <summary>
    /// 邮箱
    /// </summary>
    [MaxLength(128)]
    public string Email { get; set; }

    /// <summary>
    /// 创建时间
    /// </summary>
    public DateTime CreatedTime { get; set; } = DateTime.Now;

    /// <summary>
    /// 是否删除
    /// </summary>
    public bool IsDeleted { get; set; } = false;
}
// Core/Interfaces/IUserRepository.cs
namespace MyApp.Core.Interfaces;

/// <summary>
/// 用户仓储接口
/// </summary>
public interface IUserRepository
{
    Task<User> GetByIdAsync(long id);
    Task<List<User>> GetAllAsync();
    Task<User> CreateAsync(User user);
    Task UpdateAsync(User user);
    Task DeleteAsync(long id);
}
// Core/Enums/UserStatus.cs
namespace MyApp.Core.Enums;

/// <summary>
/// 用户状态枚举
/// </summary>
public enum UserStatus
{
    /// <summary>
    /// 正常
    /// </summary>
    Active = 0,

    /// <summary>
    /// 禁用
    /// </summary>
    Disabled = 1,

    /// <summary>
    /// 已锁定
    /// </summary>
    Locked = 2
}

1.3 EntityFramework.Core层(数据层)

数据层负责与数据库交互,包含DbContext定义、仓储实现和数据库迁移。

// EntityFramework.Core/DbContexts/DefaultDbContext.cs
using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
using MyApp.Core.Entities;

namespace MyApp.EntityFramework.Core.DbContexts;

/// <summary>
/// 默认数据库上下文
/// </summary>
[AppDbContext("DefaultConnection", DbProvider.Sqlite)]
public class DefaultDbContext : AppDbContext<DefaultDbContext>
{
    public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
    {
    }

    /// <summary>
    /// 用户表
    /// </summary>
    public DbSet<User> Users { get; set; }

    /// <summary>
    /// 配置模型
    /// </summary>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // 配置软删除全局过滤器
        modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDeleted);

        // 配置索引
        modelBuilder.Entity<User>()
            .HasIndex(u => u.UserName)
            .IsUnique();
    }
}
// EntityFramework.Core/Startup.cs
using Furion;
using Furion.DatabaseAccessor;
using Microsoft.Extensions.DependencyInjection;

namespace MyApp.EntityFramework.Core;

/// <summary>
/// EF Core层启动配置
/// </summary>
[AppStartup(600)]
public class Startup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 配置数据库上下文
        services.AddDatabaseAccessor(options =>
        {
            options.AddDbPool<DefaultDbContext>();
        }, "MyApp.Database.Migrations");
    }
}

1.4 Application层(应用层)

应用层包含业务逻辑和动态WebAPI服务,是开发者主要编写代码的地方。

// Application/Dtos/UserDto.cs
namespace MyApp.Application.Dtos;

/// <summary>
/// 用户输出DTO
/// </summary>
public class UserDto
{
    public long Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public DateTime CreatedTime { get; set; }
}

/// <summary>
/// 创建用户输入DTO
/// </summary>
public class CreateUserInput
{
    [Required(ErrorMessage = "用户名不能为空")]
    [MaxLength(64, ErrorMessage = "用户名最长64个字符")]
    public string UserName { get; set; }

    [EmailAddress(ErrorMessage = "邮箱格式不正确")]
    public string Email { get; set; }
}
// Application/Services/UserService.cs
using Furion.DatabaseAccessor;
using Furion.DynamicApiController;
using MyApp.Application.Dtos;
using MyApp.Core.Entities;

namespace MyApp.Application.Services;

/// <summary>
/// 用户管理服务
/// </summary>
public class UserService : IDynamicApiController
{
    private readonly IRepository<User> _repository;

    public UserService(IRepository<User> repository)
    {
        _repository = repository;
    }

    /// <summary>
    /// 获取所有用户
    /// </summary>
    public async Task<List<UserDto>> GetAllAsync()
    {
        return await _repository.AsQueryable()
            .Select(u => new UserDto
            {
                Id = u.Id,
                UserName = u.UserName,
                Email = u.Email,
                CreatedTime = u.CreatedTime
            })
            .ToListAsync();
    }

    /// <summary>
    /// 创建用户
    /// </summary>
    public async Task<long> CreateAsync(CreateUserInput input)
    {
        var user = new User
        {
            UserName = input.UserName,
            Email = input.Email
        };
        var entity = await _repository.InsertNowAsync(user);
        return entity.Entity.Id;
    }
}

1.5 Web.Core层(Web核心层)

Web核心层负责Web相关的配置和中间件设置。

// Web.Core/Startup.cs
using Furion;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyApp.Web.Core;

/// <summary>
/// Web核心层启动配置
/// </summary>
[AppStartup(100)]
public class Startup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 添加JWT认证
        services.AddJwt<JwtHandler>();

        // 添加跨域
        services.AddCorsAccessor();

        // 添加控制器与Furion注入
        services.AddControllers()
            .AddInject();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseRouting();

        // 使用跨域
        app.UseCorsAccessor();

        // 使用认证授权
        app.UseAuthentication();
        app.UseAuthorization();

        // 使用Furion
        app.UseInject(string.Empty);

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

1.6 Web.Entry层(入口层)

入口层是应用程序的启动点,代码极为简洁。

// Web.Entry/Program.cs
using Furion;

var builder = WebApplication.CreateBuilder(args).Inject();
var app = builder.Build();
app.Run();

2. Furion内核架构解析

2.1 App静态类

App是Furion框架中最核心的静态类,它是访问Furion所有功能的统一入口。通过App类,你可以在应用程序的任何位置获取服务、配置、环境信息等。

using Furion;

// 获取配置信息
var connStr = App.Configuration["ConnectionStrings:DefaultConnection"];

// 获取环境信息
var envName = App.HostEnvironment.EnvironmentName;
var isDev = App.HostEnvironment.IsDevelopment();

// 获取服务
var userService = App.GetService<IUserService>();
var logger = App.GetService<ILogger<Program>>();

// 获取请求上下文相关信息
var httpContext = App.HttpContext;
var user = App.User; // 当前登录用户

// 获取所有程序集
var assemblies = App.Assemblies;
var effectiveTypes = App.EffectiveTypes;

2.2 App类常用属性和方法

属性/方法 类型 说明
App.Configuration IConfiguration 获取应用程序配置
App.HostEnvironment IHostEnvironment 获取宿主环境信息
App.HttpContext HttpContext 获取当前HTTP上下文
App.User ClaimsPrincipal 获取当前登录用户
App.Assemblies IEnumerable<Assembly> 获取应用程序集
App.EffectiveTypes IEnumerable<Type> 获取有效的类型集合
App.GetService<T>() T 获取服务实例
App.GetRequiredService<T>() T 获取必须的服务实例
App.GetOptions<T>() T 获取配置选项
App.GetConfig<T>(key) T 获取强类型配置
App.WebHostEnvironment IWebHostEnvironment 获取Web宿主环境
App.ContentRootPath string 获取应用根目录路径

2.3 Inject扩展方法

Furion通过Inject扩展方法将自身集成到ASP.NET Core的启动流程中:

// WebApplicationBuilder扩展
builder.Inject();
// 等价于以下操作的组合:
// 1. 扫描所有程序集
// 2. 注册AppStartup子类
// 3. 配置日志和配置系统
// 4. 初始化App静态类

// IServiceCollection扩展
services.AddInject();
// 等价于以下操作的组合:
// 1. 注册动态WebAPI
// 2. 注册规范化结果
// 3. 配置Swagger文档

// IApplicationBuilder扩展
app.UseInject();
// 等价于以下操作的组合:
// 1. 启用Swagger中间件
// 2. 配置路由
// 3. 启用规范化结果处理

2.4 内部工作原理

Furion的内核工作流程如下:

应用启动
  │
  ├── 1. 扫描程序集
  │     └── 收集所有实现了AppStartup的类
  │
  ├── 2. 排序Startup
  │     └── 按AppStartup特性中的Order排序
  │
  ├── 3. 依次调用ConfigureServices
  │     ├── EF.Core Startup (Order=600)
  │     ├── Application Startup (Order=300)
  │     └── Web.Core Startup (Order=100)
  │
  ├── 4. 依次调用Configure
  │     └── 配置中间件管道
  │
  └── 5. 应用就绪
        └── 开始接收HTTP请求

3. 启动配置详解

3.1 Minimal API风格(.NET 6+)

从.NET 6开始,推荐使用Minimal API风格的启动方式:

// Program.cs - 最简方式
var builder = WebApplication.CreateBuilder(args).Inject();
var app = builder.Build();
app.Run();

如果需要更多控制:

// Program.cs - 详细配置
using Furion;

var builder = WebApplication.CreateBuilder(args);

// 注入Furion
builder.Inject();

// 手动配置服务
builder.Services.AddControllers()
    .AddInject();

builder.Services.AddCorsAccessor();

var app = builder.Build();

// 配置中间件
app.UseHttpsRedirection();
app.UseCorsAccessor();
app.UseAuthentication();
app.UseAuthorization();
app.UseInject(string.Empty);
app.MapControllers();

app.Run();

3.2 AppStartup类

Furion提供了AppStartup基类,允许在不同的项目层中定义启动逻辑:

using Furion;

namespace MyApp.Web.Core;

/// <summary>
/// Web核心层启动配置
/// 数字越小,优先级越高(越先执行)
/// </summary>
[AppStartup(100)]
public class WebCoreStartup : AppStartup
{
    /// <summary>
    /// 配置服务
    /// </summary>
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().AddInject();
        services.AddCorsAccessor();
    }

    /// <summary>
    /// 配置中间件管道
    /// </summary>
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseCorsAccessor();
        app.UseInject(string.Empty);
        app.UseEndpoints(endpoints => endpoints.MapControllers());
    }
}

3.3 启动顺序控制

多个AppStartup类通过Order属性控制执行顺序:

Order值 说明
Web.Core 100 最先执行,配置基础中间件
Application 300 配置业务相关服务
EntityFramework.Core 600 配置数据库相关服务
自定义模块 自定义 根据需要设置
// 数值越小,越先执行
[AppStartup(100)]  // 第一个执行
public class FirstStartup : AppStartup { }

[AppStartup(200)]  // 第二个执行
public class SecondStartup : AppStartup { }

[AppStartup(300)]  // 第三个执行
public class ThirdStartup : AppStartup { }

4. 配置选项系统

4.1 基本配置读取

Furion完全兼容ASP.NET Core的配置系统,并在此基础上提供了增强功能。

appsettings.json:

{
  "AppSettings": {
    "AppName": "MyFurionApp",
    "Version": "1.0.0",
    "MaxPageSize": 100,
    "EnableAudit": true,
    "AllowedOrigins": ["http://localhost:3000", "http://localhost:8080"]
  },
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=app.db",
    "ReadonlyConnection": "Data Source=app_readonly.db"
  },
  "JwtSettings": {
    "Issuer": "MyApp",
    "Audience": "MyApp",
    "SecretKey": "your-secret-key-at-least-32-characters-long",
    "ExpiredTime": 1440
  }
}

读取配置的多种方式:

using Furion;

// 方式1:通过App静态类读取
var appName = App.Configuration["AppSettings:AppName"];
var maxPageSize = App.Configuration.GetValue<int>("AppSettings:MaxPageSize");

// 方式2:通过IConfiguration注入
public class MyService
{
    private readonly IConfiguration _config;

    public MyService(IConfiguration config)
    {
        _config = config;
        var version = _config["AppSettings:Version"];
    }
}

// 方式3:通过App.GetConfig读取强类型配置
var appSettings = App.GetConfig<AppSettings>("AppSettings");

4.2 强类型配置(Options模式)

Furion增强了ASP.NET Core的Options模式,支持更便捷的强类型配置绑定:

// 定义配置类
using Furion.ConfigurableOptions;

namespace MyApp.Core.Options;

/// <summary>
/// 应用配置选项
/// </summary>
public class AppSettingsOptions : IConfigurableOptions
{
    /// <summary>
    /// 应用名称
    /// </summary>
    public string AppName { get; set; }

    /// <summary>
    /// 版本号
    /// </summary>
    public string Version { get; set; }

    /// <summary>
    /// 最大分页大小
    /// </summary>
    public int MaxPageSize { get; set; } = 50;

    /// <summary>
    /// 是否启用审计
    /// </summary>
    public bool EnableAudit { get; set; }

    /// <summary>
    /// 允许的跨域源
    /// </summary>
    public string[] AllowedOrigins { get; set; }
}
// appsettings.json中对应的节点名称默认为类名去掉Options后缀
{
  "AppSettings": {
    "AppName": "MyFurionApp",
    "Version": "1.0.0",
    "MaxPageSize": 100,
    "EnableAudit": true,
    "AllowedOrigins": ["http://localhost:3000"]
  }
}

注册并使用:

// 在Startup中注册
services.AddConfigurableOptions<AppSettingsOptions>();

// 在服务中通过构造函数注入使用
public class HomeService : IDynamicApiController
{
    private readonly AppSettingsOptions _options;

    public HomeService(IOptions<AppSettingsOptions> options)
    {
        _options = options.Value;
    }

    public object GetAppInfo()
    {
        return new
        {
            _options.AppName,
            _options.Version,
            _options.MaxPageSize
        };
    }
}

// 或者通过App静态类获取
var options = App.GetOptions<AppSettingsOptions>();

4.3 配置热重载

Furion支持配置文件的热重载,即修改appsettings.json后无需重启应用即可生效:

// 使用IOptionsMonitor实现配置热重载
public class DynamicConfigService : IDynamicApiController
{
    private readonly IOptionsMonitor<AppSettingsOptions> _optionsMonitor;

    public DynamicConfigService(IOptionsMonitor<AppSettingsOptions> optionsMonitor)
    {
        _optionsMonitor = optionsMonitor;

        // 监听配置变化
        _optionsMonitor.OnChange(options =>
        {
            Console.WriteLine($"配置已更新:AppName = {options.AppName}");
        });
    }

    public object GetCurrentConfig()
    {
        // CurrentValue始终返回最新的配置值
        var current = _optionsMonitor.CurrentValue;
        return new
        {
            current.AppName,
            current.Version,
            current.EnableAudit
        };
    }
}

4.4 配置验证

可以在配置类中添加验证逻辑:

using Furion.ConfigurableOptions;

public class JwtSettingsOptions : IConfigurableOptionsListener<JwtSettingsOptions>
{
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public string SecretKey { get; set; }
    public int ExpiredTime { get; set; }

    /// <summary>
    /// 配置变化时触发
    /// </summary>
    public void OnListener(JwtSettingsOptions options, IConfiguration configuration)
    {
        Console.WriteLine($"JWT配置已更新:过期时间 = {options.ExpiredTime}分钟");
    }

    /// <summary>
    /// 配置后期处理
    /// </summary>
    public void PostConfigure(JwtSettingsOptions options, IConfiguration configuration)
    {
        // 设置默认值
        if (options.ExpiredTime <= 0)
            options.ExpiredTime = 1440; // 默认24小时

        if (string.IsNullOrEmpty(options.Issuer))
            options.Issuer = "DefaultIssuer";
    }
}

5. 模块化与插件化设计

5.1 模块化架构概述

Furion的模块化设计允许将应用程序拆分为多个独立的功能模块,每个模块可以包含自己的服务、实体、接口和配置:

MyApp/
├── MyApp.Core/                    # 核心共享模块
├── MyApp.Module.System/           # 系统管理模块
│   ├── Entities/
│   ├── Services/
│   ├── Dtos/
│   └── Startup.cs
├── MyApp.Module.Business/         # 业务模块
│   ├── Entities/
│   ├── Services/
│   ├── Dtos/
│   └── Startup.cs
├── MyApp.Module.Report/           # 报表模块
│   ├── Services/
│   └── Startup.cs
└── MyApp.Web.Entry/               # 入口层

5.2 创建独立模块

每个模块可以有自己的AppStartup类来注册服务:

// MyApp.Module.System/Startup.cs
using Furion;

namespace MyApp.Module.System;

[AppStartup(200)]
public class SystemModuleStartup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 注册系统模块特有的服务
        services.AddScoped<ISystemConfigService, SystemConfigService>();
        services.AddScoped<IAuditLogService, AuditLogService>();
    }
}
// MyApp.Module.Business/Startup.cs
using Furion;

namespace MyApp.Module.Business;

[AppStartup(250)]
public class BusinessModuleStartup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 注册业务模块特有的服务
        services.AddScoped<IOrderService, OrderService>();
        services.AddScoped<IProductService, ProductService>();
    }
}

5.3 程序集扫描

Furion会自动扫描所有引用的程序集,发现并注册AppStartup子类。要确保模块被加载,需要在入口层项目中引用模块项目:

<!-- MyApp.Web.Entry.csproj -->
<ItemGroup>
  <ProjectReference Include="..\MyApp.Module.System\MyApp.Module.System.csproj" />
  <ProjectReference Include="..\MyApp.Module.Business\MyApp.Module.Business.csproj" />
  <ProjectReference Include="..\MyApp.Module.Report\MyApp.Module.Report.csproj" />
</ItemGroup>

5.4 外部程序集加载

也可以动态加载外部程序集作为插件:

// appsettings.json
{
  "AppSettings": {
    "ExternalAssemblies": [
      "MyApp.Plugin.Export",
      "MyApp.Plugin.Import"
    ]
  }
}

6. 应用生命周期管理

6.1 IStartupFilter

Furion支持通过IStartupFilter在应用启动过程中插入自定义逻辑:

using Microsoft.AspNetCore.Hosting;

namespace MyApp.Web.Core;

/// <summary>
/// 应用启动过滤器
/// </summary>
public class AppStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return app =>
        {
            // 在所有中间件之前执行
            Console.WriteLine("=== 应用正在启动 ===");

            // 调用下一个中间件
            next(app);

            // 在所有中间件之后执行
            Console.WriteLine("=== 中间件管道配置完成 ===");
        };
    }
}

注册启动过滤器:

services.AddTransient<IStartupFilter, AppStartupFilter>();

6.2 IHostedService与后台服务

Furion完全兼容ASP.NET Core的IHostedService接口:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace MyApp.Web.Core;

/// <summary>
/// 应用生命周期后台服务
/// </summary>
public class AppLifecycleService : IHostedService
{
    private readonly ILogger<AppLifecycleService> _logger;

    public AppLifecycleService(ILogger<AppLifecycleService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("应用已启动,时间:{Time}", DateTime.Now);
        _logger.LogInformation("运行环境:{Env}", App.HostEnvironment.EnvironmentName);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("应用正在关闭,时间:{Time}", DateTime.Now);
        // 执行清理操作
        return Task.CompletedTask;
    }
}

6.3 应用生命周期事件

// 在Program.cs中监听生命周期事件
var app = builder.Build();

var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();

lifetime.ApplicationStarted.Register(() =>
{
    Console.WriteLine("✅ 应用已完全启动");
});

lifetime.ApplicationStopping.Register(() =>
{
    Console.WriteLine("⚠️ 应用正在停止...");
});

lifetime.ApplicationStopped.Register(() =>
{
    Console.WriteLine("🛑 应用已停止");
});

app.Run();

7. 多环境配置

7.1 环境概述

ASP.NET Core和Furion支持三种标准环境:

环境 名称 说明
Development 开发环境 本地开发使用,启用详细错误信息
Staging 预发布环境 测试和验证使用
Production 生产环境 正式部署使用

7.2 配置文件层级

appsettings.json                    # 基础配置(所有环境共享)
appsettings.Development.json        # 开发环境覆盖配置
appsettings.Staging.json            # 预发布环境覆盖配置
appsettings.Production.json         # 生产环境覆盖配置

appsettings.json(基础配置):

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  },
  "AppSettings": {
    "AppName": "MyFurionApp"
  }
}

appsettings.Development.json(开发环境):

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.EntityFrameworkCore": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=dev.db"
  },
  "AppSettings": {
    "EnableSwagger": true,
    "EnableDetailedErrors": true
  }
}

appsettings.Production.json(生产环境):

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=prod-server;Database=myapp;..."
  },
  "AppSettings": {
    "EnableSwagger": false,
    "EnableDetailedErrors": false
  }
}

7.3 设置环境变量

# Windows (CMD)
set ASPNETCORE_ENVIRONMENT=Development

# Windows (PowerShell)
$env:ASPNETCORE_ENVIRONMENT = "Development"

# Linux/macOS
export ASPNETCORE_ENVIRONMENT=Development

# 命令行参数
dotnet run --environment Production

7.4 环境感知的代码配置

public class Startup : AppStartup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 开发环境特有配置
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            // 开发环境启用Swagger
            app.UseInject(string.Empty);
        }
        else
        {
            app.UseExceptionHandler("/error");
            app.UseHsts();
        }

        // 预发布环境特有配置
        if (env.IsStaging())
        {
            // 启用性能监控
        }

        // 生产环境特有配置
        if (env.IsProduction())
        {
            // 启用压缩
            app.UseResponseCompression();
        }

        // 所有环境共享的配置
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
    }
}

8. 项目引用关系与依赖管理

8.1 标准引用关系

<!-- MyApp.Core.csproj - 核心层,无外部引用 -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
</Project>
<!-- MyApp.EntityFramework.Core.csproj - 数据层 -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Furion" Version="4.9.*" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\MyApp.Core\MyApp.Core.csproj" />
  </ItemGroup>
</Project>
<!-- MyApp.Application.csproj - 应用层 -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Furion" Version="4.9.*" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\MyApp.Core\MyApp.Core.csproj" />
  </ItemGroup>
</Project>
<!-- MyApp.Web.Core.csproj - Web核心层 -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\MyApp.Application\MyApp.Application.csproj" />
    <ProjectReference Include="..\MyApp.EntityFramework.Core\MyApp.EntityFramework.Core.csproj" />
  </ItemGroup>
</Project>
<!-- MyApp.Web.Entry.csproj - 入口层 -->
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\MyApp.Web.Core\MyApp.Web.Core.csproj" />
  </ItemGroup>
</Project>

8.2 依赖传递规则

引用方 被引用方 可访问的层
Web.Entry Web.Core 所有层(传递依赖)
Web.Core Application, EF.Core Core层(传递依赖)
Application Core 仅Core层
EF.Core Core 仅Core层
Core 无外部依赖

8.3 避免循环引用

项目之间的引用必须是单向的,绝对不能出现循环引用:

✅ 正确的引用方向:
Web.Entry → Web.Core → Application → Core
                    → EF.Core     → Core

❌ 错误的循环引用:
Application → Core → Application  (禁止!)

如果两层之间需要互相访问,应通过接口抽象和依赖注入来解耦:

// 在Core层定义接口
public interface INotificationService
{
    Task SendAsync(string message);
}

// 在Application层实现
public class NotificationService : INotificationService, ITransient
{
    public Task SendAsync(string message)
    {
        // 实现通知发送
        return Task.CompletedTask;
    }
}

// 在Core层的其他类中通过接口使用
// 不直接引用Application层

总结

本章深入介绍了Furion的项目架构与核心设计,包括分层架构、App静态类、启动配置、配置选项系统、模块化设计、应用生命周期管理以及多环境配置等内容。

关键要点:

  • 分层架构遵循从下到上的引用关系,Core层无依赖,Web.Entry层在最顶层
  • App静态类是Furion的核心入口,提供了获取配置、服务和环境信息的统一方式
  • AppStartup类通过Order控制启动顺序,实现了模块化的启动配置
  • 配置选项系统支持强类型配置绑定和热重载
  • 多环境配置通过配置文件层级和环境变量实现不同环境的差异化配置

下一章预告:第四章将详细介绍Furion最具特色的功能——动态WebAPI开发,包括零Controller开发、路由规则、HTTP方法约定等内容。