夫天地者,万物之逆旅;光阴者,百代之过客。而浮生若梦,为欢几何?
基于Dapper的扩展方法来简单封装WebApi接口

前言

首先从网上复制一些关于Dapper的介绍:

Dapper 是StackOverflow开源的一个MiniOrm,优点主要有:开源、轻量、小巧(单文件,代码就一个SqlMapper.cs文件,编译后就40K的一个很小的Dll.),性能和原生ado.net相近。但是由于它自身对linq的支持 ,没有EntityFramework和NHibernate那样好,所以我们在项目中实际使用时,有时需要基于dapper的扩展 (DapperExtensions)来使用第三方扩展的一些方法,实现更丰富的用法支持。

准备工作

演示的测试项目是基于.net core2.1来创建的WebApi接口,同时项目中还使用了Autofac的Ioc容器,首先我们先来看下测试项目的目录结构:

其中核心的类库是AspNetCore.WebApi.ORM,基本是从网上下载的开源项目代码,其中SqlMapper.cs就是Dapper的核心文件。

Dapper原生方法

继续下面的内容之前,我们先来简单看一下Dapper的一些原生的方法,列举几个查询方法如下:

/// <summary>
/// 执行SQL返回集合
/// </summary>
/// <param name="strSql">sql语句</param>
/// <returns></returns>
public virtual List<T> Query<T>(string strSql)
{
    using (IDbConnection conn = Connection)
    {
        return conn.Query<T>(strSql, null).ToList();
    }
}
/// <summary>
/// 执行SQL返回一个对象
/// </summary>
/// <param name="strSql">SQL语句</param>
/// <returns></returns>
public virtual T QueryFirst<T>(string strSql)
{
    using (IDbConnection conn = Connection)
    {
        return conn.Query<T>(strSql).FirstOrDefault<T>();
    }
}
/// <summary>
/// 执行SQL返回一个对象(异步)
/// </summary>
/// <param name="strSql">SQL语句</param>
/// <returns></returns>
public virtual async Task<T> QueryFirstAsync<T>(string strSql)
{
    using (IDbConnection conn = Connection)
    {
        var res = await conn.QueryAsync<T>(strSql);
        return res.FirstOrDefault<T>();
    }
}

当然本身还有非常多其它的方法,如下图所示,

但这些原生的方法基本都需要传入sql语句作为参数,在某些场景下,如果能支持lambda表达式的查询方式,不用简单的CRUD也要手写sql,这样会方便很多。因此下面就来看一下Dapper的第三方扩展方法

核心代码解读

DapperContext.cs

扩展方法的核心基于DapperContext.cs这个文件,下面列出其核心代码:

namespace Dapper.Data
{
    /// <summary>
    /// 数据操作基类
    /// </summary>
    public class DapperContext : IDapperContext, IDisposable
    {
        #region 初始化
        private readonly string connString;
        private readonly DbProviderFactory dbfactory;
        /// <summary>
        /// 初始化
        /// </summary>
        public DapperContext(string connectionString, string providerName)
        {
            connString = connectionString;
            SetDatabaseType(providerName);
            switch (DBType)
            {
                case DatabaseType.SqlServer:
                    dbfactory = SqlClientFactory.Instance;
                    break;
                case DatabaseType.Oracle:
                    dbfactory = OracleClientFactory.Instance;
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 获取当前的数据库连接对象
        /// </summary>
        /// <returns></returns>
        protected virtual IDbConnection GetConnection()
        {
            DbConnection dbconn = dbfactory.CreateConnection(); 
            dbconn.ConnectionString = connString;
            return dbconn;
        }
        /// <summary>
        /// 数据库驱动类型
        /// </summary>
        public DatabaseType DBType { get; protected set; }
        #endregion
        #region IDapperContext 成员
        private IDbConnection dbConnection;
        /// <summary>
        /// 数据库连接
        /// </summary>
        public IDbConnection Connection
        {
            get
            {
                if (dbConnection == null)
                    dbConnection = GetConnection();
                return dbConnection;
            }
        }
        private IDbTransaction dbTransaction;
        #endregion
    }
}

BaseDataService.cs

此时我们就可以基于DapperContext来创建Service层,来实现调用了。我们在AspNetCore.WebApi.Service项目里创建一个BaseDataService.cs类(服务层的基类,抽象出一些公共的方法),部分核心代码如下:

namespace AspNetCore.WebApi.Service
{
    public class BaseDataService<T> where T : class, new()
    {
        private readonly DapperContext _context;
        public BaseDataService(DapperContext context)
        {
            _context = context;
        }
        
        /// <summary>
        /// 获取列表
        /// </summary>
        /// <returns></returns>
        public virtual List<T> GetList(Expression<Func<T, bool>> predicate)
        {
            return _context.Set<T>().Select(where: predicate) as List<T>;
        }
        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Add(T item)
        {
            var flag = _context.Set<T>().Insert(item);
            _context.SaveChanges();
            return flag;
        }
    }
}

UserController.cs

最后我们在UserController.cs里实现对服务层的调用,并从数据库里查询出数据返回出去:

namespace AspNetCore.WebApi.Main.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class UserController : BaseController
    {
        private readonly BaseDataService<User> _baseUserService = null;
        private readonly UserService _userService = null;
        /// <summary>
        /// 构造函数方式注入服务实例
        /// </summary>
        /// <param name="baseUserService"></param>
        /// <param name="userService"></param>
        public UserController(BaseDataService<User> baseUserService, UserService userService)
        {
            _baseUserService = baseUserService;
            _userService = userService;
        }
        // GET api/user/FindAll
        [HttpGet]
        public ActionResult<ResponseResult<List<User>>> FindAll()
        {
            Expression<Func<User, bool>> expression = p => true;
            var userList = GetList<User>(_baseUserService, expression, c => c.Asc(o => o.LOGIN_NAME));
            return userList;
        }
        // GET api/user/FindOne?name=xxx
        [HttpGet]
        public ResponseResult<User> FindOne(string name)
        {
            ResponseResult<User> result = null;
            {
                //方式1:通过手动sql方式查询
                result = _userService.GetUserByName(name);
            }
            {
                //方式2:通过扩展的方式,结合表达式树的条件查询
                Expression<Func<User, bool>> expression = p => true;
                if (!string.IsNullOrWhiteSpace(name))
                {
                    expression = expression.And(c => c.LOGIN_NAME.Equals(name));
                }
                result = Get<User>(_baseUserService, expression, "");
            }
            return result;
        }
        /// <summary>
        ///  新增
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        [HttpPost]
        public ResponseResult<string> SaveUser(User user)
        {
            return Save<User>(_baseUserService, user, c => c.USER_ID.Equals(user.USER_ID), "用户信息保存成功");
        }
    }
}

Autofac注入服务实例

到了上面这一步时,还没有结束,我们还需要注册各个服务实例,这里我们使用Autofac来实现依赖注入,下面仍通过代码展示在.net core2.1下如何使用Autofac实现注入。主要是要修改Statup.cs中的RegisterAutofac方法:

namespace AspNetCore.WebApi.Main
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            return RegisterAutofac(services);//注册Autofac
        }
        private IServiceProvider RegisterAutofac(IServiceCollection services)
        {
            //实例化Autofac容器
            var builder = new ContainerBuilder();
            builder.Populate(services);
            //从appsetting.json里获取数据库连接
            Dictionary<string, string> keyValues = Configuration.GetSection("ConnectionStrings").Get<Dictionary<string, string>>();
            var sqlconn = keyValues.FirstOrDefault(c => c.Key.Equals("SqlConnetionString")).Value;
            var provider= keyValues.FirstOrDefault(c => c.Key.Equals("ProviderName")).Value;
            //注册dapper的数据库连接
            builder.Register<DapperContext>(c => new DapperContext(sqlconn, provider)).InstancePerDependency();
            //注册service程序集
            builder.RegisterAssemblyTypes(ServiceHelper.GetExecutingAssembly()).AsImplementedInterfaces().InstancePerDependency();
            builder.RegisterAssemblyTypes(ServiceHelper.GetExecutingAssembly()).InstancePerDependency();
            //泛型不能自动注入
            builder.RegisterGeneric(typeof(BaseDataService<>)).As(typeof(BaseDataService<>)).InstancePerDependency();
            //创建容器
            var Container = builder.Build();
            //第三方IOC接管 core内置DI容器 
            return new AutofacServiceProvider(Container);
        }
    }
}

结果展示

我们运行并调试Api接口,可以看到接口被成功调用

总结

由于本文的代码较多,只选择粘贴了一些核心代码(完整示例代码后期会分享到GitHub上,其中SqlMapper及DapperExtensions等文件可以自行搜索下载),目的是为了说明,在使用Dapper的原生方法时,还可以通过自己封装或第三方的扩展方法,从而获得更丰富简便的方式,也增加了代码的可读性。后续在实际的使用过程中将进一步深入的研究~~

作者:一蓑烟雨

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

0

支持

0

反对

posted @2020-7-3  拜读(520)

评论列表

评论内容:



喜欢请打赏

支付宝 微信

请放心支付