【C#|.NET】从细节出发(三) 逻辑层事务和page object模式
一. 业务逻辑层的事务问题
如果你的程序分层清晰并且系统禁用复杂存储过程,那么在DA中的职责比较单一。程序的逻辑通过BLL调用各种不同模块的DA来实现数据操作。如果当需要不同模块在一个事务的时候,问题就产生了。
如果你在bll引用System.Data...或者你在DA中穿插各种复杂逻辑的时候基本上你的工程已经不能算是好的程序了,
1. 使用TransactionScope
TransactionScope可以使代码块成为事务性代码。但是需要开通MSDTC权限,并且TransactionScope的性能问题也一直存在争议。
2. 阶段性提交
这个涉及到框架层次的修改,会单独开篇幅来说
3. 使用委托 将多模块提交汇聚在一起 和框架层次的阶段性提交殊途同归
这个就是设计上的问题,将其他模块的方法以委托方式传入DA。举个例子假如主线为消费记录ConsumeRecord,分支线为消费详细ConsumeRecordDetail和票据信息TravelTicket。其中TravelTicket完全为另一个服务模块对自己为黑盒。ConsumeRecordDetail对自己为白盒。
DA层
public delegate BizResult<string> ArchiveTravelTicketDelegate( DbTransaction transaction, List<string> travelTicketIDList);
主线服务BLL中,其中StartArchives为其他模块但是需要包含近事务的方法
private BizResult<string> InsertConsumeRecord(ConsumeRecordEntity consumeRecordEntity, List<ConsumeRecordDetailEntity> consumeRecordDetails)
{
return consumeRecordArchiveTargetDA.DoArchive(consumeRecordEntity, consumeRecordDetails, new TravelTicketArchiveService().StartArchives);
}
主线服务DA中
public BizResult<string> DoArchive(ConsumeRecordEntity consumeRecordEntity, List<ConsumeRecordDetailEntity> consumeRecordDetails,
ArchiveHelper.ArchiveTravelTicketDelegate archiveTravelTicket)
{
return ArchiveHandle(consumeRecordEntity, consumeRecordDetails, true, ConsumeRecordHandle, archiveTravelTicket);
}
public BizResult<string> ConsumeRecordHandle(DbTransaction currentTransaction, ConsumeRecordEntity consumeRecordEntity, List<ConsumeRecordDetailEntity> consumeRecordDetails)
{
return ConsumeRecordHandle(currentTransaction, consumeRecordEntity, consumeRecordDetails,
(x, y) => InsertConsumeRecordLog(currentTransaction, consumeRecordEntity, true, false)
, InsertConsumeRecord, consumeRecordDetailArchiveDA.InsertConsumeRecordDetail);
}
其中InsertConsumeRecordLog,InsertConsumeRecord为主线自己的方法,ConsumeRecordDetail因为白盒可以直接使用consumeRecordDetailArchiveDA.InsertConsumeRecordDetail,archiveTravelTicket为黑盒。
主线BaseDA中
public BizResult<string> ArchiveHandle(ConsumeRecordEntity consumeRecordEntity, List<ConsumeRecordDetailEntity> consumeRecordDetails, bool isArchive,
ConsumeRecordHandlerDelegate consumeRecordHandlerDelegate, ArchiveHelper.ArchiveTravelTicketDelegate archiveTravelTicket)
{
using (DbConnection dbConnection = DbObject.CreateConnection())
{
dbConnection.Open(); BizResult<string> bizResult;
using (DbTransaction currentTransaction = dbConnection.BeginTransaction())
{
try
{
bizResult = consumeRecordHandlerDelegate(currentTransaction, consumeRecordEntity, consumeRecordDetails);
if (bizResult.IsSuccessful)
{
if (archiveTravelTicket != null)
{
bizResult = archiveTravelTicket(currentTransaction,
consumeRecordDetails.Select(x => x.TravelMoneyID).
Distinct().ToList());
}
if (bizResult.IsSuccessful)
{
bizResult.Message = "xxx success";
currentTransaction.Commit();
return bizResult;
}
bizResult.Message = "xxx fail";
}
bizResult.Message = string.Format("{0}:{1}", isArchive, bizResult.Message);
currentTransaction.Rollback();
}
catch (Exception ex)
{
logger.Error(ex);
currentTransaction.Rollback();
throw;
}
}
return bizResult;
}
}
二. 测试环节的page object模式
拿自动化测试Selenium为例,一般而言我们对于程序都是应付测试用例,针对测试用例写出一个一个test-method。这样无可厚非,使用page object模式可以让代码开起来更专业更精彩。
在自动化测试中,主要关注UI的测试互动区域。 Page对象只是这些模型作为测试代码中的对象可以减少了重复的代码量,并且意味着,如果用户界面的变化,也只需要修改一个地方。和软件设计模式中dry(don't repeat yourself)类似。并且在page object模式中,PageObjects也可以实现页于页之间的逻辑。总得来说减少了代码的重复,让测试更具可读性和强大的,提高了测试的可维护性,特别是当有频繁变化的ui变更。
举个列子(java版本)
public class LoginPageTest {
private LoginPage loginPage;
private final String username = "xx@163.com";
private final String pwd= "Welcome1"; @Before
public void setUp() throws Exception {
String applicationRoot = new File(
getClass().getProtectionDomain().getCodeSource().getLocation().getPath())
.getParent();
System.setProperty("webdriver.chrome.driver", applicationRoot + "/chromedriver");
WebDriver webDriver = new ChromeDriver();
loginPage = new LoginPage(webDriver);
loginPage.init();
} @Test
public void testLoginWithoutFakeCheckbox()
{
loginPage.setUserName(username);
loginPage.setPassword(pwd);
loginPage.pressLoginButton();
assertThat(loginPage.getClassByLoginWithoutFakeCheckbox(), equalTo("checkbox-wrapper not-checked"));
} @Test
public void testLoginWithWrongUsername()
{
loginPage.setUserName("wrongusername");
loginPage.setPassword(pwd);
loginPage.pressCheckBox();
loginPage.pressLoginButton();
assertThat(loginPage.getClassByLoginWithWrongUserName(), equalTo("wrapper-input-text wrong"));
} @Test
public void testLoginWithNullPwd()
{
loginPage.clearInput();
loginPage.setUserName(username);
loginPage.pressCheckBox();
loginPage.pressLoginButton();
assertThat(loginPage.getClassByLoginWithNullPwd(), equalTo("wrapper-input-text wrong"));
} @Test
public void testLoginWithRightWay()
{
loginPage.clearInput();
loginPage.setUserName(username);
loginPage.setPassword(pwd);
loginPage.pressCheckBox();
loginPage.pressLoginButton();
assertThat(loginPage.getTextByRightWay(), equalTo("选择性别"));
} }
public class LoginPage {
private final WebDriver webDriver;
private final WebDriverWait wait;
private JavascriptExecutor js;
private final String username = "xx@163.com";
private final String pwd = "Welcome1";
private WebElement webElementLogin;
private WebElement webElementPwd;
private WebElement webElementInput; public LoginPage(WebDriver webDriver) {
this.webDriver = webDriver;
this.wait = new WebDriverWait(webDriver, 10);
this.js = (JavascriptExecutor) webDriver;
} public void init() {
webDriver.get("http://root:root@localhost:8080/apitool/xxx/owF3PjkBFY5_56Q_KYs5fxmLZTKI");
WebElement webElementName = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("p.text.bold.name"))); if (webElementName != null) {
System.out.print(webElementName.getText());
}
webElementLogin = webDriver.findElement(By.cssSelector("input.input-text.login-input"));
webElementLogin.sendKeys(username);
webElementPwd = webDriver.findElement(By.cssSelector("input.input-text.password-input"));
webElementPwd.sendKeys(pwd);
} public String getClassByLoginWithoutFakeCheckbox() {
WebElement webElementCss = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.xpath("//*[contains(@class,'checkbox-wrapper not-checked')]")));
WebElement webElementHref = webDriver.findElement(By.xpath("//*[@id=\"login-page\"]/div/section/div/div[5]/div/div"));
return webElementHref.getAttribute("class");
} public String getClassByLoginWithWrongUserName() {
webElementInput = webDriver.findElement(By.xpath("//*[@id=\"login-page\"]/div/section/div/div[3]"));
return webElementInput.getAttribute("class");
} public String getClassByLoginWithNullPwd() {
webElementInput = webDriver.findElement(By.xpath("//*[@id=\"login-page\"]/div/section/div/div[3]"));
return webElementInput.getAttribute("class");
} public String getTextByRightWay() {
GenderViewPage genderViewPage = new GenderViewPage(webDriver);
return genderViewPage.getGenderText();
} public void pressCheckBox() {
js.executeScript("$('.fake-checkbox').attr('class','fake-checkbox active')");
} public void clearInput() {
webElementLogin.clear();
webElementPwd.clear();
} public void setUserName(String username) {
webElementLogin.clear();
webElementLogin.sendKeys(username); } public void setPassword(String password) {
webElementPwd.clear();
webElementPwd.sendKeys(pwd);
} public void pressLoginButton() {
js.executeScript("$('.login-button').trigger('touchstart')");
} public void closeBrowser()
{
webDriver.quit();
}
}
三. sql update 慎用位运算
update t_User set valye = Flag | 4 where UserID = 1
如果Flag字段中存在负数,在做位运算的时候,更新出非常大的数值,超过2的62次方
希望对大家有帮助。
【C#|.NET】从细节出发(三) 逻辑层事务和page object模式的更多相关文章
- 从零开始编写自己的C#框架(14)——T4模板在逻辑层中的应用(三)
原本关于T4模板原想分5个章节详细解说的,不过因为最近比较忙,也不想将整个系列时间拉得太长,所以就将它们整合在一块了,可能会有很多细节没有讲到,希望大家自己对着代码与模板去研究. 本章代码量会比较大, ...
- [Prodinner项目]学习分享_第三部分_Service层(业务逻辑层)
前两节讲到怎样生成一个Model和怎样将Model映射到数据库,这一节将讲到业务逻辑层,也就是Service层. 1.Prodinner架构已经构建好的,基本的增删改查. 假设,我现在想操作第二节中讲 ...
- 微信小程序学习笔记(三)--框架-逻辑层
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈. 开发者写的所有代码最终将会打包成一份 JavaScript 文件,并在小程序启动的时候运行,直到小程序销毁.这一行为类似 Service ...
- MVC5 网站开发之四 业务逻辑层的架构和基本功能
业务逻辑层在Ninesky.Core中实现,主要功能封装一些方法通过调用数据存储层,向界面层提供服务. 目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 ...
- 从零开始编写自己的C#框架(12)——T4模板在逻辑层中的应用(一)(附源码)
对于T4模板很多朋友都不太熟悉,它在项目开发中,会帮我们减轻很大的工作量,提升我们的开发效率,减少出错概率.所以学好T4模板的应用,对于开发人员来说是非常重要的. 园子里对于T4模板的介绍与资料已经太 ...
- C#多态“说来也说”——逻辑层BLL中的多态使用
本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址 http://www.cnblogs.com/tdws/p/5861842.html 昨天晚上,有个朋友说学了好久,依然没搞 ...
- 9.1.3 .net framework通过业务逻辑层自动生成WebApi的做法
首先需要说明的是这是.net framework的一个组件,而不是针对.net core的.目前工作比较忙,因此.net core的转换正在编写过程中,有了实现会第一时间贴出来. 接下来进入正题.对于 ...
- HL AsySocket 服务开发框架 - 业务逻辑层
一 概述 Socket服务只是提供一个网络传输服务. 业务逻辑层在整体架构中的位置在那里呢,如图: 网络层将解包后的消息包抛至业务逻辑层,业务逻辑层收到消息包后,解析消息类型,然后转入相应的处理流程处 ...
- JSP业务逻辑层
经典的三层架构:表示层.业务逻辑层和数据访问层 具体的区分方法 1:数据访问层:主要看你的数据层里面有没有包含逻辑处理,实际上他的各个函数主要完成各个对数据文件的操作.而不必管其他操作. 2:业务逻辑 ...
随机推荐
- 拉格朗日乘子法和KKT条件
拉格朗日乘子法(Lagrange Multiplier)和KKT(Karush-Kuhn-Tucker)条件是求解约束优化问题的重要方法,在有等式约束时使用拉格朗日乘子法,在有不等约束时使用KKT条件 ...
- 从DB灌值到DataTable时,字段值为NULL时报错相关信息;
报错信息: 1. 2. 3. 4. 5. 6. 解决方法: 1. Data Layer SQL 语句取数据时,把其列值有为null的字段用0.00替换,(ISNULL的用法): 2. #r ...
- PHP接口使用
<?php interface IEmployee { function working(); } interface IDeveloper { function Coding(); } cla ...
- 跟我一起学WCF(5)——深入解析服务契约[上篇]
一.引言 在上一篇博文中,我们创建了一个简单WCF应用程序,在其中介绍到WCF最重要的概念又是终结点,而终结点又是由ABC组成的.对于Address地址也就是告诉客户端WCF服务所在的位置,而Cont ...
- 【Win10】解决 模拟器调试手机 错误-> 引导阶段... 无法找到指定路径......\2052\msdbgui.dll
去弄几天的Web服务,又弄了几天的CefSharp,都是给其它组的同学做了一下支持,终于又可以回来玩下Win10啦. 今天想试一下UWP在手机上的效果,就找了台WP手机开始升级,结果下载速度遥遥无期, ...
- 在Linux CentOS上编译并安装Clang 3.5.0
编译CoreCLR需要Clang 3.5,而CentOS上安装的是Clang 3.4.2(yum repos中最新版也是这个),只能自己手工编译LLVM的源代码进行安装. (注:CentOS的版本是6 ...
- C++ Primer学习笔记一
/* 题目要求把字符串BRGBBGRRGBBGBBBGRRGBGRG按RGB顺序排列,空间复杂度为O(1) */#include<iostream> using namespace std ...
- AngularJS快速入门指南02:介绍
AngularJS是一个JavaScript框架.它可以通过<script>标记被添加到HTML页面中. AngularJS通过指令对HTML属性进行了扩展,然后通过表达式将数据绑定到HT ...
- hibernate懒加载(转载)
http://blog.csdn.net/sanjy523892105/article/details/7071139 懒加载详解 懒加载为Hibernate中比较常用的特性之一,下面我们详细来了解下 ...
- js命名规范