一切福田,不離方寸,從心而覓,感無不通。

Category Archives: Backend

CQRS架构简介

CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺。对于这3点,我觉得很有道理。所以也想谈一下,CQRS架构下是如何实现高性能的。 关于CQRS(Command Query Responsibility Segration)架构,大家应该不会陌生了。简单的说,就是一个系统,从架构上把它拆分为两部分:命令处理(写请求)+查询处理(读请求)。然后读写两边可以用不同的架构实现,以实现CQ两端(即Command Side,简称C端;Query Side,简称Q端)的分别优化。CQRS作为一个读写分离思想的架构,在数据存储方面,没有做过多的约束。所以,我觉得CQRS可以有不同层次的实现,比如: CQ两端数据库共享,CQ两端只是在上层代码上分离;这种做法,带来的好处是可以让我们的代码读写分离,更好维护,且没有CQ两端的数据一致性问题,因为是共享一个数据库的。我个人认为,这种架构很实用,既兼顾了数据的强一致性,又能让代码好维护。 CQ两端数据库和上层代码都分离,然后Q的数据由C端同步过来,一般是通过Domain Event进行同步。同步方式有两种,同步或异步,如果需要CQ两端的强一致性,则需要用同步;如果能接受CQ两端数据的最终一致性,则可以使用异步。采用这种方式的架构,个人觉得,C端应该采用Event Sourcing(简称ES)模式才有意义,否则就是自己给自己找麻烦。因为这样做你会发现会出现冗余数据,同样的数据,在C端的db中有,而在Q端的db中也有。和上面第一种做法相比,我想不到什么好处。而采用ES,则所有C端的最新数据全部用Domain Event表达即可;而要查询显示用的数据,则从Q端的ReadDB(关系型数据库)查询即可。 我觉得要实现高性能,可以谈的东西还有很多。下面我想重点说说我想到的一些设计思路: 避开资源争夺 秒杀活动的例子分析 我觉得这是很重要的一点。什么是资源争夺?我想就是多个线程同时修改同一个数据。就像阿里秒杀活动一样,秒杀开抢时,很多人同时抢一个商品,导致商品的库存会被并发更新减库存,这就是一个资源争夺的例子。一般如果资源竞争不激烈,那无所谓,不会影响性能;但是如果像秒杀这种场景,那db就会抗不住了。在秒杀这种场景下,大量线程需要同时更新同一条记录,进而导致MySQL内部大量线程堆积,对服务性能、稳定性造成很大伤害。那怎么办呢?我记得阿里的丁奇写过一个分享,思路就是当MySQL的服务端多个线程同时修改一条记录时,可以对这些修改请求进行排队,然后对于InnoDB引擎层,就是串行的。这样排队后,不管上层应用发过来多少并行的修改同一行的请求,对于MySQL Server端来说,内部总是会聪明的对同一行的修改请求都排队处理;这样就能确保不会有并发产生,从而不会导致线程浪费堆积,导致数据库性能下降。这个方案可以见下图所示: 如上图所示,当很多请求都要修改A记录时,MySQL Server内部会对这些请求进行排队,然后一个个将对A的修改请求提交到InnoDB引擎层。这样看似在排队,实际上会确保MySQL Server不会死掉,可以保证对外提供稳定的TPS。 但是,对于商品秒杀这个场景,还有优化的空间,就是Group Commit技术。Group Commit就是对多个请求合并为一次操作进行处理。秒杀时,大家都在购买这个商品,A买2件,B买3件,C买1件;其实我们可以把A,B,C的这三个请求合并为一次减库存操作,就是一次性减6件。这样,对于A,B,C的这三个请求,在InnoDB层我们只需要做一次减库存操作即可。假设我们Group Commit的每一批的size是50,那就是可以将50个减操作合并为一次减操作,然后提交到InnoDB。这样,将大大提高秒杀场景下,商品减库存的TPS。但是这个Group Commit的每批大小不是越大越好,而是要根据并发量以及服务器的实际情况做测试来得到一个最优的值。通过Group Commit技术,根据丁奇的PPT,商品减库存的TPS性能从原来的1.5W提高到了8.5W。 从上面这个例子,我们可以看到阿里是如何在实际场景中,通过优化MySQL Server来实现高并发的商品减库存的。但是,这个技术一般人还真的不会!因为没多少人有能力去优化MySQL的服务端,排队也不行,更别说Group Commit了。这个功能并不是MySQL Server自带的,而是需要自己实现的。但是,这个思路我想我们都可以借鉴。 CQRS如何实现避免资源竞争 那么对于CQRS架构,如何按照这个思路来设计呢?我想重点说一下我上面提到的第二种CQRS架构。对于C端,我们的目标是尽可能的在1s内处理更多的Command,也就是数据写请求。在经典DDD的四层架构中,我们会有一个模式叫工作单元模式,即Unit of Work(简称UoW)模式。通过该模式,我们能在应用层,一次性以事务的方式将当前请求所涉及的多个对象的修改提交到DB。微软的EF实体框架的DbContext就是一个UoW模式的实现。这种做法的好处是,一个请求对多个聚合根的修改,能做到强一致性,因为是事务的。但是这种做法,实际上,没有很好的遵守避开资源竞争的原则。试想,事务A要修改a1,a2,a3三个聚合根;事务B要修改a2,a3,a4;事务C要修改a3,a4,a5三个聚合根。那这样,我们很容易理解,这三个事务只能串行执行,因为它们要修改相同的资源。比如事务A和事务B都要修改a2,a3这两个聚合根,那同一时刻,只能由一个事务能被执行。同理,事务B和事务C也是一样。如果A,B,C这种事务执行的并发很高,那数据库就会出现严重的并发冲突,甚至死锁。那要如何避免这种资源竞争呢?我觉得我们可以采取三个措施: 让一个Command总是只修改一个聚合根 这个做法其实就是缩小事务的范围,确保一个事务一次只涉及一条记录的修改。也就是做到,只有单个聚合根的修改才是事务的,让聚合根成为数据强一致性的最小单位。这样我们就能最大化的实现并行修改。但是你会问,但是我一个请求就是会涉及多个聚合根的修改的,这种情况怎么办呢?在CQRS架构中,有一个东西叫Saga。Saga是一种基于事件驱动的思想来实现业务流程的技术,通过Saga,我们可以用最终一致性的方式最终实现对多个聚合根的修改。对于一次涉及多个聚合根修改的业务场景,一般总是可以设计为一个业务流程,也就是可以定义出要先做什么后做什么。比如以银行转账的场景为例子,如果是按照传统事务的做法,那可能是先开启一个事务,然后让A账号扣减余额,再让B账号加上余额,最后提交事务;如果A账号余额不足,则直接抛出异常,同理B账号如果加上余额也遇到异常,那也抛出异常即可,事务会保证原子性以及自动回滚。也就是说,数据一致性已经由DB帮我们做掉了。 但是,如果是Saga的设计,那就不是这样了。我们会把整个转账过程定义为一个业务流程。然后,流程中会包括多个参与该流程的聚合根以及一个用于协调聚合根交互的流程管理器(ProcessManager,无状态),流程管理器负责响应流程中的每个聚合根产生的领域事件,然后根据事件发送相应的Command,从而继续驱动其他的聚合根进行操作。 转账的例子,涉及到的聚合根有:两个银行账号聚合根,一个交易(Transaction)聚合根,它用于负责存储流程的当前状态,它还会维护流程状态变更时的规则约束;然后当然还有一个流程管理器。转账开始时,我们会先创建一个Transaction聚合根,然后它产生一个TransactionStarted的事件,然后流程管理器响应事件,然后发送一个Command让A账号聚合根做减余额的操作;A账号操作完成后,产生领域事件;然后流程管理器响应事件,然后发送一个Command通知Transaction聚合根确认A账号的操作;确认完成后也会产生事件,然后流程管理器再响应,然后发送一个Command通知B账号做加上余额的操作;后续的步骤就不详细讲了。大概意思我想已经表达了。总之,通过这样的设计,我们可以通过事件驱动的方式,来完成整个业务流程。如果流程中的任何一步出现了异常,那我们可以在流程中定义补偿机制实现回退操作。或者不回退也没关系,因为Transaction聚合根记录了流程的当前状态,这样我们可以很方便的后续排查有状态没有正常结束的转账交易。具体的设计和代码,有兴趣的可以去看一下ENode源代码中的银行转账的例子,里面有完整的实现。 对修改同一个聚合根的Command进行排队 和上面秒杀的设计一样,我们可以对要同时修改同一个聚合根的Command进行排队。只不过这里的排队不是在MySQL Server端,而是在我们自己程序里做这个排队。如果我们是单台服务器处理所有的Command,那排队很容易做。就是只要在内存中,当要处理某个Command时,判断当前Command要修改的聚合根是否前面已经有Command在处理,如果有,则排队;如果没有,则直接执行。然后当这个聚合根的前一个Command执行完后,我们就能处理该聚合根的下一个Command了;但是如果是集群的情况下呢,也就是你不止有一台服务器在处理Command,而是有十台,那要怎么办呢?因为同一时刻,完全有可能有两个不同的Command在修改同一个聚合根。这个问题也简单,就是我们可以对要修改聚合根的Command根据聚合根的ID进行路由,根据聚合根的ID的hashcode,然后和当前处理Command的服务器数目取模,就能确定当前Command要被路由到哪个服务器上处理了。这样我们能确保在服务器数目不变的情况下,针对同一个聚合根实例修改的所有Command都是被路由到同一台服务器处理。然后加上我们前面在单个服务器里面内部做的排队设计,就能最终保证,对同一个聚合根的修改,同一时刻只有一个线程在进行。 通过上面这两个设计,我们可以确保C端所有的Command,都不会出现并发冲突。但是也要付出代价,那就是要接受最终一致性。比如Saga的思想,就是在最终一致性的基础上而实现的一种设计。然后,基于以上两点的这种架构的设计,我觉得最关键的是要做到:1)分布式消息队列的可靠,不能丢消息,否则Saga流程就断了;2)消息队列要高性能,支持高吞吐量;这样才能在高并发时,实现整个系统的整体的高性能。我开发的EQueue就是为了这个目标而设计的一个分布式消息队列,有兴趣的朋友可以去了解下哦。 Command和Event的幂等处理 CQRS架构是基于消息驱动的,所以我们要尽量避免消息的重复消费。否则,可能会导致某个消息被重复消费而导致最终数据无法一致。对于CQRS架构,我觉得主要考虑三个环节的消息幂等处理。 Command的幂等处理 这一点,我想不难理解。比如转账的例子中,假如A账号扣减余额的命令被重复执行了,那会导致A账号扣了两次钱。那最后就数据无法一致了。所以,我们要保证Command不能被重复执行。那怎么保证呢?想想我们平时一些判断重复的操作怎么做的?一般有两个做法:1)db对某一列建唯一索引,这样可以严格保证某一列数据的值不会重复;2)通过程序保证,比如插入前先通过select查询判断是否存在,如果不存在,则insert,否则就认为重复;显然通过第二种设计,在并发的情况下,是不能保证绝对的唯一性的。然后CQRS架构,我认为我们可以通过持久化Command的方式,然后把CommandId作为主键,确保Command不会重复。那我们是否要每次执行Command前线判断该Command是否存在呢?不用。因为出现Command重复的概率很低,一般只有是在我们服务器机器数量变动时才会出现。比如增加了一台服务器后,会影响到Command的路由,从而最终会导致某个Command会被重复处理,关于这里的细节,我这里不想多展开了,呵呵。有问题到回复里讨论吧。这个问题,我们也可以最大程度上避免,比如我们可以在某一天系统最空的时候预先增加好服务器,这样可以把出现重复消费消息的情况降至最低。自然也就最大化的避免了Command的重复执行。所以,基于这个原因,我们没有必要在每次执行一个Command时先判断该Command是否已执行。而是只要在Command执行完之后,直接持久化该Command即可,然后因为db中以CommandId为主键,所以如果出现重复,会主键重复的异常。我们只要捕获该异常,然后就知道了该Command已经存在,这就说明该Command之前已经被处理过了,那我们只要忽略该Command即可(当然实际上不能直接忽略,这里我由于篇幅问题,我就不详细展开了,具体我们可以再讨论)。然后,如果持久化没有问题,说明该Command之前没有被执行过,那就OK了。这里,还有个问题也不能忽视,就是某个Command第一次执行完成了,也持久化成功了,但是它由于某种原因没有从消息队列中删除。所以,当它下次再被执行时,Command Handler里可能会报异常,所以,健壮的做法时,我们要捕获这个异常。当出现异常时,我们要检查该Command是否之前已执行过,如果有,就要认为当前Command执行正确,然后要把之前Command产生的事件拿出来做后续的处理。这个问题有点深入了,我暂时不细化了。有兴趣的可以找我私聊。 Event持久化的幂等处理 然后,因为我们的架构是基于ES的,所以,针对新增或修改聚合根的Command,总是会产生相应的领域事件(Domain Event)。我们接下来的要做的事情就是要先持久化事件,再分发这些事件给所有的外部事件订阅者。大家知道,聚合根有生命周期,在它的生命周期里,会经历各种事件,而事件的发生总有确定的时间顺序。所以,为了明确哪个事件先发生,哪个事件后发生,我们可以对每个事件设置一个版本号,即version。聚合根第一个产生的事件的version为1,第二个为2,以此类推。然后聚合根本身也有一个版本号,用于记录当前自己的版本是什么,它每次产生下一个事件时,也能根据自己的版本号推导出下一个要产生的事件的版本号是什么。比如聚合根当前的版本号为5,那下一个事件的版本号则为6。通过为每个事件设计一个版本号,我们就能很方便的实现聚合根产生事件时的并发控制了,因为一个聚合根不可能产生两个版本号一样的事件,如果出现这种情况,那说明一定是出现并发冲突了。也就是一定是出现了同一个聚合根同时被两个Command修改的情况了。所以,要实现事件持久化的幂等处理,也很好做了,就是db中的事件表,对聚合根ID+聚合根当前的version建唯一索引。这样就能在db层面,确保Event持久化的幂等处理。另外,对于事件的持久化,我们也可以像秒杀那样,实现Group Commit。就是Command产生的事件不用立马持久化,而是可以先积累到一定的量,比如50个,然后再一次性Group Commit所有的事件。然后事件持久化完成后,再修改每个聚合根的状态即可。如果Group Commit事件时遇到并发冲突(由于某个聚合根的事件的版本号有重复),则退回为单个一个个持久化事件即可。为什么可以放心的这样做?因为我们已经基本做到确保一个聚合根同一时刻只会被一个Command修改。这样就能基本保证,这些Group Commit的事件也不会出现版本号冲突的情况。所以,大家是否觉得,很多设计其实是一环套一环的。Group Commit何时出发?我觉得可以只要满足两个条件了就可以触发:1)某个定时的周期到了就可以触发,这个定时周期可以根据自己的业务场景进行配置,比如每隔50ms触发一次;2)要Commit的事件到达某个最大值,即每批可以持久化的事件个数的最大值,比如每50个事件为一批,这个BatchSize也需要根据实际业务场景和你的存储db的性能综合测试评估来得到一个最适合的值;何时可以使用Group Commit?我觉得只有是在并发非常高,当单个持久化事件遇到性能瓶颈时,才需要使用。否则反而会降低事件持久化的实时性,Group Commit提高的是高并发下单位时间内持久化的事件数。目的是为了降低应用和DB之间交互的次数,从而减少IO的次数。不知不觉就说到了最开始说的那3点性能优化中的,尽量减少IO了,呵呵。 Event消费时的幂等处理 CQRS架构图中,事件持久化完成后,接下来就是会把这些事件发布出去(发送到分布式消息队列),给消费者消费了,也就是给所有的Event Handler处理。这些Event Handler可能是更新Q端的ReadDB,也可能是发送邮件,也可能是调用外部系统的接口。作为框架,应该有职责尽量保证一个事件尽量不要被某个Event Handler重复消费,否则,就需要Event Handler自己保证了。这里的幂等处理,我能想到的办法就是用一张表,存储某个事件是否被某个Event Handler处理的信息。每次调用Event Handler之前,判断该Event Handler是否已处理过,如果没处理过,就处理,处理完后,插入一条记录到这个表。这个方法相信大家也都很容易想到。如果框架不做这个事情,那Event Handler内部就要自己做好幂等处理。这个思路就是select if not exist, then handle, and at last insert的过程。可以看到这个过程不像前面那两个过程那样很严谨,因为在并发的情况下,理论上还是会出现重复执行Event Handler的情况。或者即便不是并发时也可能会造成,那就是假如event handler执行成功了,但是last insert失败了,那框架还是会重试执行event handler。这里,你会很容易想到,为了做这个幂等支持,Event Handler的一次完整执行,需要增加不少时间,从而会最后导致Query Side的数据更新的延迟。不过CQRS架构的思想就是Q端的数据由C端通过事件同步过来,所以Q端的更新本身就是有一定的延迟的。这也是CQRS架构所说的要接收最终一致性的原因。 关于幂等处理的性能问题的思考 关于CommandStore的性能瓶颈分析 大家知道,整个CQRS架构中,Command,Event的产生以及处理是非常频繁的,数据量也是非常大的。那如何保证这几步幂等处理的高性能呢?对于Command的幂等处理,如果对性能要求不是很高,那我们可以简单使用关系型DB即可,比如Sql Server, MySQL都可以。要实现幂等处理,只需要把主键设计为CommandId即可。其他不需要额外的唯一索引。所以这里的性能瓶颈相当于是对单表做大量insert操作的最大TPS。一般MySQL数据库,SSD硬盘,要达到2W […]

龙生   27 Mar 2018
View Details

TPS及计算方法

TPS (transaction per second)代表每秒执行的事务数量,可基于测试周期内完成的事务数量计算得出。例如,用户每分钟执行6个事务,TPS为6 / 60s = 0.10 TPS。同时我们会知道事务的响应时间(或节拍),以此例,60秒完成6个事务也同时代表每个事务的响应时间或节拍为10秒。 利特尔法则  (Little’s law): 该法则由麻省理工大学斯隆商学院(MIT Sloan School of Management)的教授John Little﹐于1961年所提出与证明。它是一个有关提前期与在制品关系的简单数学公式,这一法则为精益生产的改善方向指明了道路。 利特尔法则的公式描述为:Lead Time(产出时间)= 存货数量×生产节拍     或     TH(生产效率)= WIP(存货数量)/ CT(周期时间) P.S: 稍后我们会列出负载模型中利特尔法则的应用公式。 我们通过2个示例来看一下此法则是如何在生产环境中发生作用的。 例1:假定我们所开发的并发服务器,并发的访问速率是:1000客户/分钟,每个客户在该服务器上将花费平均0.5分钟,根据little’s law规则,在任何时刻,服务器将承担1000×0.5=500个客户量的业务处理。假定过了一段时间,由于客户群的增大,并发的访问速率提升为2000客户/分钟。在这样的情况下,我们该如何改进我们系统的性能? 根据little’s law规则,有两种方案: 第一:提高服务器并发处理的业务量,即提高到2000×0.5=1000。 或者 第二:减少服务器平均处理客户请求的时间,即减少到:2000×0.25=500。 例2:假设你排队参观某个风景点,该风景点固定的容纳人数是:60人。每个人在该风景点停留的平均时间是:3分钟。假设在你的前面还排有20个人,问:你估计你大概等多少时间才能进入该风景点。 答案:1小时(3×20=60),和该景点固定的容纳人数无关。 为了通过利特尔法则研究负载模型,我们就先要了解两个因子:响应时间(Response time)和节拍(Pacing)。实际上节拍会超越响应时间对TPS的影响。 示例1:节拍0秒,思考时间0秒 用户执行5个事务并且每个事务的响应时间是10秒,需要花费50秒完成5个事务,即5/50=0.1 TPS (这里TPS是由响应时间控制)。 示例2:速率15秒,思考时间0秒 用户执行5个事务且每个事务的响应时间是10秒,但实际由于节拍大于响应时间,所以它优于响应时间控制了事务发生的频率。完成5个事务需要5*15 = 75秒,产生5/75=0.06667 TPS。 在第二个示例中,平均响应时间小于节拍15秒,需要75秒完成5个迭代,产生了0.06667 TPS。 上面两个例子中我们假设思考时间为0秒。如果思考时间为2秒,总时间仍是75秒完成5个迭代,产生0.06667 TPS。 节拍为0秒,则     用户数 = TPS * ( 响应时间 + 思考时间 ) 节拍不为0秒且大于响应时间与思考时间的和,则     用户数 = TPS * (速率) 事实上TPS是事务在w.r.t时间的速率,所以也被称为吞吐量(throughput)。 所以利特尔法则在负载模型中解释为:系统内平均用户数 = 平均响应时间 * 吞吐量 N = ( R + Z ) * X N, 用户数 R, 平均响应时间(也可能是速率) Z, […]

龙生   27 Mar 2018
View Details

轻量级高性能ORM框架:Dapper高级玩法

Dapper高级玩法1: 数据库中带下划线的表字段自动匹配无下划线的Model字段。 Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 备注: 这个对使用Mysql数据库的朋友最有帮助,因为Mysql默认都是小写,一般字段都带下划线,比如:user_name之类。   具体效果如下演示 1,首先创建一张表并插入数据 2,创建Model模型

    3,扩写抽取数据逻辑代码. select * from [user]

    4,无MatchNamesWithUnderscores设置时的数据抽取 没有绑定成功?? 这是因为用了Select * from的缘故,取出来的字段是带下划线的与Model的字段不匹配。   5,设置MatchNamesWithUnderscores再次数据抽取

  数据绑定成功。 就一句Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true,让我们少写了不少AS语句。   Dapper高级玩法2: 法力无边的Query,由于带有Function功能,可以自由设置模型绑定逻辑。   1,创建两张有关联的表,并填入数据。 2,抽取user和它关联的role数据。 select 1 as table1,T1.*,1 as table2,T2.* from [user] T1 inner join [role] T2 on T1.role_id = T2.role_id 扩展方法:

  使用:

  成功取到数据。 splitOn解释:模型绑定时的字段分割标志。table1到table2之间的表字段绑定到User,table2之后的表字段绑定到Role。   3,特殊Function逻辑。比如抽取role_id对应的user一览。 select 1 as table1,T1.*,1 as table2,T2.* from [role] T1 left join [user] T2 on T1.role_id = T2.role_id 外部定义了一个字典类型,Query内部模型绑定的时候每次调用Function函数,Function函数中将数据添加到外部字典中,这在复杂数据处理时很有用。

  […]

龙生   27 Mar 2018
View Details

轻量级ORM框架——第一篇:Dapper快速学习

我们都知道ORM全称叫做Object Relationship Mapper,也就是可以用object来map我们的db,而且市面上的orm框架有很多,其中有一个框架 叫做dapper,而且被称为the king of ORM。   一:为什么选择Dapper 1. 性能优越: 其实在各大网站上,我们大概都会看到这样的一个对比效果图,在超过500次poco serialization的过程中所表现的性能,我们发现dapper是第二名, 当然第一名谁也无法超越,越底层的当然久越快,同时也就越麻烦。就好像谁能超过“01代码”呢???   2. 支持多数据库 支持多数据库的本质是因为Dapper是对IDBConnection接口进行了方法扩展,比如你看到的SqlMapper.cs,一旦你这样做了,我们也知道, SqlConnection,MysqlConnection,OracleConnection都是继承于DBConnection,而DBConnection又是实现了IDBConnection的接口,对吧。。。   二:安装Dapper install dapper的方式通常有两种:   1. 通过nuget进行安装 如果你不知道怎么用nuget进行安装,或者不知道install-package是什么,可以在browser上找一下,比如下面这样:   然后我们copy到package console 试试看。     2. 在github上获取源码。 为什么要获取源码,是因为用ilspy调试dapper的源码太费劲了,毕竟现在都是异步编程了,从ilspy中看都是匿名方法很多都无法渗透,废话不多 说,我们只要把Dapper文件夹拉出来然后copy到我们的solution就可以了,如下图:   三:快速CURD操作 其实对数据库的操作莫过于CURD,在进行操作之前我们再配一个Users表。   1. 配置Users表

  2. Insert操作 通常来说,有两种insert操作: <1>单条insert操作 这是一个简单的参数化insert,而且还可以塞入匿名类型,对吧,跟原始的SqlParameter相比,是不是简单的多???

  <2> InsertBulk操作 既然是Bulk操作,那肯定就是批量插入了,我们要做的就是将上面这个 ”匿名对象" 变成 ”匿名对象集合“ 就可以了。。。为了方便操作,这里定义 一个Users类,比如下面这样。。。

  2. Query操作 其实在Dapper在query上提供的的文章太多了。。。这篇我们就按照最简单的参数化查询就好了。。。比如我要找到username=jack的记录,如下:

  图上的亮点就在于能够自动化mapper到我们object上面来,这是我们DataReader所不能办到的,对吧~~   3.update操作 这种操作方式,我们还是使用Execute方法来实现,和insert是一种套路的哦。   4. delete操作 这里我还是采用参数化的形式来删除UserID=10这条记录,方式如下:   最终sql的table展示如下,可以看到已经正确的修改了UserID=11的记录,删除了UserID=10的record。。。。当然Dapper好玩的地方多着呢, 这篇只是一个入门而已。。。希望本篇对大家有帮助~~~

龙生   27 Mar 2018
View Details

dapper使用

访问量不大的项目我都是用EF写数据库操作,因为EF除了速度上慢以外,但开发效率极快,省略了很多sql写法,并能很方便的调用外键、集合等信息,用EF写项目最爽的事。不过有些项目网站要考虑运行速度,这时不得不用其它的ORM框架,我常用dapper,因为它效果快,而且写sql非常灵活,接下来面写一些方法看看dapper的使用 1、连接语句 var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SqlDiagnosticsDb"].ConnectionString); 使用dapper不需要考虑conn是否连接,在执行dapper时自行判断 open状态,如果没有打开它会自己打开。 2、insert

book类中有name属性,就可以这样方便去写,当然也可以写成

3、update

4、 delete

5、query

6、 传统sql in (1,2,3) 用dapper就这样写

在dapper因为安全性,不能直接用sql接接    要采用参数化, 7、批量插入

也可以直接写入一个集合

这里users是一个user表的对象集合,可一次把集合中的所有数据插入到数据表中。 8、多表查询

多表联合查询是比较麻烦一些,到现在不是完全明白,多看几个例子

Post类和User类,它们存在外键, conn.Query返回的类型是最后一个参数Post, 其中Post中有一属性Owner是User对象,在(post, user)=>lamda中指定了Owner值,上边的代码中的splitOn是ID,运行时,会从查询结果所有字段列表的最后一个字段开始进行匹配,一直到找到Id这个字段(大小写忽略),找到的第一个ID字段匹配User类的ID属性,那么从ID到最后一个字段都属于User,ID以前的字段都被影射到Post, 通过 (post, user) => {return post;},把两个类的实例解析出来。 9、三表查询,一个是关联主键表(单个对象),一个是关联外键表(集合)。

 

 

 

  注意这里的对象和集合的获取方法:u.Movies = m; u.MyTYC.Add(t); 10、多结果查询

  再来一个

  11、如果某一代码中多次操作数据库,可以把conn设置为打开,最后时再close, 比如:

  from:https://www.cnblogs.com/lunawzh/p/6607116.html

龙生   27 Mar 2018
View Details

WebGL

WebGL(全写Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等。

龙生   26 Mar 2018
View Details

ElasticSearch

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。 我们建立一个网站或应用程序,并要添加搜索功能,但是想要完成搜索工作的创建是非常困难的。我们希望搜索解决方案要运行速度快,我们希望能有一个零配置和一个完全免费的搜索模式,我们希望能够简单地使用JSON通过HTTP来索引数据,我们希望我们的搜索服务器始终可用,我们希望能够从一台开始并扩展到数百台,我们要实时搜索,我们要简单的多租户,我们希望建立一个云的解决方案。因此我们利用Elasticsearch来解决所有这些问题及可能出现的更多其它问题。 下载地址:https://www.elastic.co/cn/downloads/elasticsearch

龙生   26 Mar 2018
View Details

浅谈我对DDD领域驱动设计的理解

从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决。 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品。所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的。 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备。但是最近由于各种原因,导致服务经常出故障。所以,我们希望通过各种措施提高服务的质量和稳定性。其中的一个措施就是希望能做一个灰度发布的平台,这个平台可以提供灰度发布的服务。然后,当某个业务系统做了一些修改并需要发布时,可以使用我们的灰度发布平台来非常方便的实现灰度发布的功能。比如在灰度发布平台上方便的定制允许哪些特定的客户端才会访问新服务,哪些客户端继续使用老服务。灰度发布平台可以提供各种灰度的策略。有了这样的灰度发布机制,那即便系统的新逻辑有什么问题,受影响的面也不会很大,在可控范围内。所以,如果公司里的所有对外提供服务的系统都接入了灰度平台,那这些系统的发布环节就可以更加有保障了。 总之,我们做任何一个软件系统,都是有原因的,否则就没必要做这个系统,而这个原因就是我们遇到的问题。所以,通过问题,我们就知道了我们需要一个什么样的系统,这个系统解决什么样的问题。最后,我们就很自然的得出了一个目标,即知道了自己要什么。比如我要做一个论坛、一个博客系统、一个电商平台、一个灰度发布系统、一个IDE、一个分布式消息队列、一个通信框架,等等。 DDD切入点1 – 理解概念 DDD的全称为Domain-driven Design,即领域驱动设计。下面我从领域、问题域、领域模型、设计、驱动这几个词语的含义和联系的角度去阐述DDD是如何融入到我们平时的软件开发初期阶段的。要理解什么是领域驱动设计,首先要理解什么是领域,什么是设计,还有驱动是什么意思,什么驱动什么。 什么是领域(Domain)? 前面我们已经清楚的知道我们现在要做一个什么样的系统,这个系统需要解决什么问题。我认为任何一个系统都会属于某个特定的领域,比如论坛是一个领域,只要你想做一个论坛,那这个论坛的核心业务是确定的,比如都有用户发帖、回帖等核心基本功能。比如电商平台、普通电商系统,这种都属于网上电商领域,只要是这个领域的系统,那都有商品浏览、购物车、下单、减库存、付款交易等核心环节。所以,同一个领域的系统都具有相同的核心业务,因为他们要解决的问题的本质是类似的。 因此,我们可以推断出,一个领域本质上可以理解为就是一个问题域,只要是同一个领域,那问题域就相同。所以,只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题、问题的范围边界就基本确定了。通常我们说,要成为一个领域的专家,必须要在这个领域深入研究很多年才行。因为只有你研究了很多年,你才会遇到非常多的该领域的问题,同时你解决这个领域中的问题的经验也非常丰富。很多时候,领域专家比技术专家更加吃香,比如金融领域的专家。 什么是设计(Design)? DDD中的设计主要指领域模型的设计。为什么是领域模型的设计而不是架构设计或其他的什么设计呢?因为DDD是一种基于模型驱动开发的软件开发思想,强调领域模型是整个系统的核心,领域模型也是整个系统的核心价值所在。每一个领域,都有一个对应的领域模型,领域模型能够很好的帮我们解决复杂的业务问题。 从领域和代码实现的角度来理解,领域模型绑定了领域和代码实现,确保了最终的代码实现就一定是解决了领域中的核心问题的。因为:1)领域驱动领域模型设计;2)领域模型驱动代码实现。我们只要保证领域模型的设计是正确的,就能确定领域模型可以解决领域中的核心问题;同理,我们只要保证代码实现是严格按照领域模型的意图来落地的,那就能保证最后出来的代码能够解决领域的核心问题的。这个思路,和传统的分析、设计、编码这几个阶段被割裂(并且每个阶段的产物也不同)的软件开发方法学形成鲜明的对比。 什么是驱动(Driven)? 上面其实已经提到了,就是:1)领域驱动领域模型设计;2)领域模型驱动代码实现。这个就和我们传统的数据库驱动开发的思路形成对比了。DDD中,我们总是以领域为边界,分析领域中的核心问题(核心关注点),然后设计对应的领域模型,再通过领域模型驱动代码实现。而像数据库设计、持久化技术等这些都不是DDD的核心,而是外围的东西。 领域驱动设计(DDD)告诉我们的最大价值我觉得是:当我们要开发一个系统时,应该尽量先把领域模型想清楚,然后再开始动手编码,这样的系统后期才会很好维护。但是,很多项目(尤其是互联网项目,为了赶工)都是一开始模型没想清楚,一上来就开始建表写代码,代码写的非常冗余,完全是过程是的思考方式,最后导致系统非常难以维护。而且更糟糕的是,出来混总是要还的,前期的领域模型设计的不好,不够抽象,如果你的系统会长期需要维护和适应业务变化,那后面你一定会遇到各种问题维护上的困难,比如数据结构设计不合理,代码到处冗余,改BUG到处引入新的BUG,新人对这种代码上手困难,等。而那时如果你再想重构模型,那要付出的代价会比一开始重新开发还要大,因为你还要考虑兼容历史的数据,数据迁移,如何平滑发布等各种头疼的问题。所以,就导致我们最后天天加班。 虽然,我们都知道这个道理,但是我也明白,人的习惯很难改变的,大部分人都很难从面向过程式的想到哪里写到哪里的思想转变为基于系统化的模型驱动的思维。我想,这或许是DDD很难在中国或国外流行起来的原因吧。但是,我想这不应该成为我们放弃学习DDD的原因,对吧! 概念总结: 领域就是问题域,有边界,领域中有很多问题; 任何一个系统要解决的那个大问题都对应一个领域; 通过建立领域模型来解决领域中的核心问题,模型驱动的思想; 领域建模的目标针对我们在领域中所关心的问题,即只针对核心关注点,而不是整个领域中的所有问题; 领域模型在设计时应考虑一定的抽象性、通用性,以及复用价值; 通过领域模型驱动代码的实现,确保代码让领域模型落地,代码最终能解决问题; 领域模型是系统的核心,是领域内的业务的直接沉淀,具有非常大的业务价值; 技术架构设计或数据存储等是在领域模型的外围,帮助领域模型进行落地; DDD切入点2 – 理解领域、拆分领域、细化领域 理解领域知识是基础 上面我们通过第一步,虽然我们明确了要做一个什么样的系统,该系统主要解决什么问题,但是就这样我们还无法开始进行实际的需求分析和模型设计,我们还必须将我们的问题进行拆分,需求进行细化。有些时候,需求方,即提出问题的人,很可能自己不清楚具体想要什么。他只知道一个概念,一个大的目标。比如他只知道要做一个股票交易系统,一个灰度发布系统,一个电商平台,一个开发工具,等。但是他不清楚这些系统应该具体做成什么样子。这个时候,我认为领域专家就非常重要了,DDD也非常强调领域专家的重要性。因为领域专家对这个领域非常了解,对领域内的各种业务场景和各种业务规则也非常清楚,总之,对这个领域内的一切业务相关的知识都非常了解。所以,他们自然就有能力表达出系统该做成什么样子。所以,要知道一个系统到底该做成什么样子,到底哪些是核心业务关注点,只能靠沉淀领域内的各种知识,别无他法。因此,假设你现在打算做一个电商平台,但是你对这个领域没什么了解,那你一定得先去了解下该领域内主流的电商平台,比如淘宝、天猫、京东、亚马逊等。这个了解的过程就是你沉淀领域知识的过程。如果你不了解,就算你领域建模的能力再强,各种技术架构能力再强也是使不上力。领域专家不是某个固定的角色,而是某一类人,这类人对这个领域非常了解。比如,一个开发人员也可以是一个领域专家。假设你在一个公司开发和维护一个系统已经好几年了,但是这个系统的产品经理(PD)可能已经换过好几任了,这种情况下,我相信这几任产品经理都没有比你更熟悉这个领域。 拆分领域 上面我们明白了,领域建模的基础是要先理解领域,让自己成为领域专家。如果做到了这点,我们就打好了坚实的基础了。但是,有时一个领域往往太复杂,涉及到的领域概念、业务规则、交互流程太多,导致我们没办法直接针对这个大的领域进行领域建模。所以,我们需要将领域进行拆分,本质上就是把大问题拆分为小问题,然后各个击破的思路。然后既然把一个大的领域划分为了多个小的领域(子域),那最关键的就是要理清每个子域的边界;然后要搞清楚哪些子域是核心子域,哪些是非核心子域,哪些是公共支撑子域;然后,还要思考子域之间的联系是什么。那么,我们该如何划分子域呢?我的个人看法是从业务相关性的角度去思考,也就是我们平时说的按业务功能为出发点进行划分。还是拿经典的电商系统来分析,通常一个电商系统都会包含好几个大块,比如: 会员中心:负责用户账号登录、用户信息的管理; 商品中心:负责商品的展示、导航、维护; 订单中心:负责订单的生成和生命周期管理; 交易中心:负责交易相关的业务; 库存中心:负责维护商品的库存; 促销中心:负责各种促销活动的支持; 上面这些中心看起来很自然,因为大家对电子商务的这个领域都已经非常熟悉了,所以都没什么疑问,好像很自然的样子。所以,领域划分是不是就是没什么挑战了呢?显然不是。之所以我们觉得子域划分很简单,是因为我们对整个大领域非常了解了。如果我们遇到一个冷门的领域,就没办法这么容易的去划分子域了。这就需要我们先去努力理解领域内的知识。所以,我个人从来不相信什么子域划分的技巧什么的东西,因为我觉得这个工作没有任何诀窍可以使用。当我们不了解一个东西的时候,如何去拆解它?当我们对整个领域有一定的熟悉了,了解了领域内的相关业务的本质和关系,我们就自然而然的能划分出合理的子域了。不过并不是所有的系统都需要划分子域的,有些系统只是解决一个小问题,这个问题不复杂,可能只有一两个核心概念。所以,这种系统完全不需要再划分子域。但不是绝对的,当一个领域,我们的关注点越来越多,每个关注点我们关注的信息越来越多的时候,我们会不由自主的去进一步的划分子域。比如,也许我们一开始将商品和商品的库存都放在商品中心里,但是后来由于库存的维护越来越复杂,导致揉在一起对我们的系统维护带来一定的困难时,我们就会考虑将两者进行拆分,这个就是所谓的业务垂直分割。 细化子域 通过上面的两步,我们了解了领域里的知识,也对领域进行了子域划分。但这样还不够,凭这些我们还无法进行后续的领域模型设计。我们还必须再进一步细化每个子域,进一步明确每个子域的核心关注点,即需求细化。我觉得我们需要细化的方面有以下几点: 梳理领域概念:梳理出领域内我们关注的概念、概念的关系,并统一交流词汇,形成统一语言; 梳理业务规则:梳理出领域内我们关注的各种业务规则,DDD中叫不变性(invariants),比如唯一性规则,余额不能小于零等; 梳理业务场景:梳理出领域内的核心业务场景,比如电商平台中的加入购物车、提交订单、发起付款等核心业务场景; 梳理业务流程:梳理出领域内的关键业务流程,比如订单处理流程,退款流程等; 从上面这4个方面,我们从领域概念、业务规则、交互场景、业务流程等维度梳理了我们到底要什么,整理了整个系统应该具备的功能。这个工作我觉得是一个非常具有创造性和有难度的工作。我们一方面会主观的定义我们想要什么;另一方面,我们还会思考我们要的东西的合理性。我认为这个就是产品经理的工作,产品经理必须要负起职责,把他的产品充分设计好,从各个方面去考虑,如何设计一个产品,才能更好的解决用户的核心诉求,即领域内的核心问题。如果对领域不够了解,如果想不清楚用户到底要什么,如果思考问题不够全面,谈何设计出一个合理的产品呢? 关于领域概念的梳理,我觉得可以采用四色原型分析法,这个分析法通过系统的方法,将概念划分为不同的种类,为不同种类的概念标注不同的颜色。然后将这些概念有机的组合起来,从而让我们可以清晰的分析出概念和概念之间的关系。有兴趣的同学可以在网上搜索下四色原型。 注意:上面我说的这四点,重点是梳理出我们要什么功能,而不是思考如何实现这些功能,如何实现是软件设计人员的职责。 DDD切入点3 – 领域模型设计 这部分内容,我想学习DDD的人都很熟悉了。DDD原著中提出了很多实用的建模工具:聚合、实体、值对象、工厂、仓储、领域服务、领域事件。我们可以使用这些工具,来设计每一个子域的领域模型。最终通过领域模型图将设计沉淀下来。要使用这些工具,首先就要理解每个工具的含义和使用场景。不要以为很简单哦,比如聚合的划分就是一个非常具有艺术的活。同一个系统,不同的人设计出来的聚合是完全不同的。而且很有可能高手之间的最后设计出来的差别反而更大,实际上我认为是世界观的相互碰撞,呵呵。所以,要领域建模,我觉得每个人都应该去学学哲学知识,这有助于我们更好的认识世界,更好的理解事物的本质。 关于这些建模工具的概念和如何运用我就不多展开了,我博客里也有很多这方面的介绍。下面我再讲一下我认为比较重要的东西,比如到底该如何领域建模?步骤应该是怎么样的? 领域建模的方法 通过上面我介绍的细化子域的内容,现在再来谈该如何领域建模,我觉得就方便很多了。我的主要方法是: 划分好边界上下文,通常每个子域(sub domain)对应一个边界上下文(bounded context),同一个边界上下文中的概念是明确的,没有任何歧义; 在每个边界上下文中设计领域模型,具体的领域模型设计方法有很多种,如以场景为出发点的四色原型分析法,或者我早期写的这篇文章;这个步骤最核心的就是找出聚合根,并找出每个聚合根包含的信息;关于如何设计聚合,可以看一下我写的这篇文章; 画出领域模型图,圈出每个模型中的聚合边界; 设计领域模型时,要考虑该领域模型是否满足业务规则,同时还要综合考虑技术实现等问题,比如并发问题;领域模型不是概念模型,概念模型不关注技术实现,领域模型关心;所以领域模型才能直接指导编码实现; 思考领域模型是如何在业务场景中发挥作用的,以及是如何参与到业务流程的每个环节的; 场景走查,确认领域模型是否能满足领域中的业务场景和业务流程; 模型持续重构、完善、精炼; 领域模型的核心作用: 抽象了领域内的核心概念,并建立概念之间的关系; 领域模型承担了领域内的状态的维护; 领域模型维护了领域内的数据之间的业务规则,数据一致性; 下图是我最近做个一个普通电商系统的商品中心的领域模型图,给大家参考: 领域模型设计只是软件设计中的一小部分 需要特别注意的是,领域模型设计只是整个软件设计中的很小一部分。除了领域模型设计之外,要落地一个系统,我们还有非常多的其他设计要做,比如: 容量规划 架构设计 数据库设计 缓存设计 框架选型 发布方案 数据迁移、同步方案 分库分表方案 回滚方案 高并发解决方案 一致性选型 性能压测方案 监控报警方案 […]

龙生   26 Mar 2018
View Details

RabbitMQ系列(一):Windows下RabbitMQ安装及入门

1.Windows下安装RabbitMQ需要以下几个步骤 (1):下载erlang,原因在于RabbitMQ服务端代码是使用并发式语言erlang编写的,下载地址:http://www.erlang.org/downloads,双击.exe文件进行安装就好,安装完成之后创建一个名为ERLANG_HOME的环境变量,其值指向erlang的安装目录,同时将%ERLANG_HOME%\bin加入到Path中,最后打开命令行,输入erl,如果出现erlang的版本信息就表示erlang语言环境安装成功; (2):下载RabbitMQ,下载地址:http://www.rabbitmq.com/,同样双击.exe进行安装就好(这里需要注意一点,默认的安装目录是C:/Program Files/….,这个目录中是存在空格符的,我们需要改变安装目录,貌似RabbitMQ安装目录中是不允许有空格的,我之前踩过这个大坑); (3):安装RabbitMQ-Plugins,这个相当于是一个管理界面,方便我们在浏览器界面查看RabbitMQ各个消息队列以及exchange的工作情况,安装方法是:打开命令行cd进入rabbitmq的sbin目录(我的目录是:E:\software\rabbitmq\rabbitmq_server-3.6.5\sbin),输入:rabbitmq-plugins enable rabbitmq_management命令,稍等会会发现出现plugins安装成功的提示,默认是安装6个插件,如果你在安装插件的过程中出现了下面的错误: 解决方法是:首先在命令行输入:rabbitmq-service stop,接着输入rabbitmq-service remove,再接着输入rabbitmq-service install,接着输入rabbitmq-service start,最后重新输入rabbitmq-plugins enable rabbitmq_management试试,我是这样解决的; (4):插件安装完之后,在浏览器输入http://localhost:15672进行验证,你会看到下面界面,输入用户名:guest,密码:guest你就可以进入管理界面,当然用户名密码你都可以变的; 2.安装完RabbitMQ之后,我们先来简单了解下RabbitMQ中涉及到的几个概念 producer:消息生产者 consumer:消息消费者 virtual host:虚拟主机,在RabbitMQ中,用户只能在虚拟主机的层面上进行一些权限设置,比如我可以访问哪些队列,我可以处理哪些请求等等; broker:消息转发者,也就是我们RabbitMQ服务端充当的功能了,那么消息是按照什么规则进行转发的呢?需要用到下面几个概念; exchange:交换机,他是和producer直接进行打交道的,有点类似于路由器的功能,主要就是进行转发操作的呗,那么producer到底用哪个exchange进行路由呢?这个取决于routing key(路由键),每个消息都有这个键,我们也可以自己设定,其实就是一字符串; queue:消息队列,用于存放消息,他接收exchange路由过来的消息,我们可以对队列内容进行持久化操作,那么queue到底接收那个exchange路由的消息呢?这个时候就要用到binding key(绑定键)了,绑定键会将队列和exchange进行绑定,至于绑定方式,RabbitMQ提供了多种方式,大家可以看看鸿洋大神的RabbitMQ博客系列(点击查看); 以上就是RabbitMQ涉及到的一些概念了,用一张图表示这些概念之间的关系就是: 3.RabbitMQ简单使用 producer(生产者)端步骤: (1):创建ConnectionFactory,并且设置一些参数,比如hostname,portNumber等等 (2):利用ConnectionFactory创建一个Connection连接 (3):利用Connection创建一个Channel通道 (4):创建queue并且和Channel进行绑定 (5):创建消息,并且发送到队列中 注意,在我们当前的例子中,并没有用到exchange交换机,RabbitMQ默认情况下是会创建一个空字符串名字的exchange的,如果我们没有创建自己的exchange的话,默认就是使用的这个exchange; producer端代码:

consumer(消费者)端步骤: (1):创建ConnectionFactory,并且设置一些参数,比如hostname,portNumber等等 (2):利用ConnectionFactory创建一个Connection连接 (3):利用Connection创建一个Channel通道 (4):将queue和Channel进行绑定,注意这里的queue名字要和前面producer创建的queue一致 (5):创建消费者Consumer来接收消息,同时将消费者和queue进行绑定 consumer端代码:

好了,这篇先到这了,下一篇我会简单介绍点更深入的东西,后续也会对RabbitMQ原生API进行封装,便于我们自己开发; from:https://blog.csdn.net/hzw19920329/article/details/53156015

龙生   26 Mar 2018
View Details

ABP入门教程

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。 ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。 ABP的官方网站 : http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate ABP 的由来 “DRY——避免重复代码”是一个优秀的开发者在开发软件时所具备的最重要的思想之一。我们在开发企业WEB应用程序时都有一些类似的需求,例如:都需要登录页面、用户/角色管理、权限验证、数据有效性验证、多语言/本地化等等。一个高品质的大型软件都会运用一些最佳实践,例如分层体系结构、领域驱动设计、依赖注入等。我们也可能会采用ORM、数据库迁移(Database Migrations)、日志记录(Logging)等工具。 从零开始创建一个企业应用程序是一件繁琐的事,因为需要重复做很多常见的基础工作。许多公司都在开发自己的应用程序框架来重用于不同的项目,然后在框架的基础上开发一些新的功能。但并不是每个公司都有这样的实力。假如我们可以分享的更多,也许可以避免每个公司或每个项目的重复编写类似的代码。作者之所以把项目命名为“ASP.NET Boilerplate”,就是希望它能成为开发一般企业WEB应用的新起点,直接把ABP作为项目模板。 ABP是什么? ABP是为新的现代Web应用程序使用最佳实践和使用最流行工具的一个起点。可作为一般用途的应用程序的基础框架或项目模板。它的功能包括: 服务器端: 基于最新的.NET技术 (目前是ASP.NET MVC 5、Web API 2、C# 5.0,在ASP.NET 5正式发布后会升级) 实现领域驱动设计(实体、仓储、领域服务、领域事件、应用服务、数据传输对象,工作单元等等) 实现分层体系结构(领域层,应用层,展现层和基础设施层) 提供了一个基础架构来开发可重用可配置的模块 集成一些最流行的开源框架/库,也许有些是你正在使用的。 提供了一个基础架构让我们很方便地使用依赖注入(使用Castle Windsor作为依赖注入的容器) 提供Repository仓储模式支持不同的ORM(已实现Entity Framework 、NHibernate、MangoDb和内存数据库) 支持并实现数据库迁移(EF 的 Code first) 模块化开发(每个模块有独立的EF DbContext,可单独指定数据库) 包括一个简单的和灵活的多语言/本地化系统 包括一个 EventBus来实现服务器端全局的领域事件 统一的异常处理(应用层几乎不需要处理自己写异常处理代码) 数据有效性验证(Asp.NET MVC只能做到Action方法的参数验证,ABP实现了Application层方法的参数有效性验证) 通过Application Services自动创建Web Api层(不需要写ApiController层了) 提供基类和帮助类让我们方便地实现一些常见的任务 使用“约定优于配置原则” 客户端: Bootstrap、Less、AngularJs、jQuery、Modernizr和其他JS库: jQuery.validate、jQuery.form、jQuery.blockUI、json2等 为单页面应用程序(AngularJs、Durandaljs)和多页面应用程序(Bootstrap+Jquery)提供了项目模板。 自动创建Javascript 的代理层来更方便使用Web Api 封装一些Javascript 函数,更方便地使用ajax、消息框、通知组件、忙状态的遮罩层等等 除ABP框架项目以外,还开发了名叫“Zero”的模块,实现了以下功能: 身份验证与授权管理(通过ASP.NET Identity实现的) 用户&角色管理 系统设置存取管理(系统级、租户级、用户级,作用范围自动管理) 审计日志(自动记录每一次接口的调用者和参数) ABP不是什么? ABP 提供了一个应用程序开发模型用于最佳实践。它拥有基础类、接口和工具使我们容易建立起可维护的大规模的应用程序。 然而: 它不是RAD工具之一,RAD工具的目的是无需编码创建应用程序。相反,ABP提供了一种编码的最佳实践。 它不是一个代码生成工具。在运行时虽然它有一些特性构建动态代码,但它不能生成代码。 它不是一个一体化的框架。相反,它使用流行的工具/库来完成特定的任务(例如用EF做ORM,用Log4Net做日志记录,使得Castle Windsor作为赖注入容器, AngularJs 用于SPA 框架)。 就我使用了ABP几个月的经验来看,虽然ABP不是RAD,但是用它开发项目绝对比传统三层架构要快很多。 虽然ABP不是代码生成工具,但因为有了它,使我们项目的代码更简洁规范,这有利于使用代码生成工具。 我自己使用VS2013的Scaffolder+T4开发的代码生成器,可根据领域对象的UML类图自动生成全部前后端代码和数据库,简单的CURD模块几乎不需要编写代码,有复杂业务逻辑的模块主要补充领域层代码即可。这样就能把时间多花在领域模型的设计上,减少写代码的时间。 下面通过原作者的“简单任务系统”例子,演示如何运用ABP开发项目 从模板创建空的web应用程序 ABP提供了一个启动模板用于新建的项目(尽管你能手动地创建项目并且从nuget获得ABP包,模板的方式更容易)。 转到www.aspnetboilerplate.com/Templates从模板创建你的应用程序。 你可以选择SPA(AngularJs或DurandalJs)或者选择MPA(经典的多页面应用程序)项目。可以选择Entity Framework或NHibernate作为ORM框架。 这里我们选择AngularJs和Entity Framework,填入项目名称“SimpleTaskSystem”,点击“CREATE […]

龙生   25 Mar 2018
View Details
1 153 154 155 282