由于业务需要,希望搭建一套基于Freesql(ORM)的简单易懂三层架构,按照目前主流的开发模式都是仓储层+三层架构在开发,本框架去除仓储层,有些命名也比较通俗易懂,用于学习就好,前面大部分都是在描述一些基本的三层架构搭建,想看Autofac(第三方依赖注入容器)怎么使用的可以直接拉到最下面。
正文三层架构的大致流程图:
项目框架大概组成截图:
- 0.Core:通用层
- 1.Model:实体层
- 2.DAL:数据访问层
- 3.BLL:业务逻辑层
- 4.WebApi:表示层
这层我存放一些通用的帮助类,包含封装的访问数据库上下文基类
1、直接Nuget添加这些包,或者直接项目编辑新增
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("rn---{0}---rn{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
{
///
/// appsettings.json操作类
///
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;
}
///
/// 封装要操作的字符
///
/// 节点配置
///
public static string app(params string[] sections)
{
try
{
if (sections.Any())
{
return Configuration[string.Join(":", sections)];
}
}
catch (Exception) { }
return "";
}
///
/// 递归获取配置信息数组
///
///
///
///
public static List app(params string[] sections)
{
List list = new List();
// 引用 Microsoft.Extensions.Configuration.Binder 包
Configuration.Bind(string.Join(":", sections), list);
return list;
}
}
}
4、数据类型转换的帮助类
UtilConvert.cs
using System;
namespace DXDF.Core.Common.Helper
{
///
/// 转换帮助类
///
public static class UtilConvert
{
///
///
///
///
///
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;
}
///
///
///
///
///
///
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;
}
///
///
///
///
///
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;
}
///
///
///
///
///
///
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;
}
///
///
///
///
///
public static string ObjToString(this object thisValue)
{
if (thisValue != null) return thisValue.ToString().Trim();
return "";
}
///
///
///
///
///
public static bool IsNotEmptyOrNull(this object thisValue)
{
return ObjToString(thisValue) != "" && ObjToString(thisValue) != "undefined" && ObjToString(thisValue) != "null";
}
///
///
///
///
///
///
public static string ObjToString(this object thisValue, string errorValue)
{
if (thisValue != null) return thisValue.ToString().Trim();
return errorValue;
}
///
///
///
///
///
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;
}
///
///
///
///
///
///
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;
}
///
///
///
///
///
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;
}
///
///
///
///
///
///
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;
}
///
///
///
///
///
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;
}
///
/// 获取当前时间的时间戳
///
///
///
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包
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
{
///
/// 用户表
///
[JsonObject(MemberSerialization.OptIn), Table(Name = "st_test")]
public class SystemUser
{
///
/// 编号
///
[JsonProperty]
public long? Id { get; set; }
///
/// 姓名
///
[JsonProperty, Column(DbType = "varchar(50)")]
public string Name { get; set; }
///
/// 账号
///
[JsonProperty, Column(DbType = "varchar(50)")]
public string UserName { get; set; }
///
/// 密码
///
[JsonProperty, Column(DbType = "varchar(50)")]
public string Password { get; set; }
///
/// 删除标志
///
[JsonProperty]
public int? DeleteMark { get; set; }
///
/// 有效标志
///
[JsonProperty]
public int? EnabledMark { get; set; }
///
/// 创建时间
///
[JsonProperty]
public DateTime? CreateTime { get; set; }
///
/// 编辑时间
///
[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
{
///
/// 状态枚举类
///
public enum DXCode : short
{
Unknow = -1,
///
/// 成功
///
Success = 0,
///
/// 失败
///
Failure = -128,
///
/// 错误请求
///
BadRequest = -256,
/
/ 执行失败
/
//ActionExecutingFailure = -10002,
/
/ 意外失败
/
//UnexpectedFailure = -10003,
///
/// 非法请求(登陆超时)
///
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
{
///
/// 消息返回类
///
public class DXResult
{
///
/// 状态
///
public DXCode code { get; set; } = DXCode.Unknow;
///
/// 描述
///
public string msg { get; set; }
///
/// 具体数据
///
public dynamic data { get; set; }
///
/// 数据影响行数
///
public long? count { get; set; }
}
}
2.DAL(数据访问层)
这层主要与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。
1、Nuget包
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> GetList(int pageSize, int pageIndex, ref long count, Expression> where = null, string order = null)
{
return fsql.Queryable()
.WhereIf(where != null, where).Count(out count).Page(pageIndex, pageSize).OrderBy(!string.IsNullOrWhiteSpace(order), order).ToListAsync();
}
public Task GetById(long id)
{
return fsql.Queryable().Where(it => it.Id == id).FirstAsync();
}
public Task UpdateOrInsert(SystemUser item)
{
if (item?.Id > 0)
{
return fsql.Update().SetSource(item)
.IgnoreColumns(it => new { it.CreateTime })
.ExecuteAffrowsAsync();
}
else
return fsql.Insert(item).ExecuteAffrowsAsync();
}
public Task Delete(long id)
{
return fsql.Delete().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;
///
/// 构造函数注入
///
///
public SystemUserBLL(SystemUserDAL dal)
{
_dal = dal;
}
///
/// 添加用户
///
///
///
public async Task AddUsers(SystemUser model)
{
DXResult dXResult = new DXResult();
int i = await _dal.UpdateOrInsert(model);
if (i > 0)
{
dXResult.code = DXCode.Success;
}
else
dXResult.code = DXCode.Failure;
return dXResult;
}
///
/// 获取用户
///
///
///
public async Task 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 AddUsers(SystemUser model);
Task GetUser(long id);
DXResult Update(SystemUser item);
}
}
4.WebApi(表示层)
这一层主要就是实现前后端交互的数据接口操作
本层涉及AutoFac,也会讲解为什么引用他,而不用net core自带的依赖注入
1、Nuget包
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=397860
namespace DXDF.Core.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly ISystemUserBLL _systemUserBLL;
///
/// 构造函数注入
///
public UserController(ISystemUserBLL systemUserBLL)
{
_systemUserBLL = systemUserBLL;
}
[HttpPost("addUser")]
public async Task Add(SystemUser user)
{
var obj = await _systemUserBLL.AddUsers(user);
return obj;
}
[HttpGet("getUser")]
public async Task 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-NetCoreStartup.cs
public void ConfigureServices(IServiceCollection services)
{
//单例注入
services.AddSingleton(new Appsettings(Configuration));
services.AddSingleton();
services.AddSingleton(new SystemUserDAL());
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "DXDF.Core", Version = "v1" });
});
}
运行api项目,开始测试
数据库查询一下,成功
问:为什么使用DI-AutoFac?
答:原生的服务注入方式没办法达到批量,比如写了两个服务接口,那就得分别注入两个。
services.AddSingleton(); services.AddSingleton (); ....
这样子明显会照成很多重复的功能量跟代码冗余,所以这时候我们需要引用一个批量帮我们操作的工具,那就是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.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 Service
var 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



