最近在弄一个东东,类似那种CMS的后台管理系统,方便作为其它项目的初始化框架用的。

现在遇到个问题,如标题所示:Dapper通用的多表联合分页查询怎么破?

单表的话很简单就可以实现,多表不通用的话也可以很方便的实现,那么如果多表通用的话,怎么办呢?

难道只能通过拼接sql或者使用存储过程吗?我先来展示下我的实现方式,希望你有更好的方式,然后同我分享一下,以便解决我的困扰。

因为本来就是做的传统的CMS类似的项目,所以技术选型也是比较传统,抛弃了mvvm的js框架、webapi接口、以及nosql缓存。

技术选型:MVC5、Mysql、Dapper、Autofac、Layui、阿里巴巴矢量库、T4(后面补上)。

  • MVC5:目前.net开发的主流web框架。
  • Mysql:轻量免费功能强大的关系型数据库。
  • Dapper:据说是性能最好的.net ORM框架。
  • Autofac:据说是性能最好的.net IOC框架。
  • Layui:经典的模块化UI框架,还是很适合我们这样的后端开发人员的。
  • 阿里巴巴矢量库:丰富的矢量图标库。
  • T4:强大的代码生成模板。

我选择的都是轻量级比较干净的东东来组合的框架。

我选择由外入内的方式来阐述我现在遇到的问题。以用户管理界面为例,我讲只列出涉及到用户分页查询的代码,将会省略其它代码.....

大致上的效果如下图所示:

经典的多层架构

Global.asax.cs代码,Dapper自动注入。

  public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles); //创建autofac管理注册类的容器实例
var builder = new ContainerBuilder();
SetupResolveRules(builder);
//使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册 支持属性注入
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired(); // 把容器装入到微软默认的依赖注入容器中
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
private static void SetupResolveRules(ContainerBuilder builder)
{
//WebAPI只用引用services和repository的接口,不用引用实现的dll。
//如需加载实现的程序集,将dll拷贝到bin目录下即可,不用引用dll
var iServices = Assembly.Load("RightControl.IService");
var services = Assembly.Load("RightControl.Service");
var iRepository = Assembly.Load("RightControl.IRepository");
var repository = Assembly.Load("RightControl.Repository"); //根据名称约定(服务层的接口和实现均以Services结尾),实现服务接口和服务实现的依赖
builder.RegisterAssemblyTypes(iServices, services)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().PropertiesAutowired(); //根据名称约定(数据访问层的接口和实现均以Repository结尾),实现数据访问接口和数据访问实现的依赖
builder.RegisterAssemblyTypes(iRepository, repository)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().PropertiesAutowired();
}
}

BaseController:

   public class BaseController : Controller
{// GET: Base
public virtual ActionResult Index()
{
return View();
}

UserController:

    public class UserController : BaseController
{
private IUserService service; public UserController(IUserService _service)
{
service = _service;
}
/// <summary>
/// 加载数据列表
/// </summary>
/// <param name="pageInfo">页面实体信息</param>
/// <param name="filter">查询条件</param>
/// <returns></returns>
[HttpGet]
public JsonResult List(PageInfo pageInfo, UserModel filter)
{
var result = service.GetListByFilter(filter, pageInfo);
return Json(result, JsonRequestBehavior.AllowGet);
}

PageInfo:

    public class PageInfo
{
public int page { get; set; }
public int limit { get; set; }
/// <summary>
/// 排序字段 CreateOn
/// </summary>
public string field { get; set; }
/// <summary>
/// 排序方式 asc desc
/// </summary>
public string order { get; set; }
/// <summary>
/// 返回字段逗号分隔
/// </summary>
public string returnFields { get; set; }
public string prefix { get; set; }
}

UserModel:

using DapperExtensions;
using System;
using System.ComponentModel.DataAnnotations; namespace RightControl.Model
{
[Table("t_User")]
public class UserModel:Entity
{
/// <summary>
/// 用户名
/// </summary>
[Display(Name = "用户名")]
public string UserName { get; set; }
/// <summary>
/// 真实名称
/// </summary>
[Display(Name = "真实名称")]
public string RealName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string PassWord { get; set; }
/// <summary>
/// 创建者
/// </summary>
public int CreateBy { get; set; }
/// <summary>
/// 角色ID
/// </summary>
public int RoleId { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[Display(Name = "更新时间")]
public DateTime UpdateOn { get; set; }
[Computed]
public string RoleName { get; set; }
}
}

Entity:

    public class Entity
{
[DapperExtensions.Key(true)]
public virtual int Id { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm:ss}")]
[Display(Name = "创建时间")]
public DateTime CreateOn { get; set; }
/// <summary>
/// 菜单状态(1:启用,0:禁用)
/// </summary>
public bool Status { get; set; }
#region 查询条件
[Computed]
public string StartEndDate { get; set; }
#endregion
}

IBaseService:

    public interface IBaseService<T> where T : class, new()
{
dynamic GetListByFilter(T filter, PageInfo pageInfo);
}

IUserService:

    public interface IUserService : IBaseService<UserModel>
{
...
}

BaseService:

    public abstract class BaseService<T> where T : class, new()
{
public IBaseRepository<T> baseRepository{get; set;}
public dynamic GetPageUnite(IBaseRepository<T> repository, PageInfo pageInfo, string where, object filter)
{
string _orderBy = string.Empty;
if (!string.IsNullOrEmpty(pageInfo.field))
{
_orderBy = string.Format(" ORDER BY {0} {1}", pageInfo.prefix+pageInfo.field, pageInfo.order);
}
else
{
_orderBy = string.Format(" ORDER BY {0}CreateOn desc",pageInfo.prefix);
}
long total = ;
var list = repository.GetByPageUnite(new SearchFilter { pageIndex = pageInfo.page, pageSize = pageInfo.limit, returnFields = pageInfo.returnFields, param = filter, where = where, orderBy = _orderBy }, out total); return Pager.Paging(list, total);
}
protected string CreateWhereStr(Entity filter, string _where)
{
if (!string.IsNullOrEmpty(filter.StartEndDate) && filter.StartEndDate != " ~ ")
{
var dts = filter.StartEndDate.Trim().Split('~');
var start = dts[].Trim();
var end = dts[].Trim();
if (!string.IsNullOrEmpty(start))
{
_where += string.Format(" and CreateOn>='{0}'", start + " 00:00");
}
if (!string.IsNullOrEmpty(end))
{
_where += string.Format(" and CreateOn<='{0}'", end + " 59:59");
}
} return _where;
}
}

UserService:

    public class UserService: BaseService<UserModel>, IUserService
{
public IUserRepository repository { get; set; }//属性注入
public dynamic GetListByFilter(UserModel filter, PageInfo pageInfo)
{
pageInfo.prefix = "u.";
string _where = " t_User u INNER JOIN t_role r on u.RoleId=r.Id";
if (!string.IsNullOrEmpty(filter.UserName))
{
_where += string.Format(" and {0}UserName=@UserName",pageInfo.prefix);
}
if (!string.IsNullOrEmpty(pageInfo.order))
{
pageInfo.order = pageInfo.prefix + pageInfo.order;
}
pageInfo.returnFields = string.Format("{0}Id,{0}UserName,{0}RealName,{0}CreateOn,{0}`PassWord`,{0}`Status`,{0}RoleId,r.RoleName",pageInfo.prefix);
return GetPageUnite(baseRepository, pageInfo, _where, filter);
}

IBaseRepository:

 public interface IBaseRepository<T> where T : class, new()
{
IEnumerable<T> GetByPageUnite(SearchFilter filter, out long total);
}

IUserRepository:

    public interface IUserRepository : IBaseRepository<UserModel>
{ }

BaseRepository:

    public class BaseRepository<T>: IBaseRepository<T> where T :class, new()
{
public IEnumerable<T> GetByPageUnite(SearchFilter filter, out long total)
{
using (var conn = MySqlHelper.GetConnection())
{
return conn.GetByPageUnite<T>(filter.pageIndex, filter.pageSize, out total, filter.returnFields, filter.where, filter.param, filter.orderBy, filter.transaction, filter.commandTimeout);
}
} }

UserRepository:

    public class UserRepository : BaseRepository<UserModel>, IUserRepository
{
}

最后的分页代码:

        /// <summary>
/// 获取分页数据
/// </summary>
public static IEnumerable<T> GetByPageUnite<T>(this IDbConnection conn, int pageIndex, int pageSize, out long total, string returnFields = null, string where = null, object param = null,
string orderBy = null, IDbTransaction transaction = null, int? commandTimeout = null)
{
int skip = ;
if (pageIndex > )
{
skip = (pageIndex - ) * pageSize;
} StringBuilder sb = new StringBuilder();
sb.AppendFormat("SELECT COUNT(1) FROM {0};", where);
sb.AppendFormat("SELECT {0} FROM {1} {2} LIMIT {3},{4}", returnFields, where, orderBy, skip, pageSize);
using (var reader = conn.QueryMultiple(sb.ToString(), param, transaction, commandTimeout))
{
total = reader.ReadFirst<long>();
return reader.Read<T>();
}
}

Index视图:

@{
Layout = "~/Views/Shared/_LayoutList.cshtml";
} <!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>Table</title>
</head> <body>
<div class="admin-main">
<blockquote class="layui-elem-quote p10">
<form id="formSearch" class="layui-form" action="">
<div class="layui-form-item" style="margin-bottom:0px;">
<label class="layui-form-label">用户名称:</label>
<div class="layui-input-inline">
<input name="UserName" id="UserName" lay-verify="" autocomplete="off" class="layui-input">
</div>
<label class="layui-form-label">角色名称:</label>
<div class="layui-input-inline">
@Html.DropDownList("RoleId", null, "-请选择角色-", new Dictionary<string, object> { { "lay-verify", "required" } })
</div>
<label class="layui-form-label">状态:</label>
<div class="layui-input-inline">
@Html.StatusSelectHtml()
</div>
@Html.SearchBtnHtml()
@Html.ResetBtnHtml()
<div style="float:right;">
@Html.TopToolBarHtml(ViewData["ActionFormRightTop"])
</div>
</div>
</form>
</blockquote>
<div class="layui-field-box">
<table id="defaultTable" lay-filter="defaultruv"></table>
<!-- 这里的 checked 的状态只是演示 -->
@*<input type="checkbox" name="Status" value="{{d.Id}}" lay-skin="switch" lay-text="开启|禁用" lay-filter="statusSwitch" {{ d.Status == 1 ? 'checked' : '' }}>*@
<script type="text/html" id="bar">
@Html.ToolBarHtml(ViewData["ActionList"])
</script>
</div>
</div>
<script>
layui.config({
base: '/plugins/app/'
}); layui.use(['table', 'common', 'form'], function () {
var table = layui.table,
form = layui.form,
common = layui.common;
//表格
table.render({
id: 'defaultReload'
, elem: '#defaultTable'
, height: 'full-112' //高度最大化减去差值
, url: '/Permissions/User/List' //数据接口
, page: true //开启分页
, cols: [[ //表头
{ checkbox: true, fixed: true },
{ field: 'Id', title: 'Id', width: 80, fixed: 'left' }
, { field: 'UserName', title: '用户名称', sort: true }
, { field: 'RealName', title: '真实姓名' }
, { field: 'RoleName', title: '角色名称' }
, { field: 'Status', title: '状态', templet: '<div>{{showStatus(d.Status)}}</div>', unresize: true, width: 100, align: 'center' }
, { field: 'CreateOn', title: '创建时间', width: 160, sort: true, templet: '<div>{{showDate(d.CreateOn)}}</div>' }
, { field: '', title: '操作', toolbar: "#bar" }
]]
});
var $ = layui.$, active = {
reload: function () {
var jsonWhere = urlToJson($("#formSearch").serialize());
//执行重载
table.reload('defaultReload', {
page: {
curr: 1 //重新从第 1 页开始
}
, where: jsonWhere
});
}
};
//服务器排序
table.on('sort(defaultruv)', function (obj) {
//尽管我们的 table 自带排序功能,但并没有请求服务端。
//有些时候,你可能需要根据当前排序的字段,重新向服务端发送请求,如:
table.reload('defaultReload', {
initSort: obj //记录初始排序,如果不设的话,将无法标记表头的排序状态。 layui 2.1.1 新增参数
, where: { //请求参数
field: obj.field //排序字段
, order: obj.type //排序方式
}
});
});
$('#btnSearch').on('click', function () {
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
});
//add
$('#btnAdd').on('click', function () {
common.openTop({
title: '用户添加', w: '600px', h: '360px', content: '/Permissions/User/Add/'
});
});
//监听工具条
table.on('tool(defaultruv)', function (obj) {
var data = obj.data;
if (obj.event === 'detail') {
common.openTop({
detail: true,
title: '角色详情', w: '600px', h: '360px', content: '/Permissions/User/Detail/' + data.Id, clickOK: function (index) {
common.close(index);
}
});
} else if (obj.event === 'del') {
layer.confirm('确定要删除吗?', function (index) {
$.ajax({
url: "/Permissions/User/Delete",
type: "POST",
data: { "Id": data.Id },
dataType: "json",
success: function (data) {
if (data.state == "success") {
obj.del();//删除这一行
common.msgSuccess("删除成功");
} else {
common.msgError("删除失败");
}
layer.close(index);//关闭弹框
}
});
});
} else if (obj.event === 'edit') {
common.openTop({
title: '用户编辑', w: '600px', h: '360px', content: '/Permissions/User/Edit/' + data.Id
});
} else if (obj.event === 'reset') {
layer.confirm('确定要初始化密码吗?', function (index) {
$.ajax({
url: "/Permissions/User/InitPwd",
type: "POST",
data: { "Id": data.Id },
dataType: "json",
success: function (data) {
if (data.state == "success") {
layer.close(index);//关闭弹框
common.msgSuccess("密码初始化成功");
} else {
common.msgError("密码初始化失败");
}
layer.close(index);//关闭弹框
}
});
});
}
});
});
</script>
</body> </html>

_LayoutBase模板页:

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link rel="stylesheet" href="~/plugins/layui/css/layui.css" media="all" />
<link href="~/Content/global.css" rel="stylesheet" />
<link href="~/Content/table.css" rel="stylesheet" />
<script src="~/plugins/layui/layui.js"></script>
<script src="~/plugins/app/global.js"></script>
</head>
<body>
<div id="ajax-loader" style="cursor: progress; position: fixed; top: -50%; left: -50%; width: 200%; height: 200%; background: #fff; z-index: 10000; overflow: hidden;">
<img src="~/Content/images/ajax-loader.gif" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto;" />
</div>
@RenderBody()
<script type="text/javascript">
layui.config({
base: '/plugins/app/',
version: '1522709297490' //为了更新 js 缓存,可忽略
});
layui.use(['common'], function () {
layer.config({
skin: 'layui-layer-molv'
})
var $ = layui.jquery;
$(function () {
$('#ajax-loader').fadeOut();
})
})
</script>
</body>
</html>

代码结构基本上就这样了。

作为一名有追求的程序员,当看到代码连自己都受不了,我就开始抓狂,每次写代码的时候,脑袋里都是那几句话“不要重复你的代码、依赖于抽象....”

项目详细介绍和代码获取请移步:.net项目驱动学习

.net通用CMS快速开发框架——问题1:Dapper通用的多表联合分页查询怎么破?的更多相关文章

  1. CRL快速开发框架系列教程一(Code First数据表不需再关心)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  2. CRL快速开发框架系列教程十三(嵌套查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  3. CRL快速开发框架系列教程十二(MongoDB支持)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  4. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  5. CRL快速开发框架系列教程十(导出对象结构)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. CRL快速开发框架系列教程七(使用事务)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  8. CRL快速开发框架系列教程六(分布式缓存解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  9. CRL快速开发框架系列教程五(使用缓存)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

随机推荐

  1. ajax实现跨域访问的两种方式

    一.使用jsonp实现跨域请求 在前端开发这中你会发现,所有带src属性的标签都可以跨域访问其他服务器文件.jsonp实现的原理也是如此. 以jsonp的数据类型进行请求时,JQ会动态在页面中添加sc ...

  2. python/ORM操作详解

    一.python/ORM操作详解 ===================增==================== models.UserInfo.objects.create(title='alex ...

  3. PHP 页面跳转到另一个页面的多种方法方法总结

    如何在PHP中从一个页面重定向到另外一个页面呢?这里列出了三种办法,供参考. 一.用HTTP头信息 也就是用PHP的HEADER函数.PHP里的HEADER函数的作用就是向浏览器发出由HTTP协议规定 ...

  4. Vue 项目代理设置的优化

    Vue 项目代理设置的优化 Vue 类的项目开发中项目结构基本都是类似于 Vue-cli 生成的方式, 这种方式开发中,最常用到的模式是开启代理进行 mock 调试或远程调试, 也就是使用了 Vue- ...

  5. iframe 里的高度自适应

    由于公司里的很多东西都要用到iframe 导致我不得不各种百度 首先是自适应高度 // document.domain = "caibaojian.com"; function s ...

  6. JDK安装、变量、变量的分类

    Lesson One 2018-04-17  19:50:35 JAVA语言特点: 编译型.强类型语言. 纯面向对象的语言,所有的代码都必须包含在class中的方法中 配置JAVA环境变量 1.安装J ...

  7. vue-cli的使用

    1.安装node https://nodejs.org/en/download/ 2.webpack安装[我选全局安装] 全局安装 npm install --global webpack 本地安装 ...

  8. AutoCAD常用操作命令

    前言 最近工作需要使用AutoCAD画图,在这里记一下用到的一些常用操作,都是一些很基础的操作,希望对大家有帮助. 修剪 如果两条直线相交,你需要剪掉多余的部分,可以用修剪命令TR. 我们先画两条相交 ...

  9. mac版chrome升级到Version 65.0.3325.18后无法打开百度bing搜狗

    mac版本chrome升级到Version 65.0.3325.18后发现突然无法访问百度,搜狗,bing,神马等一系列的国内搜索引擎网站.连百度的儿子们比如知道,百度百科都无法访问. 1.首先想到的 ...

  10. LeetCode Binary Search Summary 二分搜索法小结

    二分查找法作为一种常见的查找方法,将原本是线性时间提升到了对数时间范围,大大缩短了搜索时间,具有很大的应用场景,而在LeetCode中,要运用二分搜索法来解的题目也有很多,但是实际上二分查找法的查找目 ...