2024年11月

2023年10月份写过一篇《
本计划在 .NET 8 中推出的 WASI 推迟到 .NET 9
》[1],根据
此问题
[2],在 .NET 9 RTM 中似乎不会有wasi-experimental,仅使用
componentize-dotnet
[3]的项目才能工作/将被更新,WASI 实验正在 NET 10 中继续进行。

2024年9月份字节码联盟发布了一篇文章《
使用 componentize-dotnet 为 .NET/C# 开发人员简化组件
》[4],文章总结了 componentize-dotnet 项目,这是一个为.NET/C#开发者提供的工具,使得将代码编译为WebAssembly组件变得简单。这个由Bytecode Alliance发起的项目是一个NuGet包,允许从.NET应用程序创建完全AOT编译的组件,为.NET开发者提供了与Rust和TinyGo相当的组件体验。文章详细介绍了如何使用.NET 9 Preview 7和 componentize-dotnet 开始构建组件,包括安装必要的软件、配置NuGet以引用实验性包源、添加 BytecodeAlliance.Componentize.DotNet.Wasm.SDK 包到项目中,并展示了如何构建和运行一个简单的WebAssembly组件。此外,文章还探讨了如何简化组件工作流程,特别是如何使用WebAssembly Interface Type (WIT)定义来实现组件间的互操作性。最后,文章提到了.NET 9最终发布时将支持通过Mono编译器生成组件,以及 componentize-dotnet 项目将如何为用户提供选择 NativeAOT-LLVM 或Mono编译器的便利。文章鼓励有兴趣的开发者加入Bytecode Alliance社区并参与 componentize-dotnet 项目。

根据这篇文章的最新的适用于 .NET 9 RC 2 和 Wasmtime 26.0.0的代码示例:
https://github.com/henrikrxn/webassembly-experiments/blob/main/dotnet-9/componentize-dotnet/README.md[5
]

相关链接:

git-commit-id-plugin
是一个 Maven 插件,用于在 Maven 项目的构建过程中自动获取 git 仓库的信息,如最后一次提交的 ID、分支名称、构建时间等,并将这些信息注入到项目的属性文件中。这对于跟踪项目版本和构建状态非常有用。

以下是如何在 Maven 项目中使用
git-commit-id-plugin
的基本步骤:

1 添加插件到 pom.xml 文件中

在你的 Maven 项目的
pom.xml
文件中,添加
git-commit-id-plugin

<plugins>
部分。

  <build>
    <plugins>
      <plugin>
        <!-- https://mvnrepository.com/artifact/pl.project13.maven/git-commit-id-plugin -->
        <groupId>pl.project13.maven</groupId>
        <artifactId>git-commit-id-plugin</artifactId>
        <version>4.0.5</version>
        <executions>
          <execution>
            <goals>
              <goal>revision</goal>
            </goals>
          </execution>
        </executions>
        <!-- 配置选项 -->
        <configuration>
          <dotGitDirectory>${project.basedir}/../.git</dotGitDirectory>
          <generateGitPropertiesFile>true</generateGitPropertiesFile>
          <failOnNoGitDirectory>false</failOnNoGitDirectory>
          <skipPoms>false</skipPoms>
          <generateGitPropertiesFilename>
            ${project.build.outputDirectory}/git.properties
          </generateGitPropertiesFilename>
          <gitDescribe>
            <!-- don't generate the describe property -->
            <skip>false</skip>
            <!-- abbrev commit id length -->
            <abbrev>8</abbrev>
          </gitDescribe>
          <includeOnlyProperties>
            <includeOnlyProperty>^git.branch$</includeOnlyProperty>
            <includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
            <includeOnlyProperty>^git.commit.(id|time)$</includeOnlyProperty>
            <includeOnlyProperty>^git.commit.id.abbrev$</includeOnlyProperty>
          </includeOnlyProperties>
        </configuration>
      </plugin>
    </plugins>
  </build>

2 配置插件


<configuration>
标签中,你可以配置多种选项,例如生成的属性文件的位置、包含哪些 git 属性等。默认情况下,插件会生成一个
git.properties
文件在
target/classes
目录下。

   <configuration>
       <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
       <generateGitPropertiesFile>true</generateGitPropertiesFile>
       <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
       <format>properties</format>
   </configuration>
  1. 构建项目:

使用 Maven 命令构建项目,例如
mvn clean install
。在构建过程中,
git-commit-id-plugin
插件会自动运行,并在指定的位置生成包含 git 信息的
git.properties
文件。

4 查看 git.properties 文件

打开生成的
git.properties
文件,你将看到类似以下的内容,其中包含了提交时间、提交记录、分支等信息:

#Generated by Git-Commit-Id-Plugin
git.branch=main
git.build.host=heal-mac
git.build.time=2024-10-30T15\:04\:10+0800
git.build.version=1.0.0-SNAPSHOT

git.commit.id=abcdef1234567890
git.commit.id.abbrev=abcdef1
git.commit.time=2024-10-28T12\:34\:56+0800

git.commit.message.full=Initial commit
git.commit.message.short=Initial commit

git.commit.user.name=Your Name
git.commit.user.email=your.email@example.com

git.commit.author.time=2024-10-26T21\:11\:39+0800
git.commit.committer.time=2024-10-26T21\:11\:39+0800

5 在应用中使用这些信息

你可以在应用程序中读取
git.properties
文件,并使用这些信息,例如显示当前版本的 Git 分支和提交 ID。

通过这种方式,你可以利用
git-commit-id-plugin
插件来自动获取和使用 Git 的提交时间、提交记录、分支等信息,从而帮助跟踪和管理你的项目版本。

在Java中,对于基本类型可以使用“="来进行克隆,此时两个变量除了相等是没有任何关系的。

而对于
引用类型却不能简单地使用”=“进行克隆
,这与java的内存空间使用有关。

Java将内存空间分成两块,即栈和堆。

在栈中保存基本类型和引用变量;

在堆中保存对象。

对于引用变量而言,使用”=“将修改引用,而不是复制堆中的对象。

此时两个引用变量将指向同一个对象。

因此,如果一个变量对其进行修改则会改变另一个变量。

在克隆对象时,如果对象的成员变量是基本类型,则使用浅克隆即可完成。

如果对象的成员变量包括可变引用类型,则需要使用深克隆。

如果引用类型是不可变的,如String类的对象,则不必进行深克隆。

在很多程序中,封装常用消息对话框有很多好处,尤其是在 GUI 应用程序中,本篇随笔结合.net 开发Winform界面的经验,对使用wxpython开发中 wx.MessageDialog 和 wx.lib.agw.genericmessagedialog.GenericMessageDialog 两种不同消息对话框的处理,对其进行简单封装,以适应程序开发的需要。

1、消息对话框的封装处理的优势

对常用消息对话框的封装处理,具有以下是一些主要的优点:

1.
代码复用

封装消息对话框可以避免重复代码。你可以定义一个统一的函数或类来处理所有消息对话框,从而在多个地方复用这段代码。

2.
一致性

通过封装,你可以确保所有消息对话框的外观和行为一致。这有助于提高用户体验,使用户在应用程序中获得统一的交互方式。

3.
简化调用

封装可以简化调用过程。你可以将常用的参数设置(如标题、图标、按钮类型等)预先定义好,从而在调用时减少参数输入。

4.
易于维护

当需要更改对话框的行为或样式时,只需在封装函数中进行修改,而不必在应用程序中的每个调用点进行更改。这使得维护变得更加简单和高效。

5.
增强可读性

通过使用封装的函数或类,代码变得更易读。其他开发者可以一眼看出对话框的作用,而不必深入了解其具体实现。

6.
集中管理

封装有助于集中管理对话框的逻辑,比如处理用户输入、响应用户选择等。这样可以更方便地进行逻辑更新或错误处理。

7.
扩展性

如果将来需要增加新的对话框或修改现有对话框的逻辑,封装使得扩展更加容易。你可以在封装的基础上进行扩展,而不影响现有的代码结构。

我在早期开发Winform的时候,对消息对话框进行了一些简单的封装,在随笔《
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(2)----常用操作
》中有介绍。

封装的消息提示对话框包括个各种常用的对话框,如下所示:

2、对使用wxpython开发中常用消息对话框的封装

为了方便,我们先写一个页面来测试相关消息对话框的封装处理,如下界面所示。

wxpython开发中 wx.MessageDialog 和 wx.lib.agw.genericmessagedialog.GenericMessageDialog 都时跨平台支持的,GenericMessageDialog
是 wxPython 的一个扩展库,提供了一个通用的消息对话框类,用于在不同平台上显示消息框。这个类是跨平台的,支持以下主要平台:

  1. Windows
    :在 Windows 操作系统上,
    GenericMessageDialog
    会使用系统样式来渲染对话框。
  2. macOS
    :在 macOS 上,它会遵循 Cocoa 的界面风格。
  3. Linux
    :在各种 Linux 发行版上,它会适应 GTK 或 Qt(如果 wxPython 是基于这些库构建的)风格。

MessageDialog  和
GenericMessageDialog
旨在提供一致的用户体验,无论在哪个平台上运行。

MessageDialog和GenericMessageDialog 的差别

wx.MessageDialog

wx.lib.agw.genericmessagedialog.GenericMessageDialog
是 wxPython 中用于显示消息对话框的两种不同类,它们之间有一些主要的差别:

1) 类别及实现方式

  • wx.MessageDialog
    :


    • 是 wxPython 的内置类,使用系统原生的对话框实现。
    • 适应系统的外观和风格,因此在不同平台上看起来会有所不同。
    • 通常用于显示简单的消息、确认或警告对话框。
  • wx.lib.agw.genericmessagedialog.GenericMessageDialog
    :


    • 是 wxPython 的 AGW(Advanced Generic Widgets)库中的一部分,提供了一个更灵活的通用消息对话框实现。
    • 允许更多的自定义选项和更复杂的布局,适合需要更多控制和自定义的场景。
    • 可以在所有平台上保持一致的外观,因为它不依赖于系统原生对话框。

2)自定义能力

  • wx.MessageDialog
    :


    • 自定义选项有限,主要集中在按钮、图标和消息文本。
    • 不支持复杂的布局或多种控件的组合。
  • wx.lib.agw.genericmessagedialog.GenericMessageDialog
    :


    • 提供更多的自定义选项,如设置按钮图标、对话框的最小尺寸和布局。
    • 可以添加更多控件(如文本框、图片等),适合更复杂的用户交互需求。

3) 使用场景

  • wx.MessageDialog
    :


    • 适用于简单的确认消息、警告或信息提示场景。
    • 更适合于需要快速实现标准对话框的情况。
  • wx.lib.agw.genericmessagedialog.GenericMessageDialog
    :


    • 更适合于需要更高自定义和灵活性的应用程序。
    • 适用于复杂的对话框场景,比如需要显示额外信息或允许用户输入的情况。

如果你的需求简单,只需显示消息并获取用户确认,使用 wx.MessageDialog 是更简单的选择。
如果你需要更多的自定义功能或希望在多个平台上保持一致的外观,可以考虑使用 GenericMessageDialog。

消息对话框的常用代码如下所示。

    defShowMessageDialog(self):#创建消息对话框
        dlg =wx.MessageDialog(self,"This is a message dialog example.\nWould you like to proceed?","Confirmation", 
wx.YES_NO
| wx.CANCEL |wx.ICON_QUESTION)#显示对话框并处理用户选择 result =dlg.ShowModal()if result ==wx.ID_YES:
wx.MessageBox(
"You clicked Yes!", "Info")elif result ==wx.ID_NO:
wx.MessageBox(
"You clicked No!", "Info")elif result ==wx.ID_CANCEL:
wx.MessageBox(
"You clicked Cancel!", "Info")

dlg.Destroy()
#销毁对话框

以及

    defon_show_dialog(self, event):#创建 GenericMessageDialog
        dlg =GMD.GenericMessageDialog(self,"This is a message.","Message Title",
style
=wx.OK |wx.CANCEL)#设置图标(可选) dlg.SetYesNoCancelBitmaps(wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_MESSAGE_BOX))#显示对话框 result =dlg.ShowModal()if result ==wx.ID_OK:print("OK button clicked")elif result ==wx.ID_CANCEL:print("Cancel button clicked")

dlg.Destroy()

如果每次这样调用,我们需要了解很多相关的参数信息,一般我们只需要传入一些简单的信息提示即可,因此需要对它们进行简单的封装。

我们定义一个类,提供最原始简单的显示消息的处理函数,然后再在基础上进行特殊的简化封装即可。

classMessageUtil:"""封装了常用的消息对话框,以方便使用常用对话框消息。
包括提示信息、警告信息、错误信息、确认信息、询问信息、输入信息、
选择信息、多选信息、文件选择信息、目录选择信息、字体选择信息、颜色选择信息、进度条信息等。
"""@staticmethoddefshow_message(parent, message, caption, style):"""显示消息对话框-使用GenericMessageDialog实现"""dlg=GMD.GenericMessageDialog(parent, message, caption, style)
dlg.SetIcon(images.appIcon.GetIcon())
#设置窗口图标 result=dlg.ShowModal()
dlg.Destroy()
returnresult

@staticmethod
defshow_message2(parent, message, caption, style):"""显示消息对话框-使用wx.MessageDialog 实现"""dlg=wx.MessageDialog(parent, message, caption, style)

result
=dlg.ShowModal()
dlg.Destroy()
return result

其中 show_message 和show_message2 是针对两者不同消息类的封装,我们可以根据实际需要替换来用即可。如对于常规的提示消息、告警、错误消息框,简单设置一下参数即可。

    #常用消息对话框的标题
    CAPTION_TIPS = "提示信息"CAPTION_WARNING= "警告信息"CAPTION_ERROR= "错误信息"CAPTION_CONFIRM= "确认信息"@staticmethoddef show_tips(parent, message, caption=CAPTION_TIPS):"""显示一般的提示信息"""
        returnMessageUtil.show_message(
parent, message, caption, wx.OK
|wx.ICON_INFORMATION
)
@staticmethod
def show_warning(parent, message, caption=CAPTION_WARNING):"""显示警告信息""" returnMessageUtil.show_message(
parent, message, caption, wx.OK
|wx.ICON_WARNING
)

@staticmethod
def show_error(parent, message, caption=CAPTION_ERROR):"""显示错误信息""" return MessageUtil.show_message(parent, message, caption, wx.OK | wx.ICON_ERROR)

而对于提供更多按钮的,也是设置参数即可,如下所述。

@staticmethoddef show_yes_no_tips(parent, message, caption=CAPTION_TIPS):"""显示询问用户信息,并显示提示标志"""
        returnMessageUtil.show_message(
parent, message, caption, wx.YES_NO
|wx.ICON_INFORMATION
)

@staticmethod
def show_yes_no_warning(parent, message, caption=CAPTION_WARNING):"""显示询问用户信息,并显示警告标志""" returnMessageUtil.show_message(
parent, message, caption, wx.YES_NO
|wx.ICON_WARNING
)

@staticmethod
def show_yes_no_error(parent, message, caption=CAPTION_ERROR):"""显示询问用户信息,并显示错误标志""" returnMessageUtil.show_message(
parent, message, caption, wx.YES_NO
|wx.ICON_ERROR
)

另外,wxpython还提供了TextEntryDialog、SingleChoiceDialog、MultiChoiceDialog等对话框,我们也可以简单封装一下使用。

@staticmethoddefshow_input_dialog(parent, message, caption, default_value):"""显示输入对话框"""dlg=wx.TextEntryDialog(parent, message, caption, default_value)
result
=dlg.ShowModal()if result ==wx.ID_OK:returndlg.GetValue()else:returnNone

@staticmethod
defshow_choice_dialog(parent, message, caption, choices):"""显示选择对话框"""dlg=wx.SingleChoiceDialog(parent, message, caption, choices)
result
=dlg.ShowModal()if result ==wx.ID_OK:returndlg.GetStringSelection()else:returnNone

@staticmethod
defshow_multi_choice_dialog(parent, message, caption, choices):"""显示多选对话框"""dlg=wx.MultiChoiceDialog(parent, message, caption, choices)
result
=dlg.ShowModal()if result ==wx.ID_OK:returndlg.GetSelections()else:return None

最后如下效果所示。

这样我们在程序里面统一调用就会有相同的效果,而且简化了很多不必要的参数。

MessageUtil.show_tips(None, "This is a test program.", "关于")

以上就是一些日常开发的函数处理和抽象处理,主要就是为了简化实际开发的时候的一些复杂度,并提供统一的界面效果。

最小系统

创建API项目

创建API项目并安装以下依赖

修改Program.cs为以下内容

using BookApp;

var builder = WebApplication.CreateBuilder(args);

await builder.AddApplicationAsync<BookAbpModule>();

builder.Host.UseAutofac();

var app = builder.Build();

await app.InitializeApplicationAsync();

await app.RunAsync();

创建BookAbpModule.cs

using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.Application;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;

namespace BookApp
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpSwashbuckleModule)
    )]
    public class BookAbpModule : AbpModule
    {
        override public void ConfigureServices(ServiceConfigurationContext context)
        {
            ConfigureSwaggerServices(context.Services);
        }


        override public void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            app.UseStaticFiles();
            app.UseRouting();

            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookApp API");
            });

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

        private void ConfigureSwaggerServices(IServiceCollection services)
        {
            services.AddAbpSwaggerGen(
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookApp API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                }
            );
        }
    }
}

模块化编程

新建AbpModuleA类库并引用Volo.Abp.Core

//加AbpModuleAModule.cs
using System.IO;

using System;
using Volo.Abp.Modularity;
using Volo.Abp;

namespace AbpModuleA
{
    public class AbpModuleAModule: AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleA.PreConfigureServices ");
        }

        override public void PostConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleA.PostConfigureServices");
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleA.ConfigureServices");


        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("AbpModuleA.OnApplicationInitialization");

        }

        public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("AbpModuleA.OnPreApplicationInitialization");
        }
        override public void OnPostApplicationInitialization(ApplicationInitializationContext context)
        {   
            Console.WriteLine("AbpModuleA.OnPostApplicationInitialization");
        }
    }
}

新建AbpModuleB类库并引用Volo.Abp.Core

//加AbpModuleBModule.cs
using System.IO;

using System;
using Volo.Abp.Modularity;
using Volo.Abp;

namespace AbpModuleB
{
    public class AbpModuleBModule: AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleB.PreConfigureServices");
        }

        override public void PostConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleB.PostConfigureServices");
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("AbpModuleB.ConfigureServices");


        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("AbpModuleB.OnApplicationInitialization");

        }

        public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("AbpModuleB.OnPreApplicationInitialization");
        }
        override public void OnPostApplicationInitialization(ApplicationInitializationContext context)
        {   
            Console.WriteLine("AbpModuleB.OnPostApplicationInitialization");
        }
    }
}

修改Api项目的模块配置文件

//BookAbpModule.cs

using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.Application;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using AbpModuleA;
using AbpModuleB;

namespace BookApp
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpSwashbuckleModule),
        typeof(AbpModuleAModule),
        typeof(AbpModuleBModule)

    )]
    public class BookAbpModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.PreConfigureServices ");
        }

        override public void PostConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.PostConfigureServices");
        }

        override public void ConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.ConfigureServices");

            ConfigureSwaggerServices(context.Services);
        }


        override public void OnApplicationInitialization(ApplicationInitializationContext context)
        {

            Console.WriteLine("BookAbpModule.OnApplicationInitialization");

            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();
            app.UseRouting();

            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookApp API");
            });
            app.UseConfiguredEndpoints();
        }

        public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("BookAbpModule.OnPreApplicationInitialization");
        }
        override public void OnPostApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("BookAbpModule.OnPostApplicationInitialization");
        }

        private void ConfigureSwaggerServices(IServiceCollection services)
        {
            services.AddAbpSwaggerGen(
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookApp API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                }
            );
        }
    }
}


运行结果

我们会发现,系统加载所有继承AbpModule的文件,并按序运行里面的方法实现对模块的配置

访问数据库

新建Entities文件夹并创建Book.cs

using Volo.Abp.Domain.Entities;

namespace BookApp.Entities
{
    public class Book : Entity<Guid>
    {
        public string Name { get; set; }
    }
}

添加Data目录并添加BookAbpDbContext.cs

using BookApp.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Reflection.Emit;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;

namespace BookApp.Data
{
    [ConnectionStringName("Default")]
    public class BookAbpDbContext : AbpDbContext<BookAbpDbContext>
    {
        public BookAbpDbContext(DbContextOptions<BookAbpDbContext> options)
        : base(options)
        { }

        public DbSet<Book> Books { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<Book>(b =>
            {
                b.ToTable(nameof(Books));
            });
        }
    }
}

修改BookAbpModule.cs


using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.Application;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using AbpModuleA;
using AbpModuleB;
using Volo.Abp.EntityFrameworkCore;
using BookApp.Data;
using Volo.Abp.EntityFrameworkCore.Sqlite;

namespace BookApp
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpSwashbuckleModule),
        typeof(AbpDddApplicationModule),
        typeof(AbpDddDomainModule),
        typeof(AbpEntityFrameworkCoreSqliteModule),
        typeof(AbpModuleAModule),
        typeof(AbpModuleBModule)

    )]
    public class BookAbpModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.PreConfigureServices ");
        }

        override public void PostConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.PostConfigureServices");
        }

        override public void ConfigureServices(ServiceConfigurationContext context)
        {
            Console.WriteLine("BookAbpModule.ConfigureServices");

            ConfigureSwaggerServices(context.Services);

            // 使用sqlite作为数据库
            context.Services.AddAbpDbContext<BookAbpDbContext>(options =>
            {
                options.AddDefaultRepositories(includeAllEntities: true);
            });

            Configure<AbpDbContextOptions>(options =>
            {
                options.UseSqlite();
            });
        }


        override public void OnApplicationInitialization(ApplicationInitializationContext context)
        {

            Console.WriteLine("BookAbpModule.OnApplicationInitialization");

            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();
            app.UseRouting();

            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookApp API");
            });

            app.UseConfiguredEndpoints();
        }

        public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("BookAbpModule.OnPreApplicationInitialization");
        }
        override public void OnPostApplicationInitialization(ApplicationInitializationContext context)
        {
            Console.WriteLine("BookAbpModule.OnPostApplicationInitialization");
        }

        private void ConfigureSwaggerServices(IServiceCollection services)
        {
            services.AddAbpSwaggerGen(
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookApp API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                }
            );
        }
    }
}

修改appsettings.json

{
  "ConnectionStrings": {
    "Default": "Data Source=BookApp.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

安装Nuget包"Microsoft.EntityFrameworkCore.Tools",并在在项目根目录下打开命令行工具,依次执行以下命令进行数据迁移和数据库更新:

dotnet ef migrations add InitialCreate
dotnet ef database update

新建Application目录

新建IBookAppService.cs

namespace BookApp.Application
{
    using BookApp.Entities;

    public interface IBookAppService
    {
        Task<string> CreateAsync(string name);
        Task<List<Book>> GetListAsync();
    }
}

新建BookAppService.cs

using BookApp.Entities;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace BookApp.Application
{
    public class BookAppService : ApplicationService, IBookAppService
    {
        public IRepository<Book, Guid> Repository => LazyServiceProvider.LazyGetRequiredService<IRepository<Book, Guid>>();

        public async Task<string> CreateAsync(string name)
        {
            var book = await Repository.InsertAsync(new Book()
            {
                Name = name
            });

            return book.Name;
        }

        public async Task<List<Book>> GetListAsync()
        {
            var list = await Repository.GetListAsync();
            return list;
        }

    }
}

在Controllers目录新建BookController.cs

using BookApp.Application;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using BookApp.Entities;

namespace BookApp.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class BookController : AbpController
    {
        private readonly IBookAppService _service;

        public BookController(IBookAppService service)
        {
            _service = service;
        }

        [HttpGet]
        public Task<string> CreateAsync(string name)
        {
            return _service.CreateAsync(name);
        }

        [HttpGet("list")]
        public Task<List<Book>> GetListAsync()
        {
            return _service.GetListAsync();
        }
    }
}

整个文件结构与包引用情况如下

运行结果如下

我们可以通过这两个接口添加与显示Book信息。

参考文章

作者:吴晓阳(手机:13736969112微信同号)