.Net5 框架搭建(一):简单三层架构+Freesql+Autofac

2023-11-22 06:48

本文主要是介绍.Net5 框架搭建(一):简单三层架构+Freesql+Autofac,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

由于业务需要,希望搭建一套基于Freesql(ORM)的简单易懂三层架构,按照目前主流的开发模式都是仓储层+三层架构在开发,本框架去除仓储层,有些命名也比较通俗易懂,用于学习就好,前面大部分都是在描述一些基本的三层架构搭建,想看Autofac(第三方依赖注入容器)怎么使用的可以直接拉到最下面。

正文

三层架构的大致流程图:
在这里插入图片描述
项目框架大概组成截图:
在这里插入图片描述

  • 0.Core:通用层
  • 1.Model:实体层
  • 2.DAL:数据访问层
  • 3.BLL:业务逻辑层
  • 4.WebApi:表示层

0.Core(通用层)

这层我存放一些通用的帮助类,包含封装的访问数据库上下文基类

1、直接Nuget添加这些包,或者直接项目编辑新增

  <ItemGroup><PackageReference Include="FreeSql" Version="2.5.100" /><PackageReference Include="FreeSql.Provider.MySql" Version="2.5.100" /><PackageReference Include="FreeSql.Provider.Oracle" Version="2.5.100" /><PackageReference Include="FreeSql.Provider.Sqlite" Version="2.5.100" /><PackageReference Include="FreeSql.Provider.SqlServer" Version="2.5.100" /><PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" /><PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" /><PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" /></ItemGroup>

2、基于IFreeSql创建一个访问数据库基类
FSqlBase.cs

using DXDF.Core.Common.Helper;
using FreeSql;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.Common
{public sealed class FBase{private static string connStr = Appsettings.app(new string[] { "connectionString" }).ObjToString();private static int dbType = Appsettings.app(new string[] { "dbType" }).ObjToInt();//private static string ConnStr = ConfigurationManager.ConnectionStrings["FreeSql:Default"].ConnectionString;public static readonly IFreeSql _fsql = new FreeSqlBuilder().UseConnectionString((DataType)dbType, connStr)//.UseSyncStructureToUpper(true)//.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper).Build();//public FBase()//{//    _fsql.Aop.CurdBefore += (s, e) =>//    {//        //System.Console.WriteLine(e.Sql);//        log.Debug(string.Format("\r\n---{0}---\r\n{1}", System.DateTime.Now,e.Sql));//    };//}}public abstract class FSqlBase{public IFreeSql fsql{get { return FBase._fsql; }}public FSqlBase(){fsql.Aop.CurdBefore += (s, e) =>{System.Console.WriteLine(e.Sql);};}}
}

3、封装一个操作appsettings.json的帮助类
Appsettings.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.Common.Helper
{/// <summary>/// appsettings.json操作类/// </summary>public class Appsettings{static IConfiguration Configuration { get; set; }static string contentPath { get; set; }public Appsettings(string contentPath){string Path = "appsettings.json";//如果你把配置文件 是 根据环境变量来分开了,可以这样写//Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";Configuration = new ConfigurationBuilder().SetBasePath(contentPath).Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性.Build();}public Appsettings(IConfiguration configuration){Configuration = configuration;}/// <summary>/// 封装要操作的字符/// </summary>/// <param name="sections">节点配置</param>/// <returns></returns>public static string app(params string[] sections){try{if (sections.Any()){return Configuration[string.Join(":", sections)];}}catch (Exception) { }return "";}/// <summary>/// 递归获取配置信息数组/// </summary>/// <typeparam name="T"></typeparam>/// <param name="sections"></param>/// <returns></returns>public static List<T> app<T>(params string[] sections){List<T> list = new List<T>();// 引用 Microsoft.Extensions.Configuration.Binder 包Configuration.Bind(string.Join(":", sections), list);return list;}}
}

4、数据类型转换的帮助类
UtilConvert.cs

using System;
namespace DXDF.Core.Common.Helper
{/// <summary>/// 转换帮助类/// </summary>public static class UtilConvert{/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static int ObjToInt(this object thisValue){int reval = 0;if (thisValue == null) return 0;if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)){return reval;}return reval;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <param name="errorValue"></param>/// <returns></returns>public static int ObjToInt(this object thisValue, int errorValue){int reval = 0;if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)){return reval;}return errorValue;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static double ObjToMoney(this object thisValue){double reval = 0;if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)){return reval;}return 0;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <param name="errorValue"></param>/// <returns></returns>public static double ObjToMoney(this object thisValue, double errorValue){double reval = 0;if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)){return reval;}return errorValue;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static string ObjToString(this object thisValue){if (thisValue != null) return thisValue.ToString().Trim();return "";}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static bool IsNotEmptyOrNull(this object thisValue){return ObjToString(thisValue) != "" && ObjToString(thisValue) != "undefined" && ObjToString(thisValue) != "null";}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <param name="errorValue"></param>/// <returns></returns>public static string ObjToString(this object thisValue, string errorValue){if (thisValue != null) return thisValue.ToString().Trim();return errorValue;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static Decimal ObjToDecimal(this object thisValue){Decimal reval = 0;if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)){return reval;}return 0;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <param name="errorValue"></param>/// <returns></returns>public static Decimal ObjToDecimal(this object thisValue, decimal errorValue){Decimal reval = 0;if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)){return reval;}return errorValue;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static DateTime ObjToDate(this object thisValue){DateTime reval = DateTime.MinValue;if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)){reval = Convert.ToDateTime(thisValue);}return reval;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <param name="errorValue"></param>/// <returns></returns>public static DateTime ObjToDate(this object thisValue, DateTime errorValue){DateTime reval = DateTime.MinValue;if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)){return reval;}return errorValue;}/// <summary>/// /// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static bool ObjToBool(this object thisValue){bool reval = false;if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval)){return reval;}return reval;}/// <summary>/// 获取当前时间的时间戳/// </summary>/// <param name="thisValue"></param>/// <returns></returns>public static string DateToTimeStamp(this DateTime thisValue){TimeSpan ts = thisValue - new DateTime(1970, 1, 1, 0, 0, 0, 0);return Convert.ToInt64(ts.TotalSeconds).ToString();}}
}

1.Model(实体层)

实体层,我主要在存放一些数据实体表、枚举类、自定义接收返回类
1、Nuget包

  <ItemGroup><PackageReference Include="FreeSql" Version="2.5.100" /><PackageReference Include="Newtonsoft.Json" Version="13.0.1" /></ItemGroup>

2、简单写一个用户表实体

using FreeSql.DataAnnotations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.Model.Model
{/// <summary>///  用户表/// </summary>[JsonObject(MemberSerialization.OptIn), Table(Name = "st_test")]public class SystemUser{/// <summary>/// 编号/// </summary>[JsonProperty]public long? Id { get; set; }/// <summary>/// 姓名/// </summary>[JsonProperty, Column(DbType = "varchar(50)")]public string Name { get; set; }/// <summary>/// 账号/// </summary>[JsonProperty, Column(DbType = "varchar(50)")]public string UserName { get; set; }/// <summary>/// 密码/// </summary>[JsonProperty, Column(DbType = "varchar(50)")]public string Password { get; set; }/// <summary>/// 删除标志/// </summary>[JsonProperty]public int? DeleteMark { get; set; }/// <summary>/// 有效标志/// </summary>[JsonProperty]public int? EnabledMark { get; set; }/// <summary>/// 创建时间/// </summary>[JsonProperty]public DateTime? CreateTime { get; set; }/// <summary>/// 编辑时间/// </summary>[JsonProperty]public DateTime? ModifyTime { get; set; }}
}

3、状态枚举类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.Model.Enum
{/// <summary>/// 状态枚举类/// </summary>public enum DXCode : short{Unknow = -1,/// <summary>/// 成功/// </summary>Success = 0,/// <summary>/// 失败/// </summary>Failure = -128,/// <summary>/// 错误请求/// </summary>BadRequest = -256,/ <summary>/ 执行失败/ </summary>//ActionExecutingFailure = -10002,/ <summary>/ 意外失败/ </summary>//UnexpectedFailure = -10003,/// <summary>/// 非法请求(登陆超时)/// </summary>Unauthorized = -16,}
}

4、自定义消息返回类

using DXDF.Core.Model.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.Model.ViewModels
{/// <summary>/// 消息返回类/// </summary>public class DXResult{/// <summary>/// 状态  /// </summary>public DXCode code { get; set; } = DXCode.Unknow;/// <summary>/// 描述/// </summary>public string msg { get; set; }/// <summary>/// 具体数据 /// </summary>public dynamic data { get; set; }/// <summary>/// 数据影响行数/// </summary>public long? count { get; set; }}
}

2.DAL(数据访问层)

这层主要与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。
1、Nuget包

  <ItemGroup><PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" /></ItemGroup>

2、SystemUserDAL.cs

using DXDF.Core.Common;
using DXDF.Core.Model;
using DXDF.Core.Model.Enum;
using DXDF.Core.Model.Model;
using DXDF.Core.Model.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.DAL
{public class SystemUserDAL : FSqlBase{#region CURD(增删查改)public Task<List<SystemUser>> GetList(int pageSize, int pageIndex, ref long count, Expression<Func<SystemUser, bool>> where = null, string order = null){return fsql.Queryable<SystemUser>().WhereIf(where != null, where).Count(out count).Page(pageIndex, pageSize).OrderBy(!string.IsNullOrWhiteSpace(order), order).ToListAsync();}public Task<SystemUser> GetById(long id){return fsql.Queryable<SystemUser>().Where(it => it.Id == id).FirstAsync();}public Task<int> UpdateOrInsert(SystemUser item){if (item?.Id > 0){return fsql.Update<SystemUser>().SetSource(item).IgnoreColumns(it => new { it.CreateTime }).ExecuteAffrowsAsync();}elsereturn fsql.Insert(item).ExecuteAffrowsAsync();}public Task<int> Delete(long id){return fsql.Delete<SystemUser>().Where(it => it.Id == id).ExecuteAffrowsAsync();}#endregion}
}

3.BLL(业务逻辑层)

UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。

1、SystemUserBLL.cs

using DXDF.Core.DAL;
using DXDF.Core.Model;
using DXDF.Core.Model.Enum;
using DXDF.Core.Model.Model;
using DXDF.Core.Model.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DXDF.Core.BLL
{public class SystemUserBLL : ISystemUserBLL{private readonly SystemUserDAL _dal;/// <summary>/// 构造函数注入/// </summary>/// <param name="dal"></param>public SystemUserBLL(SystemUserDAL dal){_dal = dal;}/// <summary>/// 添加用户/// </summary>/// <param name="model"></param>/// <returns></returns>public async Task<DXResult> AddUsers(SystemUser model){DXResult dXResult = new DXResult();int i = await _dal.UpdateOrInsert(model);if (i > 0){dXResult.code = DXCode.Success;}elsedXResult.code = DXCode.Failure;return dXResult;}/// <summary>/// 获取用户/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<SystemUser> GetUser(long id){SystemUser dXResult = await _dal.GetById(id);return dXResult;}}
}

2、ISystemUserBLL.cs

using DXDF.Core.Model.Model;
using DXDF.Core.Model.ViewModels;
using System.Threading.Tasks;namespace DXDF.Core.BLL
{public partial interface ISystemUserBLL{Task<DXResult> AddUsers(SystemUser model);Task<SystemUser> GetUser(long id);DXResult Update(SystemUser item);}
}

4.WebApi(表示层)

这一层主要就是实现前后端交互的数据接口操作
本层涉及AutoFac,也会讲解为什么引用他,而不用net core自带的依赖注入
1、Nuget包

  <ItemGroup><PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" /><PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" /></ItemGroup>

2、新建一个用户控制器类

using DXDF.Core.BLL;
using DXDF.Core.Model.Model;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860namespace DXDF.Core.Controllers
{[Route("api/[controller]")][ApiController]public class UserController : ControllerBase{private readonly ISystemUserBLL _systemUserBLL;/// <summary>/// 构造函数注入/// </summary>public UserController(ISystemUserBLL systemUserBLL){_systemUserBLL = systemUserBLL;}[HttpPost("addUser")]public async Task<dynamic> Add(SystemUser user){var obj = await _systemUserBLL.AddUsers(user);return obj;}[HttpGet("getUser")]public async Task<dynamic> Get(long id){var obj = await _systemUserBLL.GetUser(id);return obj;}[HttpPost("updateUser")]public dynamic UpdateUser(SystemUser user){var obj = _systemUserBLL.Update(user);return obj;}}
}

3、配置文件appsettings.json

{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*",//连接字符串//MySql "Server=localhost; Port=3306; Database=admindb; Uid=root; Pwd=pwd; Charset=utf8mb4;"//SqlServer "Data Source=.;Integrated Security=True;Initial Catalog=admindb;Pooling=true;Min Pool Size=1"//PostgreSQL "Host=localhost;Port=5432;Username=postgres;Password=; Database=admindb;Pooling=true;Minimum Pool Size=1"//Sqlite "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1""connectionString": "Data Source=localhost;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa1;Password=123456;Pooling=true;Min Pool Size=1;Connection LifeTime=20",//数据库类型 MySql = 0, SqlServer = 1, PostgreSQL = 2, Oracle = 3, Sqlite = 4, OdbcOracle = 5, OdbcSqlServer = 6, OdbcMySql = 7, OdbcPostgreSQL = 8, Odbc = 9, OdbcDameng = 10, MsAccess = 11, Dameng = 12, OdbcKingbaseES = 13, ShenTong = 14, KingbaseES = 15, Firebird = 16"dbType": "1"
}

这时候我们还是没办法使用这些接口的,需要依赖注入这些服务,说到服务的注入,就不得不提到服务的生命周期,netcore 一共提供三种注入的生命周期,分别是Transient(暂时)Scoped(作用域)Singleton(单例)。

常用两种依赖注入的方法,一种是原生DI-NetCore,另外一种是第三方的依赖注入容器DI-AutoFac。

DI-NetCore

Startup.cs

        public void ConfigureServices(IServiceCollection services){            //单例注入services.AddSingleton(new Appsettings(Configuration));services.AddSingleton<ISystemUserBLL, SystemUserBLL>();services.AddSingleton(new SystemUserDAL());services.AddControllers();services.AddSwaggerGen(c =>{c.SwaggerDoc("v1", new OpenApiInfo { Title = "DXDF.Core", Version = "v1" });});}

运行api项目,开始测试
在这里插入图片描述
数据库查询一下,成功
在这里插入图片描述

DI-AutoFac

问:为什么使用DI-AutoFac?
答:原生的服务注入方式没办法达到批量,比如写了两个服务接口,那就得分别注入两个。

            services.AddSingleton<IUserBLL, UserBLL>();services.AddSingleton<IDictABLL, IDictABLL>();....

这样子明显会照成很多重复的功能量跟代码冗余,所以这时候我们需要引用一个批量帮我们操作的工具,那就是DI-AutoFac,又称IOC(控制反转容器)

具体用法

  • 修改Program.cs
    public class Program{public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())//替换默认容器.ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});}
  • 修改Startup.cs
    新增方法ConfigureContainer
        public void ConfigureContainer(ContainerBuilder builder){#region AutoFac IOC容器,实现批量依赖注入的容器try{#region SingleInstance//无接口注入单例var assemblyDAL = Assembly.Load("DXDF.Core.DAL");builder.RegisterAssemblyTypes(assemblyDAL).SingleInstance();#endregion#region Servicevar assemblyServices = Assembly.Load("DXDF.Core.BLL");builder.RegisterAssemblyTypes(assemblyServices).AsImplementedInterfaces().InstancePerLifetimeScope().PropertiesAutowired();// 属性注入#endregion}catch (Exception ex){throw new Exception(ex.Message + "\n" + ex.InnerException);}#endregion}

完整代码已上传码云:https://gitee.com/shao-jiayong/cuo-ding

这篇关于.Net5 框架搭建(一):简单三层架构+Freesql+Autofac的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/408280

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Mycat搭建分库分表方式

《Mycat搭建分库分表方式》文章介绍了如何使用分库分表架构来解决单表数据量过大带来的性能和存储容量限制的问题,通过在一对主从复制节点上配置数据源,并使用分片算法将数据分配到不同的数据库表中,可以有效... 目录分库分表解决的问题分库分表架构添加数据验证结果 总结分库分表解决的问题单表数据量过大带来的性能

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE