[转载]SQL Server CheckPoint的几个误区 - CareySon - 博客园

mikel阅读(1139)

[转载]SQL Server CheckPoint的几个误区 – CareySon – 博客园.

    有关CheckPoint的概念对大多数SQL Server开发或DBA人员都不陌生。但是包括我自己在内,大家对于CheckPoint都或多或少存在某些误区,最近和高文佳同学(感谢高同学的探讨)关于该处进行过一些探讨,整理出来几个误区。

 

1.CheckPoint实例级别,而不是数据库级别

CheckPoint的时间虽然可以在实例级别进行设置,但CheckPoint的过程是以数据库为粒度。从CheckPoint在Redo和Undo的 作用来看,CheckPoint是为了优化IO和减少Recovery时间,而Recovery是需要日志支持,因此日志是数据库级别的概念,因此可以知 道CheckPoint是以数据库为单位进行的。

我们来做一个简单的实验,分别设置两个连接A和B,A和B使用不同的数据库并修改数据产生脏数据,在A上进行了CheckPoint后,A连接的数据库脏 页全部写入磁盘,而B连接产生的脏页依然驻留在Buffer中,因此可以确定CheckPoint是数据库级别而不是服务器级别。

1

图1.CheckPoint是数据库级别的

 

2.由于日志增长导致的自动CheckPoint会将所有数据库的脏页写入磁盘

事实证明,这也是错误的,自动CheckPoint仅仅会将某些脏页或日志过多的数据库脏页写入磁盘。可以同样通过图1的例子进行。

 

3.CheckPoint仅仅将已经提交的脏数据写入磁盘

这同样是错误的,无论事务是否提交,所产生的脏数据都会被CheckPoint写入磁盘。例证可以参看我的博文:再谈SQL Server中日志的的作用中有关CheckPoint的实验。

 

4.如果一个实例上有多个数据库,则CheckPoint是并行的

错误,通过3502跟踪标记来看,CheckPoint是串行的,也就是一个数据库CheckPoint完了才会继续下一个。如图2所示。

2

图2.串行CheckPoint

我们可以注意到,CheckPoint使用的是同一个Spid。

 

5.将恢复间隔设置为1分钟,意味着每1分钟会对所有的数据库做一次CheckPoint

错误。将恢复间隔设置为1分钟不能想成建立一个Agent,每分钟写一个CheckPoint命令,这是两码事。这只是意味着每分钟去检查一次是否需要做 CheckPoint,如果期间积累的日志量足够,才会对积累足够日志量的数据库去做CheckPoint。即使中间积累了巨量的日志,不到1分钟也不会 做CheckPoint。

 

6.SQL Server一些Internal CheckPoint时,比如说关闭数据库,会对所有数据库做CheckPoint(高同学补充)

这条是正确的微笑,因为SQL Server此时需要保证所有的数据写入磁盘,从而保证了数据库一致性,如果没有活动的事务,那么这种关闭方式叫做Clean ShutDown,这意味着该数据本身一致,因此即使没有日志,MDF也可以附加。

 

7.CheckPoint是一个时间点(高同学补充)

错误,这是打游戏存档的想法,从哪存进度,从哪取进度,是某个时间点。在SQL Server中,CheckPoint是一个完整的过程,这个过程的耗时取决于脏数据的大小,更多资料,请参阅MSDN:http://technet.microsoft.com/zh-cn/library/ms188748.aspx

 

8.引发自动CheckPoint的条件是内存中脏页的多少(高同学补充)

错误,那是Lazy Writer需要考虑的问题。而CheckPoint的触发条件,是在CheckPoint期间生成日志的大小。因此,大家见过内存中有很多脏页,却不引发CheckPoint的情况。

 

9.当数据所在磁盘压力大时,通过checkpoint pages/ sec 计数器来观察写入磁盘的脏页(高同学补充)

部分正确。实际上,脏页被写入磁盘一共有3中方式,CheckPoint仅仅是其中一种,我们还需要将Lazy writes/sec考虑在内。

 

 

最后,再次感谢高文佳同学和我探讨。

[转载][译]ASP.NET WebForms vs. ASP.NET MVC - 滴答的雨(何雨泉) - 博客园

mikel阅读(927)

[转载][译]WebForms vs. MVC – 滴答的雨(何雨泉) – 博客园.

译者介绍

 

小小.NET学童,滴答…滴答…的雨……

 

正文如下=======================================================

原文示例(VS2012):

1.  Download Simple WebForm demo – 6.7 KB

2.  Download Simple MVC Demo demo – 1.5 MB

 

介绍

         我做为一名ASP.NET开发人员已经有很长时间了,并且我非常乐意使用ASP.NET Web Forms开发web应用程序。

         2008年,Microsoft首次发布ASP.NET MVC框架。我非常惊讶“为什么需要另一个ASP.NET技术框架”并且很多人和我拥有相同的想法。

         很多人说ASP.NET MVC框架将取代ASP.NET Web Forms框架,但是这不是真的。它们各有各的优点和缺点,没有人告诉或教我们什么场景使用哪一种架构,但是我们可以讨论一些事实,这将帮助我们选择最适合项目需求的框架。

clip_image001

 

我们将一起尝试找到一些问题的答案,比如

l  什么是ASP.NET

l  什么是ASP.NET Web Forms

l  什么是MVC

l  什么是ASP.NET MVC

 

如果你是高级或经验丰富的ASP.NET MVC 开发人员,那么你已经知道MVC非常棒了,而这篇文章将帮助你温故这些概念。

 

讨论内容

 

提纲

ü  可视化技术

ü  Web技术

ü  Web可视化技术

ü  什么是ASP.NET

ü  什么是Web Forms

ü  ASP.NET 4.0

ü  什么是MVC

ü  什么是ASP.NET MVC

ü  ASP.NET MVC 是如何工作的?

ü  什么是ASP.NET Web Forms框架以及什么是ASP.NET MVC框架?

 

 

1.         可视化技术

clip_image002

         微软提出了所谓的“可视化”技术,在这种技术的帮助下微软能快速开发图形用户应用程序。像拖拽和智能提示的特征让开发人员能更关注应用程序的业务功能的实现而不是UI设计。

 

2.         Web技术

当前比较经典的web技术有:aspphpjsprorasp.net web formsasp.net MVC等等。asp是微软引入的一个经典的web技术,asp最大的问题是混乱的code布局和糟糕的可维护性。

我们假设一个场景,你有一些文本和一个按钮,在按钮点击时在服务器验证数据,如果验证成功就存入数据库,如果验证失败就在表单上用红色标签显示错误信息。你知道这个场景最大的问题是什么吗?你必须自己做很多东西来实现这个效果。

1)         首先,你要设置表单的action属性,让其指向当前页本身。

2)         文本框的值将在点击按钮后会自动清空,所以只能自己尝试从提交的数据中恢复。

3)         如果服务器验证失败,你必须

a)         从提交的数据中提取相应值设置到对应的文本框中;

b)         显示错误信息;

(使用Ajax异步提交是另一种实现方式,但是这里我是为了解释在经典的asp技术中需要耗费大量的人工操作)

 

3.         Web可视化技术

Visual Basic技术仅限于单独的应用程序,当涉及到网络,就只有选择微软经典的asp

 

         当我们讨论Web应用程序和Winform应用程序时,有两个事是应该考虑的:

1)         状态管理如何工作?

2)         请求/响应如何工作?

 

Web应用程序遵循HTTP协议。http协议是无状态的,即同一个客户端的这次请求和上次请求是没有对应关系的,对http服务器来说,它并不知道这两个请求来自同一个客户端。与Winform应用程序不同,没有变量将被保存,也没有完整的事件驱动编程模型。与Winform应用程序相同的是需要等待用户输入,但每个用户的输入和交互将做为一个新的请求(get/post)发送到服务器。

最终,微软提出了所谓的ASP.NET Web Forms框架,充分考虑了应用程序快速开发的需求和简单易学性。

 

4.         什么是ASP.NET

ASP.NETMicrosoftweb应用程序框架,由公用语言运行库(CLRCommon language runtime)的C#VB.NET等程序语言构建的动态web站点。它支持两种模式:Web FormsASP.NET MVC.

clip_image003

 

5.         什么是Web Forms

Microsoft发布的ASP.NET,通过在web的无状态和为开发人员模拟状态的模型上创建更高层次的抽象解决了asp中存在的很多问题。在web forms框架中引入一些新概念比如:“self postback”(提交表单数据到本页)、“ViewState”(在postback回调期间维护视图状态)等。在web forms框架,微软已尝试将可视化模型引入到web应用程序开发中。

clip_image004

        

让我们来讨论下Web Forms框架的优缺点:

1)         优点:

a)         Web Forms支持丰富的服务器控件

clip_image005

使用单纯的HTML客户端控件时,你可能会注意到在浏览器终端控件的展现并不总是一致。比如:一个UIIE浏览器中看起来非常棒,但在Firefox浏览器中却很糟糕。ASP.NET服务器控件会检测到浏览器种类并生成对应的htmlJavaScript

很多服务器控件像GridViewListView可以绑定数据,从而减少了大量的代码编写。

b)         支持视图状态

你可能听说过几次“HTTP是无状态的协议”。通常在多个请求之间控件不会保持控件值。但是在Web Forms框架中通过将每个控件状态以“隐藏域”的形式存在当前表单页面来达到“有状态”,这就是视图状态。

c)         事件驱动编程

clip_image007

它为我们提供:

l  Code-Behind (代码后植)技术           ASP.NET中通过ASPX页面指向CS文件的方法实现显示逻辑和处理逻辑的分离,这样有助于web应用程序的创建。比如分工,美工和编程的可以个干各的,不用再像以前asp那样后台代码和html代码都混在一起,难以维护。

l  自动回发机制(Self postback mechanism

l  ViewState(视图状态)

 

开发人员不再依赖POSTGET方法来处理用户与服务器之间的交互,eg:可以拖控件放到页面上(比如按钮),只需要双击控件以生成对应的后台代码以在服务器上处理用户的点击逻辑。

d)         快速应用程序开发

丰富的服务器控件,事件驱动模型和视图状态都很大程度上增加了开发速度,开发者将透明化这背后复杂的实现,从而能更关注应用程序的业务逻辑。

e)         学习成本低

使用强大的服务器控件和视图状态的开发者,可以开发实用的应用程序而不需要太多HTMLJavaScript技能。

 

2)         缺点

a)         项目架构

当你创建Web Forms应用程序时,没有预定义的项目架构。开发者可以灵活的选择他们想要的项目架构。你可以选择基本的三层架构来将系统分成表现层(UI)、业务逻辑层(BLL)、数据层(DAL)或者更先进的MVPModel-View-Presenter)架构。你也可以选择只使用code-behind技术,将所有逻辑都写在这,但这并不是一个好的项目架构方案。

b)         单元测试

Web Forms架构的code-behind技术中,使用了大量的事件处理函数,让其不可能进行自动单元测试。

注意:据我所知,即使借助模拟测试(MOQrhinomoq)也不能模拟事件处理程序中的sender”和“eventargs”两个对象。

重要的是,现在测试驱动开发和单元测试变得越来越重要了。

clip_image009 

c)         性能

ViewState成功的解决了经典的ASP程序中的一些问题,但也引入了另一些问题。ViewState存储在页面本身,这样增加了页面的大小降低了程序性能。

d)         可重用性

clip_image011

让我们讨论另一个示例,我们构建两个UI

l  纳税员工页面

l  非纳税员工页面

现在这两个页面将共用大部分后台业务逻辑。创建一个页面后,通过添加一些if判断条件创建第二个页面。 

这 将违反程序设计的单一职责原则,即一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这 些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和 界面的分离。

e)         不能完全控制HTML

Web Forms架构中,很多时候我们无法确定服务器控件在终端浏览器中生成的html,这样很难集成第三方JavaScript 库,如JQuery

f)          SEO

URL指向特定的ASPX页面,可能还包含一些查询字符串。这样对于搜索引擎来说是不友好的。更详细请看:《更友好的网站url设计》

g)         缺少并行开发支持

ASPX页面和后台代码文件是紧密耦合的,所以不方便2个开发者同时开发页面和后台代码。

 

6.         ASP.NET 4.0

ASP.NET 4.0 发布了一些新特性来解决上面提到的一些问题。

1)         ViewState:提供禁用或控制视图状态大小的功能。

2)         URL路由:使用URL路由功能,我们能提供自己的URL来代替页面的物理路径。

3)         ID:在ASP.NET4.0中,我们能更好的控制元素的ID属性,以便更好的与其他JavaScript库集成。(不过,我们任然不能完全控制HTML元素的生成)

尽管在ASP.NET4.0大幅度改进后:

1)         任然不能解决单元测试的问题。

2)         我从未见过使用ASP.NET Web Forms架构的开发者会尝试禁用ViewState。(这是个人看法)

3)         我们能控制元素的ID属性,但是不能完全控制HTML如何生成,所以任然存在结合第三方JavaScript库编程的一些困难。

 

7.         什么是MVC

MVC是一个架构模式,现在已经很多地方使用了。

很多人在Java中使用它。微软将它引入到ASP.NET MVC架构中。我们今天就来讨论ASP.NET MVC架构,但在此之前我们先了解一些MVC中的术语。

1)         模式:其实就是解决某一类问题的方法论。把解决某类问题的方法总结归纳到理论高度,那就是模式。模式是一种指导,在一个良好的指导下,有助于你完成任务,有助于你作出一个优良的设计方案,达到事半功倍的效果。而且会得到解决问题的最佳办法。

2)         架构模式:架构模式也叫架构风格,一个架构模式描述软件系统里基本的结构组织或纲要,是从子系统级别或模块级别解决我们的问题。我们常常需要构建类库、组建、Web Services等。

3)         MVC:当我们谈论到应用,就有输入逻辑,业务逻辑,UI逻辑等元素。MVC就是一个架构模式,它让我们在开发应用程序时能将这些逻辑元素保持松散耦合。

MVC模式的主要意图是分离关注点,它使的展示或UI 业务或用户交互逻辑分离。

MVC系统应该分为M(Model)V(View) C(Controller)

clip_image012

l  Model,被定位为智能或手动的业务规则、逻辑、数据,它独立于MVCViewController部分。

l  Controller,接收并分配用户交互逻辑或输入逻辑发出的请求,它需要知道MVCModelView

l  View,被定位为模型数据的输出表现,它可能是一个excel表格,一个显示记录列表的web页面或一个简单的文本。它只需要知道MVCModel

 

8.         什么是ASP.NET MVC

clip_image013

ASP.NET MVC是微软发布的,用于设计低耦合、可测试的web应用程序框架,它是基于CLR和成熟的MVC架构构建的。ASP.NET MVC不支持ViewState和服务器控件,所以你可能认为又回到了麻烦的原生web应用程序开发。但不是,让我们来讨论下ASP.NET MVC框架的优缺点:

 

优点:

1)         项目架构

ASP.NET MVC架构降低了程序间的耦合性,即M(model)V(View)C(Controller)。如图:

clip_image014

2)         测试驱动开发和重用性

a)         MVCController,是一个独立的类,所以支持测试驱动开发。

b)         MVCController,不绑定任何特定的View,所以能重用于多个View

clip_image016

3)         性能

ASP.NET MVC框架不支持ViewState,所以不会有自动状态管理机制,从而降低了页面传递的尺寸,提高了程序性能。

4)         完全的HTML控制

ASP.NET MVC 不支持服务器控件,唯一的选择是使用客户端输入控件,这样我们能完全控制最后展现的html。所以ASP.NET MVC 架构能很容易的与第三方JavaScript库(egJQuery)集成开发。

5)         支持并行开发

ASP.NET MVC架构中的各层是松散耦合的,所以一个开发者在开发Controller的同时第二个开发者开发View并且第三个开发者开发Model。这就是所谓的并行开发。

6)         SEOURL重写和REST

丰富的路由规则让每个URL被视为一个资源,支持RESTful

clip_image017

同时,用户用好的、可读的URL能改善SEO

7)         可扩展的

ASP.NET MVC支持多种视图引擎,egaspxrazor,如果需要还能开发自己的视图引擎。

8)         继承ASP.NET特性

ASP.NET MVC框架构建在成熟的ASP.NET框架之上,所以提供开发者很多好功能,如表单验证、windows身份验证、缓存、会话等等。

缺点:

1)         提高了学习成本

没有事件驱动编程模型和视图状态,使得对于缺少web应用开发经验的开发者来说很难上手。

 

9.         ASP.NET MVC 是如何工作的?

clip_image018

1)         用户通过浏览器URL向服务器请求一些资源。(GET请求)

2)         路由引擎首先将这个请求传递给对应的Controller

3)         如果需要持久化数据,Controller就从Model中获得数据。

4)         Model访问数据库数据(或其它一些数据资源)并返回数据给Controller

5)         Controller将选择适当的View

6)         Controller将数据(第四部获取的model)传递给页面(第五部获取的view),数据将填充到页面的指定位置。

7)         ControllerView返回并呈现给用户。

 

这是一个GET请求,POST请求的流程类似。唯一不同的是:不是通过在浏览器中输入url触发,而是在页面上做一些操作(eg:点击按钮、改变下拉选项等等)来触发POST请求。

 

         有园友看的好仔细,我都没注意到哈哈不改变原来译文了,就在此进行说明吧。

      clip_image001

10.     什么是ASP.NET Web Forms框架以及什么是ASP.NET MVC框架?

哪一个才是“最佳选择”?特定的解决方案依赖于应用程序的需求和团队成员的技术背景。当你面临必须在 ASP.NET Web Forms框架 ASP.NET MVC框架中选择一个的时候,你要明白最重要的权重不是技术,而是其他的。当你做选择时,你需要考虑的两个重要因素:

1)         快速应用程序开发

如果你想快速开发任何web程序,那么ASP.NET Web Forms框架是你唯一的选择,你甚至无需考虑如何让 ASP.NET MVC框架快速开发程序。(选择ASP.NET Web Forms框架的原因:需求为快速开发的程序,客户一般不会支付太多money;或者应用程序只使用一、俩个月并且不再需要继续维护的项目)

2)         单元测试

如果自动单元测试对你来说非常重要,那么最好选择ASP.NET MVC框架

 

除了这些,你能做的是:写下你的项目需求并试图比较ASP.NET Web Forms框架 ASP.NET MVC框架的优劣。如果可能,就尝试问自己下面这些问题:

1)         你的团队是否在Web Forms框架或Windows Forms框架上拥有丰富的开发经验?

如果是,那么学习ASP.NET MVC框架对于团队来说可能是一项乏味的任务。因为开发者已经习惯使用ViewState和事件驱动的编程模型,思维的改变将是一个困难的任务。—— Web Forms框架获得一票。

2)         你的团队是否在ASP.NET MVC框架上拥有丰富的开发经验?

如果是,请选择MVC框架。—— ASP.NET MVC 框架获得一票。

3)         你的团队是否在ASP或非微软技术(如Androidiosjsprorphp等)上拥有丰富的开发经验?

如果你曾使用过JSPASP,你可能熟悉HTTPgetpost请求,甚至你可能手写过使用MVC模式的代码,因为这些技术当中大部分都默认使用MVC模式。—— ASP.NET MVC 框架获得一票。

4)         是否会大量使用JavaScript

如果是,选择MVC框架更合适,因为它能完全控制页面的HTML—— ASP.NET MVC 框架获得一票。

5)         寻找更好的性能

不支持ViewState机制的 ASP.NET MVC 框架比ASP.NET Web Forms 框架能提供更好的性能。ASP.NET MVC 框架获得一票。

6)         计划重用相同的输入逻辑

如果是,请选择MVC框架。—— ASP.NET MVC 框架获得一票。

 

结论

我认为你应该准备充分的信息来决定什么最适合你的项目。最后的决定取决于你的团队和你的项目需求。

         希望你喜欢这篇文章,谢谢你的耐心阅读。

 

哈哈哈,翻译到此结束,好文还请多帮推荐…推荐。

 

原文作者:Marla Sukesh

原文链接:http://www.codeproject.com/Articles/528117/WebForms-vs-MVC

 

 

 

一些好的评论:

@路过秋天

5) 寻找更好的性能

不支持ViewState机制的 ASP.NET MVC 框架比ASP.NET Web Forms 框架能提供更好的性能。ASP.NET MVC 框架获得一票。

—————–

性能点不在于不支持ViewState的问题,WebForm也可以禁用ViewState,而mvc为了设计的需要,从开始的遍历所有引用的程序集,到获得到所有Control到调用相应的方法,都是基于反射调用。

所以不能单纯的以ViewState VS 反射来说谁的性能更好。

具体性能还是取决于开发者的水评。

 

@slice

MVC感觉用于Web方面更合适,容易和其他JS框架结合,思维上也更容易理解和控制,更能控制HTML本身。

MVC也有很多UI控件,例如telerikkendoui等,不如webform那些控件那么傻瓜,但却更容易掌控,这个掌控不是指用起来多好用简单,而是更容易掌控起行为和过程,有个性化需求时,更容易拿它开刀。

 

@深海沉         

纠正原著的作者,首先现在的 Asp.net MVC 或者 Spring MVC 中的那个 Model, 已经不是传统MVC 模式中的那个Model了。传统MVC 模式中的model 是获得数据,含有业务逻辑;而 Asp.net MVC 或者 Spring MVC 中的那个 Model 其实是一个数据容器,Controller将获得的数据(或者已经是Mdoel)放入Model中,传递给View,这个Model 仅仅是作为数据的容器,没有任何的业务逻辑。原著的作者肯定是出错了。

 

@walleyekneel 

指出博文中的一点错误,ASP.NET MVC 框架的ASPX视图引擎也是支持服务器端控件的。(razor视图引擎才不支持)

[转载]新浪微博SDK在Eclipse中的使用 - ChriZ! - 博客园

mikel阅读(926)

[转载]新浪微博SDK在Eclipse中的使用 – ChriZ! – 博客园.

新浪微博SDKEclipse中的使用

今天在看《Android开发应用实战》,全书都在讲一个Android版的新浪微博客户端怎么做,于是按照书上步骤做。网上有人说这本书没有细节,我想对于小白来讲确实需要一些细节,因为我作为一个android小白,确实不知道如何正确地导入新浪微博SDK

我的环境:

OS:Windows7

IDE:Android Developer Tools(Versionv22.0.1-685705

SDK:weibo4j-1.2.1

这个ADT,其实就是Eclipse for android,专门用于android开发,集成了adt插件,andriodsdk也包含在其中的一个Eclipse。按照书上说法,weibo4jsrc目录下的文件拷贝到自己新建的工程sina_weibo中的src目录下,然后把weibo4jlib目录下的3jar文件拷贝到sina_weibo中的lib目录下,并在工程property中引入这3jar即可。

一开始我的做法是,新建一个android application,然后右键src,选择import–general–filesystem–browse,选择weibo4j/src

然后是复制weibo4jlib目录下3jar到项目中:

然后是导入这3jar:项目–properties–libraries–add jars,选择工程中lib目录下的jar,依次添加,添加完毕是这样:

按说,这操作完毕,就该写自己的代码了。但src目录中很多代码显示红色,报错。

于是关闭了所有工程,直接把sdk给引入:import weibo4j整个文件夹。然后竟然没有报错了。再打开先前的工程,把报错的java文件和同名的不报错的文件对比,发现报错的文件有乱码。原来是编码的问题!调整如下:选择windows–preferences–general–workspace,将Text file encoding设定为Other–utf8

OK,不再报错了!

不过,上面建立的是android application,引用的是weibo4j,也就是weibo SDK for JAVA,其实不是非常对应的感觉。。。如果是建立一个java project,代码可能还是会报错,原因是没有添加JRE system library这个解决方案是在百度知道上找到的,原文:

eclipse 不能引用 javax.imageio.ImageIO;

我曾经遇到过该问题,困扰了好久,解决方案是:Project—Properties,选择Java Build Path,选中右侧的Libraries,选择Add Library如下图,选中JRE System Library 选择next,进入下一界面选择Execution environment,点击Enviroments按钮选择你采用的JDKJavaSE-1.6等,不要选下面的OSGi,最后单击完成即可。

不过对于ADT来说,用默认的jre7就好了:

[转载]一个OSGI原形(.NET) - Aaron Dai - 博客园

mikel阅读(943)

[转载]一个OSGI原形(.NET) – Aaron Dai – 博客园.

目前只做了基础的功能,比如:

  1. 各个模块单独的AppDomain容器
  2. Activator激活
  3. 导出的服务检查

不过,虽说这样,但目前的这个版本已经能实现模块分离、互相依赖调用等功能了,对模块划分已经有很好的作用了。

先来说下基本结构:

目前这个框架对UI的模块化尚不支持,只支持单机下的模块加载(以后会改进)。

特点:

  • Runtime、Module1、Module2、Module3都是在各自的AppDomain下运行的,不会互相干扰
  • 由于采用了隔离AppDomain的方式加载Module,所以能实现轻松卸载AppDomain、某dll、dll版本切换之类的任务,对后续扩展提供了方便

来看看模块的编写

OrderModule库是主要的业务逻辑以及模块定义的地方

OrderModule.PublicInterfaces是公用定义,比如接口、事件、dto等,考虑到方便分发,因此独立了出来(如果不介意这点,也可以合并成一个库)

模块定义文件:

复制代码
<?xml version="1.0" encoding="utf-8" ?>
<Module>
  <Name>Order Module</Name>
  <Version>1</Version>
  <Assembly>OrderModule</Assembly>
  <Activator>OrderModule.Activator</Activator>  //这个是OSGI调用的第一个classType, 会执行里面的2个方法:Start和Stop
  <RequiredService>LoggingModule.PublicInterfaces.ILog</RequiredService>//此模块需要依赖哪些其他Service,可以有多个
  <ProvidedService>OrderModule.PublicInterfaces.IOrderProcessor</ProvidedService>//此模块会提供的服务,可以有多个
</Module>
复制代码

 

Activator:

复制代码
class Activator:OSGIFramework.BundleActivator
    {
        public override void Startup(BundleContext bc)
        {
            Console.WriteLine("OrderModule Startup");

            BundleContext.Current.RegisterProvidedService<IOrderProcessor, OrderProcessorImpl>();
            //注册服务
        }

        public override void Stop(BundleContext bc)
        {
            Console.WriteLine("OrderModule Stop");

            BundleContext.Current.UnRegisterProvidedService<IOrderProcessor, OrderProcessorImpl>();
            //卸载服务
        }
    }
复制代码

 

服务的实现:

复制代码
class OrderProcessorImpl : ServiceProvider, IOrderProcessor //ServiceProvider是基类,由于需要在AppDomain之间穿梭,因此必须继承这个
    {
        public Guid PlaceOrder(OrderModule.PublicInterfaces.Dtos.OrderDto order)
        {
            BundleContext ctx=BundleContext.Current;  //获得上下文

            ILog log = ctx.GetProvidedService<ILog>(); //获得一个服务
            log.Log("log something...");

            Console.WriteLine("PlaceOrder body");

            return Guid.NewGuid();
        }
    }
复制代码

 

模块化,还要考虑散落在不同目录的dll文件和xml定义文件。在Console程序中,也可以通过xml定义格式来做:

<?xml version="1.0" encoding="utf-8" ?>
<Manifests>
  <Manifest>D:\documents\visual studio 2010\Projects\OSGIDemo\LoggingModule\bin\Debug\Manifest.xml</Manifest>
  <Manifest>D:\documents\visual studio 2010\Projects\OSGIDemo\OrderModule\bin\Debug\Manifest.xml</Manifest>
  <Manifest>D:\documents\visual studio 2010\Projects\OSGIDemo\OrderPDFProcessor\bin\Debug\Manifest.xml</Manifest>
</Manifests>

 

主程序:

复制代码
static void Main(string[] args)
        {
            BundleRuntime runtime = new BundleRuntime();
            runtime.Start();

            BundleContext ctx = BundleContext.Current;

            IOrderProcessor processor = (IOrderProcessor)ctx.GetProvidedService(typeof(IOrderProcessor).FullName);

            OrderDto order = new OrderDto();
            Guid id=processor.PlaceOrder(order);
            Console.WriteLine("id="+id.ToString());

            Console.ReadKey();
            runtime.Stop();
            runtime.Dispose();
            runtime = null;
        }
复制代码

 

就可以运行了,效果图:

 

当然,由于是AppDomain隔离的,性能上肯定下降。

代码下载

[转载]最实用的10个重构小技巧排行榜,您都用过哪些呢? - 左潇龙 - 博客园

mikel阅读(924)

转载最实用的10个重构小技巧排行榜,您都用过哪些呢? – 左潇龙 – 博客园.

         LZ最近一直在研究虚拟机源码,可惜目前还只是稍微有一点点头绪,无法与各位分享,庞大的JAVA虚拟机源码果然不是一朝一夕能搞定的,LZ汗颜。

           本次我们抛开JAVA虚拟机源码这些相对底层的东西,LZ来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于LZ维护项目 当中的一些实践经验。如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得、小体会共享与他人,也可以拿来冲击LZ自己定义的排行 榜,LZ不甚欢迎。

           重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提到所有,LZ这里也只能提出一些平时会经常使用的一些手法,像一些比较高端的手法,各位有兴趣的可以去找一些专门的书籍涉猎。

           另外还有一点,由于LZ是做JAVA开发的,因此部分重构小技巧可能与JAVA语言,或者说与面向对象的语言息息相关,不过大多数技巧,无论是面向过程的语言,还是面向对象的语言,都是可以相互通用的。

           废话不多说,我们来看看实用重构技巧的排行榜吧。

 

No.1:重复代码的提炼

 

           重复代码是重构收效最大的手法之一,进行这项重构的原因不需要多说。它有很多很明显的好处,比如总代码量大大减少,维护方便,代码条理更加清晰易读。

           它的重点就在于寻找代码当中完成某项子功能的重复代码,找到以后请毫不犹豫将它移动到合适的方法当中,并存放在合适的类当中。

 

小实例

           

复制代码
class BadExample {

    public void someMethod1(){
        //code
        System.out.println("重复代码");/* 重复代码块 */
        //code
    }

    public void someMethod2(){
        //code
        System.out.println("重复代码");/* 重复代码块 */
        //code
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public void someMethod1(){
        //code
        someMethod3();
        //code
    }

    public void someMethod2(){
        //code
        someMethod3();
        //code
    }

    public void someMethod3(){
        System.out.println("重复代码");/* 重复代码块 */
    }

}
复制代码

 

No.2:冗长方法的分割

 

         有关冗长方法的分割,其实有时候与重复代码的提炼是有着不可分割的关系的,往往在我们提炼重复代码的过程中,就不知不觉的完成了对某一个超长方法的分 割。倘若在你提炼了大部分的重复代码之后,某一些冗长方法依然留存,此时就要静下心来专门处理这些冗长方法了。

         这其中有一点是值得注意的,由于我们在分割一个大方法时,大部分都是针对其中的一些子功能分割,因此我们需要给每一个子功能起一个恰到好处的方法名,这很重要。可以说,能否给方法起一个好名字,有时候能体现出一个程序猿的大致水准。

 

小实例

 

复制代码
class BadExample {

    public void someMethod(){
        //function[1]
        //function[2]
        //function[3]
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public void someMethod(){
        function1();
        function2();
        function3();
    }

    private void function1(){
        //function[1]
    }

    private void function2(){
        //function[2]
    }

    private void function3(){
        //function[3]
    }

}
复制代码

 

No.3:嵌套条件分支的优化(1)

 

           大量的嵌套条件分支是很容易让人望而却步的代码,我们应该极力避免这种代码的出现。尽管结构化原则一直在说一个函数只能有一个出口,但是在这么大量的嵌套条件分支下,让我们忘了这所谓的规则吧。

           有一个专业名词叫卫语句,可以治疗这种恐怖的嵌套条件语句。它的核心思想是,将不满足某些条件的情况放在方法前面,并及时跳出方法,以免对后面的判断造 成影响。经过这项手术的代码看起来会非常的清晰,下面LZ就给各位举一个经典的例子,各位可以自行评判一下这两种方式,哪个让你看起来更清晰一点。

 

小实例

           

复制代码
class BadExample {

    public void someMethod(Object A,Object B){
        if (A != null) {
            if (B != null) {
                //code[1]
            }else {
                //code[3]
            }
        }else {
            //code[2]
        }
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public void someMethod(Object A,Object B){
        if (A == null) {
            //code[2]
            return;
        }
        if (B == null) {
            //code[3]
            return;
        }
        //code[1]
    }

}
复制代码

 

No.4:嵌套条件分支的优化(2)

           

          此处所说的嵌套条件分支与上面的有些许不同,它无法使用卫语句进行优化,而应该是将条件分支合并,以此来达到代码清晰的目的。由这两条也可以看出,嵌套条件分支在编码当中应当尽量避免,它会大大降低代码的可读性。

          下面请尚且不明觉厉的猿友看下面这个典型的小例子。

 

小实例

 

复制代码
class BadExample {

    public void someMethod(Object A,Object B){
        if (A != null) {
            if (B != null) {
                //code
            }
        }
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public void someMethod(Object A,Object B){
        if (A != null && B != null) {
            //code
        }
    }

}
复制代码

 

No.5:去掉一次性的临时变量

 

         生活当中我们都经常用一次性筷子,这无疑是对树木的摧残。然而在程序当中,一次性的临时变量不仅是对性能上小小的摧残,更是对代码可读性的亵渎。因此我们有必要对一些一次性的临时变量进行手术。

 

小实例

 

复制代码
class BadExample {

    private int i;

    public int someMethod(){
        int temp = getVariable();
        return temp * 100;
    }

    public int getVariable(){
        return i;
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    private int i;

    public int someMethod(){
        return getVariable() * 100;
    }

    public int getVariable(){
        return i;
    }

}
复制代码

 

No.6:消除过长参数列表

 

          对于一些传递了大批参数的方法,对于追求代码整洁的程序猿来说,是无法接受的。我们可以尝试将这些参数封装成一个对象传递给方法,从而去除过长的参数列 表。大部分情况下,当你尝试寻找这样一个对象的时候,它往往已经存在了,因此绝大多数情况下,我们并不需要做多余的工作。

 

小实例

 

复制代码
class BadExample {

    public void someMethod(int i,int j,int k,int l,int m,int n){
        //code
    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public void someMethod(Data data){
        //code
    }

}

class Data{

    private int i;
    private int j;
    private int k;
    private int l;
    private int m;
    private int n;

  //getter&&setter
    
}
复制代码

 

No.7:提取类或继承体系中的常量

 

         这项重构的目的是为了消除一些魔数或者是字符串常量等等,魔数所带来的弊端自不用说,它会让人对程序的意图产生迷惑。而对于字符串等类型的常量的消除, 更多的好处在于维护时的方便。因为我们只需要修改一个常量,就可以完成对程序中所有使用该常量的代码的修改。

         顺便提一句,与此类情况类似并且最常见的,就是Action基类中,对于INPUT、LIST、SUCCESS等这些常量的提取。

 

小实例

 

复制代码
class BadExample {

    public void someMethod1(){
        send("您的操作已成功!");
    }

    public void someMethod2(){
        send("您的操作已成功!");
    }

    public void someMethod3(){
        send("您的操作已成功!");
    }

    private void send(String message){
        //code
    }
}

/* ---------------------分割线---------------------- */

class GoodExample {

    protected static final String SUCCESS_MESSAGE = "您的操作已成功!";

    public void someMethod1(){
        send(SUCCESS_MESSAGE);
    }

    public void someMethod2(){
        send(SUCCESS_MESSAGE);
    }

    public void someMethod3(){
        send(SUCCESS_MESSAGE);
    }

    private void send(String message){
        //code
    }

}
复制代码

 

No.8:让类提供应该提供的方法

 

         很多时候,我们经常会操作一个类的大部分属性,从而得到一个最终我们想要的结果。这种时候,我们应该让这个类做它该做的事情,而不应该让我们替它做。而且大部分时候,这个过程最终会成为重复代码的根源。

 

小实例

 

复制代码
class BadExample {

    public int someMethod(Data data){
        int i = data.getI();
        int j = data.getJ();
        int k = data.getK();
        return i * j * k;
    }

    public static class Data{

        private int i;
        private int j;
        private int k;

        public Data(int i, int j, int k) {
            super();
            this.i = i;
            this.j = j;
            this.k = k;
        }

        public int getI() {
            return i;
        }

        public int getJ() {
            return j;
        }

        public int getK() {
            return k;
        }

    }

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public int someMethod(Data data){
        return data.getResult();
    }

    public static class Data{

        private int i;
        private int j;
        private int k;

        public Data(int i, int j, int k) {
            super();
            this.i = i;
            this.j = j;
            this.k = k;
        }

        public int getI() {
            return i;
        }

        public int getJ() {
            return j;
        }

        public int getK() {
            return k;
        }

        public int getResult(){
            return i * j * k;
        }

    }

}
复制代码

 

No.9:拆分冗长的类

 

         这项技巧其实也是属于非常实用的一个技巧,只不过由于它的难度相对较高,因此被LZ排在了后面。针对这个技巧,LZ很难像上面的技巧一样,给出一个即简单又很容易说明问题的小例子,因为它已经不仅仅是小手段了。

         大部分时候,我们拆分一个类的关注点应该主要集中在类的属性上面。拆分出来的两批属性应该在逻辑上是可以分离的,并且在代码当中,这两批属性的使用也都 分别集中于某一些方法当中。如果实在有一些属性同时存在于拆分后的两批方法内部,那么可以通过参数传递的方式解决这种依赖。

         类的拆分是一个相对较大的工程,毕竟一个大类往往在程序中已经被很多类所使用着,因此这项重构的难度相当之大,一定要谨慎,并做好足够的测试。

 

No.10:提取继承体系中重复的属性与方法到父类

 

         这项技巧大部分时候需要足够的判断力,很多时候,这其实是在向模板方法模式迈进的过程。它的实例LZ这里无法给出,原因是因为它的小实例会毫无意义,无非就是子类有一样的属性或者方法,然后删除子类的重复属性或方法放到父类当中。

         往往这一类重构都不会是小工程,因此这一项重构与第九种类似,都需要足够的谨慎与测试。而且需要在你足够确认,这些提取到父类中的属性或方法,应该是子类的共性的时候,才可以使用这项技巧。

 

结束语

 

         由于LZ目前的工作就是维护一个相对古老的项目,因此上面这十种手法,LZ几乎都已经一一尝试过了,可喜的是效果都还不错。

         限于最后两种与实际情况的联系太过紧密,因此LZ无法给出简单的实例,不过后面两种毕竟不是常用的重构手法,因此也算是可以接受了。不过不常用不代表不 重要,各位猿友还是要知道这一点的。另外LZ还要说的是,上面的实例只是手法的一种简单展示,实际应用当中,代码的结构可能是千奇百怪,但却万变不离其 宗。因此只要抓住每种手法的核心,就不难从这些乱军丛中安然穿过。

         好了,本次的小分享到此结束,希望各位猿友如果觉得有所收获,可以推荐一下鼓励下LZ,顺便也让更多的人看到。这样的话,或许我们每一个接手的项目代码,都不至于十分的糟糕了,也算是给像LZ这样的项目维护者一条生路吧。

[转载]白话插件框架原理 - 道法自然 - 博客园

mikel阅读(1042)

[转载]白话插件框架原理 – 道法自然 – 博客园.

本文将用尽可能简单的文字来描述插件框架原理。很多人以为插件化很复杂,所以就一直将这 类框架阻挡在门外。实际上,在我们的实践过程中,从框架的使用角度来看,它非常简单,我们团队里面非正规院校毕业的女生也可以来实际使用。如果说插件框架 难的地方,我反倒觉得克服人的天然惰性更加困难。我们不能习惯于墨守成规,日复一日年复一年,按照相同的模式来开发,将自己打造成一部“编码机器”,成为 没有价值的“程序猿/媛”。使用插件框架,没有多少技术难点,不过需要我们提升我们的软件开发思想,改变现有开发方式。

 

1 插件框架本质

 

在.NET 平台,一个程序是由“程序集 + 资源”构成的。程序集是由我们开发的一个个的类。这些类可能是通用功能的辅助类、数据访问类、业务逻辑类,也可以是WinForm应用的窗体类或者Web 应用的Web窗体类。以传统模式开发的程序,一般情况下,不管是我们开发的程序集,还是引用的第三方程序集,它们都在应用程序的bin目录下,如下所示。

1

 

插件化开发方式与传统方式不同在于,它会把程序集按照一定结构进行组织,比如下面这个程序是基于插件化方式来构建的,不同功能的程序集则组织到Plugins目录下,如下所示。

2

 

此时,bin目录则仅仅包含几个很通用的程序集。

3

 

在Plugins目录下,则按照功能进行分组,每一个目录为一个插件,它实现一组功能。

4

 

下面我们来看其中一个插件——AlarmManagementPlugin插件的目录结构,如下所示,你会发现,插件拥有自己独立的bin目录,在自己的bin目录下,放置着这个功能涉及的程序集。

5

 

在传统开发方式中,放在bin目录下的程序集会由.NET类加载器按需去加载,但是当我们要实现插件化方式开发时,需要依赖于插件框架实现从不同的插件目录中加载程序集。因此,插件框架本质上是扩展了.NET类加载器的功能,使其能够从插件目录中加载程序集。

 

2 进一步看插件框架

 

插件化开发方式不仅仅从程序集的组织方式上发上了变化,更重要的是,在功能的组织和实现也发生了变化。我们用一个非常典型的分层架构看看二者区别。

 

下 图是一个分层架构的应用程序,由表示层、业务层、数据层等组成,每一个层次都有相对应的功能组成,在表示层,我们一般是构建了一个主界面,然后由不同的开 发者在主界面上直接放置上菜单及菜单点击事件的响应,同理,其它层次也类似,不同开发者根据需要实现的功能来添加不同的代码。

6

 

在 这种模式下,所有程序员开发的不同层次的功能代码一般都在同一个程序集里面,在开发过程中,团队需要不停的进行合并,并执行集成测试。下图是大家都很熟悉 的PetShop的项目结构。这里面BLL程序集(项目)放置的是业务逻辑层的代码,Web程序集(项目)放置的是表示层代码,DALFactory及涉 及的其它DAL程序集则是数据访问层的相关代码。

7

 

下面我们来看看一个功能所涉及的程序集。这意味着,所有程序员都需要获取到项目的代码,然后遵循规则,在各个项目中添加自己的代码,再将这些代码合并起来,最后完整编译再交付软件系统。

8

 

现在我们来看看使用插件化开发方式的组织方式,如下图所示。

9

 

在这种模式下,功能被封装到插件中,一个程序员负责若干个插件,每一个插件由其相应功能涉及的界面、业务逻辑和数据访问等代码组成。每一个插件可以独立开发、测试和部署,开发者间不再需要对代码进行合并。下图是一个插件化应用程序的项目。

10

 

在这里,每一个插件完成一组功能,它们可以有自己独立的界面、业务逻辑和数据访问实现,插件间具有物理隔离性,开发者可以独立开发自己的功能,独立测试、部署与升级,一旦开发完成后,可以由插件框架在进行组合,不再需要进行代码合并和整体发布。

 

3  从简单示例看插件化

 

现在我们看看使用插件化来开发的示例。下图是该软件的集成后的界面,有5个一级菜单,其中“服务器”为GPRS通讯服务器插件定义,由程序猿A开发;“基础数据、能耗分类、安装数据”菜单为基础数据管理插件定义,由程序猿B开发。

11

下图是“服务器”的子菜单和其中一个页面。

12

下图是“基础数据”的子菜单和其中一个页面。

13

 

该系统使用插件化开发方法如下。

(1)该系统使用了一个通用界面框架插件,你可以从iOpenWorks网站来下载到该插件(http://www.iopenworks.com/Products/ProductDetails/Introduction?proID=386),这个界面框架插件提供了一个空白的界面,这个界面支持三级菜单,允许我们通过以下配置将自己定义的菜单及菜单对应的窗体注册到主界面。如下XML定义是由GPRS服务器插件来定义的。它配置了一个一级菜单和两个二级菜单,以及对应的两个用户控件。

<Extension Point="UIShell.WpfShellPlugin.LinkGroups">
    <LinkGroup DisplayName="服务器" DefaultContentSource="UIShell.EcmCommServerPlugin.CommServerUserControl">
        <Link DisplayName="通讯服务器" Source="UIShell.EcmCommServerPlugin.IntegratedCommServerUserControl" />
        <Link DisplayName="测试服务器" Source="UIShell.EcmCommServerPlugin.CommServerUserControl" />
    </LinkGroup>
</Extension>

 

(2)程序猿A创建一个自己的项目来开发通讯服务器,独立的实现具体的应用和业务逻辑,其项目结构如下所示。在这里,他分成两个项目来实现GPRS 通讯服务器,一个是界面表示层UIShell.EcmCommServerPlugin项目,另一个是业务逻辑层 UIShell.EcmCommServerService项目。在开发过程中与程序猿B互相独立,他们可以独立开发、测试、部署和维护。

14

运行这个项目后,程序猿A得到以下的结果。

15

 

(3)同理,程序猿B也创建一个自己的项目来开发基础数据管理,独立的实现具体的应用和业务逻辑,其项目结构如下所示。在这里,他分成两个项目来实 现,一个是界面表示层UIShell.EcmConfigurationPlugin项目,另一个是业务逻辑层 UIShell.EcmDomainService项目,该项目可以被A来重用。在开发过程中与程序猿A互相独立,他们可以独立开发、测试、部署和维护。

16

运行这个项目后,程序猿B得到以下的结果。

17

 

(4)程序猿A和B开发到一个阶段的时候,就可以来随时发布自己的插件,点击项目右键,直接将插件发布到插件仓库。

18

19

以下是发布的结果,该项目的插件仓库由3个项目组成。

20

通讯服务器项目的插件列表如下。

21

配置管理项目的插件列表如下所示。

22

(5)测试人员/部署人员可以通过插件管理来获取到这两个程序猿开发的插件,然后将这些插件下载组装起来。

23

下载安装后,就是如下的效果了。

24

 

4 插件化有什么好处

 

从以上简单的例子,我们看到,插件化最直接的好处就是可以以模块化的方式来独立并行构建软件系统,在构建的过程中可以随时进行集成。下面我把使用插件化的优点总结一下:

(1) 插件化优化了团队协作,避免团队开发过程中互相交叉,不再需要更改各自的代码将开发的成果集成到一起;

(2) 使用插件化开发后,每一个人的工作都非常的独立,可以有独立的架构、独立开发、独立测试、独立部署、独立升级,并行构建;

(3) 插件化使得重用度更高,在我们开发的一个项目中,超过50%的模块都直接重用了原有的结果,不需要更改任何的代码;

(4) 插件化使开发和维护更加简单,这也是得益于每一个开发人员可以单独开发自己的应用,每个人都很专注,并且只需要关注系统中很小的一部分,在以上示例中,每一个开发人员能看到的代码都是自己来开发的;

(5) 使用插件化开发,我们的发布、升级也很简单,右键发布到插件仓库,部署测试人员通过插件管理界面来下载每一个人的成果,组装成软件,并且可以随时升级,这避免了很多手工发布、集成;

(6) 使用插件化,可以很容易的构建自己的知识库,是完全可复用,并持续不断改进和增长。

 

5 插件框架原理

 

从上述小节,我们看到,插件化开发更多的是软件方法和思想上的改变。为了满足这种开发方法的团队协作模型,插件框架需要来解决一下几个问题:

 

5.1 如何实现模块化

 

实现模块化,意味着我们需要把功能相关的类组织到若干独立的程序集,这些程序集是在插件目录下。因此,实现模块化就必须解决以下几个问题:

(1)正确的类加载:即插件框架必须对CLR类加载进行扩展,使其能够从插件目录中正确加载到插件程序集;

(2)插件描述:必须引入一个插件描述方法,它来告知当前插件的基本信息、版本标识、以及这个插件所包含的程序集;

(3)解决插件的依赖关系:在一个实际应用系统中,必然存在相互依赖。在这里,依赖是指一个插件使用了另一个插件定义的类型或者创建的对象。也就是 说,我们可以直接使用另一个插件定义的类型,在更加麻烦的场景中,还存在循环依赖。插件框架够必须很好的处理好插件依赖关系的解析与管理。只有当所有的依 赖关系都满足后,一个插件才能够对外暴露功能;

(4)类加载空间定义:插件既有自己的程序集,也可以依赖其它插件的程序集,那这时候就必须对插件的类型空间做一个唯一的定义,保证当前插件能够加载的类型独立性和隔离性;

(5)插件多版本问题:由于一个插件应用系统中的插件是由若干团队开发,每一个团队独立开发自己的插件,很有可能一个插件使用了NLog的1.0版本呢,而另一个团队则更新到最新的1.1版本,这时候,我们必须保证两个团队的插件能够正确运行;

(6)插件状态的定义:插件可以具备不同的状态,并在不同状态下有不同的体现。最常用的有:安装、解析、正在启动、激活、正在停止、卸载等,当插件 处于解析状态表示这个插件所有依赖关系都已经满足,可以被正常启动;当插件处于激活状态则表示这个插件已经被启动,所有功能都已经暴露了;

(7)插件启动初始状态和启动顺序:当插件被框架加载时,插件处于什么状态;用什么样的方式来设置插件的启动顺序;当插件有依赖关系时,这时候对启动顺序又有什么影响;

(8)插件的初始化和终止处理:即相当于插件的入口和出口,当插件被启动或停止需要执行的操作是如何来定义的。

 

5.2 如何实现模块通讯

 

插件框架必须解决模块通讯。传统的通讯,我们都是基于CLR类加载来实现的,即我们可以直接引用另一个程序集的类型,比如:ClassA cls = new ClassA();或者ClassA.Singleton.SayHello()。插件框架可以提供两种通讯方式,如下:

(1)传统通讯方式:即我们可以像以前一样,直接通过引用类型来进行通讯,或可以通过动态加载类型使用反射来通讯;

(2)面向服务:基于SOA模型来实现通讯,即服务 = 服务契约(接口) + 实现(类),服务提供商将服务注册到总线,服务消费者使用服务契约从服务总线获取服务,绑定使用。

 

5.3 如何实现模块扩展

 

模块扩展指的是在不更改已经发布的插件的任何代码的情况下,来变更该插件的功能。这也是插件重用的支撑之一。在OSGi.NET插件框架,使用了基 于ExtensionPoint-Extension机制,暴露扩展点的插件可以被扩展,扩展的插件通过XML来定义Extension的内容,从而被暴 露扩展点的插件来获取,并变更其功能。

 

5.4 插件框架高级支持

 

除了上述的三大基础功能是插件框架需要实现的,它还可以提供以下更高级的功能:

(1)对自动升级的支持:插件框架可以支持插件的更新,当发现有新版本在插件框架内部时,可以应用更新;此外,还可以对插件框架本身实现自动更新;

(2)对动态性的支持:插件框架可以支持动态安装、启动、停止、更新和卸载插件,允许灵活控制整个应用系统;

(3)对远程管理的支持:可以通过远程管理的API,来实现插件内核的浏览和管理,从而实现内核情况的查看和插件的远程管理控制;

(4)对插件仓库的支持:可以将插件发布到插件仓库,而插件框架则可以利用插件插件仓库来实现动态安装和更新,有利于应用系统的知识积累、发布和团队协作;

(5)对DevOps的支持:插件开发者可以一键发布更新,而测试人员和部署的应用,则可以及时下载到更新。

 

6 OSGi.NET与其它插件框架的比较

 

一个好的插件框架不能有太多的侵略性,如果有太多侵略性的话,我们需要了解特别多的框架API,意味着我们在开发过程中动不动就得和框架打交道,这 非常不利于应用插件化开发。OSGi.NET框架之所以能够成为一个通用的插件框架产品,主要原因在于它的低侵入性。有很多插件框架,它基本上是和UI、 数据访问等操作严格绑定起来的,要应用这些框架时,你需要了解框架关于插件化、UI、数据访问等操作的API,需要完全掌握这些API并在项目中与这些 API打交道,更麻烦的是,一旦这些API无法满足时,这个框架便难以再使用。OSGi.NET插件框架没有像其它框架这么来干了,它把所有功能都当前插 件,都可以被定制或者替代,这里,我们可以根据需要来定制和安装界面框架插件、数据访问插件等,使用这些插件时,都不再需要与框架API打交道了。

 

下面我来用几个指标详细的介绍OSGi.NET与其他插件框架比较。

 

目前在.NET平台,除了OSGi.NET插件框架之外,流行的插件开发平台有SharpDevelop内核、微软的MEF/MAF/SCSF、思科的Mono.Addins。这些框架详细比较如下表所示。

比较项 OSGi.NET SharpDevelop MEF MAF Mono.Addins SCSF
Web开发 支持 不支持 不支持 不支持 不支持 不支持
线程安全 支持 不支持 不支持 不支持 不支持 不支持
国际规范 OSGi R4
多版本 支持 不支持 不支持 支持 不支持 不支持
插件仓库 支持 不支持 不支持 不支持 支持 不支持
依赖管理 支持 支持 不支持 不支持 支持 不支持
面向服务 支持 不支持 不支持 不支持 支持
动态性 支持 很弱 不支持 较弱 不支持
晚加载 支持 不支持 不支持 支持 支持 不支持
扩展性 强但抽象 强但复杂 不支持
隔离性 很弱 强但跨域 很弱
可移植性
易用性 一般
辅助支持 强,模板、打包工具、迁移工具、远程控制、调试 很弱,打包工具 弱,IDE模板
.NET框架 .NET 2.0+ .NET 2.0+ .NET 4.0+ .NET 1.0+ .NET 2.0+ .NET 2.0+

 

在目前所有流行的插件框架中,OSGi.NET框架是唯一支持Web和线程安全的框架,同时OSGi.NET框架能够支持所有.NET应用,它使我 们可以在ASP.NETASP.NET MVC、WPF、WinForm、控制台、Windows服务、移动框架中使用统一的开发模式。

 

从规范化角度来看,只有OSGi.NET插件框架是遵循OSGi R4规范来设计的,其它框架均使用自己的方法来开发。OSGi R4规范是由OSGi联盟在1999年提出的服务平台规范,Eclipse 3.0以后其内核使用OSGi规范来构建,从此OSGi得到快速发展,在IDE、Web容器、云计算等复杂领域中得到成功应用。

 

对于大规模复杂应用,多版本是必须具备的功能,因为有可能两个团队开发的功能会使用同一个第三方库的不同版本。目前只有OSGi.NET框架和 MAF框架支持多版本,允许在同一个应用系统中加载不同版本的库。MAF框架对多版本的支持是通过使用AppDomain来实现,因此插件间需要跨域调 用;OSGi.NET框架对多版本的支持是通过对CLR底层类加载机制进行扩展,无需跨域调用,具备很高的性能,也是支持Web等其它应用开发的保证。

 

插件仓库可以用于管理插件的不同版本,并实现插件的升级和动态安装,在这些框架中只有OSGi.NET插件框架和Mono支持插件仓库。不 过,Mono的插件仓库仅实现插件的文件管理,而OSGi.NET的插件仓库则从项目开发、管理、发布、维护的角度出发,构建了基于插件仓库的软件生产线 平台。OSGi.NET插件仓库不仅仅实现插件的各个版本管理,除此之外,它提供了项目管理、共有插件仓库、私有插件仓库、项目成员及权限、插件内核版本 管理等,适合各个团队的统一协作,是一个创新性的软件生产线平台。

 

插件式应用开发,一个插件会引用另一个插件的类,也就意味着插件框架需要对这种依赖关系提供支持并实现管理。MEF、MAF和SCSF没有提供这类 功能,SharpDevelop、Mono.Addins和OSGi.NET框架提供了完善的依赖管理,其中OSGi.NET对循环依赖提供了更好的支持 并且在插件仓库中也实现依赖管理,因而在下载安装插件时,可以自动下载安装必要的插件。

 

面向服务开发模型实现了服务提供商和服务消费者的解耦,具有良好的扩展性和动态性。OSGi R4规范的服务层对面向服务提供了支持。在这些框架中,OSGi.NET和Mono.Addins对服务提供了支持,SharpDevelop则很弱,其 它框架不支持该开发模型。面向服务实现了插件间的通讯,这种通讯方式是送耦合的方式。

 

动态性是指在应用系统运行过程中可以动态的安装、启动、停止、卸载和更新插件。目前只有OSGi.NET框架具备最全面的动态性支持,它提供了插件 各个生命周期状态,并提供了相应的API来支持,而相比较只有SD、MAF和Mono.Addins提供比较弱的动态性,他们对这些操作仅提供部分的支 持。

 

晚加载是指在必要的时候才加载插件的程序集,这可以更好的提高应用程序的启动速度和节省内存开支。目前支持MAF、Mono.Addins和OSGi.NET框架支持晚加载,其中只有OSGi.NET框架支持插件晚激活,即插件的激活可以推迟到必要的时候。

 

可扩展性是插件框架非常重要的功能,因为在这里,插件一般是封装好的,对外需要暴露可扩展能力,可以通过扩展来变更功能。在这些框架中,只有 SCSF不支持扩展。OSGi.NET和Mono.Addins均提供了灵活且简单的ExtensionPoint-Extension机制的扩展,而 SharpDevelop和MAF的扩展则非常的抽象且复杂。

 

隔离性是指插件具备自己独立的目录结构和类型空间。MAF、Mono.Addins和OSGi.NET框架提供了良好的隔离,插件具备独立的结构和类型空间,但是SharpDevelop、MEF和SCSF的隔离性则相对来讲弱很多。

 

可移植性指的是可以将传统的代码快速移植成插件,在插件框架中使用,目前MEF、MAF和SCSF对移植性支持很差,需要更改很多代码才可以在插件 框架中应用,这不利于我们的使用。在SharpDevelop、Mono.Addins和OSGi.NET框架中,他们虽然具备很强可移植性,不过,只有 OSGi.NET框架提供了迁移工具,可以在5分钟内将传统程序集移植成插件。

 

易用性是是否应用插件框架的重要指标,OSGi.NET框架具备最强的易用性,拥有完善的编程工具,并且提供了很好且很友好的API,可以在5分钟 内构建出一个插件应用。相比较,MEF的易用性也不差,它的编程模型也很简单。次之,Mono.Addins和SharpDevelop的易用性为一般, 但MAF和SCSF的易用性最差,应用起来非常复杂。

 

辅助支持是易用性的重要支撑。在辅助支持方面,OSGi.NET框架构建了插件迁移工具、插件发布工具、远程管理控制台、项目模板、调试日志、插件 仓库、示例和文档,提供了非常完善的辅助支撑,相比较,SCSF只提供项目开发模板,Mono.Addins只提供插件打包工具,而其它框架则没有任何辅 助支持。

 

这些框架对.NET框架版本的支持不完全一致,MAF对.NET框架版本没有要求,OSGi.NET、MAF、SharpDevelop、SCSF、Mono.Addins要求.NET框架版本为2.0以上,而MEF则要求.NET框架版本为4.0以上。

 

根据上述比较可以发现,OSGi.NET框架目前是最简单、最强大、最通用且最易移植的框架,并构建了符合现代复杂大规模软件的开发方法——基于软件生产线的组装式开发。其独特优势有:

(1)唯一支持Web开发,并同时支持WPF、WinForm、ASP.NETASP.NET MVC等任意.NET应用;

(2)唯一支持线程安全,这也是支持Web开发的前提;

(3)唯一支持国际规范;

(4)唯一不使用跨域支持多版本的框架;

(5)唯一具备完善的辅助支持,并构建了完善的软件生产线平台。

 

对插件框架的介绍,我简单到这,这是一个免费的框架,可以从http://www.iopenworks.com来下载,有问题可以加入到我们的插件交流群:121369588。

[转载]Web API 强势入门指南 - 微软互联网开发支持 - 博客园

mikel阅读(954)

[转载]Web API 强势入门指南 – 微软互联网开发支持 – 博客园.

Web API是一个比较宽泛的概念。这里我们提到Web API特指ASP.NET Web API。

这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例展示如何通过Web API构建http服务,同时也展示了Visual Studio构建.net项目的各种强大。

目录

什么是 Web API

官方定义如下,强调两个关键点,即可以对接各种客户端(浏览器,移动设备),构建http服务的框架。

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

Web API在ASP.NET完整框架中地位如下图,与SignalR一起同为构建Service的框架。Web API负责构建http常规服务,而SingalR主要负责的是构建实时服务,例如股票,聊天室,在线游戏等实时性要求比较高的服务。

Picture20

 

为什么要用 Web API

Web API最重要的是可以构建面向各种客户端的服务。另外与WCF REST Service不同在于,Web API利用Http协议的各个方面来表达服务(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。

Picture2

 

当你遇到以下这些情况的时候,就可以考虑使用Web API了。

  • 需要Web Service但是不需要SOAP
  • 需要在已有的WCF服务基础上建立non-soap-based http服务
  • 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置
  • 发布的服务可能会被带宽受限的设备访问
  • 希望使用开源框架,关键时候可以自己调试或者自定义一下框架

功能简介

Web API的主要功能

1. 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作

通过不同的http动作表达不同的含义,这样就不需要暴露多个API来支持这些基本操作。

2. 请求的回复通过Http Status Code表达不同含义,并且客户端可以通过Accept header来与服务器协商格式,例如你希望服务器返回JSON格式还是XML格式。

3. 请求的回复格式支持 JSON,XML,并且可以扩展添加其他格式。

4. 原生支持OData

5. 支持Self-host或者IIS host。

6. 支持大多数MVC功能,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection。

Web API vs MVC

你可能会觉得Web API 与MVC很类似,他们有哪些不同之处呢?先上图,这就是他们最大的不同之处。

Picture1

详细点说他们的区别,

  • MVC主要用来构建网站,既关心数据也关心页面展示,而Web API只关注数据
  • Web API支持格式协商,客户端可以通过Accept header通知服务器期望的格式
  • Web API支持Self Host,MVC目前不支持
  • Web API通过不同的http verb表达不同的动作(CRUD),MVC则通过Action名字表达动作
  • Web API内建于ASP.NET System.Web.Http命名空间下,MVC位于System.Web.Mvc命名空间下,因此model binding/filter/routing等功能有所不同
  • 最后,Web API非常适合构建移动客户端服务

Web API vs WCF

发布服务在Web API和WCF之间该如何取舍呢?这里提供些简单地判断规则,

  • 如果服务需要支持One Way Messaging/Message Queue/Duplex Communication,选择WCF
  • 如果服务需要在TCP/Named Pipes/UDP (wcf 4.5),选择WCF
  • 如果服务需要在http协议上,并且希望利用http协议的各种功能,选择Web API
  • 如果服务需要被各种客户端(特别是移动客户端)调用,选择Web API

Web API 实战 (Web API + MongoDB + knockoutjs)

ASP.NET网站上有很多简单的Web API实例,看看贴图和实例代码你就明白怎么用了。这里我们通过一个稍微复杂一点的实例来展示下Web API的功能。

涉及技术

在我们的实例里面用到了:

服务URI Pattern

Action Http verb URI
Get contact list GET /api/contacts
Get filtered contacts GET /api/contacts?$top=2
Get contact by ID GET /api/contacts/id
Create new contact POST /api/contacts
Update a contact PUT /api/contacts/id
Delete a contact DELETE /api/contacts/id

准备工作

1. 下载并安装Mongo DB,步骤看这里

2. Mongo DB C# driver下载可以在nuget搜索mongocsharpdriver。

3. 如果想本地察看数据库中内容,下载MongoVUE

4. Knockoutjs下载可以在nuget搜索knockoutjs。

代码实现

1. 创建项目

创建MVC4 Web Application

1

在Project Template中选择Web API

2

然后项目就创建成了,Controllers里面有一个ValuesController,是自动生成的一个最简单的Web API Controller。

正如我们前面所说,里面引用的是System.Web.Http命名空间。

3

2. 创建model

在model里面添加Contact类

4

代码如下,其中BsonId需要mongocsharpdriver。

1
2
3
4
5
6
7
8
9
public class Contact
    {
        [BsonId]
        public string Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
        public DateTime LastModified { get; set; }
    }

我们需要添加mongosharpdriver。

7

8

另外我们需要在Model中添加Repository,Controller通过该类来访问Mongo DB。

1
2
3
4
5
6
7
public interface IContactRepository {
        IEnumerable GetAllContacts();
        Contact GetContact(string id);
        Contact AddContact(Contact item);
        bool RemoveContact(string id);
        bool UpdateContact(string id, Contact item);  
    }

ContactRepository的完整实现如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class ContactRepository : IContactRepository
    {
        MongoServer _server = null;
        MongoDatabase _database = null;
        MongoCollection _contacts = null;
        public ContactRepository(string connection)
        {
            if (string.IsNullOrWhiteSpace(connection))
            {
                connection = "mongodb://localhost:27017";
            }
            _server = new MongoClient(connection).GetServer();
            _database = _server.GetDatabase("Contacts");
            _contacts = _database.GetCollection("contacts");
            // Reset database and add some default entries
            _contacts.RemoveAll();
            for (int index = 1; index < 5; index++)
            {
                Contact contact1 = new Contact
                {
                    Email = string.Format("test{0}@example.com", index),
                    Name = string.Format("test{0}", index),
                    Phone = string.Format("{0}{0}{0} {0}{0}{0} {0}{0}{0}{0}", index)
                };
                AddContact(contact1);
            }
        }
        public IEnumerable GetAllContacts()
        {
            return _contacts.FindAll();
        }
        public Contact GetContact(string id)
        {
            IMongoQuery query = Query.EQ("_id", id);
            return _contacts.Find(query).FirstOrDefault();
        }
        public Contact AddContact(Contact item)
        {
            item.Id = ObjectId.GenerateNewId().ToString();
            item.LastModified = DateTime.UtcNow;
            _contacts.Insert(item);
            return item;
        }
        public bool RemoveContact(string id)
        {
            IMongoQuery query = Query.EQ("_id", id);
            WriteConcernResult result = _contacts.Remove(query);
            return result.DocumentsAffected == 1;
        }
        public bool UpdateContact(string id, Contact item)
        {
            IMongoQuery query = Query.EQ("_id", id);
            item.LastModified = DateTime.UtcNow;
            IMongoUpdate update = Update
                .Set("Email", item.Email)
                .Set("LastModified", DateTime.UtcNow)
                .Set("Name", item.Name)
                .Set("Phone", item.Phone);
            WriteConcernResult result = _contacts.Update(query, update);
            return result.UpdatedExisting;
        }
    }

3. 添加Controller

右键Controllers目录选择添加Controller

5

选择Empty API controller,将Controller命名为ContactsController

6

添加如下代码,可以看到Controller中的API方法名就是以http verb命名的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ContactsController : ApiController
    {
        private static readonly IContactRepository _contacts = new ContactRepository(string.Empty);
        public IQueryable Get()
        {
            return _contacts.GetAllContacts().AsQueryable();
        }
        public Contact Get(string id)
        {
            Contact contact = _contacts.GetContact(id);
            if (contact == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return contact;
        }
        public Contact Post(Contact value)
        {
            Contact contact = _contacts.AddContact(value);
            return contact;
        }
        public void Put(string id, Contact value)
        {
            if (!_contacts.UpdateContact(id, value))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }
        public void Delete(string id)
        {
            if (!_contacts.RemoveContact(id))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }
    }

4. 添加View

首先添加Knockoutjs库,

9

Knockoutjs通过MVVM模式来实现动态html绑定数据,如下图,其中View-Model是客户端的JavaScript object保存的model数据。

webapi_ef16

先打开HomeController,里面添加一个新的Action代码如下,因为我们要在MVC中对于ContactsController添加对应的View。

1
2
3
4
5
6
7
public ActionResult Admin()
        {
            string apiUri = Url.HttpRouteUrl("DefaultApi", new { controller = "contacts", });
            ViewBag.ApiUrl = new Uri(Request.Url, apiUri).AbsoluteUri.ToString();
            return View();
        }

然后右键Admin方法,选择添加View

10

选择Create strongly-typed view,在model class中选择Contact类。

11

添加View的完整代码,注意view中我们通过js去访问WebAPI,以及通过动态绑定将数据呈现在网页上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@model WebAPIDemo.Models.Contact
@{
    ViewBag.Title = "Admin";
}
@section Scripts {
  @Scripts.Render("~/bundles/JQueryval")
  <script type="text/JavaScript" src="@Url.Content("~/Scripts/knockout-2.3.0.js")"></script>
  <script type="text/JavaScript">
      function ProductsViewModel() {
          var self = this;
          self.products = ko.observableArray();
          var baseUri = '@ViewBag.ApiUrl';
          self.create = function (formElement) {
              // If valid, post the serialized form data to the web api
              $(formElement).validate();
              if ($(formElement).valid()) {
                  $.post(baseUri, $(formElement).serialize(), null, "json")
                      .done(function (o) { self.products.push(o); });
              }
          }
          self.update = function (product) {
              $.ajax({ type: "PUT", url: baseUri + '/' + product.Id, data: product });
          }
          self.remove = function (product) {
              // First remove from the server, then from the UI
              $.ajax({ type: "DELETE", url: baseUri + '/' + product.Id })
                  .done(function () { self.products.remove(product); });
          }
          $.getJSON(baseUri, self.products);
      }
      $(document).ready(function () {
          ko.applyBindings(new ProductsViewModel());
      })
  </script>
}
<h2>Admin</h2>
<div class="content">
    <div class="float-left">
    <ul id="update-products" data-bind="foreach: products">
        <li>
            <div>
                <div class="item">ID</div> <span data-bind="text: $data.Id"></span>
            </div>
            <div>
                <div class="item">Name</div>
                <input type="text" data-bind="value: $data.Name"/>
            </div>
            <div>
                <div class="item">Phone</div>
                <input type="text" data-bind="value: $data.Phone"/>
            </div>
            <div>
                <div class="item">Email</div>
                <input type="text" data-bind="value: $data.Email"/>
            </div>
            <div>
                <div class="item">Last Modified</div> <span data-bind="text: $data.LastModified"></span>
            </div>
            <div>
                <input type="button" value="Update" data-bind="click: $root.update"/>
                <input type="button" value="Delete Item" data-bind="click: $root.remove"/>
            </div>
        </li>
    </ul>
    </div>
    <div class="float-right">
    <h2>Add New Product</h2>
    <form id="addProduct" data-bind="submit: create">
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>Contact</legend>
            @Html.EditorForModel()
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>
    </form>
    </div>
</div>

接下来在_layout.cshtml中添加一个admin页面的链接如下

1
2
3
4
5
6
<ul id="menu">
    <li>@Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>
    <li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>
    <li>@Html.ActionLink("Admin", "Admin", "Home")</li>
</ul>

5. 测试与调试

大功告成,直接运行下我们的作品,我们的admin链接也显示在右上角,

12

Admin页面的样子,Contact list是动态加载进来的,可以通过这个页面做添加,修改,删除的操作。

13

通过IE network capture来查看请求内容,

重新加载页面,可以看到回复的格式为JSON,

14

JSON内容就是我们mock的一些数据。

image

接下来我们修改,删除,又添加了一条记录,可以看到使用了不同的http method。

image

通过前面安装的mongovue来查看下DB种的数据,先添加的user也在其中,令我感到欣慰。。。

image

其实还有两个有趣的实例,不过文章一写就长了,不好意思耽误大家时间,只好先放放,以后再写。

[转载]可在广域网部署运行的QQ高仿版 -- GG叽叽V1.8(源码) - GG叽叽 - 博客园

mikel阅读(964)

[转载]可在广域网部署运行的QQ高仿版 — GG叽叽V1.8(源码) – GG叽叽 – 博客园.

距离的GG 1.0发布已经三周了,这三周内,我利用业余时间为GG增加了视频聊天的功能。个人觉得进展有些缓慢,主要是因为大多数时间都花在了UI上。由于本人不会PS,所以图片素材都是从网上一个一个搜下来的,这个过程确实很烦人,而且最终有些素材还不是很满意。

一.GG V1.8 新增功能展现

(1)发送视频会话请求,对方可以接受或拒绝对话。

(2)在视频会话的过程中,可以控制自己麦克风的输出、摄像头的输出、以及扬声器是否播放对方的声音。

(3)在视频会话的过程中,可以关闭/显示自己的小窗口。

(4)在视频会话的过程中,任意一方掉线,将结束视频会话。

废话不多说,还是先上图。邀请对方进行视频会话(图一):

被邀方(图二):

视频会话界面(图三):

(图三中左下侧的第二和第三个按钮,用于控制麦克风输出和扬声器的播放的,每个按钮有两种状态,所以共4个图标素材,个人觉得都不太好,希望能替换掉,如果能提供的朋友,请发到我邮箱,我会在下个版本中将其更新。)

 

二.实现思路

虽然提供了源代码,但是,我还是想将主要的思路列一下(包括上一版本主要功能的实现思路,上篇文章漏掉了,这里一起补上),这样,大家理解起源码来,会节省更多的时间。

(1)GG早期版本,都将假设所有在线的用户都是好友。后面的高级版本将会提供好友管理的功能。

(2)用户登录帐号和QQ一样,必须为数字。而且,GG内部是根据用户帐号的数字来自动设定其昵称和头像的。

(3)GG服务端中集成了ESFramework通信服务器和OMCS语音视频服务器,在GG客户端的配置文件中可以配置服务器的IP和端口。

(4)客户端还未实现通过UI来进行麦克风和摄像头的测试功能(后续高级版本将会提供),麦克风、摄像头以及扬声器的选择可在配置文件中指定。

 

三.GG V1.8 源码下载

GG V1.8 源码

注意:如果要将GG部署到广域网,则可以在服务端的配置文件中设置监听的端口(Port以及OmcsPort);而在客户端的配置文件中,则可以指定服务器的ServerIP、ServerPort以及OmcsServerIP、OmcsServerPort。

 

我会努力争取2~3个星期发布一个新版本,使GG慢慢成熟起来。

大家有什么问题和建议,可以留言,也可以发送email到我邮箱:ggim2013@163.com。

如果你觉得还不错,请粉我,顺便再顶一下啊,呵呵

[转载]SQL Server怎样玩转千万级别的数据 - Sam Xiao - 博客园

mikel阅读(880)

[转载]怎样玩转千万级别的数据 – Sam Xiao – 博客园.

大数据处理是一个头疼的问题,特别当达不到专业DBA的技术水准时,对一些数据库方面的问题感到无赖。所以还是有必要了解一些数据库方面的技 巧,当然,每个人都有自己的数据库方面的技巧,只是八仙过海,所用的武功不同而已。我把我最常用的几种方式总结来与大家分享,大家还有更多的数据库设计和 优化的技巧,尽量的追加到评论中,有时一篇完整的博客评论比主题更为精彩。

方法1:采用表分区技术。

第一次听说表分区,是以前的一个oracle培训。oracle既然有表分区,就想到msSQL是否有表的分区,当时我回家就google了一把,资料还是有的,在这我儿只是再作一次推广,让更多的人了解和运用这些技术。

表分区,就是将一个数据量比较大的表,用某种方法把数据从物理上分成若干个小表来存储,从逻辑来看还是一个大表。首先来个结构图:

上图虽然不能很清晰的表达表分区的执行过程,但是可以看出表分区要用到那些对象,比如数据文件,文件组,分区方案,分区函数等。

我们以一个用户表(TestUser)为例,假设这个表准备用来存储中国部分公民的数据,每条数据记录着每个人所属的省份(Area),以及每个人的姓名(UserName),如下图所示。当数据量达到1千万的时候,查询就比较慢了,这时候的数据优化就迫在眉睫。

在优化之前,根据数据的结构,读写操作等,肯定会提出若干个解决方案。在这儿就以分区表的方案来优化数据库的查询,这儿以区域来分别存储数据,比如 广东的公民存放在AreaFile01.MDF文件中,湖南的公民存放在AreaFile02.MDF的文件中,四川的公民存放在 AreaFile03.MDF的文件中,以此类推其它省份,为了实现这个功能我们就得做分区方案。在做分区方案时,首先要搞清楚分区方案要涉及到的四个对 象:文件组,文件,分区函数,分区方案。

a:文件组,用来组织数据文件(.MDF)的一个虚拟名称,一个文件组可以添加多个数据文件(.MDF)。打开SQL管理器,找到具体的数据库,然后右键【属性】,进入到【文件组】选项卡,添加Area01,Area02,Area03,Area04四个文件组。如图:

b:然后选择中【文件】选项卡,添加 AreaFile01,AreaFile02,AreaFile03,AreaFile04,AreaFile05,AreaFile06六个数据文件 (.MDF),然后指定每个文件属于那个文件组(一个文件组可以存储多个数据文件),以及这个文件的物理路径。在这儿大家已经看明白了,这些数据文件,就 是物理上来分割一个数据表的数据的。也就是说一个表的数据有可能存储在AreaFile01中,也有可能存储在AreaFile02中,只要用某种方法来 指定他们的存储规则就行了。

c:分区函数,就是指定数据的存储规则。就是告诉SQL,把新增的数据如何分区。创建一个分区函数,可以用下边的SQL语句来实现。

CREATE PARTITION FUNCTION partitionFunArea (nvarchar(50))
AS RANGE Left  FOR VALUES ('广东','湖南','四川')

d:辛苦的创建了文件,又为其指定文件组,还建一个分区函数,目的只有一个,就是为了创建一个分区方案。分区方案可以用以下代码来创建。

复制代码
CREATE PARTITION SCHEME partitionSchemeArea
AS PARTITION partitionFunArea
TO (
    Area01,
    Area02,
    Area03,
    Area04)
复制代码

经过紧张的四步操作,一个分区方案就呈现在我们的眼前了。接下来的事,就是我们要怎样来消费这个分区方案。
首先我们创建一人普通的表,然后给这个表指定一个分区方案。如下代码。

CREATE TABLE TestUser(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Area] nvarchar(50),
    [UserName] nvarchar(50)
) ON partitionSchemeArea([Area])

为了能看到效果,再插入一些数据。

复制代码
INSERT TestUser ([Area],[UserName]) Values('四川','肖一');
INSERT TestUser ([Area],[UserName]) Values('四川','肖二');
INSERT TestUser ([Area],[UserName]) Values('四川','肖三');
INSERT TestUser ([Area],[UserName]) Values('四川','肖四');

INSERT TestUser ([Area],[UserName]) Values('广东','张一');
INSERT TestUser ([Area],[UserName]) Values('广东','张二');
INSERT TestUser ([Area],[UserName]) Values('广东','张三');

INSERT TestUser ([Area],[UserName]) Values('湖南','杨一');
INSERT TestUser ([Area],[UserName]) Values('湖南','杨二');
复制代码

查询所有的数据,可以用select * from TestUser; 按分区查询:就用如下方法:

select $PARTITION.partitionFunArea([Area]) as 分区编号,count(id) as 记录数 
from TestUser group by $PARTITION.partitionFunArea([Area])
select * from TestUser where $PARTITION.partitionFunArea([Area])=1
select * from TestUser where $PARTITION.partitionFunArea([Area])=2
select * from TestUser where $PARTITION.partitionFunArea([Area])=3
select * from TestUser where $PARTITION.partitionFunArea([Area])=4

效果图:

你们看我一个简单的表的分区是不是就已经完成了。呵呵,当然在实际应用中,仅仅掌握这点是不够的,比如在原分区方案上添加一个分区删除一个分区

 

方法2:用xml类型代替主从表设计,从而达到提高查询性能。

优化和提高数据库的性能,是从一个良好的数据库设计开始的。以一个会议预订系统为例,一个预订会议系统包括了会议时间,会议地点,主持人,参与 人,知会人,记录者等相关信息。在的TDD,DDD模型主导的时代,在这儿为了更好的想表达我要阐述的问题,还是以表驱动模型来进行开发。

用户需求:

a:一个会议可能有多个主持人,虽然这种情况比较少,但是也有可能有。

b:一个会议有多个参与人,这个不难理解。

c:一个会议有可能要让某人知晓,这人可以参与或不参与会议,一般为高层。

d:一个会议有可能有零个或者多个记录者。

e:一个会议需要远程视频,投影仪,电脑,麦克风等会议设备中的某些设备。

f:会议预订成功,或者会议时间,会议地点等重要信息修改后,邮件通知与会人员。

常规数据库设计:

a:建一个Meeting的主表,用于存放会议名称,会议地点,会议时间等的相关信息。

b:再建一个MeetingUser的表存储主持人,参与人,知会人,记录者。

c:同样,会议所需要的设备用MeetingDevice表来存储相关的信息。如图:

这样的表结构,是比较常规的设计方法,但是在实际应用中,你会发现一些待改进的问题。比如:

a:在提取一个会议的相关信息时,会连接多个表进行查询。这种查询在很大的程序上影响了数据库性能。

b:在做修改操作时也够呛的,先修改主表的相关信息,再把主表关联的子表信息全部删除重新插入一次,这样的操作是否够吐血了。当然有人精益求精,会比较修改前和修改后的数据,再用增加,删除,修改的手段达到子表数据的更新。这样的操作在有些ORM操作中已经实现了,但当自己code代码来实现的时候,特别是在多次code的时候,感觉总是那么烦心。

吐槽了这么多,是否有更好的解决方案呢?当然,在SQL里,我们可以XML数据类型来消除主从表的设计。如图:

上面的表结构设计,是不是有一个小清新的感觉呢?很明显,可以把第一种表的设计缺陷给消除了。一个会议的相关信息都存储在了一个表的一条记录中,这样的数据看起来是不是更直观呢?

a:获取一个预订会议的详细信息,我不需要进行多个表的连接查询,我要做的是只需用C#的Linq.Xml来解析查询出来的XML字符串即可。

b:修改操作时,我只需要重新组合XML数据,一个Update就更新了与会议相关的信息,操作是不是简单多了。

表面上看这种设计已经完美了,但是用户的需求是无止境的,有一天,你收到了一个需求,查询某个用户参与过的所有会议(就是只要主持人,参与人, 或者记录者中包括了这个用户,就把这些记录都给查询出来),Oh!My God  这种表结构设计应该怎么解决这个问题呢?其实可以用XQuery解决这个问题,还没接触过XQuery的那得赶快充一下电了。XQuery中最常用的有 exist(),value()这些函数,这儿就不详细的介绍了,网上搜索一下有很多相关资料,如果有必要,我会把以前项目中用的XQuery技巧与大家 分享。

最后,如果博客对你有帮助,我将感觉到荣幸之至。别忘记了右下角的【推荐】,谢谢!

[转载]ASP.NET MVC页面UI之联动下拉选择控件(省、市、县联动选择) - kwstu - 博客园

mikel阅读(893)

[转载]ASP.NET MVC页面UI之联动下拉选择控件(省、市、县联动选择) – kwstu – 博客园.

地区选择操作在WEB应用中比较常见的操作,本文在.net mvc3下实现了省市县三级联动选择功能。

本文博客出处:http://www.kwstu.com/ArticleView/admin_201392171455268 转载请注明!

一、效果图:

二、页面代码

复制代码
<script src="../../Content/Js/selectlist.js"></script>

<script src="../../Content/Js/systools.js"></script>

                    @Html.DropDownListFor(model => model.PROVINCEID, Kzrcw2013.Models.ProvinceViewModel.GetProvinceSelectList())

                    @Html.DropDownListFor(model => model.CITYID, Kzrcw2013.Models.CityViewModel.GetCitySelectList("15"))

                    @Html.DropDownListFor(model => model.DISTRICTID, Kzrcw2013.Models.DistrictViewModel.GetDistrictSelectList("142"))
复制代码

 

以上参数15为数据库中省份ID,主要作用给省份选择框一个默认省份,142参数同意。

 

三、JS代码

1、JS调用代码

$("#PROVINCEID").change(function () { GetCity($(this).val(), "CITYID", "DISTRICTID", "/SysTools/GetCityList"); });
$("#CITYID").change(function () { GetArea($(this).val(), "/SysTools/GetDistrictList", "DISTRICTID"); });

联动操作触发事件。

2、JS执行代码

 

复制代码
//省市县联动下拉列表选择共用方法
 function GetCity(message, tmp6, tmp7, tmp8) {
     city = tmp6;
     area = tmp7;
     url4 = tmp8;
     $("#" + city).html("");   //清空市区SELECT控件
     $("#" + area).html("");
     $("#" + city).append("<option value='0' selected='selected'></option>");
     $("#" + area).append("<option value='0' selected='selected'></option>");
     //设置异步传输参数
     var option = {
         url: url4,
         type: 'Get',
         chche: false,
         dataType: 'json',
         data: { Message: message }, //发送服务器数据
         success: function (data) {  //成功事件
             $("#" + city).empty();
             $.each(data, function (i, item) {
                 $("<option></option>")
                         .val(data[i].Value)
                         .text(data[i].Text)
                         .appendTo($("#" + city));
             });
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) { //发送失败事件
             alert(textStatus);
         }
     };
     //进行异步传输
     $.ajax(option);
 }
 function GetArea(message, tmp9, tmp7) {
     area = tmp7;
     url5 = tmp9;
     $("#" + area).html(""); //清空市区SELECT控件
     $("#" + area).append("<option value='0' selected='selected'></option>");
     //设置异步传输参数
     var option = {
         url: url5,
         type: 'Get',
         chche: false,
         dataType: 'json',
         data: { Message: message }, //发送服务器数据
         success: function (data) {  //成功事件
             $("#" + area).empty();
             $.each(data, function (i, item) {
                 $("<option></option>")
                         .val(data[i].Value)
                         .text(data[i].Text)
                         .appendTo($("#" + area));
             });
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) { //发送失败事件
             alert(textStatus);
         }
     };
     //进行异步传输
     $.ajax(option);
}
复制代码

 

 

 

四、后台代码

页面初始化后台代码:

复制代码
/***********获取省份列表****************/
    /** 使用方法:@Html.DropDownListFor(model => model.SOURCE, KwstuBlogs.Models.TemplateViewModel.GetSelectList())*/
    public static class ProvinceViewModel
    {
        public static IEnumerable<SelectListItem> GetProvinceSelectList()
        {
            KzrcwDbContent db = new KzrcwDbContent();
            var selectList = db.STANDARD_PROVINCE.Select(a => new SelectListItem
            {
                Text = a.NAME,
                Value = a.ID
            });
            return selectList;
        }
    }
    /***********获取城市模版列表****************/
    /** 使用方法:@Html.DropDownListFor(model => model.SOURCE, KwstuBlogs.Models.TemplateViewModel.GetSelectList())*/
    public static class CityViewModel
    {
        public static IEnumerable<SelectListItem> GetCitySelectList(string sort)
        {
            KzrcwDbContent db = new KzrcwDbContent();
            var selectList = db.STANDARD_CITY.Where(a => a.PROVINCE_ID == sort).Select(a => new SelectListItem
            {
                Text = a.CITY_NAME,
                Value = a.ID
            });
            return selectList;
        }
    }

    /***********获取县区模版列表****************/
    /** 使用方法:@Html.DropDownListFor(model => model.SOURCE, KwstuBlogs.Models.TemplateViewModel.GetSelectList())*/
    public static class DistrictViewModel
    {
        public static IEnumerable<SelectListItem> GetDistrictSelectList(string sort)
        {
            KzrcwDbContent db = new KzrcwDbContent();
            var selectList = db.STANDARD_DISTRICT.Where(a => a.CITY_ID == sort).Select(a => new SelectListItem
            {
                Text = a.DISTRICT_NAME,
                Value = a.ID
            });
            return selectList;
        }
    }
复制代码

联动选择后台代码

复制代码
// 获取某[省份]的所有[城市]数据
        public ActionResult GetCityList(string message)
        {
            List<STANDARD_CITY> list = db.Database.SqlQuery<STANDARD_CITY>("Select ID,CITY_NAME,PROVINCE_ID,DISTRICT_NUM from TS.STANDARD_CITY where PROVINCE_ID='" + message + "'").ToList();
            SelectList pList = new SelectList(list, "ID", "CITY_NAME");
            return Json(pList, JsonRequestBehavior.AllowGet);
        }
        // 获取某[城市]的所有[县区]数据
        public ActionResult GetDistrictList(string message)
        {
            List<STANDARD_DISTRICT> list = db.Database.SqlQuery<STANDARD_DISTRICT>("Select ID,DISTRICT_NAME,CITY_ID from TS.STANDARD_DISTRICT where CITY_ID='" + message + "'").ToList();
            SelectList pList = new SelectList(list, "ID", "DISTRICT_NAME");
            return Json(pList, JsonRequestBehavior.AllowGet);
        }
复制代码