.NET框架设计(高级框架架构模式)—钝化程序、逻辑冻结、冻结程序的延续、瞬间转移
阅读目录:
- 1.开篇介绍
- 2.程序书签(代码书签机制)
- 2.1ProgramBookmark 实现(使用委托来锚点代码书签)
- 2.2ProgramBookmarkManager书签管理器(对象化书签集合的处理,IEnumerable<T>书签管理)
- 3.可恢复语句组件(将语句对象化)
- 3.1可恢复语句组件管理器(将可恢复语句视为普通的对象成员,IEnumerable<T>可恢复语句组件)
- 3.2可恢复语句组件运行时(Program CLR(简介))
- 3.3可恢复语句逻辑配置(规则的配置(简介))
- 3.4可恢复语句逻辑传输(将逻辑语句对象远程传输(简介))
- 4.DomainModel规则引擎(规则持久化后管理配置(简介))
1】开篇介绍
这一篇文章我早准备写的,迟迟未写的原因是它过于抽象不太容易表达,也很难掌握;之前对它的理解还处于比较简单的功能性上,但是最近随着对领域驱动设计及架构的研究,设计思想有了一个提升对它的理解也有了一个更清晰的轮廓,所以才敢下手去写,这么好的一篇文章不能搞砸了;
“钝化语句” 简单描述:将基于栈的调用抽象成基于我们自己构建的虚拟运行时调用;
比如我们可以将普通的IF\ELSE调用进行对象化,然后就可以对他们进行面向对象的设计了;能做的事情就太多了,比如将所有的方法放入一个for循环语句组件当中去,它会自动的去循环执行,而不需要我们再去自己写for语句;然后在此基础上进行代码书签抽象对所有的代码片段进行类似逻辑锚点的设定;
更吓人的是可以瞬间将语句组件钝化,其实也就是瞬间冻结然后持久化,在遥远的地方再将它唤醒执行,很可能你的语句在你这台电脑上执行了一半由于你临时有事然后语句被钝化,在另外一台电脑上继续你的工作,是不是很方便;当然它的使用方式多种多样了;
我相信这篇文章绝对让你对 .NET框架设计 感兴趣,框架设计思想其实真的很美,让人陶醉;
2】程序书签(代码书签机制)
美好的一切都要有一个良性的开始,程序的钝化少不了对程序的逻辑保存的功能;有一个连续的调用穿过N个方法,方法一调用方法二,方法二调用方法三,这样的调用层次是根据业务的需求来定的,就好比一个复杂的业务逻辑这样的处理下去合情合理;
那么什么是代码书签呢?其实我们仔细分析一下我们日常所写的代码基本上都是由方法组合而成,不管是实例类还是静态类都是通过方法将彼此联系起来,所有的业务逻辑都是包装在方法的内部处理的,这里的代码书签就是方法的可持久化抽象;
试想一下,我们要想将程序的逻辑流程钝化肯定是少不了对逻辑调用的保存;原本的程序逻辑是线程本地的执行路径,属于.NETCLR直接管理的,依赖于栈的执行,所以我们无法干预其生命周期过程,那么我们只有将它们对象化后才能由我们自己操控;
图1:
上图的意思是说在一个流程的开始到结束基本上三个重要环节,Begin\Processs…\End过程,在每个过程中需要不同的处理逻辑,在图的偏上方,我们有三个ProcessName名称的小方块表示程序的调用顺序,ProcessName1调用ProcessName2调用ProcessName3;
在ProcessName2的上面我们加了一个Bookmark的标记,表示我们这里所说的代码书签,通过代码书签我们就可以记录下本次执行到哪里了,就好比我们在看书的时候都有一个买书时赠送的书签卡,我们看到哪里就把这个书签卡插在那里,当下次要看的时候直接找到这个书签卡继续看;
这里的代码书签跟这个是一样的道理,理论就是这些我们下面通过示例代码来亲身体验一下这种设计模式;
2.1】ProgramBookmark 实现(使用委托来锚定代码书签)
委托是天生的方法标签,通过委托我们完全可以将一个实例的方法直接锚定下来;
【有关对委托的高级应用不太清楚的可以参见本人的这两篇文章:
我们来构造代码书签对象:
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ProgramComponent
{
using System; /// <summary>
/// Program book mark.
/// </summary>
[Serializable]
public class ProgramBookmark
{
/// <summary>
/// Mark program book mark.
/// </summary>
/// <param name="name">Mark name.</param>
/// <param name="continueAt">Program continue.</param>
public ProgramBookmark(string name, ProgramBookmarkLocation continueAt)
{
this.markname = name;
this.continueAt = continueAt;
} private string markname;
/// <summary>
/// Book mark name.
/// </summary>
public string BookmarkName { get { return markname; } } private ProgramBookmarkLocation continueAt;
/// <summary>
/// Continue location.
/// </summary>
public ProgramBookmarkLocation ContinueAt { get { return continueAt; } } /// <summary>
/// Program load data.
/// </summary>
public object Payload { get; set; }
}
/// <summary>
/// Program book mark location.
/// </summary>
/// <param name="resumed">Resumed bookmark.</param>
public delegate void ProgramBookmarkLocation(ProgramBookmark resumed);
}
这段代码是对代码书签的抽象,构造函数传入一个代码书签的名称、书签所表示的物理代码锚点,Payload是表示每次执行物理代码时的输入参数;
上面代码看似简单其实很不简单,它的背后隐藏着一个很大的设计思想:
将一块很大的逻辑代码拆成很多零碎的方法片段,很多人可能会觉得设计本身不就这样要求的嘛,那你可能真的没有深入理解代码碎片会后需要对所有的方法参数进行对象化,不管什么方法都会是同样的参数,只有这样才能让书签连续起作用;
下面我们来看一下代码书签有多巧妙,我们来构造一个简单的示例代码,当然你完全可以设计的很复杂很强大,这里毕竟是传递这种设计思想为主;
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication1
{
[Serializable]
public class OrderCheckFlows
{
private IList<ProgramComponent.ProgramBookmark> flowsManager = new List<ProgramComponent.ProgramBookmark>(); public OrderCheckFlows()
{
ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices =
new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderPrices)); flowsManager.Add(bookmarkCheckOrderPrices);
} public void StartCheck()
{
do
{
flowsManager[].ContinueAt(flowsManager[]);
}
while (flowsManager.Count > );
} #region business flows
public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
{
Console.WriteLine("checkPrices..."); ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices =
new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderItems));
bookmarkCheckOrderPrices.Payload = true;//method parameters.
flowsManager.Add(bookmarkCheckOrderPrices); flowsManager.RemoveAt();
} public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
{
if ((bool)nextCheck.Payload)
{
Console.WriteLine("checkItems...");
}
else
{
Console.WriteLine("end check items.");
}
flowsManager.RemoveAt();
}
#endregion
}
}
这个类是一个简单的模拟检查订单的一系列的业务流程;
图2:
上图能看见流程顺利执行完毕了,那么我们来解释一下重要的代码片段;
图3:
在第一个流程里面我们构造一个通往下一个流程的 ProgramComponent.ProgramBookmark 对象,如果这里出现关于流程无法继续下去的条件就可以不创建往下执行的代码书签;在第二流程里面我们获取第一个流程设置的参数,这里是一个Bool值,可以用来判断上一个执行是否成功等信息;
2.2】ProgramBookmarkManager书签管理器(书签集合的处理,IEnumerable<T>书签管理)
上一节我们完成了对代码书签的抽象实现,但是代码还有很多值得抽象设计的地方,上面的代码中最不太理解的地方就是对书签集合的操作上,很不OO;
那么这一节我们将把它改进,形成OO方式的调用,先看一下哪里不太理解;
图4:
第一个地方就是在声明ProgramCompoent.ProgramBookmark集合上,这样写问题太大了,无法进行扩展改进;然后就是在构造函数中,我们使用了很长一段代码来构造一个ProgramCompoent.ProgramBookmark对象,完全可以减少很多;还有就是在StartCheck方法的内部中进行循环调用书签的代码,也很有问题,完全可以封装在内部实现,外部直接一个CurrentProgram属性执行就行了;
那么对这些问题我们其实少一个ProgramCompoent.ProgramBookmark的管理器对象ProgramCompoent.ProgramBookmarkManager对象,它负责管理所有跟ProgramCompoent.ProgramBookmark对象相关的工作;
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ProgramComponent
{
using System.Collections.Generic; /// <summary>
/// Program book mark Manager.<see cref="System.Collections.Dictionary{BookmarkName,ProgramBookmark}"/>
/// </summary>
public class ProgramBookmarkManager : Dictionary<string, ProgramBookmark>
{
/// <summary>
/// Add programbookmark and instant next programbookmark.
/// </summary>
/// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param>
public void Add(ProgramBookmark bookmark)
{
base.Add(bookmark.BookmarkName, bookmark);
}
/// <summary>
/// Remove programbookmark.
/// </summary>
/// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param>
public void Remove(ProgramBookmark bookmark)
{
base.Remove(bookmark.BookmarkName);
}
/// <summary>
/// Resume bookmark by bookmarkname.
/// </summary>
/// <param name="bookmarkName">bookmark name.</param>
/// <param name="payload">Continue load.</param>
public void Resume(string bookmarkName, object payload)
{
ProgramBookmark bookmark;
this.TryGetValue(bookmarkName, out bookmark);
if (bookmark != null)
{
bookmark.Payload = payload;
bookmark.ContinueAt(bookmark);
}
}
}
}
书签管理器基本功能还算简单,主要的方法Resume是用来恢复指定的书签的;再来看一下订单检查流程调用;
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ConsoleApplication1
{
using System;
using ProgramComponent; [Serializable]
public class OrderCheckFlows
{
private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderCheckFlows()
{
BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices)));
} public void StartCheck()
{
BookmarkManager.Resume("checkPrices", null);
} #region business flows
public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
{
Console.WriteLine("checkPrices...");
BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems)));
BookmarkManager.Resume("checkItems", true);
} public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
{
if ((bool)nextCheck.Payload)
Console.WriteLine("checkItems...");
else
Console.WriteLine("end check items."); BookmarkManager.Remove(nextCheck);
}
#endregion
}
}
是不是比之前的代码好多了,我感觉是好多了,当然还有很大的重构空间;
这里其实已经可以和链式编程的机制挂钩了,我们可以通过给书签管理器添加N个扩展方法来使书签管理器具有跟链式的调用;
3】可恢复语句组件(将可恢复语句对象化)
要想把所有的调用都拆开来使用松散的方式组合,通过使用书签机制基本上能将所有的方法进行松散组合;那么我们还需要将逻辑语法进行对象化才能做到无死角的松散;
什么叫语句组件,就是将一些原本无法独立的一些逻辑判断、循环之类的语句对象化,形成更具有对象的组件;
试想一下,如果我们将所有的这些逻辑语法对象化后我们的代码中还有精密耦合的代码吗?就算有也应该会很少,是不是很神奇;
其实对 企业应用架构 中的 规约模式 有所了解的人应该会比较熟悉这一节的内容,跟规约模式很像,但不是一个东西,侧重点不同;语句组件全面的概念是将所有的调用都对象化,包括一些输出、输入、网络调用等等,这样才是全部的语句组件定义,还记得我们上面的订单检查对象嘛,那个也是语句组件之一;
我们来构造可恢复语句组件对象;
ProgramComponent.LanguageComponent.LanguageComponent类代码:
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ProgramComponent.LanguageComponent
{
using System; [Serializable]
public abstract class LanguageComponent
{
public abstract void Run(ProgramBookmarkManager mgr);
}
}
ProgramComponent.LanguageComponent.LanguageComponentBlock类代码:
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ProgramComponent.LanguageComponent
{
using System.Collections.Generic; public class LanguageComponentBlock : LanguageComponent
{
List<LanguageComponent> statements = new List<LanguageComponent>();
public List<LanguageComponent> Statements
{
get { return statements; }
} public void AddLangugateComponent(LanguageComponent lc)
{
statements.Add(lc);
} public override void Run(ProgramBookmarkManager mgr)
{ }
}
}
ProgramComponent.LanguageComponent.IfElseLanguageComponent类代码:
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ProgramComponent.LanguageComponent
{
using System;
using System.Linq;
using System.Linq.Expressions; public class IfElseLanguageComponent : LanguageComponentBlock
{
public Func<bool> Exp { get; set; } public override void Run(ProgramBookmarkManager mgr)
{
if (Exp())
base.Statements[].Run(mgr);
else
base.Statements[].Run(mgr);
}
}
}
检查流程代码,OrderCheckFlows\OrderSubmitFlows类代码:
/*==============================================================================
* Author:深度训练
* Create time: 2013-08-10
* Blog Address:http://www.cnblogs.com/wangiqngpei557/
* Author Description:特定领域软件工程实践;
*==============================================================================*/ namespace ConsoleApplication1
{
using System;
using ProgramComponent;
using ProgramComponent.LanguageComponent; [Serializable]
public class OrderCheckFlows : LanguageComponent
{
private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderCheckFlows(ProgramBookmarkManager bookmarkManager)
{
this.BookmarkManager = bookmarkManager;
BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices)));
}
public override void Run(ProgramBookmarkManager mgr)
{
this.BookmarkManager = mgr;
StartCheck();
}
public void StartCheck()
{
BookmarkManager.Resume("checkPrices", null);
} #region business flows
public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
{
Console.WriteLine("checkPrices...");
BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems)));
BookmarkManager.Resume("checkItems", true);
} public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
{
if ((bool)nextCheck.Payload)
Console.WriteLine("checkItems...");
else
Console.WriteLine("end check items."); BookmarkManager.Remove(nextCheck);
}
#endregion
} [Serializable]
public class OrderSubmitFlows : LanguageComponent
{
private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderSubmitFlows(ProgramBookmarkManager bookmarkManager)
{
this.BookmarkManager = bookmarkManager;
BookmarkManager.Add(new ProgramBookmark("CheckSubmitPrices", new ProgramBookmarkLocation(CheckSubmitPrices)));
} public override void Run(ProgramBookmarkManager mgr)
{
this.BookmarkManager = mgr;
StartCheck();
}
public void StartCheck()
{
BookmarkManager.Resume("CheckSubmitPrices", null);
} #region business flows
public void CheckSubmitPrices(ProgramComponent.ProgramBookmark nextCheck)
{
Console.WriteLine("CheckSubmitPrices...");
BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("CheckSubmitItems", new ProgramBookmarkLocation(CheckSubmitItems)));
BookmarkManager.Resume("CheckSubmitItems", true);
} public void CheckSubmitItems(ProgramComponent.ProgramBookmark nextCheck)
{
if ((bool)nextCheck.Payload)
Console.WriteLine("CheckSubmitItems...");
else
Console.WriteLine("end check CheckSubmitItems."); BookmarkManager.Remove(nextCheck);
}
#endregion
}
}
调用代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication1
{
using ProgramComponent;
using ProgramComponent.LanguageComponent; class Program
{
static void Main(string[] args)
{
ProgramBookmarkManager bookmarkManager = new ProgramBookmarkManager(); OrderCheckFlows orderCheckFlow = new OrderCheckFlows(bookmarkManager);
OrderSubmitFlows submitCheckFlow = new OrderSubmitFlows(bookmarkManager); IfElseLanguageComponent languageComponent = new IfElseLanguageComponent();
languageComponent.Exp = () => { return true; };
languageComponent.AddLangugateComponent(orderCheckFlow);
languageComponent.AddLangugateComponent(submitCheckFlow);
languageComponent.Run(bookmarkManager); Console.ReadLine();
}
}
}
一切都已经被对象化,我们来看一下逻辑;
图5:
这里的返回值决定了后面要执行的语句组件的路径,如果是true,则应该检查OrderCheckFlows流程;
图6:
如果是false,则应该检查OrderSubmitFlows流程;
图7:
可恢复语句对象模型基本构造完成,当然复杂的问题还需要仔细的去分析设计,这里只是一个简单的示例;
3.1】可恢复语句组件管理器(将可恢复语句视为普通的对象成员,IEnumerable<T>语句组件)
跟代码书签管理器一个道理,这里我们也可以实现一个LanguageComponentManager来对LanguageComponent管理,当然也要看需要不需要;可恢复语句管理器其实有很多文章可以做,因为它是所有语句组件的中心,这对于后面的持久化有很大的用处;
//由于内容比较多且相当抽象,下一篇文章介绍;
3.2】可恢复语句组件运行时(Program CLR)
所有的语句代码都已经被对象化,但是在运行时需要一个中心来管理这些被对象化的语句组件,因为我们要脱离对栈的依赖;一组语句组件是单个示例流程的一部分,但是我们可能会存在很多一起并行运行的流程,所以这是必须要提供的运行时;
//由于内容比较多且相当抽象,下一篇文章介绍;
3.3】可恢复语句逻辑配置(规则的配置)
领域驱动设计在使用规约模式的时候会存在动态配置的需求,可以参见这里的语句组件模型,让规约最大化的提供配置;
//由于内容比较多且相当抽象,下一篇文章介绍;
3.4】可恢复语句逻辑传输(将可恢复语句对象远程传输)
//由于内容比较多且相当抽象,下一篇文章介绍;
4】DomainModel规则引擎(规则持久化后管理配置)
//由于内容比较多且相当抽象,下一篇文章介绍;
示例DEMO地址:http://files.cnblogs.com/wangiqngpei557/ConsoleApplication3.zip
作者:王清培
出处:http://www.cnblogs.com/wangiqngpei557/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
.NET框架设计(高级框架架构模式)—钝化程序、逻辑冻结、冻结程序的延续、瞬间转移的更多相关文章
- unity热更新AssetBundle框架设计_框架篇
目录 第1 章 : AssetBundle框架整体设计 课时1:实战项目开发AB技术问题分析 10:53 课时2:实战项目开发AB技术解决方案 10:24 课时3:AB框架整体设计方案详解 09:45 ...
- Python+Selenium框架设计之框架内封装基类和实现POM
原文地址https://blog.csdn.net/u011541946/article/details/70269965 作者:Anthony_tester 来源:CSDN 博客地址https ...
- .NET架构设计、框架设计系列文章总结
从事.NET开发到现在已经有七个年头了.慢慢的可能会很少写.NET文章了.不知不觉竟然走了这么多年,热爱.NET热爱c#.突然想对这一路的经历进行一个总结. 是时候开始下一阶段的旅途,希望这些文章可以 ...
- .NET框架设计—常被忽视的C#设计技巧
.NET框架设计—常被忽视的C#设计技巧 阅读目录: 1.开篇介绍 2.尽量使用Lambda匿名函数调用代替反射调用(走进声明式设计) 3.被忽视的特性(Attribute)设计方式 4.扩展方法让你 ...
- 领域驱动设计业务框架DMVP
DMVP,全称DDD-MVP,是基于领域驱动设计(DDD)搭建的业务框架,整体设计符合DDD领域模型的规范,业务上达成了领域模型和代码的一一映射,技术上达成了高内聚低耦合的架构设计,开发人员不需要关注 ...
- 基于WPF系统框架设计(1)-为什么要仿Office2010 Ribbon?
为什么系统框架设计使用Ribbon导航模式? 这得从Office软件的演变说起.微软为什么最后选择使用Ribbon,也许就是很多系统设计要使用Ribbon做功能导航的原因. 你是否还记得曾经使用过的M ...
- 读书笔记《PHP高级程序设计、模式、框架与测试》
序言 闲来无事,下载了一些电子书,然后看书名不错<PHP高级程序设计_模式.框架与测试>,翻了一下虽然书有点老了但是讲的内容经常会碰到!给大家推荐一下,然后这里放上我的读书笔记,每日更新. ...
- 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序
如何在Visual Studio 2017中使用C# 7+语法 前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...
- 新书出版《.NET框架设计—模式、配置、工具》感恩回馈社区!
很高兴我的第一本书由图灵出版社出版.本书总结了我这些年来对框架学习.研究的总结,里面纯干货,无半句废话. 书的详情请看互动网的销售页面:http://product.china-pub.com/377 ...
随机推荐
- 几个最常用的git命令
之前在Windows下一直用可视化的tortoise git,在Linux下最好是用命令行,以下是常用的git命令: git status:显示当前已修改的文件,新增的文件 git checkout ...
- IOS----友盟推送详解
这两天好好的研究了下推送这功能,关于它我将分成两部分来讲,一.IOS手机端,二.Servlet服务端,今天先讲下IOS端 一.感受 下面讲下我对推送这个功能在IOS下的感受,这个算是我做了服务端的功能 ...
- php PDO调用带有out参数的存储过程(原创)
这几天比较闲学了下PHP, 开发工具zendphp,server下的一个集成工具WampServer. 感觉php实现一个功能写的代码比asp.net java都少,特别是数据库访问这块,如果是asp ...
- [moka同学笔记]Yii2中多表关联查询(join、joinwith) (摘录)
表结构 现在有客户表.订单表.图书表.作者表, 客户表Customer (id customer_name) 订单表Order (id order_name cu ...
- 为vs2012添加背景和皮肤
每天对着电脑用VS进行编码开发,难免有点单调.我们可以试着像设置电脑桌面壁纸一样设置VS皮肤. 先看下效果 1.打开vs2012,工具——扩展和更新 2.选择联机,搜索并分别安装如下两个插件 3.安装 ...
- liMarquee演示12种不同的无缝滚动效果
很实用的一款liMarquee演示12种不同的无缝滚动效果 在线预览 下载地址 实例代码 <!DOCTYPE html> <html lang="zh-CN"&g ...
- bootstrap字体图标在谷歌显示正常,在火狐显示异常的问题
bootstrap字体图标的使用 现在有很多的网站支持字体图标,我所知道的有bootstrap,fontawesome,iconmoon,等等,可能还有其他我并不知道 bootstrap只要你的文件夹 ...
- oracle表的管理
表名和列的命名规则 必须以字母开头: 长度不能超过30字符: 不能使用oracle的保留字: 只能使用如下字符:A-Z,a-z,0-9,$,#等: 数据类型: 字符型: char 定长 ...
- 安装 Ubuntu 后的个人常用配置
在 ASA 猪队友的带领下,拥抱开源世界,用上了Ubuntu.资深强迫症现身说法,配置符合自己使用习惯的Ubuntu. 1. 窗口标题栏显示菜单项 打开系统设置->外观->行为,在[显示窗 ...
- iOS Assigning to 'id<XXXDelegate>' from incompatible type 'BViewController *__strong'
在使用代理的时候, BViewController *BVC = [[BViewController alloc]init]; self.delegate = BVC; 出现这样的警告Assignin ...