.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();
            }
            else
                return 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;
            }
            else
                dXResult.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=397860

namespace 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 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello,Mr.S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值