本文主要是介绍.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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!