.NET桌面应用架构Demo与实战|WPF+MVVM+EFCore+IOC+DI+Code First+AutoMapper

目录

.NET桌面应用架构Demo与实战|WPF+MVVM+EFCore+IOC+DI+Code First+AutoPapper

该应用程序旨在高效地管理日常工作中所需的AI Prompt,支持新增、编辑、删除、快速复制以及查询等功能。然而,这些功能本身并不是本文讨论的重点;更关注的是背后的技术实现与架构设计思路。功能灵感来自 ChatHub - GPT-4o, Claude 3.5, Gemini 1.5 side by side

技术栈简述

  • MVVM设计模式 将应用程序分为三个核心组件:模型(Model)、视图(View)和视图模型(ViewModel)。这种分离关注点的方法提高了应用程序的可维护性和可测试性。
  • EFCore :一个轻量级的、可扩展的ORM(对象关系映射)框架,用于.NET平台。它允许使用C#代码来处理数据库操作,而不是直接编写SQL语句。
  • Code First模式:EFCore支持Code First开发模式,开发者可以直接通过C#类定义来创建数据库模型,然后EFCore会自动生成数据库架构。
  • IOC :一种设计原则,它倒置了传统程序设计中的控制流,使得高层模块不依赖于低层模块,两者都依赖于抽象。
  • DI :实现IOC的一种方式,它通过外部注入依赖来实现模块间的解耦。
  • AutoMapper:是一个对象到对象的映射库,它可以帮助开发者自动转换不同类型的对象,例如将数据库实体(Model)映射到视图模型(ViewModel)。

总体来讲,EFCore简化了数据库操作,而IOC和DI则提高了应用程序的可维护性和可测试性,AutoMapper的加入进一步减少了对象映射的复杂性。使用这些技术栈的组合,使得AI Prompt管理应用的开发更加高效、灵活和可靠。

项目地址:

https://github.com/Nita121388/NitasDemo/tree/main/08WPFArctitectureDemo

功能展示

软件启动后,会展示一个列表,其中包含了所有的AI Prompt(AI Prompt)。用户可以通过以下操作来管理这些Prompt:

  1. 查看所有Prompt:软件启动时,用户将看到一个包含所有AIPrompt的列表,方便快速浏览。
  2. 新增Prompt:用户可以通过点击"新增"按钮来添加新的AI Prompt。点击后,会弹出一个编辑界面,用户可以在这里输入标题和内容,完成新Prompt的创建。
  3. 编辑Prompt:在Prompt列表中,用户可以通过点击"编辑"按钮来修改现有的AIPrompt。编辑界面会加载当前Prompt的内容,用户可以对其进行修改。
  4. 删除Prompt:用户可以通过点击"删除"按钮来移除不再需要的AIPrompt。
  5. 使用Prompt:用户可以通过点击"使用"按钮来将选中的AI Prompt复制到系统剪贴板,用户就可以将Prompt粘贴到任何需要的AI对话系统中,以便快速输入或引用。
  6. 导出Prompt:用户可以通过点击"导出"按钮来保存当前所有的AI Prompt到一个Json文件中。这个操作允许用户将Prompt列表导出为JSON文件,方便备份或在其他设备上导入。
  7. 导入Prompt :用户可以通过点击"导入"按钮来从文件中加载AI Prompt。点击后,会弹出一个文件选择对话框,用户可以选择包含Prompt信息的文件进行导入。导入成功后,软件会将文件中的Prompt添加到现有的Prompt列表中。

项目结构

应用分层架构通过将应用程序划分为UI层、服务层、业务逻辑层、数据访问层和领域模型层,实现了关注点分离。每个层级都有明确的职责,UI层负责用户界面,服务层提供业务操作接口,业务逻辑层处理具体的业务需求,数据访问层处理数据库交互,而领域模型层则定义了业务实体。

c# 复制代码
WFArchitectureDemo                                                         
 │                                                                            
 ├───────►UI                                                                  
 │         │                                                                  
 │         ├─────►Common                                                      
 │         │        Helper                                                    
 │         │        HostBuilders               //主机构建器(HostBuilder)    配置应用程序的各种设置
 │         ├─────►Converters                                                  
 │         │        PercentageConverter.cs                                       
 │         ├─────►ViewModels                                                  
 │         ├─────►Views                                                       
 │         ├─────►App.xaml                                                    
 │         │      App.xaml.cs                  //依赖注入与配置管理                                 
 │         ├─────►appsettings.json             //数据库设置                               
 │         └─────►MainWindow.xaml              //启动界面                                                                                                          
 │                                                                            
 ├────────►Service                                                            
 │         │                                                                  
 │         ├─────►IServices                    //服务接口                              
 │         │        IPromptService.cs                                         
 │         ├─────►Services                     //服务实现                                
 │         │        PromptService.cs                                          
 │         └─────►Result.cs                                                                                                                              
 │                                                                            
 ├────────►Business                                                           
 │         │                                                                  
 │         ├────►IManager                      //业务逻辑接口                              
 │         │      IPromptManager.cs                                           
 │         │                                                                  
 │         ├────►Manager                       //具体的业务逻辑                               
 │         │      PromptManager.cs                                            
 │         │                                                                  
 │         └────►DTO                           //数据传输对象                               
 │                PromptDTO.cs                                                
 │                PromptUsageDTO.cs                                           
 │                                                                            
 ├────────►Data                                //负责与数据库的直接交互,包括CRUD操作                              
 │         │                                                                  
 │         ├────►RepositoryFactory                                            
 │         │      RepositoryFactory.cs                                        
 │         │                                                                  
 │         ├────►IRepository                   //仓储模式、CURD接口  
 │         │      IRepository.cs
 │         │      IPromptRepository.cs                                        
 │         │      IPromptUsageRepository.cs                                  
 │         │                                                                  
 │         ├────►Repository                    //仓储模式、CURD实现                            
 │         │      PromptRepository.cs                                         
 │         │      PromptUsageRepository.cs                                   
 │         │                                                                  
 │         ├────►DatabaseContexts              //EF Core的数据库上下文                               
 │         │      DbContextFactory.cs                                         
 │         │      PromptDbContext.cs                                          
 │         │                                                                  
 │         └────►Migrations                    //数据迁移文件,管理数据库变化                               
 │                                                                            
 └────────►Domainn                             //领域模型                               
           │                                                                  
           └────►Models                                                       
                  Prompt.cs                                                   
                  PromptUsage.cs                                              

项目引用

  • CommunityToolkit.Mvvm
  • Microsoft.Xaml.Behaviors.Wpf
  • AutoMapper
  • Microsoft.Extensions.Configuration
  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Hosting
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.EntityFrameworkCore.Design

以下为该项目一步一步一层层的实现大致过程和概述。

1. 新建模型

c# 复制代码
public class Prompt
{
    public long ID { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public bool IsDelete { get; set; }
    public DateTime CreateDateTime { get; set; }
    public DateTime UpdateDateTime { get; set; }

    public ICollection<PromptUsage> PromptUsages { get; set; } = new List<PromptUsage>();
}

public class PromptUsage
{
    public long ID { get; set; }
    public long PromptID { get; set; }
    public DateTime CreateDateTime { get; set; }
    public Prompt Prompt { get; set; }
}

2. Data层,依赖EF Core,实现数据库增删改查

该层引用:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.EntityFrameworkCore.Design
  1. 定义仓储接口IRepository

    c# 复制代码
    public interface IRepository<T>
    {
        T Add(T entity);
        void Delete(long id);
        void Update(T entity);
        List<T> Get();
        List<T> Select(string filterSql,string orderBySql);
        T Get(long id);
        List<T> GetByForeignKey(long id);
    }
  2. IPromptRepository和IPromptUsageRepository都继承自IRepository 接口,在由PromptRepository和PromptUsageRepository

  3. PromptRepository和PromptUsageRepository依赖DbContextFactory提供的数据库上下文,具体实现增删改查

  4. PromptDbContext是构建数据访问层的核心,继承DbContext,使用 EF Core 来定义数据库上下文、配置实体和它们之间的关系。

    c# 复制代码
     public class PromptDbContext : DbContext
     {
         public DbSet<Prompt> Prompts { get; set; }
         public DbSet<PromptUsage> PromptUsages { get; set; }
    
         public PromptDbContext(DbContextOptions options) : base(options)
         { } //设计时不能包含此行内容 
         
    
         protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              // 配置 Prompt 实体
              modelBuilder.Entity<Prompt>(entity =>
              {
                  entity.HasKey(e => e.ID);
                  entity.HasMany(e => e.PromptUsages)
                      .WithOne(u => u.Prompt)
                      .HasForeignKey(u => u.PromptID);
              });
    
              // 配置 PromptUsage 实体
              modelBuilder.Entity<PromptUsage>(entity =>
              {
                  entity.HasKey(e => e.ID);
              });
          }
     }
  5. Code First模式,生成迁移文件,创建数据库 💡

    1. Microsoft.EntityFrameworkCore.Tools是一个包含了用于创建、更新和管理数据库迁移的命令行工具的程序包。这些工具允许开发者在不直接修改数据库架构的情况下,通过代码来跟踪和更新数据库的变化。这些迁移可以应用于不同的数据库环境,从而帮助实现数据库的持续集成和持续部署。
    2. Microsoft.EntityFrameworkCore.Design包则主要用于在设计时为EF Core提供支持和功能,包括生成代码和模型等。虽然它本身不直接提供迁移功能,但它是迁移功能所依赖的一个重要组件。
      在Vistual Studio →工具→NuGet包管理器→程序包管理器控制台,执行以下命令:
    markdown 复制代码
    PM> Add-Migration Init
    Build started...
    Build succeeded.
    To undo this action, use Remove-Migration.
    PM> update-database
    Build started...
    Build succeeded.
    Applying migration '20241108031752_Init'.
    Done.
    PM> 

    然后可以看到项目中新增了文件夹Migrations,其包含20241114045010_init.cs和PromptDbContextModelSnapshot

  • 20241114045010_init.cs:包含了一个迁移类,它定义了数据库架构的变化。这个类包含了UpDown方法,分别用于应用迁移和撤销迁移。
  • PromptDbContextModelSnapshot:它包含了当前数据库上下文的模型快照。这个快照用于在添加新的迁移时比较模型的变化,确保迁移的准确性。

3. Bussiness层,实现具体的业务逻辑

Bussiness层调用Data层实现具体的业务逻辑,要依旧这层依赖倒置原则(Dependency Inversion Principle, DIP)------高层模块不应该依赖于低层模块,二者都应该依赖于其抽象;抽象不应该依赖于细节,细节应该依赖于抽象。IManager中是对外提供的接口,Manager中是具体的实现。

4. Service层,业务逻辑封装

Service层调用Bussiness层 可以进一步封装和抽象业务逻辑,使其更加模块化和可重用。

5. UI层,用户界面

初始化应用程序的服务和组件

App.xaml.cs开始,这是WPF应用程序的入口点。创建了一个继承自ApplicationApp类,并在其中初始化应用程序的服务和组件,处理启动和退出事件,并确保数据库的迁移和主窗口的显示。

主要组件

  1. IServiceProvider:提供对应用程序服务的访问。
  2. IHost:.NET Core的主机构建器,用于配置和启动应用程序。

IHost主机构建器

定义一个CreateHostBuilder方法,它使用Host.CreateDefaultBuilder来设置主机构建器,并通过一系列静态类来扩展IHostBuilder,来简化主机构建器的配置,以便在应用程序启动时注册所需的服务。

c# 复制代码
 public static IHostBuilder CreateHostBuilder(string[] args = null)
    {
        return Host.CreateDefaultBuilder(args)
            .AddConfiguration()
            .AddAutoMapper()
            .AddDbContext()
            .AddServices()
            .AddViewModels()
            .AddViews();
    }

通过模块化配置,将不同的功能(如数据库访问、业务逻辑、视图模型等)分离到不同的类中,从而提高代码的可读性和可维护性。添加了以下组件:

配置:通过AddConfiguration方法加载appsettings.json和环境变量。
c# 复制代码
 public static IHostBuilder AddConfiguration(this IHostBuilder host)
 {
     host.ConfigureAppConfiguration(c =>
     {
         c.AddJsonFile("appsettings.json");
         c.AddEnvironmentVariables();
     });

     return host;
 }
注册对象映射

在代码中涉及将DTO对象转为ViewModel,此处采用AutoMapper来实现便捷的映射

c# 复制代码
 public static IHostBuilder AddAutoMapper(this IHostBuilder host)
 {
     host.ConfigureServices((context, services) =>
     {
         services.AddAutoMapper(typeof(App));
     });
     return host;
 }
数据库配置

通过AddDbContext方法配置Entity Framework Core,数据库上下文

c# 复制代码
public static IHostBuilder AddDbContext(this IHostBuilder host)
{
    host.ConfigureServices((context, services) =>
    {
        string connectionString = context.Configuration.GetConnectionString("sqlite");
        Action<DbContextOptionsBuilder> configureDbContext = o => o.UseSqlite(connectionString);

        // 注册 DbContext
        services.AddDbContext<PromptDbContext>(configureDbContext);
        services.AddSingleton<DbContextFactory>(sp => new DbContextFactory(configureDbContext));
        services.AddSingleton<RepositoryFactory>(sp => new RepositoryFactory(configureDbContext));
    });
    return host;
}
注册应用程序服务
c# 复制代码
public static IHostBuilder AddServices(this IHostBuilder host)
{
    host.ConfigureServices(services =>
    {
        // 注册 IPromptManager 和 IPromptService
        services.AddSingleton<IPromptManager, PromptManager>(sp =>
        {
            var repositoryFactory = sp.GetRequiredService<RepositoryFactory>();
            return new PromptManager(repositoryFactory);
        });

        services.AddSingleton<IPromptService, PromptService>(sp =>
        {
            var promptManager = sp.GetRequiredService<IPromptManager>();
            return new PromptService(promptManager);
        });
    });
    return host;
}

使用

c# 复制代码
 private IPromptService _promptService;
 private IMapper _mapper;
 public PromptsViewModel()
 {
     _promptService = App.Current.Services.GetRequiredService<IPromptService>();
     _mapper = App.Current.Services.GetRequiredService<IMapper>();
 }
注册视图模型
c# 复制代码
 public static IHostBuilder AddViewModels(this IHostBuilder host)
 {
     host.ConfigureServices(services =>
     {
         services.AddTransient<PromptsViewModel>();
     });

     return host;
 }

使用:

c# 复制代码
 public MainWindow()
 {
     InitializeComponent(); 
     ViewModel = App.Current.Services.GetRequiredService<PromptsViewModel>();
     ViewModel.Refresh();
     DataContext = ViewModel;
 }
注册视图
c# 复制代码
public static IHostBuilder AddViews(this IHostBuilder host)
{
    host.ConfigureServices(services =>
    {
        services.AddSingleton<MainWindow>();
    });

    return host;
}

使用

c# 复制代码
 Window window = _host.Services.GetRequiredService<MainWindow>();
 window.Show();
定义AutoMapper映射规则

AutoMapper用于简化对象之间的映射。创建了一个MappingProfile类来定义映射规则。

c# 复制代码
public class MappingProfile : Profile
{
    public MappingProfile()
    {
        // PromptUsageViewModel => PromptUsageDTO
        CreateMap<PromptUsageViewModel, PromptUsageDTO>();

        //PromptUsageDTO => PromptUsageViewModel
        CreateMap<PromptUsageDTO, PromptUsageViewModel>();

        // PromptViewModel => PromptDTO
        CreateMap<PromptViewModel, PromptDTO>()
            .ForMember(
            dest => dest.Usages, 
            opt => opt.MapFrom(src => src.Usages.ToList()));

        CreateMap<PromptDTO, PromptViewModel>()
            .ForMember(
            dest => dest.Usages,
            opt => opt.MapFrom(src => src.Usages.ToList()));

    }
}

使用示例

c# 复制代码
var promptViewModel = _mapper.Map<PromptViewModel>(promptDTO);
应用程序启动与关闭

OnStartup方法中,启动主机(这会触发所有服务的注册和初始化),然后执行数据库迁移,最后显示主窗口。在OnExit方法中,停止并释放主机资源。

c# 复制代码
/// <summary>
/// 在应用程序启动时执行的操作。
/// </summary>
protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e); 

    // 启动主机,这将触发所有服务的注册和初始化。
    _host?.Start();

    // 获取DbContextFactory服务的实例,用于创建数据库上下文。
    DbContextFactory contextFactory = _host.Services.GetRequiredService<DbContextFactory>();

    // 使用DbContextFactory创建数据库上下文实例,并应用所有未应用的迁移。
    // 这确保了数据库结构是最新的,以匹配应用程序的数据模型。
    using (var context = contextFactory.CreateDbContext())
    {
        context.Database.Migrate();
    }

    // 获取MainWindow服务的实例
    Window window = _host.Services.GetRequiredService<MainWindow>();

    // 显示主窗口
    window.Show();
}
c# 复制代码
protected override async void OnExit(ExitEventArgs e)
{
    // 等待_host对象的StopAsync方法完成,这个方法应该是停止一些后台服务的操作。
    await _host.StopAsync();
    
    // 释放_host对象所占用的资源。
    _host.Dispose();

    // 调用基类的OnExit方法,确保基类的退出逻辑也能被执行。
    base.OnExit(e);
}

示例

c# 复制代码
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    public new static App Current => (App)Application.Current;
    public IServiceProvider Services { get; }
    private readonly IHost _host;
    public App()
    {
        _host = CreateHostBuilder().Build();
        Services = _host.Services;
    }

    public static IHostBuilder CreateHostBuilder(string[] args = null)
    {
        return Host.CreateDefaultBuilder(args)
            .AddConfiguration()
            .AddAutoMapper()
            .AddDbContext()
            .AddServices()
            .AddViewModels()
            .AddViews();
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        _host?.Start();

        DbContextFactory contextFactory = _host.Services.GetRequiredService<DbContextFactory>();
        using (var context = contextFactory.CreateDbContext())
        {
            context.Database.Migrate();
        }

        Window window = _host.Services.GetRequiredService<MainWindow>();
        window.Show();
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        await _host.StopAsync();
        _host.Dispose();
        base.OnExit(e);
    }
}

UI+ViewModelM界面交互处理

在用户界面层(UI Layer)中,采用了CommunityToolkit.Mvvm库来实现MVVM(Model-View-ViewModel)设计模式。通过MVVM模式,实现了命令绑定和数据绑定,使得用户界面与业务逻辑之间的交互更加清晰和高效。因为此处不是本文重点所以不详细介绍啦。

参考

  1. 实体框架文档中心 | Microsoft Learn
  2. https://www.youtube.com/playlist?list=PLA8ZIAm2I03jSfo18F7Y65XusYzDusYu5
  3. Construction --- AutoMapper documentation
  4. SingletonSean/SimpleTrader:全栈 WPF MVVM 交易应用程序。 --- SingletonSean/SimpleTrader: A full stack WPF MVVM trading application.
  5. xtenzQ/WPF-MVVM-EFC-Example: 📲 MVVM (WPF) application built with EFCore, Abstract Factory pattern and dependency injection (Microsoft Unity)

平平无奇的代码小白,如有问题请多指正!

相关推荐
pengkai火火火16 分钟前
基于springmvc拓展机制的高性能日志审计框架的设计与实现
spring boot·安全·微服务·架构
想用offer打牌1 小时前
数据库大事务有什么危害(面试版)
数据库·后端·架构
踏浪无痕1 小时前
别再只会用 Feign!手写一个 Mini RPC 框架搞懂 Spring Cloud 底层原理
后端·面试·架构
一个帅气昵称啊2 小时前
.Net通过EFCore和仓储模式实现统一数据权限管控并且相关权限配置动态生成
.net·efcore·仓储模式
guslegend2 小时前
第2节:项目性能优化(中)
架构
Xの哲學2 小时前
Linux链路聚合深度解析: 从概念到内核实现
linux·服务器·算法·架构·边缘计算
山沐与山3 小时前
【RabbitMQ】架构与集群模式详解
架构·rabbitmq·ruby
未来影子3 小时前
agent构建狼人杀案例
架构
莫比乌斯环3 小时前
【日常随笔】Android 跳离行为分析 - Instrumentation
android·架构·代码规范
Coder个人博客3 小时前
LinuxPTP 整体架构框图与源码深度分析
架构