【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:业务逻辑 ...
随机推荐
- 6.3 Android Framework
Android的四层架构分别为Linux2.6内核层,核心库层,应用框架层,应用层.Framework层为我们开发应用程序提供了非常多的API,满足我们业务上的需求.(Android是基于Linux内 ...
- ymPrompt消息组件
<script src="jb51.net/prompt/jquery-1.7.1.js" type="text/javascript"></ ...
- JAVA学习博客---2015.5
上一次的学习博客写的和流水账差不多,有点生硬的和背目录一样,所以既然学习的目的是程序,那么这个月的学习博客就用程序来说点东西吧.这个月看了一些C和JAVA的视频,开始看别人写的程序,能看的懂但是自己去 ...
- 国内CDN公共库
CDN公共库是指将常用的JS库存放在CDN节点,以方便广大开发者直接调用.与将JS库存放在服务器单机上相比,CDN公共库更加稳定.高速.一般的CDN公共库都会包含全球所有最流行的开源JavaScrip ...
- 使用FEST-Swing测试GUI
在Java中最常用的单元测试工具要算JUnit了.FEST-Swing是一个能够与JUnit集成的GUI测试框架.使用FEST-Swing可以更方便的对Swing进行一系列的测试.下面就说 ...
- Hadoop:Windows 7 32 Bit 编译与运行
所需工具 1.Windows 7 32 Bit OS(你懂的) 2.Apache Hadoop 2.2.0-bin(hadoop-2.2.0.tar.gz) 3.Apache Hadoop 2.2.0 ...
- [ucgui] 对话框7——按钮触发与模式窗口
>_<" 模式窗口,只有结束该窗口时才能聚焦到其他的窗口上~
- EF结合三层:三层中数据层父类和业务层父类的使用
今天我们主要讨论下数据层父类和业务层父类的使用.众所周知,数据层无非就是实现增删改查的方法.无论是哪个实体类,无非就是为了实现增删改查方法,所有我们在三层的DAL层封装了一个BaseDAL类,来做增删 ...
- ORM SQLOBJECT SIMPLE
step01: (Install sqlobject) sudo easy_install sqlobject step02: (Connect MySQL) step03: (Install sup ...
- Atitit.可视化编程jbpm6 的环境and 使用总结...
Atitit.可视化编程jbpm6 的环境and 使用总结... 1. Jbpm的意义 1 2. Jbpm6环境配置 2 2.1. Down 2 2.2. Install eclipse jbpm p ...