[工具]Haxe

mikel阅读(994)

Haxe和两三年前相比,现在已经很成熟了,很多人都说它的编译器比Flex的还要好,在性能优化、语法上有很多改进。不过并不冲突,可以取长补短。

Haxe的库现在是越来越多,各种引擎、框架、扩展。File Format是一个文件相关的库,用来解析或生成各种文件格式,比如swf、ABC(swf中的字节码)、Flv、Pbj(PixelBender的二进制格式)等等,在作者的TODO列表上,还有一串跟着。

我比较感兴趣的是pdf,编译了一个小小的例子试了下,在解析pdf时碰到了运行时错误。使用反编译器看了下代码,比较多比较乱,看的头昏,希望作者在后续可以完善这个库。

[JQuery]JQuery Maps

mikel阅读(939)

转载:http://www.cnblogs.com/RuiLei/archive/2009/11/25/1610163.html

Google Map应用在网站之上已经从单一的浏览性慢慢转化为服务概念(同时也大大增强用户体验度)

如下利用 Google Map Api —— 简单实现地图网站应用。(仅参考) 

实现目标:

1.在地图中进行地点标注

2.选择标注时,出现相关信息

3.点击页面上的测试按钮,显示相关的地点标注信息

Html Code:

<body > 
    <h1>Google Map</h1> 
    <div id="Googlemap" style="width: 70%; height: 480px; float: left; border: 1px solid black;"> 
    </div> 
    <input type="button" id="btnTest" title="test" value="显示第二店信息" />
 
</body>

 

前期准备:

  • 注册Google Api Key
  • 引用Google Api 脚本
 <script language="javascript" type="text/javascript" src=http://ditu.google.com/maps?file=api&v=2&key=XXX></script>

  

  • 在地图中进行地点标注

                如果要实现地点标注,首先要准备数据

                数据如下:

             var message = [ 
                         { "X": "31.223822", "Y": "121.336311", "Content": { "Title": "别克4s店", "Context": "北翟路1571号"} }, 
                         { "X": "31.175449", "Y": "121.395134", "Content": { "Title": "别克4s店", "Context": "虹梅路1601号"} }, 
                         { "X": "31.095711", "Y": "121.456276", "Content": { "Title": "别克4s店", "Context": "龙吴路"} }, 
                         { "X": "31.078356", "Y": "121.395607", "Content": { "Title": "别克4s店", "Context": "沪闵路"} }, 
                         { "X": "31.200939", "Y": "121.365707", "Content": { "Title": "别克4s店", "Context": "哈密路1231号"} } 
                                     ]; 

               然后调用

           addOverlay(overlay:GOverlay) 将叠加层添加到地图中,并触发 addoverlay 事件。

                  var point = new GLatLng(X, Y); 
                  var newMkr = GMaps.prototype.CreateMarker(point, i);
                  map.addOverlay(newMkr);

 

  • 选择标注时,出现相关信息

                 在标注点上显示相关信息,需要调用openInfoWindowHtml。

          openInfoWindowHtml(content:String, opts?:GInfoWindowOptions)
                通过标记图标打开地图信息窗口。信息窗口的内容为包含 HTML 文本的字符串。只适用于 GInfoWindowOptions.maxWidth 选项。

 

 

 

  • 点击页面上的测试按钮,显示相关的地点标注信息

                实现这点只需要定义一个Trigger即可

           GEvent.trigger(XX, "click");

 

代码解析:(看看注释即可)

        var message = [
                           { "X": "31.223822", "Y": "121.336311", "Content": { "Title": "别克4s店", "Context": "北翟路1571号"} },
                           { "X": "31.175449", "Y": "121.395134", "Content": { "Title": "别克4s店", "Context": "虹梅路1601号"} },
                           { "X": "31.095711", "Y": "121.456276", "Content": { "Title": "别克4s店", "Context": "龙吴路"} },
                           { "X": "31.078356", "Y": "121.395607", "Content": { "Title": "别克4s店", "Context": "沪闵路"} },
                           { "X": "31.200939", "Y": "121.365707", "Content": { "Title": "别克4s店", "Context": "哈密路1231号"} }
                      ];
 
        var GMaps = function() {
        };
 
        GMaps.prototype = {
 
            //定义标记容器
            makers: [],
 
 
            //定义鹰眼图标
            SetIcon: function(index) {
                var markerIcon = new GIcon();
                markerIcon.image = "../Images/" + index + ".png";
                return markerIcon;
            },
 
            //设置地图中心
            SetCenter: function(lats, lngs) {
                var maxLat = Math.max.apply(null, lats),
                maxLng = Math.max.apply(null, lngs),
                minLat = Math.min.apply(null, lats),
                minLng = Math.min.apply(null, lngs),
                lat = minLat + (maxLat - minLat) / 2,
                lng = minLng + (maxLng - minLng) / 2,
                //定义缩放率
                bounds = new GLatLngBounds(new GLatLng(minLat, minLng), new GLatLng(maxLat, maxLng));
                map.setCenter(new GLatLng(lat, lng), map.getBoundsZoomLevel(bounds));
            },
 
            //定义标记内容
            CreateMarker: function(latlng, index) {
                if (!latlng) return;
                var marker = new GMarker(latlng, { icon: GMaps.prototype.SetIcon(index) });
                marker.id = index;
 
                GEvent.addListener(marker, "click", function() {
                    var myHtml = new Array();
                    myHtml.push("<span id=\"Info\">");
                    myHtml.push("<h2>" + message[index].Content.Title + "</h2><br />");
                    myHtml.push(message[index].Content.Context + "<br />");
                    myHtml.push("</span>");
                    map.openInfoWindowHtml(latlng, myHtml.join(''));
                });
 
                return marker;
            },
 
            //设置触发标记内容显示
            TriggerMaker: function(maker) {
                GEvent.trigger(maker, "click");
            },
 
            //创建地图
            BuildMap: function(map) {
                //判断当前浏览器是否支持google 地图
                if (GBrowserIsCompatible()) {
                    //定义经纬度容器
                    var lats = [], lngs = [];
 
                    //从地图中删除所有叠加层
                    map.clearOverlays();
 
                    $.each(message, function(i) {
 
                        var point = new GLatLng(message[i].X, message[i].Y);
                        var newMkr = GMaps.prototype.CreateMarker(point, i);
                        if (newMkr) {
                            //存储当前Maker到Makers中,用于在地图之外进行选择
                            GMaps.prototype.makers.push(newMkr);
                            (function(map, newMkr) {
                                    //将叠加层添加到地图中
                                    map.addOverlay(newMkr);
 
                            })(map, newMkr);
                        }
                        //存储所有经纬度用于计算当前显示区域
                        lats.push(message[i].X);
                        lngs.push(message[i].Y);
                    });
 
                    //设置地图中心区域
                    GMaps.prototype.SetCenter(lats, lngs);
 
                    map.enableDoubleClickZoom();
                    map.enableScrollWheelZoom();
                    map.enableContinuousZoom();
 
                    map.addControl(new GLargeMapControl())
                    map.addControl(new GOverviewMapControl());
                    map.addControl(new GScaleControl());
                    map.addControl(new GMapTypeControl());
                }
                else {
                    alert("No Support Google Map");
                }
            }
        }

最后调用代码:

 

        var map = new GMap2(document.getElementById("Googlemap"));
 
        var mapView = new GMaps();
 
        mapView.BuildMap(map);
 
        $(function() {
 
            $("input[id=btnTest]").click(function() {
                mapView.TriggerMaker(mapView.makers[1]);
                if (document.documentElement.scrollTop > 250) document.documentElement.scrollTop = 170;
            });
        });

最终实现效果:

image

 

资源:

Google 地图 API 开发指南

 

待续  。。。。有关于Bing Maps的话题

[MVC]ASP.NET MVC实践系列9-filter原理与实践

mikel阅读(892)

转载:http://www.cnblogs.com/nuaalfm/archive/2009/11/24/1609752.html

filter实际上是一个特性(attribute),它提供了一种向controller 或 action中添加某些任务的方法,当controller 或 action被调用时,会触发filter中定义的相应方法。filter应该算AOP的一种实现方式,关于AOP的内容大家可以参考张逸的文章http://www.cnblogs.com/wayfarer/articles/241024.html,图文并茂对AOP讲解的十分清楚。所以我们就可以在某种程度上利用filter来分解横向和纵向的应用,比方说日志,权限,缓存,防盗链等等应用。

一、我们先来看看ASP.NET MVC 框架提供的几种默认filter类型:
1、Authorize:

准备工作:进入C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727文件夹,双击 aspnet_regSQL.exe选择好相应的数据库,创建membership,AuthorizeAttribute使用membership来进 行权限验证的,所以我们需要先在membership中准备一个用户lfm,一个角色Admin,我们使用studio的项目-》ASP.NET配置创建 即可。

  [Authorize(Roles="Admin")]
        
public ActionResult Index()
        {
            ViewData[
"Message"= "Welcome to ASP.NET MVC!";
            
return View();
        }

如果lfm不属于Admin角色时Index页是不能访问的

2、OutputCache:

 [OutputCache(Duration=60, VaryByParam="none")]
        
public ActionResult About()
        {
            
return View();
        }

然后我们修改About加入:

 <%=DateTime.Now.ToString() %>

我们会发现在一分钟内我们刷新About页面其输出并不改变。这个和webform中的页面缓存机制非常相似。

这里我们也可以统一的配置时间和条件


    <system.web>
      
<caching>
        
<outputCacheSettings>
          
<outputCacheProfiles>
            
<add name="MyProfile" duration=60” varyByParam=”none” />
          
</outputCacheProfiles>
        
</outputCacheSettings>
      
</caching>
    
</system.web>

Controler中输入

  [OutputCache(CacheProfile="MyProfile")]
        
public ActionResult About()
        {
            
return View();
        }

3、Exception


       [HandleError(ExceptionType = typeof(ArgumentException), View = "Error")]
        
public ActionResult GetProduct(string name)
        {
            
if (name == null)
            {
                
throw new ArgumentNullException("名字为空");
            }
            
return View();
        }

标明HandleError属性后的Action,当内部出现异常时会根据异常类型跳转到相应的View,这里需要注意的是上面的源码在开发期无法 看到效果,必须部署到iis上才能看到效果。实际上这个简单处理在项目中用处不大,一般我们都会写自己的异常处理方式,自定义异常处理我们一会再自定义 filter中讲解。

二、自定义filter实例:

我们先来看一下跟filter相关的类结构:

一般情况下我们自定义的filter都是继承FilterAttribute类然后再扩展相应的接口的,下面我们举几个例子:

1、自定义异常处理

自定义异常处理

使用

Controller

浏览器中输入:http://localhost:3983/Home/GetView

这样我们就可以根据自己的项目情况来处理异常了。

2、监控Action运行时间的Timer

TimerAttribute

使用

 [Timer]
        
public ActionResult TestTimer()
        {
            Thread.Sleep(
100);
            
return View();
        }

页面显示:

<%=ViewData["__Duration"]%>

三、filter相关接口方法的执行顺序:

根据上面的结构图我们知道跟filter相关的总共有四个接口,六个方法,这些方法如果在同一个类中实现时是有个优先级顺序的

IAuthorizationFilter>IActionFilter>IResultFilter>IExceptionFilter
接下来我们写个程序来验证这个顺序:

TestOrder属性类

使用

[TestOrder]
        
public ActionResult TestFilterOrder()
        {
           
// throw new Exception("lfm");
            return View();
        }

前端

<%throw new Exception("异常出现"); %>

这时候我们打开c:\test.txt得到的结果为:

OnAuthorization
OnActionExecuting
OnActionExecuted
OnResultExecuting
OnResultExecuted
OnException

四、参考

《Professional ASP.NET MVC 1.0》

http://www.cnblogs.com/leoo2sk/archive/2008/11/05/1326655.html

http://www.cnblogs.com/wayfarer/articles/241024.html

http://www.cnblogs.com/chsword/archive/2009/03/12/zd_mvc6.html

五、源码

 

我的ASP.NET MVC实践系列

ASP.NET MVC实践系列1-UrlRouting

ASP.NET MVC实践系列2-简单应用

ASP.NET MVC实践系列3-服务器端数据验证

ASP.NET MVC实践系列4-Ajax应用

ASP.NET MVC实践系列5-结合JQuery

ASP.NET MVC实践系列6-Grid实现(上)

ASP.NET MVC实践系列7-Grid实现(下-利用Contrib实现)

ASP.NET MVC实践系列8-对查询后分页处理的解决方案

其他:

在ASP.NET MVC中对表进行通用的增删改

[JQuery]thickbox使用指南

mikel阅读(774)

引用thickbox.css样式
引用thickbox.js脚本
动态创建thickbox方法:tb_show(caption, url, imageGroup);
参数说明:
caption:弹出窗口的标题
url:链接地址
imageGroup:图标
页面直接调用方法:
<a href="/info/index?width=240&hieght=300&id=23234" title="标题" class="thickbox">显示窗口</a>

[工具]让代码看起来更舒服(2):选择适合的字体

mikel阅读(935)

转载:http://www.cnblogs.com/xiaoshatian/archive/2009/11/23/1608432.html

上一篇文章我和大家分享了一些Visual Studio的配色方案,以及一个用来生成配色方案的网页版工具,现在我再来和大家分享一下Visual Studio的字体设置。

字体不仅是设计师手中重要的武器,对我们开发人员来说,字体的选择也有许多讲究,一个好的、适合展示代码的字体,应该具备以下要素:

  • 等宽的字符
  • 简洁、清晰并且规范的字符形状
  • 支持ASCII码为128以上的扩展字符集
  • 与字符同等宽度的空格
  • 易于分辨的小写字母l、大写字母I、数字1和符号|
  • 易于分辨的大写字母O和数字0
  • 易于分辨的前引号和后引号,最好能够前后对称
  • 易于分辨的其他标点符号,尤其是大括号、中括号、小括号和尖括号
  • 良好的中文支持和显示

下图展示了一个极端的反例,虽然很有个性,但并不适合用来显示代码。它的大写字母大的可怕,小写字母却小的可怜;字符不仅不清晰,而且不规范;小写字母l、大写字母I、数字1和符号| 难以分辨;大写字母O和数字0难以分辨;标点符号还凑合,只是下划线为什么是断开的?

image

当然,这个反例的确有些夸张,但其上文所列举的条件是比较苛刻的,我们在选择字体时没有必要非要完全满足所有条件,事实上这种字体也是凤毛麟角。下文所分享的一些字体也并没有完全满足所有条件,我们发现,只要满足了其中一些关键条件,用来显示代码就已经很不错了。

在Visual Studio中,更改编辑器的字体是件简单的事情,选择菜单【工具】【选项】,在弹出的“选项”对话框中依次选择“环境”和“字体和颜色”,然后在“显示其设置”中选择“文本编辑器”,最后在“字体”中选择字体就可以了,如下图所示:

image

在上图中,我们发现Visual Studio已经将等宽字体用粗体标识了出来,这样做是为了让我们更容易找到等宽的字体,并不表示等宽字体就一定适合显示代码,比如下图所示的“新宋体”就是一个反例:

image

在新宋体中,数字0之比大写字母O瘦那么一点点,小写字母l和数字1也十分相像,幸亏配色方案能够颜色将它们区别开来,否则实在难以分辨。这也从另一个角度说明了配色方案与字体是相辅相成的,搭配恰当时,会让代码更加清晰易辨。

下面就和大家分享一些在编程界声名烜赫的字体,截图所采用的配色方案为HumaneStudio,展示顺序为字体首字母升序。这些字体可能乍一看都一样,但仔细品味,还是能够发现不同的味道,希望大家都能找到适合自己的字体。

Andale Mono,演示字号为14,猛击这里下载

image

 

Anonymous Pro,演示字号为14,猛击这里下载

image

 

Bitstream Vera Sans Mono,演示字号为14,猛击这里下载

image

 

Consolas,演示字号为14,Windows或Visual Studio已内置:

image

 

Courier New,演示字号为14,Windows已内置:

image

 

DejaVu Sans Mono,演示字号为14,猛击这里下载

image

 

 

Envy Code R,演示字号为16,猛击这里下载

image

 

Inconsolata,演示字号为16,猛击这里下载

image

 

Monaco,演示字号为12,猛击这里下载

image

 

Monofur,演示字号为16,猛击这里下载

image

 

Progmata,演示字号为14,猛击这里下载

image

 

Share TechMono,演示字号为16,猛击这里下载

注:此字体似乎会将连在一起的fl显示为一个点,不建议使用,可惜可惜。

image

尾注:本文所分享的字体均支持ClearType。

[Chrome]用虚拟机实际体验Google Chromium OS (Chrome OS)

mikel阅读(1450)

转载:http://www.cnblogs.com/zhubo/archive/2009/11/20/Experience_Google_Chromium_OS_Pre_Beta_with_SUN_VirtualBox.html

前言

今 天凌晨(20091120)2:05左右,与Chrome技术交流QQ群(75448027)的朋友们一起聆听了远在美国的ChromeOS发布会,经过 一番实验后,终于在自己PC的虚拟机中成功运行了ChromeOS。这篇文章旨在帮助所有对ChromeOS感兴趣的朋友一起实际体验一下 ChromeOS,比看截图要更有趣一些。

环境/准备

安装

  • 将下载的VMDK Image解压,如:E:\ChromiumBuild\ChromiumBuild.vmdk
  • 打开Sun VirtualBox,点击新建(如图1)

Snap1 图1

  • 在“新建虚拟电脑”中点击下一步(如图2)

Snap2 图2

  • 名称随意填写,如Chrome,操作系统选Linux,Version选Ubuntu(如图3),下一步

Snap3 图3

  • 内存大小根据自己的需要选择,我选的是1024M(如图4),下一步

Snap4 图4

  • 虚拟硬盘选择使用现有虚拟硬盘,并点击右侧的向上箭头小图标,(如图5)

Snap5 图5

  • 在弹出的虚拟介质管理器中,点击注册(如图6)

Snap6 图6

  • 选择一个虚拟硬盘中,选择刚才解压的E:\ChromiumBuild\ChromiumBuild.vmdk(如图7)

Snap7 图7

  • 选中ChromiumBuild.vmdk,点击选择,(如图8)

Snap8 图8

  • 下一步,(如图9),再点击完成

Snap9 图9

  • 此时,在VirtualBox左侧就会出现刚才我们新建的这个虚拟机,右键点击开始(如图10)(或直接双击)

Snap10 图10

  • 稍待片刻,即进入了ChromeOS的登录界面,用自己的Gmail帐号登录即可(如图11)

Snap11 图11

  • 登录后,进入ChromeOS(如图12),接下来就尽情的折腾它吧。

Snap12 图12

Q&A

  • Q:Chrome能安装到普通PC里么?
    A:不能,目前Chrome只支持5款硬件[2]
    * Acer Aspire One AOD250-1165 10.1-Inch Blue Netbook
    * ASUS Eee PC 1008HA Seashell 10.1-Inch Pearl Black Netbook
    * HP Mini 5101
    * Lenovo Ideapad S10-1311UW 10.2-Inch White Netbook
    * Dell Mini 10V
  • Q:如何自己编译ChromeOS?
    A:参照:http://www.chromium.org/chromium-os/building-chromium-os

参考

  • [1]http://digiex.net/applications/2900-google-chromium-os-chrome-os-pre-beta-vmdk-download.html
  • [2]http://sites.google.com/a/chromium.org/dev/chromium-os/getting-dev-hardware/dev-hardware-list

[MVC]Oxite分析之初始化(更新版)

mikel阅读(871)

转载:http://www.cnblogs.com/alby/archive/2009/11/20/oxite-initialize-new.html

change set:46759
download:http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
Web应用程序的初始化我觉得应该分两类,一类是系统级的初始化,另一类是应用程序级的初始化。两者也有交叉的部分,如将会谈到的Application_StartApplication_End ,正是利用其在系统级的特殊性来完成应用程序级的初始化工作。关于系统级的初始化,MSDN上有简要的描述:《ASP.NET应用程序生命周期概述》http://msdn.microsoft.com/zh-cn/library/ms178473(VS.80).aspx 。本篇主要分析Oxite在配置级的初始化工作。
一、对ASP.NET应用程序生命周期的理解

摘要《ASP.NET 应用程序生命周期概述》中的几段话:
A:

第一次在应用程序中请求 ASP.NET 页或进程时,将创建 HttpApplication 的一个新实例。不过,为了尽可能提高性能,可对多个请求重复使用 HttpApplication 实例。
理解:也就是说,在应用程序域中将实例化多个HttpApplication对象。每一次请求都将为该请求分配一个HttpApplication对象。而多个请求可能会重复使用同一个HttpApplication对象。但不会存在并发的情况。

B:

Application_StartApplication_End 方法是不表示 HttpApplication 事件的特殊方法。在应用程序域的生命周期期间,ASP.NET 仅调用这些方法一次,而不是对每个 HttpApplication 实例都调用一次。

请求 ASP.NET 应用程序中第一个资源(如页)时调用。在应用程序的生命周期期间仅调用一次 Application_Start 方法。可以使用此方法执行启动任务,如将数据加载到缓存中以及初始化静态值。

在应用程序启动期间应仅设置静态数据。由于实例数据仅可由创建的 HttpApplication 类的第一个实例使用,所以请勿设置任何实例数据。
理解:Application_Start 是在应用程序发生第一次请求,创建第一个HttpApplication对象时发生。(在创建了所有模块之后,对 HttpApplication 类的每个实例都调用一次Application_Init方法)
C:

在应用程序的生命周期期间,应用程序会引发可处理的事件并调用可重写的特定方法。若要处理应用程序事件或方法,可以在应用程序根目录中创建一个名为 Global.asax 的文件。

如果创建了 Global.asax 文件,ASP.NET 会将其编译为从 HttpApplication 类派生的类,然后使用该派生类表示应用程序。
理解:我们创建Global.asax 目的是“替换”原来的HttpApplication 类,而为每一次请求分配的将是Global.asax类的实例。在Global.asax中,我们可以捕获相关事件或重写如Application_StartApplication_End 之类的特殊方法。
二、OixteSite项目中Global.asax文件中的Application_StartApplication_End方法

查看OxiteSite项目的Global.asax文件,发现其实现代码在Oxite项目的OxiteApplication类中。在Application_Start方法中对OxiteSite进行了初始化工作。
Application_Start方法具体做了哪些事呢?
一是设置依赖注入容器并将之存入应用程序状态中(HttpApplicationState)
二是根据配置加载模块

protected void Application_Start()
{
Application["container"] = setupContainer();
Application["bootStrappersLoaded"] = false;
load();
}

Oxite中使用的依赖注入容器为Unity(详见Enterprise Library 4.0以上版本)。
setupContainer方法设置注入容器并返回一个UnityContainer对象,保存为Application["container"]。稍后将仔细分析该方法。
Application["bootStrappersLoaded"]用于标认初始化是否完成。
load()方法调用静态方法Load(HttpContextBase context)。作用是根据配置加载指定模块(Module)。

 

Application_End方法调用unload()方法。在应用程序结束时,完成某些模块的清理工作。
三、setupContainer方法
预备知识:Unity (IOC/DI)、自定义web.config配置结点
通过setupContainer方法的方法名不难看出是用于设置依赖注入容器的。
在setupContainer方法中,首先定义一个IUnityContainer变量parentContainer:

IUnityContainer parentContainer = new UnityContainer();

首先,将几个基础对象注册为单例:

parentContainer
.RegisterInstance((OxiteConfigurationSection)ConfigurationManager.GetSection("oxite"))
.RegisterInstance(new AppSettingsHelper(ConfigurationManager.AppSettings))
.RegisterInstance(RouteTable.Routes)
.RegisterInstance(System.Web.Mvc.ModelBinders.Binders)
.RegisterInstance(ViewEngines.Engines)
.RegisterInstance(HostingEnvironment.VirtualPathProvider);

OxiteConfigurationSection类,自定义配置节点。其定义位与Oxite.Configuration命名空间下。是Oxite中实现模块化的配置文件。配置的结点单独放在OxiteSite项目下的oxite.config文件中。
AppSettingsHelper 类对ConfigurationManager.AppSettings 进行包装, 提供几个读取方法GetInt32、GetString等,用于读取web.config文件中的appSettings节点下的值。其实完全可以将这几 个读取方法放入NameValueCollectionExtensions类(Oxite.Extensions命名空间下)。不过后来想了想,这里用 AppSettingsHelper命名其实也可以明确该类的目的就是为了操作AppSettings结点。
RouteTable.Routes静态属性返回一个RouteCollection静态对象。RouteCollection类在System.Web.Routing程序集中定义。用于保存URL路由设置。 注入容器的目的是为了单元测试。
ModelBinders.Binders静态属性返回一个ModelBinderDictionary静态对象。用于处理数据绑定相关操作(获取表单、查询数据并转换;生成URL路径)。
ViewEngines.Engines静态属性返回一个ViewEngineCollection静态对象。用于视图引擎方面。
HostingEnvironment.VirtualPathProvider静态属性返回一个VirtualPathProvider静态对象。个人猜测可能会用在自定义ViewEngine中,不过目前Oxite版本中好像还没地方用,注释掉也没地方报错。
RouteTable.Routes、ModelBinders.Binders和ViewEngines.Engines是ASP.NET MVC底层比较基础性的属性或对象。值得花时间单独去学习。
接着,将web.config中的connectionStrings和自定义节点“oxite”(oxite.config文件)下的connectionStrings注册为单件。

foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
foreach (ConnectionStringSettings connectionString in parentContainer.Resolve<OxiteConfigurationSection>().ConnectionStrings)
parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);

疑问1:在运行时,站点不重启的情况下,如果oxite结点下的connectionStrings改变后,要怎样才能更新到依赖注入容器?这里的 处理似乎欠妥。 后来我到Oxite.codeplex.com去问了,Oxite项目组的ErikPorter说目前得重启站点才行。希望他们尽快修正,不然所谓的模块 热插拔会大打折扣。
接着看setupContainer方法:

parentContainer
.RegisterInstance<IBootStrapperTask>("LoadModules", new LoadModules(parentContainer))
.RegisterInstance<IBootStrapperTask>("LoadBackgroundServices", new LoadBackgroundServices(parentContainer));

LoadModules类和LoadBackgroundServices类位于Oxite.BootStrapperTasks命名空间。从类命 名上看,一个是和加载模块相关的,另一个是和加载后台服务相关的,具体是什么得往后细看了。两者都实现了Oxite.Infrastructure命名空 间下的IBootStrapperTask接口。IBootStrapperTask就两个方法:Execute和Clearup。 IBootStrapperTask我觉得可以直译为引导程序接口,其实例可以称为引导程序。
在这里我们只需要知道,Modules实例和LoadBackgroundSercies实例分别注册为单件。
接着看setupContainer方法中将一些类型也注册到依赖注入容器中,除了几个自定义生命周期的类型外,其他的都只是简单的映射,这里就不多说了。
在 setupContainer结束返回值之前,会将web.config文件中Unity配置结点注册入容器中。如果配置结点和我们硬编码中的设置重复, 则会覆盖硬编码中的配置。这一特性非常有用,它允许我们在使用程序的默认配置的同时,又提供了一个接口以供我们替换。详情可以查看相关Unity方面的资 料。
四、静态方法Load

Application_Start中调用私有load方法,load将请求上下包装成HttpContextBase对象作为参数,调用静态方法Load。
Load方法虽为静态,但其接受一个HttpContextBase型参数,从而保证了其线程安全。

在静态方法Load中将会从依赖注入容器中将实现了IBootStrapperTask接口的类的实例(引导程序)从依赖注入容器中提取出来,并执行其Execute方法,具体过程如下分析。
通过对setupContainer的分析,我们知道在Load方法中,tasks集合中会有两个对象:LoadModules类和LoadBackgroundServices类的实例。
另外,Application["bootStrapperState"]可能是预留下来(也有可能是遗留下来的),暂时没有明确其具体的目的(可以自己想想)。
如果Application["bootStrappersLoaded"]为true,表示曾经加载过,则先逐个进行清理工作(Cleanup)。
然后在逐个执行其引导程序的Execute方法。完成后将Application["bootStrappersLoaded"]设为true表示加载完毕。

public static void Load(HttpContextBase context)
{
IEnumerable<IBootStrapperTask> tasks = ((IUnityContainer)context.Application["container"]).ResolveAll<IBootStrapperTask>();
bool bootStrappersLoaded = (bool)context.Application["bootStrappersLoaded"];
IDictionary<string, object> state = (IDictionary<string, object>)context.Application["bootStrapperState"];
if (state == null)
{
context.Application["bootStrapperState"] = state = new Dictionary<string, object>();
}
// If the tasks have been executed previously, call Cleanup on them to rollback any changes
// they caused.
if (bootStrappersLoaded)
{
foreach (IBootStrapperTask task in tasks)
{
task.Cleanup(state);
}
}
foreach (IBootStrapperTask task in tasks)
{
task.Execute(state);
}
context.Application["bootStrappersLoaded"] = true;
}

 
Oxite的初始化有很大一部分在IBootStrapperTask接口的两个实现类中,通过Execute方法来完成的。
五、LoadModules类
打 一个不十分恰当的比喻,Oxite就象一个Windows操作系统,在系统启动时会执行一些引导程序。有一个引导程序根据注册表的配置,运行一些应用程 序,比如杀毒软件、防火墙等等;另一个引导程序根据“启动”菜单中保存的链接,运行另外一些应用程序,如QQ。(Windows引导程序可能不用两个来完 成这两步操作)
在这里,LoadModules可以看成是通过注册表获取开机启动程序并启动的引导程序;LoadBackgroundServices可以看成是通过“启动”菜单中保存的链接来启动程序的引导程序。
Windows平台下的程序能够实现开机启动,前提条件它得是Windows程序。
我们换成Oxite模块的角度上考虑,他们都应该实现了某个或某些接口。的确,Oxite中的模块都实现了IOxiteModule接口。
而Oxite中模块,可以看成需要开机运行的程序。

Oxite可以看成是由一个个模块(Module)组成的,而模块指实现了IOxiteModule接口的类。
Oxite001

(图1:从模块的角度分析Oxite解决方案结构)
AspNetCache、Core、Membership、Oxite.Blogs、Oxite.CMS等都称之为模块。
LoadModules类实现于接口IBootStrapperTask,其目的正是用于加载模块(Module)。

public void Execute(IDictionary<string, object> state)
{
OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
RouteCollection routes = this.container.Resolve<RouteCollection>();
IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
filterRegistry.Clear();
modelBinders.Clear();
//todo: (nheskew) get plugin routes registered on load in the right order instead of just clearing the routes before module init
routes.Clear();
foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
if (moduleInstance != null)
{
moduleInstance.RegisterWithContainer();
moduleInstance.Initialize();
moduleInstance.RegisterFilters(filterRegistry);
moduleInstance.RegisterModelBinders(modelBinders);
this.container.RegisterInstance(modulesLoaded);
}
}
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LoadFromModules(modulesLoaded);
routes.LoadCatchAllFromModules(modulesLoaded);
container.RegisterInstance(filterRegistry);
}

首先获取oxite.config配置文件。Oxite配置结点在setupContainer方法的分析中提到过,他是Oxite模块化的配置。它相当与注册表,用于保存需要开机启动的程序列表。
OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
接着获取IModulesLoaded对象(在setupContainer方法中,IModulesLoaded映射为ModulesLoaded类)。
IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
ModulesLoaded的作用是保存“已经加载的模块”。之后会注册为单件。

接着获取RouteCollection对象,即RouteTable.Routes。在setupContainer方法被注册为单件。
RouteCollection routes = this.container.Resolve<RouteCollection>();
接着获取IFilterRegistry实例。FilterRegistry是和Filter相关的(ActionFilter、ResultFilter等),可能需要单独的篇幅来分析。
IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
这里只是简单的实例化,因为IFilterRegistry 并没有在依赖注如容器中注册过。不过在最后,会将IFilterRegistry实例注册为单件。
接着获取ModelBinderDictionary实例。即System.Web.Mvc.ModelBinders.Binders,在setupContainer方法被注册为单件。
ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
本质上FilterRegistry、ModelBinderDictionary、RouteCollection都是集合类。接下来将filterRegistry、modelBinders、routes清空。

接下来是一个foreach循环。ModulesLoaded类实例modulesLoaded的Load方法将Module进行实例化,返回IOxiteModule对象。

foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
//...
}

如果正常返回IOxiteModule对象,就调用如下四个方法:
      RegisterWithContainer
      Initialize
      RegisterFilters
      RegisterModelBinders

      接着:

      this.container.RegisterInstance(modulesLoaded);

疑问2:上面这句写在循环里,为什么不写在循环外面? 
 

跳出循环后,对路由规则进行设置:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LoadFromModules(modulesLoaded);
routes.LoadCatchAllFromModules(modulesLoaded);

LoadFromModules和LoadCatchAllFromModules方法是扩展方法。
方法内部会遍历 ModulesLoaded对象中保存的IOxiteModuel对象并分别调用对象的RegisterRoutes和 RegisterCatchAllRoutes方法。详情请看Oxite.Extensions.RouteCollectionExtensions 类。
疑问3,这两行代码为什么没有像调用ModulesLoaded类的RegisterWithContainer,Initialize,RegisterFilters,RegisterModelBinders这四个方法那样调用。
我觉得完全可以这样:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
if (moduleInstance != null)
{
moduleInstance.RegisterWithContainer();
moduleInstance.Initialize();
moduleInstance.RegisterFilters(filterRegistry);
moduleInstance.RegisterModelBinders(modelBinders);
moduleInstance.RegisterRoutes(routes);
moduleInstance.RegisterCatchAllRoutes(routes);
}
}
this.container.RegisterInstance(modulesLoaded);
this.container.RegisterInstance(filterRegistry);

(疑问3的解释:大家都知道,Routing规则设置的顺序非常重要,RegisterRoutes方法中先对Modules进行倒序再注册,目的 是使排在后面的Module的Routing先注册。我觉得这也带来一点麻烦,也许你会覆盖(说成隐藏比较合适)了原本不想覆盖的,比如新加的模块有可能 会覆盖系统模块的Routing规则。也说明了两个方面问题,一方面模块之间也有依赖关系,比如其他模块对系统模块的依赖,当然我们要尽量普通模块之间的 依赖;另一方面模块在oxite.config中的顺序也很重要。)
此文为《Oxite分析之初始化》的更新版,老版有些错误的认识,也留着被拍吧。

[C#]ESBasic 可复用的.NET类库(16)定时刷新缓存管理器

mikel阅读(1486)

转载:http://www.cnblogs.com/zhuweisky/archive/2009/11/21/1607429.html

1.缘起:

    为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中。如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择。

    如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎。如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力。

    我设计了定时刷新缓存管理器IRefreshableCacheManager来帮助你管理你系统中所有需要进行定时刷新的缓存。它可以根据每个缓存的刷新时间间隔要求,在正确的时刻,刷新其所管理的缓存。你不用再自己手动去处理定时器或循环引擎,IRefreshableCacheManager自动为你完成这一切。

 

2.适用场合:

(1)系统中需要使用一个或多个需要进行定时刷新的缓存。

(2)每个缓存所要求的刷新时间间隔可能都不一样。

(3)刷新的时间间隔并不要求精确。

 

3.设计思想与实现

    本节所讲述的是定时刷新缓存管理器IRefreshableCacheManager,其重点在于“管理器”,而不是在于“缓存”,这点是必须清楚的。缓存只是被管理器管理的对象。

能够被IRefreshableCacheManager管理的缓存必须是可定时刷新的缓存,也就是说,这些缓存必须实现IRefreshableCache接口以表明自己可以接受管理器的管理。IRefreshableCache定义如下:

    public interface IRefreshableCache
    {
        
/// <summary>
        
/// RefreshSpanInSecs 定时刷新的时间间隔(秒)。如果设置为0,则表示与IRefreshableCacheManager的刷新时间统一。
        
/// </summary>
        int RefreshSpanInSecs { get; }
        
/// <summary>
        
/// LastRefreshTime 最后一次刷新时间。
        
/// </summary>
        DateTime LastRefreshTime { getset; }        
        
        
void Refresh();       
    }

这个接口相当简单,它只要求缓存提供Refresh方法进行刷新,并通过RefreshSpanInSecs属性表示出自己希望进行定时刷新的时间间隔。如果这个属性设置为0(默认值),则表示该缓存接受管理器的统一调度。

LastRefreshTime属性用于记录最后一次刷新结束的时间,该属性的值由管理器进行设置。

我们从IRefreshableCache接口的定义看到,管理器不用关心与缓存中的数据相关的任何信息,这是由具体的应用在实现IRefreshableCache接口时才指定的。

接下来,我们看看IRefreshableCacheManager接口的定义:

    public interface IRefreshableCacheManager
    {
        
/// <summary>
        
/// RefreshSpanInSecs 定时刷新缓存的时间间隔。
        
/// </summary>
        int RefreshSpanInSecs { set; }
        
IList<IRefreshableCache> CacheList { set; }
        
void Initialize();
        
/// <summary>
        
/// RefreshNow 手动刷新被管理的所有缓存。
        
/// </summary>
        void RefreshNow();
        
/// <summary>
        
/// AddCache 动态添加缓存。
        
/// </summary>       
        void AddCache(IRefreshableCache cache);
        
/// <summary>
        
/// RemoveCache 动态移除缓存。
        
/// </summary>       
        void RemoveCache(IRefreshableCache cache);      
        
/// <summary>
        
/// CacheRefreshFailed 当某个缓存刷新抛出异常时,将触发该事件。
        
/// </summary>
        event CbCacheException CacheRefreshFailed;
    }

这个接口也有一个RefreshSpanInSecs属性,如果被管理的缓存的RefreshSpanInSecs属性设置的是0,那么管理器将用自身的这个属性对其进行调度。IRefreshableCacheManager接口的RefreshSpanInSecs属性是为了简化那种被管理的所有缓存都采用统一刷新时间间隔的情况而存在的。

      RefreshableCacheManager在实现时仍然是借助前面介绍的循环引擎AgileCycleEngine来进行定时控制的。

IRefreshableCacheManager提供了AddCacheRemoveCache方法用于在运行中动态的添加或移除缓存。

当某个缓存在刷新时,抛出异常,则IRefreshableCacheManager会触发CacheRefreshFailed事件,事件参数包含了出现异常的缓存和异常对象。

 

    关于RefreshableCacheManager的实现,要注意以下几点:

(1)管理器的实现是线程安全的,可以使用于多线程的环境中。我们对其内部的缓存列表进行了加锁控制。

(2)管理器在初始化(Initialize)时,启动了循环引擎(其DetectSpanInSecs必须设置为1秒),而且,将所有被管理的缓存的LastRefreshTime都设置为当前时间。

(3)在实现EngineAction方法时,必须在foreach块中使用try来捕捉引擎刷新时抛出的异常,而不是在try块中进行foreach。这个顺序是重要的,如果在try块中进行foreach,那么当一个缓存在刷新时抛出异常而导致foreach中断后,后续缓存的刷新方法都将不会被检测和调用。

(4)使用锁不仅仅是为了同步对内部缓存列表集合的修改,手动调用刷新方法RefreshNow也需要被同步,否则就可能出现两个线程同时进行检测和刷新缓存的情况(一个是循环引擎的线程,另一个是手动调用RefreshNow方法的线程)。

 

4. 使用时的注意事项

(1)由于管理器内部实现采用了循环引擎,所以定时刷新的时间间隔不可能很精确,而且,针对每个缓存的刷新方法的顺序调用也是导致这种不精确的另一个原因。

(2)也由于管理器内部实现采用了循环引擎,循环引擎能设置的最小检测时间间隔为1秒,所以缓存的刷新时间间隔也不可能小于1秒。

(3)如果某个缓存在刷新时抛出异常,那么其LastRefreshTime属性还是记录的上一次成功刷新的时间。

 

5.扩展

     定时刷新缓存管理器通过事件暴露缓存刷新失败的通知,当缓存刷新发生异常时,我们可能需要将异常记录到日志中。如果是这样,那就可以直接使用ESBasic提供的RefreshableCacheExceptionLogBridge来完成。

       RefreshableCacheExceptionLogBridge借助ESBasic.Logger.IAgileLogger组件来将异常的详细信息记录到目标日志中。日志可以是文本文件,也可以是数据库等其他存储。ESBasic提供了IAgileLogger接口的实现FileAgileLogger,用于将日志写入文本文件中。

 

注:ESBasic源码可到http://esbasic.codeplex.com/下载。
    ESBasic讨论QQ群:37677395
    
ESBasic开源前言

[工具]支持jQuery版本vsdoc.js的智能提示的VS2008 SP1补丁下载

mikel阅读(816)

Jeff King发布了一个JQuery智能提示Visual Studio 2008 SP1 补丁,安装这个补丁后,Visual Studio 2008可以自动找到vsdoc.js文件,我们不需要在脚本文件中定义<%if (false)%>代码段就可以看到JQuery方法说明信息了,这个补丁支持English, Français, Deutsch, Italiano, 한국어, 繁體中文, 简体中文, 日本語, Русский, Português (Brasil) 各种语言。

下载地址:http://code.msdn.microsoft.com/KB958502/Release/ProjectReleases.aspx?ReleaseId=1736

安装界面如下:

安装界面

安装完之后,我新建一个Web程序测试一下,引入JQuery库文件jQuery-1.2.6.js和jQueryVS智能文件jQuery-1.2.6-vsdoc.js。

解决方案

你可以到jQuery官方网站上下载jQuery。地址为:http://docs.jQuery.com/Downloading_jQuery#Download_jQuery

1.使用ScriptManager ScriptReference方式:

ScriptReference

2.使用脚本标记方式:

脚本标记

3.使用引入XML标记方式:

引入XML标记

相关帖子:

http://blogs.msdn.com/webdevtools/archive/2008/11/07/hotfix-to-enable-vsdoc-js-intellisense-doc-files-is-now-available.aspx

http://blogs.msdn.com/webdevtools/archive/2008/10/28/rich-intellisense-for-jquery.aspx

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

 原文地址 http://lyj.cnblogs.com

[工具]让代码看起来更舒服(1):选择适合的配色方案

mikel阅读(817)

转载:http://www.cnblogs.com/xiaoshatian/archive/2009/11/20/1606440.html

“让代码看起来更舒服”,看到这个标题,也许你会条件反射地以为我要讲“重构”或者“编码规范”等等。噢,可爱的开发人员,我们暂且不谈技术,只谈体验。让我们来装扮一下每天都要面对的Visual Studio,让代码看起来更舒服。

下图展示了Visual Studio 2008默认的编辑器,为了让代码更加容易辨识,Visual Studio用不同的颜色将各种关键词区分开来。

image

默认的配色方案固然无可厚非,每当夜深人静的时候,它总是能让我热泪盈眶。我当然没有矫情到被自己的代码感动,而是我脆弱的眼睛实在受不了它那惨白的背景色。

于是我决定要做出一些改变。

好在Visual Studio可以方便地设置代码配色方案,选择菜单【工具】【选项】,在弹出的“选项”对话框中依次选中“环境”、“字体和颜色”,在“显示其设置”中选中“文本编辑器”,然后我们就可以随意地更改代码编辑器的字体和配色方案了。

image

好吧,我承认自己动手调整配色方案是一件机械式的体力活,开发人员最痛恨机械式的体力活。有这时间还不如Google一下,看看有没有前人栽好的树供我们乘凉,于是我找到了一些配色方案,只要导入到Visual Studio中,就可以马上看到效果!

导入配色方案的方法很简单,选择菜单【工具】【导入和导出设置】,在弹出的“导入和导出设置向导”对话框中选择“导入选定的环境设置”,然后根据自己的实际情况选择“是,保存当前设置”或“否,仅导入新设置,覆盖我的当前设置”,然后点击“浏览”来打开一个Visual Studio Settings File(扩展名为.vssettings)。

需要注意的是,Visual Studio Settings File包含了Visual Studio的各种设置,所以如果你导入的设置文件除了下图所示的“字体和颜色”之外,还有其他设置,请小心勾选。

image

下面是一些我找到的配色方案,以及它们显示同一段代码的效果图,大家挑选喜欢的下载吧。

Jeff-atwood,猛击这里下载

image

HumaneStudio,猛击这里下载

image

2008-DarkGrey,猛击这里下载

image 

2008-Ragnarok_Grey,猛击这里下载

image

2008-Moria_Alt,猛击这里下载

image

2008-Nightingale,猛击这里下载

image

2008-DesertNights,猛击这里下载

image 

尾注:

  • 由于年代久远,我已经记不清这些配色方案的出处了,所以没有注明来源。
  • 这些配置文件均为Visual Studio 2008配置文件,我没有在Visual Studio其他版本做测试,不保证兼容性。