最近在弄一个东东,类似那种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. 200行Py代码带你实现"打飞机"

    前言 多年前,你我在一起"打飞机".为了实现真正的打飞机,在下一年前踏足帝都学习了无所不能的Python,辣么接下来带你在俩个小时用200行代码学会打飞机. python中提供了一 ...

  2. IDEA里面创建maven项目,依赖

    在IDEA里面创建一个简单的Maven项目: 在file-->new-->project ,选择maven,点击next 里面的一些简单参数的定义(第一次使用的话可以使用默认的值进行后面的 ...

  3. Mysql官方文档翻译系列-7.3.1 Establishing a Backup Policy

    原文链接 (https://dev.mysql.com/doc/refman/5.7/en/backup-policy.html) 正文 To be useful, backups must be s ...

  4. 查看centos版本及32还是64位

    1.[root@mini1 ~]# cat /etc/issue 2.[root@mini1 ~]#  cat /etc/redhat-release 查看位数: [root@mini1 ~]#  g ...

  5. Java:现有线程T1/T2/T3,如何确保T1执行完成之后执行T2,T3在T2执行完成之后执行。

    要实现多个线程执行完成先后,就要知道如何实现线程之间的等待,java线程等待实现是join.java的jdk中join方法实现如下: public final synchronized void jo ...

  6. Struts(二十六):文件上传

    表单的准备 想要使用html表单上传一个或多个文件 1.须把html表单的enctype属性设置为multipart/form-data 2.须把html表单的method属性设置为post 3.须添 ...

  7. 区块链3.0:拥抱EOS

    EOS是当下最火的区块链技术,被社会广泛看好为下一代区块链3.0.不同于以太坊的学习,EOS的主语言是C++,本文作为EOS研究的首篇文章,重点介绍EOS的创新点,它的周边生态,各种概念原理的解释,以 ...

  8. CLR-基元类型以及溢出检查

    =========(CLR via C#阅读笔记)======== 基元类型(primitive type): 基元类型也不做过多的解释,举个例子即可清晰的辨别 在java里曾使用过Sting s=& ...

  9. Eclipse代码块折叠插件,安装使用

    在代码编写中经常会遇到一些很长的set(xxx)的代码,非常影响体验. 而Eclipse的folding插件可以自定义的将代码块进行折叠. 效果如下图所示: 可以根据代码块的功能来进行折叠,从而保证代 ...

  10. 一览Django框架(转载)

    本文面向:有python基础,刚接触web框架的初学者. 环境:windows7   python3.5.1  pycharm专业版  Django 1.10版 pip3 一.Django简介 百度百 ...