[转载]SQL Server里面Deadlock 似乎无可避免

mikel阅读(1101)

[转载]SQL Server里面Deadlock 似乎无可避免 – Procez One 基于.Net 的工作流 – 博客园.

SQL Server的Online Book里面描述Deadlock策略里面发现有下面的一种情况:

Worker threads. A queued task waiting for an available worker thread can cause deadlock. If the queued task owns resources that are blocking all worker threads, a deadlock will result. For example, session S1 starts a transaction and acquires a shared (S) lock on row r1 and then goes to sleep. Active sessions running on all available worker threads are trying to acquire exclusive (X) locks on row r1. Because session S1 cannot acquire a worker thread, it cannot commit the transaction and release the lock on row r1. This results in a deadlock.

  • 工作线程。 排队等待可用工作线程的任务可能导致死锁。如果排队等待的任务拥有阻塞所有工作线程的资源,则将导致死锁。例如,会话 S1 启动事务并获取行 r1 的共享锁(S 锁)后,进入睡眠状态。在所有可用工作线程上运行的活动会话正尝试获取行 r1 的排他锁(X 锁)。因为会话 S1 无法获取工作线程,所以无法提交事务并释放行 r1 的锁。这将导致死锁。也就是说并不一定要是都是Update、Insert这种 会上排它锁操作的语句才会造成死锁。只要在不同的线程里面访问了同一个资源(某个数据行)的情况下,一个Select动作碰上了一个Update动作也会 产生死锁。你的SQL语句执行时间越长碰上这种情况的机会越大。看看下面这张图
  • image

    左边的进程完全就是一个普通的查询语句,对资源上的也只是一个共享锁(S).而右边的进程是上了一个Index的排它锁(IX).跟通常意义上的两边都上排它锁才可能产生死锁的情况完全不一样,下面的图是我们写程序可以避免的死锁情况

  • 怎么处理好SQL Server里面的死锁的话可以参照下面这篇文章:

    Reducing SQL Server Deadlocks

    里面提到了,要避免的Deadlock,把不同事务里面两个Update或者Insert资源的操作锁定资源顺序保持一致是最基本的,不过也就是能避免第二张图里面出现的情况。

    • 而要避免第一张图的情况好像是不太可能,除非你每个SQL 语句都加上With Nolock,第一张图的情况基本上只能够通过减少查询时间,减少事务处理的时间来减少而无可避免。

    [转载]如何在Google Map中处理大量标记(ASP.NET)

    mikel阅读(1248)

    [转载]如何在Google Map中处理大量标记(ASP.NET)(原创-翻译) – Happy Coding – 博客园.

    在你有一个合理的标记数量的时候,使Google Map标记是很平常的。但是一旦你有几百个、甚至更多地标的时候,性能迅速的开始降低。在本文章中,我会告诉你一些提高性能的方法。同时我会放一个测试页面去比较它们的效率。

    如果你是第一次使用Google Map的标记,我建议你先去了解一下在Google Map上使用标记的一些基本原理和操作。

    The Marker Manager-Keeps track of them

    您的第一选择可能是利用MarkerManager,因为它是一个由谷歌提供的实用工具库。首先要将标记添加到MarkerManager,而不是 使用GMap2.addOverlay()逐个的将每一个Marker添加到Map。MarkerManager会不断跟踪你所有的标记。通过定义几个不 同的zoom-levels,将可能会同时出现的Marker集合放在同一level上,避免在同一时间大量的Marker同时显示。

    MarkerManager 最初比直接加入到地图中有些慢,但是这样添加的好处是你可以更好的控制它们。

    使用addMarker(GMarker, minZoom, maxZoom?)将标记添加到MarkerManager,这个方法携带三个参数,第一个是你要添加的标记,后两个参数是可选的,但是界定了在什么level上这个标记是可见的。

    A simple example

    //Create a new map

    var map=new GMap2(document.getElementById(‘map’));

    map.setCenter(new GLatLng(59.5,14.0),6);

    //Create a new instance of the MarkerManager

    var mgr = new MarkerManager(map);

    // Create a new marker

    var marker = new GMarker(new GLatLng(59.0, 13.80));

    // Add marker to the MarkerManager

    mgr.addMarker(marker);

    明显的,没有谁会想MarkerManager添加单一的标记,但是如果你有几百个标记,那么可能就要这么做了。

    Bulk adding the markers

    使用MarkerManager更高效的办法是,首先将所有标记添加到一个数组中,然后用addMarkers(markerArray, minZoom, maxZoom?)将这个数组添加到MarkerManager。

    // Create a new instance of the MarkerManager

    var mgr = new MarkerManager(map);

    // Create marker array

    var markers = [];

    // Loop to create markers and adding them to the MarkerManager

    for(var i = 0; i < 50; i += 0.1) {

    var marker = new GMarker(new GLatLng(59.0 + i, 13.80 + i));

    markers.push(marker);

    }

    // Add the array to the MarkerManager

    mgr.addMarkers(markers);

    // Refresh the MarkerManager to make the markers appear on the map

    mgr.refresh();

    请注意,在将存有标记数组添加到MarkerManager之后,必须调用mgr.refresh()。在逐个添加Marker的时候是不需要的。

    附加方法

    removeMarker(marker)

    从MarkerManager中移除一个标记。

    clearMarkers()

    移除所有标记。

    getMarkerCount(zoom)

    返回在指定的zoom-level下的标记个数。

    MarkerManager是Google提供的一个实用工具库。从下面的链接你可以下载源代码以及说明文档和例子。Google Maps MarkerManager SVN

    http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/

    Marker Light – Markers on a diet(高亮标记-首都标记)

    Google的Pamela Fox 为减少复杂标记,从而提高效率的MarkerLight制作了一个简单的程序。这样做的代价是,真的仅仅是在地图上显示一个图片,但你不能与之交互。如果 你不需要与程序交互,那么这种办法真的是一种简单的提高性能的办法,这样做唯一的区别就是你创建的是一个MarkerLight,而不是一个 GMarker。

    作者Pamelas后来解释了为什么这种方法会提高效率:

    GMarker之所以耗时这么长是因为它实际上是由多个DOM元素构成-前景,阴影,打印版本,可点击区域等。

    如果你的目的只是显示,那么你可以选择象MarkerLight那样创建一个带有背景URL的DIV那样去创建一个GOverlay的扩展(或者背景颜色、甚至更好的)

    ——Pamela Fox

    以下是如何使用它:

    map.addOverlay(new MarkerLight(latlng, {image: “red_dot.png”}));

    red_dot.png这个图片是用于marker中的。这是最小最简单的一个。你可以尝试在Pamelas test page上用不同数量的标记测试效率。Download markerlight.js

    Using Marker Light in combination with MarkerManager-结合MarkerManager使用Marker Light

    用MarkerManager的集中式添加MarkerLight的好处很多,而且真的很简单,只是将二者结合起来。

    mgr.addMarker(new MarkerLight(latlng, {image: “red_dot.png”}));

    这样做的原因是你可以显示在不同缩放级别下不同数量的标记。这种方式可以确保不会有太多的标记在同一时间显示。

    Clusterer – Only show what you need

    另一种方法是使用ACME实验室群集Clusterer。这是一个第三方库,提供了更快捷的方式加入标记。它是在BSD许可证下发布,并免费提供。

    只需要做两件事情,效率会更快:

    1.只有当前可见的标记会被建立。

    2.如果有太多的标记需要显示,那么它们会组合在一起成为群集标记。

    这会让你的地图上即使有成千上万个标记依然能保持良好的性能。我的测试表明,这种方法的效率要显著快于使用MarkerManager的方式。

    以下是如何使用它:

    // Create a Clusterer object

    var clusterer = new Clusterer(map);

    // Create marker

    var marker = new GMarker(new GLatLng(57.8, 14.0));

    // Add marker to the map

    clusterer.AddMarker(marker, ‘text to infobox’);

    调用clusterer.RemoveMarker(marker)方法来从Map中移除标记。另外还有一些方法来改变标志的行为。

    1. 1. clusterer.SetIcon(GIcon)

    改变簇的图标

    1. 2. clusterer.SetMaxVisibleMarkers(n)

    设置标记最多可见的数量门限,默认值是150.

    1. 3. clusterer.SetMinMarkersPerCluster(n)

    为一个标记集合设置最少的标记数,默认值是5.

    1. 4. clusterer.SetMaxLinesPerInfoBox(n)

    设置信息框内文本的最大行数,默认值是10.

    Download Clusterer2.js

    ClusterMarker – Chunk ’em all up

    ClusterMarker是一个根据GNU通用公共许可证发布的免费JavaScript库,可以集中添加标记。这个库的独特行在于它会自动检测标记,彼此相交和集成为一个标记群集。

    下面的图片说明是如何工作的:

    Images by Martin Pearman

    该构造函数有两个参数,如下:

    var cluster = new ClusterMarker(map, options).

    Map是对一个有如下属性的对象引用的 map对象和选项的参考(map is a reference to the map object and options is an object literal that can have these properties:)

    1. 1. clusterMarkerIcon [GIcon]

    将默认的簇标记图标更改为你所选择的图标。

    1. 2. markers [array]

    所有你想传递给ClusterMarker的标记的数组

    除了这些属性,你也可以使用类中的其他所有属性,查看完整的文档说明列表documentation

    如下是如何使用最少的代码量,用ClusterMarker添加标记:

    var markerArray = [];

    // Insert code to fill the markerArray with markers…

    // Creating a new cluster by adding the map and the markerarray

    var cluster = new ClusterMarker(map, {markers: markerArray});

    // Refreshing to show the added markers

    cluster.refresh();

    这段代码将会在地图上插入标记,如果他们足够近的话,就用一个图标表示他们。欲了解更多关于如何细粒度操作的几种方法和属性,有关详细说明类库是如何工作的,在Clustermarker 项目页面上有很多优秀的文档。Clustermarker Project page.

    MarkerClusterer – The new kid in town

    这个使用工具库的所有都是最新的,在我最初写这篇文章的时候还没有。这个库,是吴小溪写的,谷歌地图的开放源码工具库的一部分,易于使用,具有优良的性能。

    像其他的类库一样,他们聚集在一起使其更容易有一个概观,减少了可见标记的数量。观看下面的图像,看看如何:

    Image by Xiaoxi Wu

    它的构造函数需要三个参数,第一个是地图的引用,第二个是一个GMarkers数组,第三个被选择的对象与文字。只有第一个参数是必须的。

    var markers = [];

    // Insert code to fill the markerArray with markers…

    // Creating a new cluster by adding the map and the array of markers

    var markerCluster = new MarkerClusterer(map, markers);

    查看完整的说明文档和它的功能documentation。更多信息去GoogleGeo开发博客读MarkerClusterer: A Solution to the Too Many Markers Problem

    Compare performance

    Compare the different techniques on the Test page

    我的灵感来自于Pamela Fox的对MarkerLight的测试页,我也做了一个我自己的测试页

    ,你也可以测试本文中所有不同做法的性能。

    结果:

    我在几个不同的浏览器运行了测试,每一个测试,我使用不同的技术添加500个标记,每次测试之间我刷新浏览器。所有的测试平台采用一个具有3.60 GHz的奔腾4处理器和超线程运行Windows XP的2 GB内存的PC。

    Load times in milliseconds (ms)

    在这个测试中,Clusterer2是所有技术中最快的一个。但是要知道这是不是真实的数据,因为我只能够测量从我提交它们到标记传递到地图的时 间,而不是知道在地图上看见标记的实际时间。当考虑到这方面,我觉得MarkerClusterer是最快的技术,紧随其后的是 ClusterMarker。

    当谈到浏览器,性能最高的是带有绿色标记的浏览器,性能最差的带有红色标记的浏览器。毫无疑义,IE性能很差。谷歌浏览器和Safari整体上表现出,他们的JavaScript引擎和性能一直很快。

    如果你有什么好的想法去改善这个测试,使之能够更好的衡量直到标记在地图上显示实际时间,请在评论中通过共享或我的主页联络我。Contact page.

    结论

    利用这篇文章中的技术,执行效果一般都很好,因为你没有一个令人难以置信的大量的标记。对于一些特殊的场合,这些技术并不能满足你的需求,你将不得不采取更加极端的措施,例如,创建一个大的覆盖层,覆盖所有的标记,但是这超出了本文的讲述范围。

    我希望这些技术能 帮到你,Happy coding!

    [转载]弹出窗口与切换页面的对比总结

    mikel阅读(1031)

    [转载]弹出窗口与切换页面的对比总结 – 站长网 admin5.com.

    分享较早前的一个小总结,关于在选择弹出窗口还是选择切换页面来承载信息时,可用用来判断的几个角度,合适初学者来读。

    1.弹出窗口与切换页面的区别

    1) 操作的连贯性 弹出窗口的速度会比打开一个页面的速度要快,响应更及时一些,点击后可以比较快的得到操作的反馈,而切换页面则经常会让用户等待一段时间。

    实例一:个人中心中,查看好友照片的操作:

    之前查看照片的操作页面会跳转到该好友的空间“相册”页面,而现在则更改进为弹出窗口,能够很快看到照片的放大图,关掉窗口后,又可以继续浏览其他好友的信息了。

    2)操作的延续性 弹出窗口可以保留原页面上的内容,不至于页面被刷新掉;切换页面很有可能刷新,在以下两种情况下是尤其要避免的:用户在原界面上有输入的内容,用户需要根据原有界面的内容,来选择或判断新界面的内容。在这两种情况下最好首选弹出窗口。

    实例一:Qzone写日志中插入图片:

    用户在写日志的时候插入一张照片,如果这时候插入照片的界面是切换一个页面,用户可能就会担心之前的输入是否还在,而弹出窗口则可以在保留原页面已编辑的文字,在此之上出现一个小型的界面,显示可以插入的照片。

    实例二,秀世界中我的储物箱,用户需要看到房间中的效果,来决定从“我的储物箱”中选择哪个物品放入房间里,用弹出窗口可以比较方便的解决这个问题:

    实例三:一个相反的例子,欢乐卡片的应用,制从“我的首页”页面当中,制作卡片的时候需要哪些卡面是在“卡片全攻略”页面来说明的,因此用户在 制作卡片的时候需要不断切换到“卡片全攻略”页面来查看需要合成的卡片,因此,在这样的情形下,采用弹出窗口的形式来表现卡片合成向导应该更合适一些。

    3) 操作的自然过度 弹出窗口不容易让人迷路,知道自己身处何处;而页面切换了以后用户有时候会产生一下子不知道在哪里的迷惑;弹出窗口页面的内容往往只和当前的操作有关,而 且是浮在原有页面上的,用户可以很自然的延续上一操作的结果而继续操作或是浏览下去,而切换的新页面中往往会包含新内容之外的信息: banner、导航、一些固定的栏目等等,用户不能立刻定位到自己想要关注的内容上,因此切换页面的设计应更加关注一致性和承前启后的关联性。

    实例一:网吧达人的首页页面1中点击“常去的网吧”,进入页面2,因为两个页面的结构有所不同,很容易让用户觉得不知身处何处。

    页面1:

    页面2:

    4) 承载的信息量 弹出窗口可承载的信息量有限,操作流程不宜过多。

    5) 内容的推广 弹出窗口没有独立的连接地址,在页面推广上有所限制。

    实例一:在做信用卡频道的活动列表时,最初的设计是点击活动信息的标题后,用弹出窗口来展示活动详情,然而因为活动页面需要在推广时有独立的地址便于用户能够从其他途径直接访问到活动详情页,因此改为采用切换页面的方式。

    2. 弹出窗口与页面内展开的区别?

    1)页面内展开能够即时的反馈用户的操作;容易给用户带来自然过度的体验;适合于对主要内容的扩充,更详细的、更近一步的内容,当用户没有看到这些内容并不会影响使用,而打开这些内容,用户可以看到更多、或是得到更丰富的功能。

    实例一:个人中心的展开操作,通过页面内展开的形式显示评论信息,它并不是该好友动态的主要内容,因此作为附加信息收在了“详情”当中。

    2)弹出窗口是用户点击按钮后,在页面上浮出一个层来显示进一步的内容,与页面内展开的形式不同的是,弹出层更适用于区域内的内容较为重要的时候使用,比如弹出层内包含一些操作、或重要提示。

    实例二:编辑导航的界面中更改选择,下面的预览界面中会直接看到效果。

    实例三:二次确认的提示应当采用弹出窗口:

    [转载]ASP.NET MVC: 使用 Filters 附加过滤敏感信息功能

    mikel阅读(1053)

    [转载]ASP.NET MVC: 使用 Filters 附加过滤敏感信息功能 – 鹤冲天 – 博客园.

    技术准备

    看本文,需要对以下技术有所了解:

    1. AOP
    2. ASP.NET MVC

    ASP.NET MVC Filters 简介

    ASP.NET Filters:ASP.NET MVC 中加在 Controller 或 Action 上的一种 Attribute。 通过它,MVC 网站在处理用户请求时,可以处理一些附加的操作,如:用户权限验证、系统日志、异常处理、缓存等。使用Filters可以使用简洁、强大的方式实现 AOP,Filter 中定义的行为可以在网站中多处简单方便的复用。

    ASP.NET MVC 中包含以下四种Filter:

    1. Authorization filter
    2. Action filter
    3. Result filter
    4. Exception filter

    本文只使用Action filter。

    Action filter:

    创建 Action filter 必须实现 IActionFilter 接口,它定义了两个方法:

    方法名 调用时机 可进行的操作
    OnActionExecuting Action方法执行之前 可以给 filterContext.Result 赋值,以阻止 Action 的执行
    可以通过 filterContext.ActionParameters 查看或修改 Action 的参数
    OnActionExecuted Action方法执行之后 可以通过 filterContext.Exception 获取 Action 执行时抛出的异常,并能把它标记为“已处理”:filterContext.ExceptionHandled = true。
    可以查看通过filterContext.Result查看 Action 的执行结果,但是不能修改。

    下面就使用 OnActionExecuting 可以在 Action 方法执行前 能查看并修改参数值的特性 来完成敏感信息的过滤功能。

    过滤敏感信息

    过滤敏感信息对一个网站来说非常重要,没有一个站长愿意看到自己网站因为一些敏感信息而被封。

    然而过滤敏感信息也不是一件容易事:

    1. 需要一个好的过滤算法:简单的字符替换容易误伤,大多算法要用到正则表达式,档次高的还会用的语义分析;
    2. 需要对很多地方过滤:网站大了,接收用户输入的地方很多,每个地方都要进行围堵。

    本仍不介绍过滤算法,只解决第二个问题。

    使用 Filters 附加过滤敏感信息功能

    创建SensitiveWordsFilterAttribute:

    新建一个类SensitiveWordsFilterAttribute,从ActionFilterAttribute继承,重写OnActionExecuting方法:

    
    
    
        public class SensitiveWordsFilterAttribute: ActionFilterAttribute     {         public override void OnActionExecuting(ActionExecutingContext filterContext)         {             var parameters = filterContext.ActionDescriptor.GetParameters();             foreach (var parameter in parameters)             {                 if (parameter.ParameterType == typeof(string))                 {                     //获取字符串参数原值                     var orginalValue = filterContext.ActionParameters[parameter.ParameterName] as string;                     //使用过滤算法处理字符串                     var filteredValue = SensitiveWordsFilter.Instance.Filter(orginalValue);                     //将处理后值赋给参数                     filterContext.ActionParameters[parameter.ParameterName] = filteredValue;                 }             }         }     }
    上面的例子中,我们通过 filterContext.ActionDescriptor.GetParameters() 方法能获取到所有参数的详细信息。

    通过 filterContext.ActionParameters 可以获取和设置参数的值。

    附加过滤功能:

    我们只需要将SensitiveWordsFilterAttribute标在 Controller 或 Action 上:

    
    
        [SensitiveWordsFilter]     public class ArticlesController : Controller     {         //...     }
    过滤好比抗洪,要全面围堵,遗漏任何一个地方,都将是灾难性的。如果懒的去一一查找要进行过滤的 Action,不如对所有的 Controller 都进行处理(有点"宁错杀,勿放过"的感觉),可采用以下方法之一:
    1. 每个 Controller 逐一添加SensitiveWordsFilterAttribute;
    2. 让所有 Controller 都从一个 Controller 基类继承,只需要给这个 Controller 基类加上SensitiveWordsFilterAttribute;
    3. 如果你开始尝鲜使用 ASP.NET MVC 3,那就更简单了,只需要在 Global.asax 文件中处理:
           protected void Application_Start() {      //...      RegisterGlobalFilters(GlobalFilters.Filters); } protected void RegisterGlobalFilters(GlobalFilterCollection filters) {     filters.Add(new SensitiveWordsFilterAttribute()); }

    补充说明

    实际情况要复杂的多,比如需要将过滤情况反馈给用户,提示哪儿出了问题,高度显示敏感词等。这些都可以通过 Filter 简单实现。

    参考

    《Pro ASP.NET MVC 2 Framework.pdf》

    Introducing ASP.NET MVC 3 (Preview 1)

    [转载]ASP.NET MVC 2生成动态表单的一种最简单的思路

    mikel阅读(851)

    [转载]ASP.NET MVC 2生成动态表单的一种最简单的思路 – 海纳百川 – 博客园.

    在BPM、OA等系统中,都会存在一个表单设计器。有些是通过操作gridview来完成一个表单的设计;有些是通过类似VS拖拽的方法完成一个表单的设计。很明显后面一种优越于前面一种。无论是哪种,最后都会产生一些XML之类的表单结构的数据。

    这篇文章将讲述,在表单设计器设计好表单之后,在ASP.NET MVC中如何将表单结构的xml转换成实际应用系统中的表单。看下面一个xml文件,我们假设它是由一个表单设计器设计出来的。

    <?xml version="1.0" encoding="utf-8" ?>
    <form name="form1">
        <field type="text" name ="firstname" class ="textbox" left="300"  top="200"></field>
        <field type="text" name ="lastname" class ="textbox" left="700"  top="200">祁林</field>
        <field type="text" name ="sex" class ="textbox" left="300"  top="240"></field>
        <field type="text" name ="age" class ="textbox" left="700"  top="240">24</field>
    </form>
    

    下面,我将把它转化成实际的ASP.NET mvc系统中的表单。首先,使用LinqtoXML将上面的XML文件转换成XElement,代码如下。在控制器中最好不要直接读取文件,这里为了简单直观起见,就直接在Controller中读取xml文件了。

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Index()
    {
        XElement xml = XElement.Load(Server.MapPath("~/App_Data/form1.xml"));
        ViewData["xml"] = xml;
        return View();
    }
       接着我们将在View中,将上面的XElement转换成真正的HTML表单。

    在表单设计器中很难的一块就是控件的定位。从我提供的XML中可以看到,它里面是存放了位置信息的。这使我们想到了div的绝对布局。这个方法在这种情况下非常的适合。

      下面,我定义两个字符串模板:
    string label = " <div  style=\"left: {0}px; position: absolute; top: {1}px\">{2}</div>";
    string input = "<input name=\"{0}\" type=\"{1}\"  class=\"{2}\"   style=\"left: {3}px; position: absolute; top: {4}px\" value=\"{5}\"  />";

    label用于显示文本信息,input用于显示表单上的value。下面通过循环产生html脚本。

    StringBuilder sb = new StringBuilder();
    sb.Append("    <div style=\"height:200px\"> ");
    foreach(XElement f in xml.Elements()) 
    {
      sb.Append(string.Format(label, int.Parse(f.Attribute("left").Value) - 60, f.Attribute("top").Value, f.Attribute("name").Value));
      sb.Append(string.Format(input, f.Attribute("name").Value, f.Attribute("type").Value, f.Attribute("class").Value, f.Attribute("left").Value, f.Attribute("top").Value, f.Value));
    }
    sb.Append("</div > ");

    最后通过Response.Write(sb.ToString())输出。

    整个View的代码如下:

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
    
    <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
        Home Page
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% XElement xml = (XElement)ViewData["xml"]; %> 
     <%Html.BeginForm(); %>
       <%
       string label = " <div  style=\"left: {0}px; position: absolute; top: {1}px\">{2}</div>";
       string input = "<input name=\"{0}\" type=\"{1}\"  class=\"{2}\"   style=\"left: {3}px; position: absolute; top: {4}px\" value=\"{5}\"  />";
       StringBuilder sb = new StringBuilder();
       sb.Append("    <div style=\"height:200px\"> ");
       foreach(XElement f in xml.Elements()) 
       {
         sb.Append(string.Format(label, int.Parse(f.Attribute("left").Value) - 60, f.Attribute("top").Value, f.Attribute("name").Value));
         sb.Append(string.Format(input, f.Attribute("name").Value, f.Attribute("type").Value, f.Attribute("class").Value, f.Attribute("left").Value, f.Attribute("top").Value, f.Value));
       }
         sb.Append("</div > ");
         Response.Write(sb.ToString());
        %> 
     <input type="submit" value="Submit!" />  
     <%Html.EndForm(); %> 
    </asp:Content>
    

    效果:

    1、显示XML表单:

    ggg

    2、提交表单:

    hhh

    总结:这个是一种通过表单设计器产生动态表单最简单的方式。也是非常通用的一种方式。在产生html的时候,你可以带上JQuery的验证的脚本。当然还有很多的扩展和完善。点击下载本文代码

    求助与讨论:开发表单设计器是我心里的一块石头,估计也是很多正在开发BPM、OA之类童鞋心中的一块石头。最 近一直在思考这个问题,从这篇文章可以看到,我我设计的表单设计器上的控件可以随意拖放的。在生成HTML的时候,使用div的绝对布局,打算朝这个方向 做,不知道可行否。下面是我想到的一些问题:

    1、采用什么技术或者模式开发表单设计器;2、如何最好的定位;3、在设计器上如何操作表格以及主从表;4、数据源的绑定;5、数据的验证;

    如果你开发过表单设计器、或者有这方面的研究和心得、或者有这方面的兴趣、欢迎在此一起讨论。

    作者:朱祁林
    出处:http://zhuqil.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    [转载]正解TDD(测试驱动开发)

    mikel阅读(1130)

    [转载]正解TDD – Teddy’s Knowledge Base – 博客园.

    本文的目的是以最精炼的语言,正解什么是TDD,为什么要TDD,和TDD的难点。

    什么是TDD?

    简单的说,TDD = 测试先行(TFD, Test First Development) + 重构(Refactoring) + 回归测试(Regression Test)。

    Image

    如 果要实现某个功能,TDD要求在初步定义完这个功能的外部接口之后,先根据这个功能的用例写测试代码(黑盒测试),测试代码检验的是这个功能的外部接口的 使用场景,而非具体的实现细节。然后,才是实现这个功能的这些外部接口,在实现的过程中,同时还会根据需要写一些单元测试,一般单元测试测试的不仅仅是外 部接口,还包括实现的内部细节(白盒测试)。关于单元测试的更多讨论,请参考:[UnitTesting|Unit Testing]。如果发现这些外部接口设计得有问题,则需要进行修改和重构。每次代码修改之后,或者增加新功能之前,都需要回归运行已有的所有测试(不 仅仅是测试先行的这些测试,还包括所有的单元测试)。

    测试先行保证了功能的所有使用场景的逻辑性和完备性;重构则在保证功能语义的前提 下,尽可能安全的改进设计和实现;回归测试则保证了任何修改不破坏任何已有的功能,这样开发人员就能放心大胆地专注于实现或改进眼前的功能。这样无疑既提 高了开发人员的信心和工作效率,而且时刻保证了功能的完整性和代码的正确性。

    如果对TDD详细的前世今生感兴趣,请参考:TDD on WikiPedia

    为什么TDD?

    TDD最大的好处是时刻确保了每次添加或修改任何代码的过程中,对任何已有的功能都是安全的。尤其是对于编译和回归测试的运行时间较短的情况下,有大牛甚 至建议每新增或修改超过10行可运行的代码,就运行一次回归测试。这样不仅仅能确保你写的每一行代码的正确性,而且,基本可以避免使用单步调试来发现问 题,因为,问题往往就应该在最新的这10多条代码上。

    TDD的另一个好处是,相对于几十页上百页的天知道和什么年代的代码同步的技术文档来说,TDD的测试代码因为描述的就是功能的实际使用场景,并且和代码一定是实时同步的,对开发人员来说,它其实是一种既准确,又易于理解,易于维护的“技术文档”。

    TDD的难点

    • 并非所有类型的代码都适合TDD,尤其是那些不能由机器简单的判断对错的情形,比如图形UI和数据库设计。
    • TDD需要让管理层意识到TDD的价值,为TDD预留额外的开发时间,并且强制每个开发人员按TDD的流程来写代码,需要自上而下的管理和开发流程的支持。

    英文资料

    [转载]ASP.NET中在不同的子域中共享Session

    mikel阅读(994)

    [转载]ASP.NET中在不同的子域中共享Session – Assion Yang – 博客园.

    今天遇到了这个问题,于是研究了一下。要解决这个问题,首先就要明白一些Session的机理。Session在服务器是以散列表形式存在的,我们都知道Session是会话级的,每个用户访问都会生成一个Session。那么服务器是怎么区分不同用户的Session?又是怎么将不同用户的Session与不同的用户绑定的呢?下面我们来研究一下,以下纯属我个人的理解,如有错误请指证。

    Session在服务器端是以散列表的形式存在的,区分每一个Session是通过SessionID来实现的,所以可以说这个SessionID是一个Key是一个全局唯一的值。我们可以通过ASP.NET来打印出SessionID,如下代码:

    protected void Page_Load(object sender, EventArgs e)
    {
    Response.Write(Session.SessionID.ToString());
    }

    这样我们就得到了这样的值:0julmoedn0kz3gyfnr1vksv0, 有点像是GUID,就算不是算法也都是类似的,主要就是为了保证全局唯一性。这样就达到了区分不同用户的Session的目的。接下来还有第二个问题,那 就是SessionID有了,但是它又是怎么和相应的访问者(用户)绑定的呢?比如说用户A访问维护了自己的SessionID,用户B访问也维护了自己 的SessionID。我们都知道web是基于http无链接的,他们又是怎么做到的呢?没错,答案就是在客户端存储了自己的SessionID。浏览器 存储SessionID有两种方式,一种就是利用Cookies;还有一种就是利用url参数(这种我们不常用,很不友好)。

    话题说到Cookies上来了,怎么的?没想到 Session和Cookies还有这样的关系吧?(很多人知道,别BS我)没错,当我们请求一个URL时候,服务器会生成一个全局的 SessionID,并且把这个值以Cookies的形式保存在客户端也就是浏览器(这里暂不讨论url方式)。这样当用户再去请求的时候,在http头 把这个SessionID的Cookie发到服务器端,服务器就去找这个SessionID,如果找到了。就证明这个用户的状态是存在的。

    知道了这个原理,我们的问题也就有眉头了,即然是用Cookies来保存SessionID,那么我们就可以在Cooikes上做手脚了。我们都知道Cooikes记录方式是以域(例如:http://www.local.com/)为区分的,这也是各种浏览器规定的。如果不这么做,安全性就会有问题。我们要做的就是让指定Cookies的父域方式,不指定具体指域,这样Cookies就可以跨子域了。Cookies可以像这样指定域:

    protected void Page_Load(object sender, EventArgs e)
    {
    Response.Cookies[
    MyCook].Domain = .local.com;
    }

    这样,我们所有的二级域全部是认这一个主域的,比如 a.local.com;b.local.com;user.local.com等等。有了这个认识,我想大家心里也有数了,该怎么怎么做,但是现在问题 是用来生成SessionID的方法是ASP.NET自动实现的,我们又怎么去干涉它呢?这是这样做的,不主动干涉它,但是我可以操作它的Cookies 啊。接下来我们就研究ASP.NET存SessionID的Cooike的名字是什么。经过网上很容易就查找到了,名字 是:ASP.NET_SessionId,这个就是SessionId的Cookies名字。我们可以在Session_Start中这样写:

    代码

    protected void Session_Start(object sender, EventArgs e)
    {
    Response.Cookies[
    ASP.NET_SessionId].Value = Session.SessionID.ToString();
    Response.Cookies[
    ASP.NET_SessionId].Domain = .local.com;
    }

    代码的意思是每次会话开始的时候,我都把ASP.NET_SessionId这个 Cookie重写成我们已有的SessionID,并且把这个Cookie的domain指定为父域,比如:.local.com,这样就可以实现跨子域 的Session共享了。怎么样很简单吧?

    我们还有一个外题问题,就是客户端保存的问题解决了,但是服务器端的Session怎么 办?一般情况下我们不同的子域做的是指向不同的服务器的,比如user.local.com 专门一台服务器,yellow.local.com专门一台服务器。这时它们别说是进程了,连物理上都不是一个了。Session怎么共享?这时就用到另 一个方法了,我们默认的Session是存储在asp.net进程中的,这样没法互相访问,如下面所示:

    <sessionState mode=”InProc” />

    我们可以修改为State Server方式,这是一个单独的服务可以用来存储ASP.NET Session的,它支持分布式远程主机的,这样我们可以用一台服务器来提供Session服务,如下所示:

    <sessionState mode=”StateServer” stateConnectionString=”tcpip=127.0.0.1:42424″ timeout=”30″ />

    这样,就完全实现了不同子域的Session共享了。

    前面说到Url保存SessionId的方式,由于不常用,给大家演示一下,如下配置就可以了:

    <sessionState mode=”StateServer” stateConnectionString=”tcpip=127.0.0.1:42424″ timeout=”30″ cookieless=”true” />

    cookieless属性指定是否用cookie来保存SessionId,我们运行一下得到下面的样子:

    http://localhost:3380/(S(dqxcs455n4u2vg55ia51fvqg))/default.aspx

    [转载]JQuery最佳实践-JQuery自定义事件的应用

    mikel阅读(895)

    [转载]JQuery最佳实践-JQuery自定义事件的应用 – Open the web via JS,Silverlight,Css… – 博客园.

    本文主要介绍JQuery框架里面支持的自定义事件模型,通过实例说明什么时候可以利用事件模型进行面向对象的JS编程,以及利用“带命名空间的事件处理函数”来避免unbind时影响别的事件订阅。

    知识要点:

    1,自定义事件custom events及事件的订阅

    2,trigger、bind、unbind方法的使用

    3,带命名空间的自定义事件

    将下面内容粘贴至txt文档后再直接粘贴至firebug的控制台,运行看看。

    /* JQuery自定义事件的应用-Javascript OO 有一帅哥叫Levin,在某公司前端设计部工作,他的主管是大帅哥A,经理是大美眉B。 领导们希望可以抽空瞄瞄这家伙每周的工作情况,于是。。。 Levin每周一要用google doc写一份关于上周工作的总结,并被要求写完后要MSN通知A和B。。。 Levin非常乐意做周总结,但是他提出了一个建议: 既然我用google doc写好了,你们喜欢看便用google reader订阅我的文档更新吧,我不喜欢用微软的MSN。。。 这丫...A和B头顶乌鸦飘了几秒,但欣然答应。 */

    //这个是Levin同学

    var Levin={name:"Levin"}; Levin=$(Levin);

    //每周要写周总结

    Levin.bind("evt_weeklyReport",function(evt){ alert(this.name+"高呼:Yeah!周总结已经写好啦."); });

    //主管A

    var A={name:"帅哥A"};

    //他要订阅Levin的周总结

    A.rssLevin=function(){ Levin.bind("evt_weeklyReport.fromA",function(evt,data){ alert(A.name+":嗯,不错,Levin还是挺积极的嘛!"); }) }; A.rssLevin();

    //经理B

    var B={name:"美眉B"};

    //她也订阅Levin的周总结

    B.rssLevin=function(){ Levin.bind("evt_weeklyReport.fromB",function(evt,data){ alert(B.name+":周总结呆会看,先看看他说的那个网站"); window.location=evt.site; }) }; B.rssLevin();

    //每次Levin同学写好周总结后便会用google doc发布一下

    Levin.trigger({type:"evt_weeklyReport",site:"http://vivasky.com"});

    //经理B突然有一天去搜狐做了,不再订阅Levin的周结…

    B.unRssLevin=function(){ alert("我要去搜狐做副总裁啦,哈哈"); Levin.unbind("evt_weeklyReport.fromB"); return true; }();

    //尽管B跳槽了,周总结还是要按时发布的。。。

    Levin.trigger({type:"evt_weeklyReport",site:"http://vivasky.com"});

    [转载]一步一步用C#编写三国杀(二):牌堆的设计

    mikel阅读(1229)

    [转载][原创]一步一步用C#编写三国杀(二):牌堆的设计 – 忘却之都 – 博客园.

    前一节说到了一些基础性的定义。这一节开始将进入流程的分析。

    首先,在游戏的场景建立之后,你就必须有一个牌堆。对于目前的需求来说,只要有手牌的牌堆即可;尽管后面可能还要有身份牌堆和武将牌堆,但目前只考虑手牌,即游戏牌。于是有以下定义:

    /// <summary>
    /// 定义牌堆的基本类型。
    /// </summary>
    /// <typeparam name=”T”>参数类型。</typeparam>
    public abstract class CardHeap<T> : Collection<T>
    {

    }

    定义为抽象的,是我希望能提供一些通用的方法以简化其他牌堆的设计。

    对于牌堆来说,其一个重要的功能就是能够压出牌以供使用,因此定义如下:

    压牌

    /// <summary>
    /// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
    /// </summary>
    /// <param name=”number”>要压出的牌的数量。</param>
    /// <returns>所压出的牌的数组。</returns>
    public T[] Pop(int number)
    {
    if (number <= 0)
    return new T[0];

    if (Items.Count < number)
    number
    = Items.Count;

    T[] newT = new T[number];
    for (int i = 0; i < number; i++)
    {
    newT[i]
    = Items.First();
    Items.RemoveAt(
    0);
    }

    return newT;
    }

    牌堆定义之后就需要定义洗牌的操作。由于我定义了从Collection<T> 继承,其内部有个IList<T>类型的Items属性,因此编写一个扩展方法,对IList<T>类型的数据进行类似洗牌的操作:

    洗牌扩展

    /// <summary>
    /// 定义对List的扩展方法。
    /// </summary>
    public static class ListExtension
    {
    /// <summary>
    /// 将IList中的元素进行洗牌操作。
    /// </summary>
    /// <typeparam name=”T”>类型参数。</typeparam>
    /// <param name=”list”>所要洗牌的List。</param>
    public static void Shuffle<T>(this IList<T> list)
    {
    Random random
    = new Random();
    int count = list.Count;

    for (int i = 0; i < count; i++)
    {
    int currentIndex = random.Next(0, count i);
    T tempCard
    = list[currentIndex];
    list[currentIndex]
    = list[count 1 i];
    list[count
    1 i] = tempCard;
    }
    }
    }

    很明显,只要游戏没有结束,牌堆中拿出使用过的废牌总要回收进行循环利用,所以废牌要保存起来,并让牌堆支持其中的Items的重生。因此CardHeap中便多了如下定义:

    牌堆重生

    private readonly IList<T> usedItems = new List<T>();

    private void ReCreate()
    {
    // 将usedItems的牌还原到Items中,并进行洗牌,然后清空usedItems
    ((List<T>) Items).AddRange(usedItems);
    usedItems.Clear();
    Items.Shuffle();
    }

    既然多了usedItems,那么上面定义的Pop就需要有个重载,以指定牌堆是否可以进行重生,所以重构上面的Pop方法,改为重载:

    重载Pop

    /// <summary>
    /// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。如果牌堆的牌数量不够,则只返回牌堆中剩余的牌。
    /// </summary>
    /// <param name=”number”>要压出的牌的数量。</param>
    /// <returns>所压出的牌的数组。</returns>
    public T[] Pop(int number)
    {
    return Pop(number, false);
    }

    /// <summary>
    /// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
    /// </summary>
    /// <param name=”number”>要压出的牌的数量。</param>
    /// <param name=”recreateHeap”>在压出牌数量不够的时候是否重新创建牌堆。</param>
    /// <returns>所压出的牌的数组。</returns>
    public T[] Pop(int number, bool recreateHeap)
    {
    if (number <= 0)
    return new T[0];

    if (Items.Count < number && !recreateHeap)
    number
    = Items.Count;

    T[] newT = new T[number];
    for (int i = 0; i < number; i++)
    {
    if (recreateHeap && Items.Count == 0)
    {
    ReCreate();
    }

    newT[i] = Items.First();
    Items.RemoveAt(
    0);
    usedItems.Add(newT[i]);
    }

    return newT;
    }

    这样,牌堆的定义就算基本完成了。但在此基础上考虑考虑扩展包的问题。实际上扩展包主要是牌的扩展,而且在设计初期,就已经考虑将标准版的牌从这个Core中分离,即除了基本牌的杀、闪、桃之外,锦囊和装备、武将都是由扩展包来提供。因此定义了个扩展包的接口:

    扩展包接口

    /// <summary>
    /// 定义扩展包所必须实现的接口。
    /// </summary>
    public interface IPackage
    {
    /// <summary>
    /// 扩展包中的游戏牌。
    /// </summary>
    GameCard[] GameCards { get; }
    }

    好,牌堆的设计就说到这里,后面就定义实际的基本牌,并将进入实际流程循环。

    [转载]用vs.net2010做flex/flash/as3开发

    mikel阅读(951)

    [转载]用vs.net2010做flex/flash/as3开发 – 菩提树下的杨过.Net – 博客园.

    是的,没错,就是用vs2010来开发flex/flash !有图有真相:

    1、在vs2010中创建as3/air/flex项目

    2、ide环境中的as代码自动提示

    对于不想安装flash cs/flash builder/flash developer,又想体验一下actionscript编程的.net程序员来说,这无疑是一个好消息!

    言归正传:

    1、先到http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4/ 上去下载flex sdk 4 (将.as文件编译成swf就靠它了)

    2、http://www.ensemble.com/products/tofino.shtml 下载vs2008/vs2010的插件“Ensemble Tofino”

    Ensemble 的 Tofino 是一个 Microsoft Visual Studio 增效工具,它使 .NET 开发人员能为自己的应用程序创建 Flex 前端。借助 Tofino,.NET 开发人员可以在本机 Visual Studio 开发环境中创建和编辑 MXML 及 ActionScript 源文件代码、运行和调试 Flex 应用程序、在 MXML 与 .NET 文件类型之间自由移动以及调用 Flex 构建和运行命令。–关键:它目前是免费的!

    安装好以后,记得设置flex sdk的路径 vs2010–>tools–>option

    剩下的事情,地球人都知道了.

    如果愿意花点银子,还有一款貌似更强大的插件:Amethyst 可以提供可视化的flex编程环境

    vs2010中的可视化编辑

    vs2010中的断点调试

    preview change

    ruby,phyhon通过ironruby,ironphyhon已经能用vs.net做开发了,现在flex/as3也进来了,下一个会是谁? java for vs2010?

    更新:看到平如水兄弟的回复后,在百度里搜索了下 VS.PHP,果然也有for 2010的版本了,vs.net真是“万能”编程利器!