[Architecture Pattern] Repository实作查询功能
[Architecture Pattern] Repository实作查询功能
范例下载
范例程序代码:点此下载
问题情景
在系统的BLL与DAL之间,加入Repository Pattern的设计,能够切割BLL与DAL之间的相依性,并且提供系统抽换DAL的能力。但在软件开发的过程中,套用Repository Pattern最容易遇到的问题就是,如何在Repository中实作「查询」这个功能。像是在下列这个查询订单的页面,系统必须要依照用户输入的查询条件,来从DAL中查询出所有符合条件内容的Order对象集合,并且将这些Order对象逐一呈现给在系统页面给用户浏览。
因为系统页面上的查询条件是可填、可不填,这对应到提供数据的Repository上,就变成Repository必须依照各种查询条件填或不填的各种组合,来提供对应的Method。但这样的查询功能设计,在查询条件少的情景能够正常的开发设计;但在查询条件较多的情景,就会发现为每种查询条件组合建立对应的Method,近乎是一项不可能的任务。(EX:每个条件内容可填可不填,3个查询条件就需要2^3=8个Method、7个查询条件就需要2^7=128个Method。)
public interface IOrderRepository
{
// Methods
IEnumerable<Order> GetAllByCondition(string userId, OrderState state, DateTime startDate, DateTime endDate);
IEnumerable<Order> GetAllByCondition(OrderState state, DateTime startDate, DateTime endDate);
IEnumerable<Order> GetAllByCondition(string userId, DateTime startDate, DateTime endDate);
IEnumerable<Order> GetAllByCondition(DateTime startDate, DateTime endDate);
IEnumerable<Order> GetAllByCondition(string userId, OrderState state);
IEnumerable<Order> GetAllByCondition(OrderState state);
IEnumerable<Order> GetAllByCondition(string userId);
IEnumerable<Order> GetAllByCondition();
}
这时最直觉的做法,会在Repository上加入GetAllBySql这个Method,让系统依照用户输入的查询条件来组合SQL指令,再交由实作Repository的DAL去数据库做查询。
public interface IOrderRepository
{
// Methods
IEnumerable<Order> GetBySql(string sqlCommand, params object[] parameters);
}
Repository加入GetAllBySql的这个设计,的确可以满足用户需求、提供正确信息给用户。但仔细思考Repository加入GetAllBySql的这个设计,是让DAL的职责污染到了BLL,BLL必须要知道DAL所使用的数据表名称、数据库字段才能组合出SQL指令,也就是在程序代码中隐性的让BLL相依于DAL,这大幅降低了BLL的内聚力。而一般来说只有关系数据库能够剖析SQL指令来提供数据,也就是DAL实作被绑死在关系数据库上,这也就大大降低了BLL的重用性。
接着,以下列这个开发情景:「系统的数据源,需要依照网络联机状态来决定使用本地数据库还是使用外部API」,来思考Repository加入GetAllBySql的这个设计。当外部API不支持SQL指令查询,系统就无法建立外部API的GetAllBySql实作,这也就限制了BLL抽换DAL成为外部API的能力。(感谢91提供范例~^^)
解决方案
IRepository设计
为了解决Repository实作查询功能的问题,回过头思考一般函式库、Web服务提供查询功能的方式。会发现很多查询功能的设计,会在查询功能中提供所有的查询条件,在这些条件内容中填null代表忽略这个条件、填值代表加入这个条件。
遵循这个设计原则,开发人员可以为Repository上加入GetAllByCondition这个Method,接着把每个查询条件都设计为这个Method的函式参数;最后替不可为null的值类型参数(enum、DateTime...)加上「?」关键词,将这些值类型改为可输入null的Nullable类别。
public interface IOrderRepository
{
// Methods
IEnumerable<Order> GetAllByCondition(string userId, OrderState? state, DateTime? startDate, DateTime? endDate);
}
IRepository使用
完成GetAllByCondition的设计之后,系统就可以将用户在窗体中所输入的查询条件,对应到GetAllByCondition的每个函式参数。(窗体中条件内容有填的对应为函式参数内容、窗体中条件内容没填的对应为函式参数null。)
// UserId
string userId = null;
if(string.IsNullOrEmpty(this.UserIdTextBox.Text) == false)
{
userId = this.UserIdTextBox.Text.Trim();
}
// State
OrderState? state = null;
if (this.StateComboBox.SelectedValue != null)
{
if (this.StateComboBox.SelectedValue.ToString() != "All")
{
state = Enum.Parse(typeof(OrderState), this.StateComboBox.SelectedValue.ToString()) as OrderState?;
}
}
// StartDate
DateTime? startDate = null;
if(string.IsNullOrEmpty(this.StartDateTextBox.Text) == false)
{
startDate = DateTime.Parse(this.StartDateTextBox.Text) as DateTime?;
}
// EndDate
DateTime? endDate = null;
if (string.IsNullOrEmpty(this.EndDateTextBox.Text) == false)
{
endDate = DateTime.Parse(this.EndDateTextBox.Text) as DateTime?;
}
// Query
var orderCollection = _orderRepository.GetAllByCondition(userId, state, startDate, endDate);
// Display
this.OrderGridView.DataSource = orderCollection;
执行范例(All)
执行范例(userId=A123)
执行范例(userId=A123, state=Completed)
SqlRepository实作
接着设计封装本地数据库的Repository实作,GetAllByCondition函式就可以依照这些函式参数是否为null、不为null的参数内容,来组合查询条件的SQL指令、提交给本地数据库并且回传查询结果。
依照条件内容是否为null,来组合SQL指令的Where条件。
// CommandText
command.CommandText = @"SELECT USER_ID, STATE, DATE FROM Orders"; // ConditionText
var conditionList = new List<string>();
if (string.IsNullOrEmpty(userId) == false) conditionList.Add("USER_ID = @USER_ID");
if (state.HasValue == true) conditionList.Add("STATE = @STATE");
if (startDate.HasValue == true && endDate.HasValue == true) conditionList.Add("Date >= @START_DATE");
if (startDate.HasValue == true && endDate.HasValue == true) conditionList.Add("Date <= @END_DATE");
var conditionString = string.Join(" AND ", conditionList);
if (string.IsNullOrEmpty(conditionString) == false) command.CommandText += " WHERE " + conditionString;
依照条件内容是否为null,来加入Command.Parameters。
// CommandParameters
if (string.IsNullOrEmpty(userId) == false) command.Parameters.Add(new SqlParameter("@USER_ID", userId));
if (state.HasValue == true) command.Parameters.Add(new SqlParameter("@STATE", state.ToString()));
if (startDate.HasValue == true && endDate.HasValue == true) command.Parameters.Add(new SqlParameter("@START_DATE", startDate.Value));
if (startDate.HasValue == true && endDate.HasValue == true) command.Parameters.Add(new SqlParameter("@END_DATE", endDate.Value));
执行范例(All)
执行范例(userId=A123)
执行范例(userId=A123, state=Completed)
IRepository查询
完成上列这些步骤之后,也就完成了Repository实作查询功能的开发工作,用户就能在系统页面上填写查询条件,来从系统中查询所有符合条件内容的数据对象集合。
执行范例(All)
执行范例(userId=A123)
执行范例(userId=A123, state=Completed)
后记
Repository实作查询功能的开发工作套用本篇的解决方案,能在BLL中完全不需要牵扯DAL的信息,只需要单纯传递C#类别来做为查询条件,这部分提高了BLL的内聚力。而GetAllByCondition的设计,因为单纯使用C#类别来传递查询条件,这让DAL实作不会被绑死在特定数据源上,也大幅提高了BLL的重用性。开发人员设计系统时遇到需要Repository实作查询功能的开发工作,参考本篇提供的解决方案应该就能满足大部分的开发需求。
[Architecture Pattern] Repository实作查询功能的更多相关文章
- [Architecture Pattern] Factory Builder
[Architecture Pattern] Factory Builder 目的 同时提供延迟注入对象.挂载注入项目这两个功能 情景 在开发系统时,如果需要在运行时间才生成并注入对象,可以套用Fac ...
- [Architecture Pattern] Singleton Locator
[Architecture Pattern] Singleton Locator 目的 组件自己提供Service Locator模式,用来降低组件的耦合度. 情景 在开发系统时,底层的Infrast ...
- FreeSql 新查询功能介绍
FreeSql FreeSql 是一个功能强大的 NETStandard 库,用于对象关系映射程序(O/RM),提供了 CodeFirst/DbFirst/CURD/表达式函数/读写分离 等基础封装. ...
- 一步一步学SpringDataJpa——JpaRepository查询功能
原文地址: https://blog.csdn.net/ming070423/article/details/22086169 1.JpaRepository支持接口规范方法名查询.意思是如果在接口中 ...
- Software Architecture Pattern(Mark Richards)笔记
软件架构模式 缺少规范架构的程序通常会变得紧耦合.脆弱.难以更改,缺少清晰的发展方向和愿景.这本小书用50多页介绍了常用的5种常见架构模式,相信不管是大牛还是萌新都会有所收获,特别是对我这种偏爱系统设 ...
- Python与数据库[2] -> 关系对象映射/ORM[5] -> 利用 sqlalchemy 实现关系表查询功能
利用 sqlalchemy 实现关系表查询功能 下面的例子将完成一个通过关系表进行查询的功能,示例中的数据表均在MySQL中建立,建立过程可以使用 SQL 命令或编写 Python 适配器完成. 示例 ...
- SpringDataJpa——JpaRepository查询功能(转)
1.JpaRepository支持接口规范方法名查询.意思是如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现,目前支持的关键字如下. Keyword Sample JPQL snippet ...
- 通过维基API实现维基百科查询功能
通过英文维基的免费API,可以实现对维基百科的搜索查询或者标题全文查询等,尝试了一下通过title实现全文查询,返回的结果是wikitext格式,暂时不知道该如何应用,所以仅实现了查询功能,可以返回最 ...
- 创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段 添加查询功能 本文将实现通过Name查询用户信息. 首先更新GetAll方法以启用查询: public async ...
随机推荐
- ORACLE自定义顺序排序-转
ORACLE可以借助DECODE函数,自定义顺序排序: select * from ( select 'Nick' as item from dual union all select 'Viki' ...
- dbvis MySQL server version for the right syntax to use near 'OPTION SQL_SELECT_LIMIT=DEFAULT' at line 1
转自:http://www.cnblogs.com/_popc/p/4053593.html 今天使用数据库查询工具DBvis链接mysql数据库时, 发现执行如何sql语句, 都报如下错误: 后来想 ...
- nginx rewrite重写与防盗链配置
nginx rewrite重写规则与防盗链配置方法 时间:2016-02-04 15:16:58来源:网络 导读:nginx rewrite重写规则与防盗链配置方法,rewrite规则格式中flag标 ...
- C# 通过WebService方式 IIS发布网站 上传文件到服务器
应用场景:要将本地的文件 上传到服务器的虚拟机上 网络环境:公司局域网(如下图中第二种) 开发环境:VS2010 服务器环境:WinServer2008 虚拟机环境:WinServer2008 ...
- 【转载】div层调整zindex属性无效原因分析及解决方法
在做的过程中,发现了一个很简单却又很多人应该碰到的问题,设置Z-INDEX属性无效.在CSS中,只能通过代码改变层级,这个属性就是z-index,要让z-index起作用有个小小前提,就是元素的pos ...
- (转)linux内核虚拟文件系统浅析
转自http://hi.baidu.com/_kouu/item/4e9db87580328244ef1e53d0 ###### 虚拟文件系统(VFS)在我看来, "虚拟"二字主要 ...
- AssetBundle系列——场景资源之解包(二)
本篇接着上一篇继续和大家分享场景资源这一主题,主要包括两个方面: (1)加载场景 场景异步加载的代码比较简单,如下所示: private IEnumerator LoadLevelCoroutine( ...
- Could not load file or assembly 'System.Core, Version=2.0.5.0 和autofac冲突的问题
在部署到iis的时候会出现这个状况. 解决:下载安装这个补丁 http://support.microsoft.com/kb/2468871 http://www.microsoft.com/zh-c ...
- linux shell 流程控制(条件if,循环【for,while】,选择【case】语句实例 --转载
http://www.cnblogs.com/chengmo/archive/2010/10/14/1851434.html nux shell有一套自己的流程控制语句,其中包括条件语句(if),循环 ...
- GitHub上那些值得一试的JAVA开源库
作为一名程序员,你几乎每天都会使用到GitHub上的那些著名Java第三方库,比如Apache Commons,Spring,Hibernate等等.除了这些,你可能还会fork或Star一些其他的开 ...