最近在弄一个东东,类似那种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. linux下的Shell编程(3)shell里的流程控制

    if 语句 if 表达式如果条件命令组为真,则执行 then 后的部分.标准形式: if 判断命令,可以有很多个,真假取最后的返回值 then 如果前述为真做什么 [ # 方括号代表可选,别真打进去了 ...

  2. j2ee基础(1)servlet的生命周期

    Servlet的生命周期 Servlet 生命周期规定了 Servlet 如何被加载.实例化.初始化. 处理客户端请求,以及何时结束服务. 该生命周期可以通过 javax.servlet.Servle ...

  3. Android开发——发布第三方库到JitPack上

    前言: 看到大神们的写的第三方控件,比较好用,我们使用的时候直接是在gradle上加上代码就可以使用了,现在到我们写了一个第三方控件,想要别人使用的时候也是直接在gradle加上相关的代码就可以用了, ...

  4. python基础(初识Python)

    python基础(初识Python) 本章内容: Python 的种类 Python 的环境 Python 入门(解释器.编码.pyc文件.脚步传入参数.变量.输入.流程控制与缩进.while循环) ...

  5. python JavaScript

    JavaScript 一. JavaScript Javascript 在开发中绝大多数情况是基于对象的.也是面向对象的. a. JavaScript的引入方式 1 2 3 4 5 6 7 #直接编写 ...

  6. python基础—迭代器、生成器

    python基础-迭代器.生成器 1 迭代器定义 迭代的意思是重复做一些事很多次,就像在循环中做的那样. 只要该对象可以实现__iter__方法,就可以进行迭代. 迭代对象调用__iter__方法会返 ...

  7. jQuery系列 第五章 jQuery框架动画特效

    第五章 jQuery框架动画特效 5.1 jQuery动画特效说明 jQuery框架中为我们封装了众多的动画和特效方法,只需要调用对应的动画方法传递合适的参数,就能够方便的实现一些炫酷的效果,而且jQ ...

  8. [LeetCode] Prefix and Suffix Search 前后缀搜索

    Given many words, words[i] has weight i. Design a class WordFilter that supports one function, WordF ...

  9. Event 发布与订阅(一)

    前言 主要讲的是发布与订阅在Event中的一个简单实现用来加深理解. C #中的事件(Event)的理解: 事件具有以下属性:(From Events) 发行者确定何时引发事件:订户确定对事件作出何种 ...

  10. [POI 2007]ZAP-Queries

    Description Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency) ...