[Debug]监控 SQL Server (2005/2008) 的运行状况

mikel阅读(549)

原文地址:
http://technet.microsoft.com/zh-cn/library/bb838723.aspx

Microsoft SQL Server 2005 提供了一些工具来监控数据库。方法之一是动态管理视图。动态管理视图 (DMV) 和动态管理函数 (DMF) 返回的服务器状态信息可用于监控服务器实例的运行状况、诊断问题和优化性能。

常规服务器动态管理对象包括:

  • dm_db_*:数据库和数据库对象

  • dm_exec_*:执行用户代码和关联的连接

  • dm_os_*:内存、锁定和时间安排

  • dm_tran_*:事务和隔离

  • dm_io_*:网络和磁盘的输入/输出

此部分介绍为监控 SQL Server 运行状况而针对这些动态管理视图和函数运行的一些常用查询。

摘录部分精彩SQL如下:

下面的查询显示 CPU 平均占用率最高的前 50 个 SQL 语句。

Select TOP 50
total_worker_time
/execution_count AS [Avg CPU Time],
(
Select SUBSTRING(text,statement_start_offset/2,(CASE WHEN statement_end_offset = 1 then LEN(CONVERT(nvarchar(max), text)) * 2 ELSE statement_end_offset end statement_start_offset)/2FROM sys.dm_exec_sql_text(sql_handle)) AS query_text, *
FROM sys.dm_exec_query_stats 
ORDER BY [Avg CPU Time] DESC

下面的查询显示一些可能占用大量 CPU 使用率的运算符(例如 ‘%Hash Match%’、‘%Sort%’)以找出可疑对象。

select *
from 
      sys.dm_exec_cached_plans
      
cross apply sys.dm_exec_query_plan(plan_handle)
where 
      
cast(query_plan as nvarchar(max)) like '%Sort%'
      
or cast(query_plan as nvarchar(max)) like '%Hash Match%'

运行下面的 DMV 查询以查看 CPU、计划程序内存和缓冲池信息。

select 
cpu_count,
hyperthread_ratio,
scheduler_count,
physical_memory_in_bytes 
/ 1024 / 1024 as physical_memory_mb,
virtual_memory_in_bytes 
/ 1024 / 1024 as virtual_memory_mb,
bpool_committed 
* 8 / 1024 as bpool_committed_mb,
bpool_commit_target 
* 8 / 1024 as bpool_target_mb,
bpool_visible 
* 8 / 1024 as bpool_visible_mb
from sys.dm_os_sys_info

下面的示例查询显示已重新编译的前 25 个存储过程。plan_generation_num 指示该查询已重新编译的次数。

select top 25
sql_text.
text,
sql_handle,
plan_generation_num,
execution_count,
dbid,
objectid 
from sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(sql_handle) as sql_text
where plan_generation_num > 1
order by plan_generation_num desc

下面的 DMV 查询可用于查找哪些批处理/请求生成的 I/O 最多。如下所示的 DMV 查询可用于查找可生成最多 I/O 的前五个请求。调整这些查询将提高系统性能。

select top 5 
    (total_logical_reads
/execution_count) as avg_logical_reads,
    (total_logical_writes
/execution_count) as avg_logical_writes,
    (total_physical_reads
/execution_count) as avg_phys_reads,
     Execution_count, 
    statement_start_offset 
as stmt_start_offset, 
    sql_handle, 
    plan_handle
from sys.dm_exec_query_stats  
order by  (total_logical_reads + total_logical_writes) Desc

[Debug]一个强大的LogParser的UI工具--logparserlizard简介

mikel阅读(755)

转载:http://www.cnblogs.com/downmoon/archive/2009/09/02/1558409.html
日志分析,特别是IIS日志,一般人都会想到LogParser工具,的确很强。但是命令行的操作界面令很多非专业的管理人员望而生畏,现在好了,有一个可视化的LogParser的UI工具可以使用了!
Log Parser Lizard 1.1, 这是一款用Vc++.net写的logParser增强工具。主要有以下特点:
1、封装了logParser命令,带图形界面,大大降低了LogParser的使用难度。
2、集成了几个开源工具,如log4net等。可以对IIS logs\EventLogs\active directory\log4net\File Systems\T-SQL进行方便的查询。
3、集成了Infragistics.UltraChart.Core.v4.3、Infragistics.Excel.v4.3.dll等,使查询结果可以方便的以图表或EXCEL格式展示。
4、集成了常见的查询命令,范围包含六大模块:IIS
5、可以将查询过的命令保存下来,方便再次使用。
最重要的是,它是完全免费的。
下载地址为:
http://www.brothersoft.com/log-parser-lizard-download-238815.html
需要先安装LogParser 2.2,下载地址:
http://www.microsoft.com/DownLoads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en
下载后4.9M的一个MSI文件,直接安装即可。注意安装位置尽量不要带空格。
注意:如果LogParser没有安装,会运行出错!

初始界面如图:


下面以一个检查IISLog的例子来比较LogParser与LogParserLizard的区别。
某日早上,发现网站阻塞,
第一种速查方案:
迅速启动LogParser:
进入命令行模式:
输入:

LOGPARSER -i:IISW3C file:D:\Log\log_SQL\Slowest20FilesInIIS_MySite.sql -o:DataGrid -q:off

其中,Slowest20FilesInIIS_MySite.sql的内容如下:

Code

执行结果如图:

从图中可以看出,访问最慢而且最频繁的页面是/Company/List.aspx, 而且集中在一个IP: 116.7.16.249  ,基本可以肯定主·这是有人恶意爬数据
,再输入:

LOGPARSER -i:IISW3C file:D:\Log\log_SQL\Slowest10IPInIIS_MySite.sql -o:DataGrid -q:off

 其中,Slowest10IPInIIS_MySite.sql的内容如下:

Code


启动管理工具,禁IP!!!
第二种速查方案:
启动logParserLizard,在界面中选择IIS Log–"New Query"–格式选"IIS W3C Logs"
在查询窗口输入同样的SQL,点击"Generate"

Code

,得到如下结果:

再在查询窗口输入

Code

得到下图:

且慢,右上角,Chart,打开一看,好多的饼图哟,

处理方法同上
为了下次方便使用,将该query保存即可。
此外 用户除了前面的六大类型查询外,还可以自增加新的Group,把自己常用的查询整理一下。
本文至此结束。有任何问题请联系邀月3w@live.cn。助人等于自助!   3w@live.cn

[MVC]ASP.NET MVC 开源博客

mikel阅读(585)

转载:http://www.dsjian.com/10002/00191

前段时间学习ASP.NET MVC的一个小小成果。能支持搭建个人博客,具体细节如下:

1、多层架构
以面向接口的开发模式,将前端页面、服务层、数据库层完全解耦。为什么要这样做,我想既然用MVC模式来搭建系统,目的就是构建低耦合的应用程序,就因充分发挥其系统结构清晰、高可测试性、高效率开发的特点。

2、ORM使用MySoft.Data
选择一个好的ORM工具,可以大大的提高开发效率。在这个项目中,我选择了MySoft.Data, 为什么选择这个ORM框架,有兴趣的可以看看之前我写的文章。对于数据库层,使用了经典的Repository模式,这样可以使你可以方便的替换ORM框 架,你可以很方便的重新构建一个数据访问层。只要你遵守我的IRepository接口规范,就可以通过WebConfig配置的方式替换数据访问层。

3、DsJian.OC — 简易的IOC容器
根据我对IOC的理解,实现了一个自认为简易的IOC容器(但不知道真正意义上的IOC具体是什么样子,也没有深入的研究)。实现IOC容器的目的也就是为了将数据访问层、服务层、Web层中的对象管理起来,实现依赖注入,最终目的也是构建低耦合的应用程序。

4、脚本框架用JQeury
这也是ASP.NET MVC推荐的脚本框架,个人认为JQuery也是不错的,轻量级但功能强大的脚本框架。

有兴趣的朋友,可以在这里下载源码:
http://dsjian.codeplex.com

特别提醒,UI我套用了wordpress的 iNove 主题。
演示:http://demo.dsjian.com/

代码配置说明,看这里

[EXT]Ext、以及Coolite下实现表格锁定列和多行表头

mikel阅读(730)

转载:http://www.cnblogs.com/zhujiechang/archive/2009/11/10/1599930.html

      这段时间我们的项目中采用了Coolite来做富客户端,Coolite目前的版本是0.8.1,它所采用的Ext版本是2.2.1,因此下面主要是说明 在这个版本下所实现的锁定列和多行表头,需要说明的是Ext在2.0版本以后就不自带锁定列功能了,只能自己实现,中国人的习惯和国外的不太一样,喜欢表 格化得数据,而且形式又多种,表格最基本的功能之一,就是要锁定列和多行表头,而这两项又是Coolite和Ext所提供的组件中所没有直接包含的,虽然 他们这两个框架没有提供,但依然要说的是Ext的js功能很强大, Coolite为Ext在ASP.NET上的应用封装了绝大多数功能,还扩展了一部分,庞大的功能模块,非常不错,可以和官方开源封装的java版 GWT2.0媲美。
     看了些废话,讲入主题了,看下效果图:

    

     实现锁定列目前较为常见的是继承Grid,然后重写模块,实现双表格,下面的示例是采用MeDavid所实现的锁定列方式,版本为[Update 5],Ext的代码很长就贴点主要和修正的,例子在后面有完整提供。Ext为这些控件都提供了一个模板的方式以供重写,就是重写了主模板.

 


 1 initTemplates : function(){
 2         if(!this.templates){
 3             this.templates = {};
 4         }
 5         if(!this.templates.master){
 6             this.templates.master = new Ext.Template(
 7                 '<div class="x-grid3" hidefocus="true">',
 8                     '<div class="x-grid3-locked">',
 9                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{lockedHeader}</div></div><div class="x-clear"></div></div>',
10                         '<div class="x-grid3-scroller"><div class="x-grid3-body">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
11                     '</div>',
12                     '<div class="x-grid3-viewport">',
13                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{header}</div></div><div class="x-clear"></div></div>',
14                         '<div class="x-grid3-scroller"><div class="x-grid3-body">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
15                     '</div>',
16                     '<div class="x-grid3-resize-marker"> </div>',
17                     '<div class="x-grid3-resize-proxy"> </div>',
18                 '</div>'
19             );
20         }
21         Ext.grid.LockingGridView.superclass.initTemplates.call(this);
22     },
23 

     同时为ColumnModel增加getTotalLockedWidth方法,原有[Update 5]中的脚本中Ext.grid.LockingColumnModel要去掉。


1 Ext.grid.ColumnModel.prototype.getTotalLockedWidth=function(){
2         var totalWidth = 0;
3         for(var i = 0; i < this.config.length; i++){
4             if(this.isLocked(i) && !this.isHidden(i)){
5                 totalWidth += this.getColumnWidth(i);
6             }
7         }
8         return totalWidth;
9     };

      另外,版本为[Update 5]目前支持行选择模式有点BUG,就是在获得焦点时行会移动,这是因为没有重新定位造成的,在Ext.grid.LockingGridView中加入以下方法即可修正。

 


1 getResolvedXY : function(resolved){
2         if(!resolved){
3             return null;
4         }
5         var c = resolved.cell, r = resolved.row;
6         return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
7     },
8 

     多行表头分别对应有两个文件,一个是针对有锁定列的表格,一个是针对没有锁定列的表格,两个表格输出的对象是不一样的,要经过判断来输出不一样的值,至于实现的方式大家看下代码就知道了。

     下面把它融入进Coolite中。新建一个ExtGridPanel继承自GridPanel,然后重写OnPreRender如下:

 


 1 protected override void OnPreRender(EventArgs e)
 2         {
 3             //判断是否有锁定列
 4             bool haveLock=false;
 5             ExtColumn extcolumn;
 6             int i;
 7             for (i = 0; i < ColumnModel.Columns.Count; i++)
 8             {
 9                 extcolumn = ColumnModel.Columns[i] as ExtColumn;
10                 if (extcolumn == null)
11                     break;
12                 else if (extcolumn.Locked)
13                 {
14                     haveLock = true;
15                     break;
16                 }
17             }            
18             //判断是否有多行表头            
19             foreach (Plugin plugin in Plugins)
20             {
21                 if (plugin is ExtGroupHeaderGrid)
22                 {
23                     //如果表格是由锁定的,那一样通知插件也是要求锁定的
24                     //这里用一个属性进行区分
25                     ((ExtGroupHeaderGrid)plugin).Locked = haveLock;                    
26                     break;
27                 }
28             }
29 
30             System.ComponentModel.AttributeCollection attrscoll = TypeDescriptor.GetAttributes(thistrue);
31 
32             //注册js
33             if (haveLock)
34             {
35                 //锁定列之前的列也必须锁定
36                 for (int j = 0; j < i; j++)
37                 {
38                     extcolumn = ColumnModel.Columns[j] as ExtColumn;
39                     if (extcolumn != null)
40                         extcolumn.Locked = haveLock;                    
41                 }
42                 ((InstanceOfAttribute)attrscoll[typeof(InstanceOfAttribute)]).ClassName = "Ext.grid.LockingEditorGridPanel";
43                 ScriptManager.RegisterClientScriptInclude(base.GetType(), "CDPFLibrary.Build.extjs.Grid.LockingGridPanel.js");
44             }
45             else
46             {
47                 ((InstanceOfAttribute)attrscoll[typeof(InstanceOfAttribute)]).ClassName = "Coolite.Ext.GridPanel";
48             }
49              
50             base.OnPreRender(e);
51         }

     同时新增了几个类,新增类如下:ExColumnModel、ExtColumn、ExtGridPanel、ExtGroupHeaderGrid、 ExtRow、ExtRowCollection、ExtRows、ExtRowsCollection这些,具体代码都在源码中可以自己分析。
因为同一个表格同时要支持锁定列和非锁定列,因为功能不同输出的js也不一样,那么InstanceOfAttribute的ClassName类名要进行变动,所以要采用TypeDescriptor.GetAttributes来取得属性,这样可以保存当前实例所进行修改过后的自定义属性值,而用Type. GetCustomAttributes只能取得实例定义时的值,不能获得当前实例改动后的值,这一点要在Coolite.Ext.Web程序集的WebControl_Helpers类的GetClientConstructor方法和InstanceOf属性中进行修改,个人认为它是一个明显的BUG。

     其实涉及的变动的地方还挺多,上面仅仅是关键点,源码随文章发布,Coolite的源码可以去官方下来,下面也有链接。

     下载:锁定列和多表头源码    Coolite源码0.8.1
     项目需要用Vs2008打开,需要ASP.NET3.5支持,欢迎发表意见。

[C#]解除具体依赖的技术

mikel阅读(774)

转载:http://www.cnblogs.com/wayfarer/archive/2009/11/10/1600467.html

一 个外部具体对象的引入,必然会给一个模块带来与外部模块之间的依赖。而具体对象的创建始终是我们无法规避的。即使我们可以利用设计模式的工厂方法模式或抽 象工厂封装具体对象创建的逻辑,但却又再次引入了具体工厂对象的创建依赖。虽然在设计上有所改进,但没有彻底解除具体依赖,仍让我心有戚戚焉。

以 一个电子商务网站的设计为例。在该项目中要求对客户的订单进行管理,例如插入订单。考虑到访问量的关系,系统为订单管理提供了同步和异步的方式。显然,在 实际应用中,我们需要根据具体的应用环境,决定使用这两种方式的其中一种。由于变化非常频繁,因而我们采取了“封装变化”的设计思想。譬如,考虑应用Strategy模式,因为插入订单的行为,实则就是一种插入订单的策略。我们可以为此策略建立抽象对象,如IOrderStrategy接口。

public interface IOrderStrategy

{

   void Insert(OrderInfo order);

}

然后分别定义两个类OrderSynchronousOrderAsynchronous实现IOrderStrategy接口。类结构如图1所示。

image 1  订单策略的设计

当领域对象Order类需要插入订单时,将根据IOrderStrategy接口的运行期类型,执行相关的订单插入策略,如下代码所示。

public class order

{

    private IOrderStrategy m_orderStrategy;

    public order(IOrderStrategy orderStrategy)

   {

          m_orderStrategy = orderStrategy;

   }

     public void Insert(OrderInfo order)

   {

          m_orderStrategy.Insert(order);

   }

}

由于用户随时都可能会改变插入订单的策略,因此对于业务层的订单领域对象而言,绝不能与具体的订单策略对象产生耦合关系。也就是说,在领域对象Order类中,不能new一个具体的订单策略对象,如下面的代码:

IOrderStrategy orderStrategy = new orderSynchronous();

虽然在前面的实现中,我们通过领域对象的构造函数传递了IOrderStrategy接 口对象。但这样的实现仅仅是将具体订单策略对象的创建推迟到了领域对象的调用者那里而已,调用者无法避免具体订单策略对象的创建。显然,这是一种“治标不 治本”的做法。我们当然也期望能够有一种理想的状态,就是具体对象的创建永远都不要在代码中出现。事实上,模块与模块间之所以产生依赖关系,正是因为有具 体对象的存在。一旦在一个模块中创建了另一个模块中的具体对象,依赖就产生了。现在,我们的目的就是要将这些依赖消除。

1、配置文件与反射技术

 

 

 

使用硬编码方式创建一个对象,必然会带来对象之间的具体依赖。一种最简单的方式是将反射技术与配置文件相结合,在具体对象拥有共同抽象的前提下,通过配置文件获得具体对象的类型信息,然后利用反射创建相应的对象。例如,在领域对象Order类中,可以如此实现:

public class order

{

   private static readonly IOrderStrategy orderInsertStrategy =

        LoadInsertStrategy();

   private static IOrderStrategy LoadInsertStrategy()

   {

           //通过配置文件找到具体的订单策略对象

               string path = ConfigurationManager.AppSettings["OrderStrategyAssembly"];

 

 

 

               string className = ConfigurationManager.AppSettings["OrderStrategyClass"];

 

 

 

 

 

            //通过反射创建对象实例

            return (IOrderStrategy)Assembly.Load(path).CreateInstance(className);

 

 

 

   }

}

在配置文件web.config中,配置如下的节:

<add key="OrderStrategyAssembly" value="AgileDon.BLL"/>

<add key="OrderStrategyClass"     value="BLL.OrderSynchronous"/>

通过引入泛型,我们可以对前面的逻辑进行有效的封装,例如定义如下的工厂辅助类。

public static class FactoryHelper<T>

    where T:class

{

    private static T instance = null;

 

 

    public static T Create(string typeNameKey,

                                 string nameSpace,

                                 string assemblyPath)

    {

        if (instance == null)

        {

            string typeName = ConfigurationManager.AppSettings[typeNameKey];

            string className = nameSpace + "." + typeName;

 

 

            instance = (T)Assembly.Load(assemblyPath).

                                CreateInstance(className);

        }

 

 

        return instance;

    }

}

注意, Create()辅助方法中的typeNameKey,是指向具体对象类型的键值。通常建议将其键值赋值为具体对象类型的抽象接口类型名,而对应的值则是目标创建对象的类型名。例如:

<add key="IOrderStrategy"    value="OrderSynchronous"/>

然后,我们可以为属于相同命名空间的类统一定义工厂类,并在其中调用工厂辅助类FactoryHelperCreate()辅助方法。例如,为业务逻辑层的对象定义工厂类BLLFactory

    public static class BLLFactory<T>

        where T:class

    {

        public static T Create(string typeNameKey)

        {          

            string nameSpace = ConfigurationManager.AppSettings["BLLAssembly"]

            string assemblyPath = ConfigurationManager.AppSettings["BLLPath"];

            return BaseFactory<T>.CreateT(

                          typeNameKey, nameSpace, assemblyPath);

        }

}

针对订单策略对象,对应的配置文件为:

<add key="BLLAssembly"      value="AgileDon.BLL"/>

<add key="BLLPath"          value="AgileDon.BLL"/>

<add key="IOrderStrategy"  value="OrderSynchronous"/>

现在,我们就可以调用BLLFactory类的Create ()方法,传入类型名以获得具体的对象。例如:

IOrderStrategy orderInsertStrategy = BLLFactory<IOrderStrategy>.Create(

    "IOrderStrategy");

如果需要将订单插入策略从同步修改为异步方式,只需将配置文件中IOrderStrategy键对应的值修改为"OrderAsynchronous"即可。

2、表驱动法

 

 

 

借鉴表驱动法【注:参见Steve McConnell著作《代码大全》第18章】的思想,我们可以利用一个Dictionary集合来维护目标对象与键值之间的映射关系。当我们需要获得对象时,可以利用键值对表进行查询,这样就可以有效地消除if语句。例如,可以在Strategy模式中使用表驱动法,将其作为模式的上下文对象,而不必执行对策略对象类型的逻辑判断。利用表驱动法,我们也可以解除对象之间的具体依赖。

仍然以订单的管理为例。我为订单的管理专门定义了一个OrderManager类,它负责初始化并维持对象表。

public static class orderManager

{

    private static IDictionary<string,IOrderStrategy> m_strategyTable;

    static orderManager()

    {

        Init();

    }

    private static void Init()

    {

        m_strategyTable = new Dictionary<string,IOrderStrategy>();

        m_strategyTable.Add("sync",new orderSynchronous());

        m_strategyTable.Add("async",new orderAsynchronous());

    }

 

 

    public static IOrderStrategy GetOrderStrategy(string strategyKey)

    {

        IOrderStrategy strategy;

 

 

        if (m_strategyTable.TryGetValue(strategyKey, out strategy))

        {

            return strategy;

        }

        else

        {

            throw new Exception("无法找到正确的订单策略对象");

        }

    }

}

在调用OrderManagerGetOrderStrategy()方法时,为提供更好的灵活性,寻找策略对象的键值应该放在配置文件中,以避免修改源代码。

string strategyKey = ConfigurationManager.AppSettings["StrategyKey"]

IOrderStrategy strategy = orderManager.GetOrderStrategy(strategyKey);

我们甚至可以提供一个注册方法RegisterStrategy(),用以应对未来可能的扩展。

public static class orderManager

{

    //其余实现略

 

 

    public static void RegisterStrategy(

          string strategyKey,

          IOrderStrategy strategy)

    {

        if (String.IsNullOrEmpty(strategyKey))

        {

            throw new ArgumentNullException(strategyKey);

        }

        if (strategy == null)

        {

            throw new ArgumentNullException("策略对象不能为null");

        }

        if (m_strategyTable.ContainsKey(strategyKey))

        {

            throw new ArgumentException("已经存在键值" + strategyKey);

        }

        m_strategyTable.Add(strategyKey,strategy);

    }

}

3、依赖注入

 

 

 

依赖注入(Dependency Injection) 是一个漂亮的隐喻。依赖关系就像是被注入的液体,我们可以在任何时候将依赖关系注入到模块中,而不只限于在编译时绑定。既然这种依赖关系是通过注入的方式 完成,就意味着我们可以随时更新,因为注入的液体与模块并无直接关联。实现依赖注入的前提是面向接口编程,而辅助的技术则是利用反射技术。

依赖注入是目前大多数轻量级IoC(控制反转,Inversion of Control)容器用于解除外部服务与容器服务之间依赖关系的一把利刃。首先,容器服务包含了外部服务接口的定义。然后,依赖注入通过使用一个专门的装配器对象,提供外部服务的具体实现,并其赋值给对应的容器服务对象。Martin Fowler将依赖注入的形式分为三种:构造函数注入(Constructor Injection)、设置方法注入(Setter Injection)和接口注入(Interface Injection)。 其中,接口注入通过定义接口约束的方式实现依赖注入,会给容器带来设计的限制。而构造函数注入与设置方式注入则表现了产生依赖的两个连接点:构造函数与属 性。如果构造函数参数或属性对象的类型为抽象的接口类型,则产生具体依赖的源头在于具体对象的创建。将创建具体对象的职责转移到IoC容器,就可以在运行时为构造函数参数或属性对象传递依赖。

目前,实现了依赖注入的轻量级容器已经应用在许多框架产品中,如Java平台下的SpringPicoContainer等。在.NET平台下,常见的依赖注入框架包括AutoFacNinjectSpring.NETStructureMapWindsor等。

Ninject框架为例,我们可以定义这样的Order类:

public class order

{

    private IOrderStrategy m_strategy;

 

 

    public order(IOrderStrategy strategy)

    {

        m_strategy = strategy;

    }

    public void Insert(OrderInfo order)

    {

        m_strategy.Insert(order);

    }

}

然后,我们需要自定义一个OrderModule类,它派生自Ninject.Core.StandardModule类。这是Ninject实现依赖注入的一个特色,它抛弃了传统的xml映射文件,而是利用类型绑定的方式,并根据要创建的具体对象分组建立对应的Module类。注意,它不同于之前的解耦方法,因为它对业务逻辑代码没有造成任何侵略与污染。如上定义的Order类,保留了领域对象的本来面貌。使得领域层的开发人员可以专心致志着力于业务逻辑的实现。OrderModule类的定义如下所示:

using Ninject.Core;

 

 

public class orderModule:StandardModule

{

    public override void Load()

    {

        Bind<IOrderStrategy>().To<OrderSynchronous>();

    }

}

客户端调用的代码可以通过Ninject提供的IKernel对象获得Order对象:

OrderModule module = new orderModule();

IKernel kernal = new StandardKernel(module);

 

 

Order order = kernal.Get<Order>();

order.Insert(new orderInfo());

4、惯例优于配置

 

 

 

使 用配置文件固然可以解除与具体对象之间的依赖,然而,它带来的良好可扩展性,却是以牺牲系统的可维护性乃至于可靠性为代价的。配置文件很难管理,尤其是在 配置信息相对较多的情况下。不管是集中管理还是分散管理,都存在一些与生俱来的缺陷。如果采用集中管理,则配置文件过大,既影响性能,也不能很好地展现配 置信息的分类与层次。在.NET中,虽然可以利用<section></section>对 配置文件进行分节,但终究不够直观。采用分散管理,则不同大小的配置文件千头万绪,既会给维护者带来管理的障碍,也不利于部署与使用。使用配置文件尤其不 便于调试。开发环境提供的编译期检查,对于配置文件只能是“望洋兴叹”。所谓“差之毫厘,谬以千里”,小小的一个配置项错误,可能会造成难以弥补的巨大损 失。为了弥补这些缺陷,许多产品或框架都提供了专门的配置或管理工具,使用直观的UI界面对配置文件进行操作,但繁杂的配置项仍然有可能让使用者望而却步。

惯例优于配置(Convention over Configuration)来源于Ruby On Rails框架的设计理念,也被认为是Rails大获成功的关键因素之一。这里所谓的惯例,可以理解为框架对编程的一些约束,我们可以根据实现制订的默认规则,通过反射技术完成对象的创建,对象的协作,甚至是应用程序的组装。例如在Rails中对MVC模式的实现中,就事先确立了ModelViewController的目录结构与命名规范。在这种情况下,我们不需要对元数据进行任何配置。ASP.NET MVC框架同样采纳了惯例优于配置的思想。采用惯例,虽然在一定程度上损失了系统的灵活性,带来的却是良好的可维护性。同时,它仍然可以解除系统与具体对象之间的强耦合关系。

惯例优于配置的技术并不是非常适合于本文中的订单策略示例。不过,在.NET框架中,有关WebRequest对象的创建,却可以改用惯例优于配置的思想来实现。图2WebRequest对象的继承体系:

image

2 WebRequest的类结构

.NET框架中,创建一个WebRequest实例的方法是调用WebRequest的静态方法Create()

WebRequest myRequest = WebRequest.Create("http://www.agiledon.com");

由于,传入的Uri地址其前缀为"http",因此创建的myRequest对象应该为HttpWebRequest具体对象。如果需要根据不同的Request协议,扩展不同的WebRequest对象,就需要引入一些设计技巧,来解除与具体对象创建的依赖。.NET框架的实现能够达到这样的目的,但非常复杂,这里不提。我想要介绍的是如何利用惯例优于配置来实现WebRequest对象的扩展。利用“惯例优于配置”的思想有一个前提,就是我们要对WebRequest对象的命名规范进行惯例约束。例如,我们规定所有的WebRequest子类对象均由协议名加上“WebRequest”后缀构成。通过解析传入的Uri,可以获得传输协议的名称,之后将它与“WebRequest”连接起来,获得WebRequest子类对象的类名,再利用反射技术创建该对象。在WebRequest类中定义如下的Create()静态方法:

public static WebRequest Create(Uri requestUri)

{

   if (requestUri == null)

   {

                 throw new ArgumentNullException("requestUri");

   }

   string prefix = requestUri.Scheme.ToLower();

              if (prefix == null)

   {

                 throw new ArgumentNullException("requestUri");

   }

   if (prefix.Contains(""))

   {

          prefix = prefix.Replace(".","");

   }

   StringBuilder typeName = new StringBuilder();

   typeName.Append("System.Net.");

   typeName.Append(prefix.Substring(0,1).ToUpper());

   typeName.Append(prefix.ToLower().Substring(1,prefix.Length – 1));

   typeName.Append("WebRequest");

 

 

   return (WebRequest)Activitor.CreateInstance(

          System.Type.GetType(typeName));

}

只要WebRequest的子类对象能够遵循我们的惯例,即该类的类型名符合事先制订的规范,改进后的Create()方法就能够运行良好。以新增Tcp协议的WebRequest对象为例。该协议的Schema为“net.tcp”,因此其类名必须为“NettcpWebRequest”,并放在“System.Net”命名空间下。如果客户端调用WebRequest.Create()方法,并传入“net.tcp://www.agiledon.com”值,则Create()方法就会对该Uri地址进行解析,获得完整的类型名为“System.Net.NettcpWebRequest”,然后,利用反射技术创建该对象。采用“惯例优于配置”的方式,可以极大地简化工厂方法的实现代码,抛弃了繁琐的设计理念,具有非常灵活的扩展性以及良好的代码可读性。或许,唯一的遗憾是由于反射技术带来的性能损耗。

利用抽象的方式封装变化,固然是应对需求变化的王道,但它也仅仅能解除调用者与被调用者之间的耦合关系。只要还涉及具体对象的创建,即使引入了创建型模式,例如Factory Method模 式,具体工厂对象的创建依然是必不可少的。不要小看这一点点麻烦,需知“千里之堤,溃于蚁穴”,牵一发而动全身,小麻烦可能会酿成大灾难。对于那些业已被 封装变化的对象,我们还应该学会利用诸如“依赖注入”、“表驱动法”等技术,彻底解除两者之间的耦合;至于选择何种技术,则需要根据具体的应用场景做出判 断。当然,模块或对象解耦的重要前提,则源于封装变化,要求我们针对接口编程,而不是实现。这也是GOF提出的面向对象设计原则。

[工具]VS2008 快捷键大全

mikel阅读(519)

转载:http://hi.baidu.com/crp8/blog/item/1617262ad20b543c5343c1c7.html
Ctrl+m+Crtr+o折叠所有大纲
Ctrl+M+Crtr+P: 停止大纲显示
Ctrl+K+Crtr+C: 注释选定内容
Ctrl+K+Crtr+U: 取消选定注释内容
Ctrl+J : 列出成员 智能感知
Shift+Alt+Enter: 切换全屏编辑
Ctrl+B,T / Ctrl+K,K: 切换书签开关
Ctrl+B,N / Ctrl+K,N: 移动到下一书签
Ctrl+B,P: 移动到上一书签
Ctrl+B,C: 清除全部标签
Ctrl+I: 渐进式搜索
Ctrl+Shift+I: 反向渐进式搜索
Ctrl+F: 查找
Ctrl+Shift+F: 在文件中查找
F3: 查找下一个
Shift+F3: 查找上一个
Ctrl+H: 替换
Ctrl+Shift+H: 在文件中替换
Alt+F12: 查找符号(列出所有查找结果)
Ctrl+Shift+V: 剪贴板循环
Ctrl+左右箭头键: 一次可以移动一个单词
Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。
Ctrl+Shift+L: 删除当前行
Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态
Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态
Ctrl+E,S: 查看空白
Ctrl+E,W: 自动换行
Ctrl+G: 转到指定行
Shift+Alt+箭头键: 选择矩形文本
Alt+鼠标左按钮: 选择矩形文本
Ctrl+Shift+U: 全部变为大写
Ctrl+U: 全部变为小写
代码快捷键
Ctrl+Shift+空格键 / Ctrl+K,P: 参数信息
Ctrl+K,I: 快速信息
Ctrl+E,U / Ctrl+K,U: 取消选定注释内容
Ctrl+K,M: 生成方法存根
Ctrl+K,X: 插入代码段
Ctrl+K,S: 插入外侧代码
F12: 转到所调用过程或变量的定义
窗口快捷键
Ctrl+W,W: 浏览器窗口
Ctrl+W,S: 解决方案管理器
Ctrl+W,C: 类视图
Ctrl+W,E: 错误列表
Ctrl+W,O: 输出视图
trl+W,P: 属性窗口
Ctrl+W,T: 任务列表
Ctrl+W,X: 工具箱
Ctrl+W,B: 书签窗口
Ctrl+W,U: 文档大纲
Ctrl+D,B: 断点窗口
Ctrl+D,I: 即时窗口
Ctrl+Tab: 活动窗体切换
Ctrl+Shift+N: 新建项目
Ctrl+Shift+O: 打开项目
Ctrl+Shift+S: 全部保存
Shift+Alt+C: 新建类
Ctrl+Shift+A: 新建项
Shift+Alt+Enter: 切换全屏编辑
Ctrl+B,T / Ctrl+K,K: 切换书签开关
Ctrl+B,N / Ctrl+K,N: 移动到下一书签
Ctrl+B,P: 移动到上一书签
Ctrl+B,C: 清除全部标签
Ctrl+I: 渐进式搜索
Ctrl+Shift+I: 反向渐进式搜索
Ctrl+F: 查找
Ctrl+Shift+F: 在文件中查找
F3: 查找下一个
Shift+F3: 查找上一个
Ctrl+H: 替换
Ctrl+Shift+H: 在文件中替换
Alt+F12: 查找符号(列出所有查找结果)
Ctrl+Shift+V: 剪贴板循环
Ctrl+左右箭头键: 一次可以移动一个单词
Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。
Ctrl+Shift+L: 删除当前行
Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态
Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态
Ctrl+M,P: 停止大纲显示
Ctrl+E,S: 查看空白
Ctrl+E,W: 自动换行
Ctrl+G: 转到指定行
Shift+Alt+箭头键: 选择矩形文本
Alt+鼠标左按钮: 选择矩形文本
Ctrl+Shift+U: 全部变为大写
Ctrl+U: 全部变为小写

[NUnit]Nunit中如何进行事务性单元测试

mikel阅读(423)

单元测试要求:单元测试方法并不真正去变更数据库,也就是说单元测试不依赖于数据库中的数据。那我们如何解决执行单元测试方法后,不变更数据库中数据呢?

一般的解决方案有两种:

 

1、 新建一个单元测试数据库,开发数据库与单元测试数据库分离,单元测试方法完全基于单元测试数据库。

此中方法的优点是:,开发人员在开发期间不会对单元测试数据库中数据进行变更,也就不会影响单元测试方法 在任何时间执行。

缺点:单元测试数据库和开发数据库同步问题,特别是对迭代式开发项目,数据库是根据需求在不断地跟进或者变更,同步问题成为了单元测试正常运行的瓶颈。

 

2、 使用事务对单元测试方法的执行进行回滚。

此种方法的优点:解决了方法一中缺点,不会出现数据库结构不同步的问题。

缺点:在进行CRUD(Create/Read/Update/Delete操作时,需要在单元测试方法中进行一些插入数据操作,从而保证单元测试与开发数据库的独立,造成了单元测试工作量增加。

 

在实际的项目中,可以根据需要选择符合自己的解决方案,如果数据库结构在项目进入开发阶段已经确定,并且以后不会有变动,建议采用第一种方案,否则建议第二种方案。目前我们项目采用第二中方案。

一、NUnit事务性单元测试

 

那使用Nunit框架如果保证数据的会滚呢?这里我们使用了COM+事务。

System.EnterpriseServices;

具体如下:

/// <summary>

    ///单元测试基类,所有单元测试类都需要继承此类

    /// </summary>

    [TestFixture]

    [Transaction(TransactionOption.Required)]

    public class DatabaseFixture:ServicedComponent

    {

        public DatabaseFixture()

        {

            //

            // TODO: Add constructor logic here

            //

        }

        [TearDown]

        public void TransactionTearDown()

        {

            if (ContextUtil.IsInTransaction)

            {

                ContextUtil.SetAbort();

            }

        }

所有的单元测试方法都需要继承与此类。比如:

public class AddressSQLDAOTest : DatabaseFixture

这样,单元测试方法执行完后,会继续执行DatabaseFixture类中的TransactionTearDown()方法。从而会滚之前的数据操作,单元测试方法也就不会影响开发数据库,同样开发数据库也不会影响单元测试方法的执行,从而保证了单元测试与数据库数据的独立。

 

二、如何CRUD单元测试

1、测试增加方法:判断返回的主键是否>0,如果主键>0 说明单元测试方法成功,否则失败

2、测试查询方法:首先在执行单元测试类中的插入数据方法(不是被测试类中的插入方法,而是在单元测试类中写的插入方法,一定要区分开),然后执行查询方法。

3、测试更新方法:首先在执行单元测试类中的插入数据方法,然后执行更新方法。

4、测试删除方法:首先在执行单元测试类中的插入数据方法,然后执行删除方法。

 

三、单元测试的命名规范

为了便于后期单元测试方法的维护,建议如下命名单元测试类 和单元测试方法。

单元测试类名:被测试类名称+Test

单元测试方法名:被测试方法名称+Test

四、总结

至此,大家就可以利用Nunit中如何进行事务性单元测试已经完毕,相信大家也已经了解了如何让单元测试独立于数据库数据,从而更高效地进行单元测试,也不影响开发。

 

版权

作者:灵动生活

出处:http://www.cnblogs.com/ywqu

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

 

[算法]遗传编程算法

mikel阅读(734)

转载;http://www.cnblogs.com/zgw21cn/archive/2009/11/07/1598238.html

假设从向银行申请贷款的顾户中,要选出优质顾客。怎么做?

现在有学习数据如下

ID

孩子个数

薪水

婚姻状况

是否优质顾客?

ID-1

2

45000

Married

0

ID-2

0

30000

Single

1

ID-3

1

40000

Divorced

1

       

如果从学习数据中学习出如下规则

IF (孩子个数(NOC) = 2) AND (薪水(S) > 80000) THEN 优良顾客 ELSE 不良顾客。

这条规则以一条树的形式可以表现如下。

image

遗传编程(genetic programming)基于遗传算法,传统的遗传算法是用定长的线性字符串表示一个基因。而遗传编程基于树的形式,其树的深度和宽度是可变的。树可以轻易表达算术表达式,逻辑表达式,程序等。例如

(1)算术表达式image

表示成树为

image

(2) 逻辑表达式:(x Ù true) ® (( x Ú y ) Ú (z « (x Ù y)))。可以由树表达为

image

(3)程序

i =1;

while (i < 20){

i = i +1

}

可以表示为

image

正因为遗传编程中,以树的形式来表达基因,因此遗传编程更适于表达复杂的结构问题。其用武之地也比遗传算法广泛得多了。开始的银行寻找优良顾客就是其中一例子。

遗传编程算法的一个最为简单的例子,是尝试构造一个简单的数学函数。假设我们有一个包含输入和输出的表,如下

x

y

Result

2

7

21

8

5

83

8

4

81

7

9

75

7

4

65

其背后函数实际上是x*x+x+2*y+1。现在打算来构造一个函数,来拟合上述表格中的数据。

首先构造拟合数据。定义如下函数。

 

 

 

 

def examplefun(x, y):
return x * x + x + 2 * y + 1
def constructcheckdata(count=10):
checkdata = []
for i in range(0, count):
dic = {}
x = randint(0, 10)
y = randint(0, 10)
dic['x'] = x
dic['y'] = y
dic['result'] = examplefun(x, y)
checkdata.append(dic)
return checkdata

实际上一棵树上的节点可以分成三种,分别函数,变量及常数。定义三个类来包装它们:

class funwrapper:
def __init__(self, function, childcount, name):
self.function = function
self.childcount = childcount
self.name = name
class variable:
def __init__(self, var, value=0):
self.var = var
self.value = value
self.name = str(var)
self.type = "variable"
def evaluate(self):
return self.varvalue
def setvar(self, value):
self.value = value
def display(self, indent=0):
print '%s%s' % (' '*indent, self.var)
class const:
def __init__(self, value):
self.value = value
self.name = str(value)
self.type = "constant"
def evaluate(self):
return self.value
def display(self, indent=0):
print '%s%d' % (' '*indent, self.value) 

现在可以由这些节点来构造一棵树了。

class node:
def __init__(self, type, children, funwrap, var=None, const=None):
self.type = type
self.children = children
self.funwrap = funwrap
self.variable = var
self.const = const
self.depth = self.refreshdepth()
self.value = 0
self.fitness = 0
def eval(self):
if self.type == "variable":
return self.variable.value
elif self.type == "constant":
return self.const.value
else:
for c in self.children:
result = [c.eval() for c in self.children]
return self.funwrap.function(result)
def getfitness(self, checkdata):#checkdata like {"x":1,"result":3"}
diff = 0
#set variable value
for data in checkdata:
self.setvariablevalue(data)
diff += abs(self.eval() - data["result"])
self.fitness = diff
def setvariablevalue(self, value):
if self.type == "variable":
if value.has_key(self.variable.var):
self.variable.setvar(value[self.variable.var])
else:
print "There is no value for variable:", self.variable.var
return
if self.type == "constant":
pass
if self.children:#function node
for child in self.children:
child.setvariablevalue(value)
def refreshdepth(self):
if self.type == "constant" or self.type == "variable":
return 0
else:
depth = []
for c in self.children:
depth.append(c.refreshdepth())
return max(depth) + 1
def __cmp__(self, other):
return cmp(self.fitness, other.fitness)
def display(self, indent=0):
if self.type == "function":
print ('  '*indent) + self.funwrap.name
elif self.type == "variable":
print ('  '*indent) + self.variable.name
elif self.type == "constant":
print ('  '*indent) + self.const.name
if self.children:
for c in self.children:
c.display(indent + 1)
##for draw node
def getwidth(self):
if self.type == "variable" or self.type == "constant":
return 1
else:
result = 0
for i in range(0, len(self.children)):
result += self.children[i].getwidth()
return result
def drawnode(self, draw, x, y):
if self.type == "function":
allwidth = 0
for c in self.children:
allwidth += c.getwidth()*100
left = x - allwidth / 2
#draw the function name
draw.text((x - 10, y - 10), self.funwrap.name, (0, 0, 0))
#draw the children
for c in self.children:
wide = c.getwidth()*100
draw.line((x, y, left + wide / 2, y + 100), fill=(255, 0, 0))
c.drawnode(draw, left + wide / 2, y + 100)
left = left + wide
elif self.type == "variable":
draw.text((x - 5 , y), self.variable.name, (0, 0, 0))
elif self.type == "constant":
draw.text((x - 5 , y), self.const.name, (0, 0, 0))
def drawtree(self, jpeg="tree.png"):
w = self.getwidth()*100
h = self.depth * 100 + 120
img = Image.new('RGB', (w, h), (255, 255, 255))
draw = ImageDraw.Draw(img)
self.drawnode(draw, w / 2, 20)
img.save(jpeg, 'PNG')
 其中计算适应度的函数getfitness(),是将变量赋值后计算所得的值,与正确的数据集的差的绝对值的和。Eval函数即为将变量赋值后,计算树的值。构造出的树如下图,可由drawtree()函数作出。 

image

其实这棵树的数学表达式为x*x-3x。

然后就可以由这此树来构造程序了。初始种群是随机作成的。

def _maketree(self, startdepth):
if startdepth == 0:
#make a new tree
nodepattern = 0#function
elif startdepth == self.maxdepth:
nodepattern = 1#variable or constant
else:
nodepattern = randint(0, 1)
if nodepattern == 0:
childlist = []
selectedfun = randint(0, len(self.funwraplist) - 1)
for i in range(0, self.funwraplist[selectedfun].childcount):
child = self._maketree(startdepth + 1)
childlist.append(child)
return node("function", childlist, self.funwraplist[selectedfun])
else:
if randint(0, 1) == 0:#variable
selectedvariable = randint(0, len(self.variablelist) - 1)
return node("variable", None, None, variable(self.variablelist[selectedvariable]), None)
else:
selectedconstant = randint(0, len(self.constantlist) - 1)
return node("constant", None, None, None, const(self.constantlist[selectedconstant]))

当树的深度被定义为0时,表明是从重新开始构造一棵新树。当树的深度达到最高深度时,生长的节点必须是变量型或者常数型。

当然程序不止这些。还包括对树进行变异和交叉。变异的方式的方式为,选中一个节点后,产生一棵新树来代替这个节点。当然并不是所有的节点都实施变异,只是按一个很小的概率。变异如下:

def mutate(self, tree, probchange=0.1, startdepth=0):
if random() < probchange:
return self._maketree(startdepth)
else:
result = deepcopy(tree)
if result.type == "function":
result.children = [self.mutate(c, probchange, startdepth + 1) for c in tree.children]
return result

交叉的方式为:从种群中选出两个优异者,用一棵树的某个节点代替另一棵树的节点,从而产生两棵新树。

 def crossover(self, tree1, tree2, probswap=0.8, top=1):
if random() < probswap and not top:
return deepcopy(tree2)
else:
result = deepcopy(tree1)
if tree1.type == "function" and tree2.type == "function":
result.children = [self.crossover(c, choice(tree2.children), probswap, 0)
for c in tree1.children]
return result

以上变异及交叉都涉及到从现有种群中选择一棵树。常用的选择算法有锦标赛方法,即随机选出几棵树后,按fitness选出最优的一棵树。另一种方法是轮盘赌算法。即按fitness在种群的比率而随机选择。Fitness越大的树,越有可能被选中。如下所列的轮盘赌函数。

 def roulettewheelsel(self, reverse=False):
if reverse == False:
allfitness = 0
for i in range(0, self.size):
allfitness += self.population[i].fitness
randomnum = random()*(self.size - 1)
check = 0
for i in range(0, self.size):
check += (1.0 - self.population[i].fitness / allfitness)
if check >= randomnum:
return self.population[i], i
if reverse == True:
allfitness = 0
for i in range(0, self.size):
allfitness += self.population[i].fitness
randomnum = random()
check = 0
for i in range(0, self.size):
check += self.population[i].fitness * 1.0 / allfitness
if check >= randomnum:
return self.population[i], i

其中参数reverse若为False,表明fitness越小,则这棵树表现越优异。不然,则越大越优异。在本例中,选择树来进行变异和交叉时,选择优异的树来进行,以将优良的基因带入下一代。而当变异和交叉出新的子树时,则选择较差的树,将其淘汰掉。

现在可以构造进化环境了。

def envolve(self, maxgen=100, crossrate=0.9, mutationrate=0.1):
for i in range(0, maxgen):
print "generation no.", i
child = []
for j in range(0, int(self.size * self.newbirthrate / 2)):
parent1, p1 = self.roulettewheelsel()
parent2, p2 = self.roulettewheelsel()
newchild = self.crossover(parent1, parent2)
child.append(newchild)#generate new tree
parent, p3 = self.roulettewheelsel()
newchild = self.mutate(parent, mutationrate)
child.append(newchild)
#refresh all tree's fitness
for j in range(0, int(self.size * self.newbirthrate)):
replacedtree, replacedindex = self.roulettewheelsel(reverse=True)
#replace bad tree with child
self.population[replacedindex] = child[j]
for k in range(0, self.size):
self.population[k].getfitness(self.checkdata)
self.population[k].depth=self.population[k].refreshdepth()
if self.minimaxtype == "min":
if self.population[k].fitness < self.besttree.fitness:
self.besttree = self.population[k]
elif self.minimaxtype == "max":
if self.population[k].fitness > self.besttree.fitness:
self.besttree = self.population[k]
print "best tree's fitbess..",self.besttree.fitness
self.besttree.display()
self.besttree.drawtree()  

每次按newbirthrate的比率,淘汰表现不佳的旧树,产生相应数目的新树。每次迭代完后,比较fitness,选出最佳的树。迭代的终止条件是其fitness等于零,即找到了正确的数学表达式,或者迭代次数超过了最大迭代次数。

还有其它一些细节代码,暂且按下不表。自由教程可按这里下载:http://www.gp-field-guide.org.uk/

全部代码可在这里下载:http://wp.me/pGEU6-z

[JQuery]星级评分--jQuery插件

mikel阅读(644)

转载:http://www.cnblogs.com/studyplay/archive/2009/11/08/1598382.html

   以前写过一篇文章JQuery为基础的星星评分 ,今天有时间把这个功能重写,并以JQuery插件的形式出现以便以后使用。 首先看一下运行效果如下图所示。

    鼠标移到星星上该星星前面的所有星星全部变亮,鼠标单击将记录点击的星星数,前面的所有星星将变亮。

    一、原理

    本程序的原理是这样的:一个“ul”标签,该标签的背景为灰色的星星,控制“ul”标签的宽度显示星星的数量。例如:一个星星图片的宽度为23px,那么要显示10个星星,则“ul”的宽度为230px就可以显示10个星星。

    n个“li”标签,n表示您要显示星星的个数,例如你要显示10个星星那么将有10个“li”标签。那么这10个标签的宽度分别为1个星星的宽度 23px,2个星星的宽度46px,……,10个星星的宽度230px。这些“li”标签的背景将为蓝色的星星。

   则另外还有一个“li”标签记录鼠标单击的星星或初始设置。

   这些标签都是重叠在一起的,通过鼠标滑动效果切换这些标签的层叠顺序,显示不同的星星数量,单击记录星星个数。

   二、源码

   $.fn.studyplay_star=function(options,callback){
 //默认设置
 var settings ={
  MaxStar      :20,
  StarWidth    :23,
  CurrentStar  :5,
  Enabled      :true
  }; 
 if(options) { jQuery.extend(settings, options); };
 var container = jQuery(this);
 container.css({"position":"relative"})
 .html('<ul class="studyplay_starBg"></ul>') 
 .find('.studyplay_starBg').width(settings.MaxStar*settings.StarWidth)
 .html('<li class="studyplay_starovering" style="width:'+settings.CurrentStar*settings.StarWidth+'px; z-index:0;" id="studyplay_current"></li>');
 if(settings.Enabled)
 {
 var ListArray = ""; 
 for(k=1;k<settings.MaxStar+1;k++)
 {
  ListArray +='<li class="studyplay_starON" style="width:'+settings.StarWidth*k+'px;z-index:'+(settings.MaxStar-k+1)+';"></li>';
 }
 container.find('.studyplay_starBg').append(ListArray)
 .find('.studyplay_starON').hover(function(){
             $("#studyplay_current").hide();
             $(this).removeClass('studyplay_starON').addClass("studyplay_starovering");
             },
           function(){
            $(this).removeClass('studyplay_starovering').addClass("studyplay_starON");
            $("#studyplay_current").show();
            })
 .click(function(){
     var studyplay_count = settings.MaxStar – $(this).css("z-index")+1;
     $("#studyplay_current").width(studyplay_count*settings.StarWidth)
     //回调函数
     if (typeof callback == 'function') {
     callback(studyplay_count);
     return ;
     }
     })
 } 
}

  这个插件有两个参数一个是options表示插件的一些基本设置;callback表示回调函数,该函数存在一个参数表示用户选择的星星数量。

   三、使用

  如果我们想在id为“z”的div上显示5个星星,默认有一颗星星选中,弹出选择星星的个数对话框,就可以如下编写代码:

<div id="z"></div>

<script type="text/JavaScript">
  $(document).ready(function(){
   $("#z").studyplay_star({MaxStar:5,CurrentStar:2},function(value){alert(value)});
  }); 
</script>

如果想显示评分结果 可以把Enabled设置false就ok了

 四、代码下载

/Files/studyplay/star.rar

[C#]ASP.NET缓存

mikel阅读(776)

原文地址:http://www.codeproject.com/KB/aspnet/AspDotNetCache.aspx

AspDotNetCache2

 

介绍

缓存是在内存存储数据的一项技术,也是ASP.NET中提供的重要特性之一。例如你可以在复杂查询的时候缓存数据,这样后来的请求就不需要从数据库中取数据,而是直接从缓存中获取。通过使用缓存可以提高应用程序的性能。

主要有两种类型的缓存:

1.输出缓存Output caching
2.数据缓存Data caching

 

1. 输出缓存(Output Caching)

使用输出缓存,你可以缓存最后输出的HTML页面,当相同的页面再次请求的时候,ASP.NET不会再执行页面的生命周期和相关代码而是直接使用缓存的页面,语法如下:

<%@ OutputCache Duration=”60” VaryByParam=”None”  %> 

Duration 属性设置页面将被缓存60妙。任何的用户请求都会被缓存,在缓冲的60秒内相同的请求都会直接使用缓存的页面。当缓存过期后ASP.NET会再次执行页面代码并且为下一个60秒创建一个新的HTML缓存。

  <%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
CodeFile="OutputCachingTest.aspx.cs" Inherits="OutputCachingTest" Title="Untitled Page" %>
 <%@ OutputCache Duration="20" VaryByParam="None" %>
 <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<div class="title">Output Cache</div>
Date: <asp:Label ID="lblDate" runat="server" Text="" />
Time: <asp:Label ID="lblTime" runat="server" Text="" />
</asp:Content>
protected void Page_Load(object sender, EventArgs e)
{
lblDate.Text = DateTime.Now.ToShortDateString();
lblTime.Text = DateTime.Now.ToLongTimeString();
}

在这个例子中页面将被缓存20秒。

通过查询字符串缓存(Cache by Query String )

在实际应用中页面往往会根据一些参数动态的改变页面的内容。如果你的页面是通过查询字符串来获取信息的,你可以根据查询字符串很容易的缓存页面的不同拷贝。VarByParam=”None”指定ASP.NET只存储缓存页面的一个拷贝。VarByParam=”*” 指定ASP.NET根据不同的查询字符串存储不同的缓存页面。

<%@ OutputCache Duration="60" VaryByParam="*" %>
<div align="right">
<a href="OutputCachingTest2.aspx">No Query String</a> |
<a href="OutputCachingTest2.aspx?id=1">ID 1</a> |
<a href="OutputCachingTest2.aspx?id=2">ID 2</a> |
<a href="OutputCachingTest2.aspx?id=3">ID 3</a> |
<a href="OutputCachingTest2.aspx?id=3&langid=1">ID 3</a>
</div> 


上面的例子中,在查询字符串中传了不同的ID.ASP.NET为每一个ID都存储了单独的缓存页面。这种方式会有一些问题就是当查询字符串范围很广的时候。
这个时候我们可以在VarByParam 属性中指定重要的查询字符串变量的名字,如下: 

<%@OutputCacheDuration="60"VaryByParam="id;langid"%>

这样,ASP.NET可以根据id” or “langid”来缓存不同的缓存版本。

 

自定义缓存(Custom Caching)

你也可以创建自定义的程序来缓存页面。ASP.NET提供了一种很便捷的方式来创建自定义缓存,使用VarByCustom属性指定自定义缓存类型的名字。

%@OutputCacheDuration="20"VaryByParam="None"VaryByCustom="browser"%

你还要创建为缓存生成自定义字符串的方法,如下:

public override stringGetVaryByCustomString(HttpContext context, stringcustom)
{
    if(custom == "browser")
    {
       returncontext.Request.Browser.Browser +
              context.Request.Browser.MajorVersion;
    }
    else
  
{
       return base.GetVaryByCustomString(context, custom);
    }
}

这个方法必须写在global.asax文件中。ASP.NET使用该方法返回的字符串来实现缓存,如果这个方法在不同的请求中返回相同的字符串,ASP.NET就会使用缓存的页面,否则就会生成新的缓存版本。

上面的例子中GetVaryByCustomString()方法根据浏览器的名字创建缓存字符串,ASP.NET会根据不同的浏览器请求创建不同版本的缓存。

控件缓存(Control Cache )

上面的缓存技术可以让你很容易的缓存整个页面,如果要缓存指定控件的内容,可以通过指定VaryByControl 属性来完成。

<%@OutputCacheDuration="20"VaryByControl="MyControl_1"%>

上面代码ASP.NET将会缓存MyControl_1控件20分钟。如果要根据一些属性值来缓存控件只需要将OutPutCache指令加入*.ascx页面。 

<%@Control Language="C#"AutoEventWireup="true"CodeFile="MyControl.ascx.cs"Inherits="Controls_MyControl"%>
<%
@OutputCacheDuration="20"VaryByControl="EmployeeID"%>
……
……

VaryByControl=”EmployeeID”告诉ASP.NET根据控件中声明的EmployeeID属性来创建不同版本的缓存。

.ascx.cs 文件加入EmplyeeID属性为ASP.NET 缓存使用。

private int_employeeID;
public intEmployeeID
{
   get{ return_employeeID; }
   set{ _employeeID = value; }
}
protected voidPage_Load(objectsender, EventArgs e)
{
   lblDate.Text = DateTime.Now.ToShortDateString();
   lblTime.Text = DateTime.Now.ToLongTimeString();
   lblEmployeeID.Text = EmployeeID.ToString();
}

在页面中增加控件并且设置 EmployeeID.

<%@RegisterSrc="Controls/MyControl.ascx"TagName="MyControl"TagPrefix="uc1"%>
<asp:ContentID="Content1"ContentPlaceHolderID="ContentPlaceHolder1"runat="Server">
    <
divalign="center">
        <
uc1:MyControlID="MyControl1"runat="server"EmployeeID="1"></uc1:MyControl>
    </
div>
</
asp:Content>

 

缓存配置文件(Cache Profile )

web.config可以配置缓存相关的设置,

<system.web>
  <
caching>
    <
outputCacheSettings>
      <
outputCacheProfiles>
     <
addname="ProductItemCacheProfile" duration="60"/>
   </
outputCacheProfiles>
</
outputCacheSettings>
   </
caching>
</
system.web>

你可以通过设置 CacheProfile=”ProfileName” 属性 来使用上面的配置:

%@OutputCacheCacheProfile="ProductItemCacheProfile"VaryByParam="None"%

2. 数据缓存(Data Caching)

ASP.NET还提供了另一种灵活的缓存类型:数据缓存。你可以将一些耗费时间的条目加入到一个对象缓存集合中,以键值的方式存储。

Cache["Name"] = data;

我们可以通过使用Cache.Insert()方法来设置缓存的过期,优先级,依赖项等。

date1 = DateTime.Now;
Cache.Insert("Date1", date1, null, DateTime.Now.AddSeconds(20), TimeSpan.Zero);

ASP.NET允许你设置一个绝对过期时间或滑动过期时间,但不能同时使用。

缓存依赖项Cache dependency

缓存依赖项使缓存依赖于其他资源,当依赖项更改时,缓存条目项将自动从缓存中移除。缓存依赖项可以是应用程序的 Cache 中的文件、目录或与其他对象的键。如果文件或目录更改,缓存就会过期。

date2 = DateTime.Now;
string[] cacheKeys = { "Date1"};
CacheDependency cacheDepn = newCacheDependency(null, cacheKeys);
Cache.Insert("Date2", date2, cacheDepn);

上面的例子“Date2”缓存对象依赖“Date1”缓存条目,当 “Date1” 对象过期后“Date2” 将会自动过期。CacheDependency(null, cacheKeys)中的第一个参数为空是由于我们只监视缓存键的更改情况。

回调函数和缓存优先级(Callback Method and Cache Priority)

ASP.NET允许我们写一个回调函数,当缓存条目从缓存中移除的时候触发。还可以设置缓存条目的优先级。

protected void Page_Load(object sender, EventArgs e)
{
DateTime? date1 = (DateTime?)Cache["Date1"];
if (!date1.HasValue) // date1 == null
{
date1 = DateTime.Now;
Cache.Insert("Date1", date1, null, DateTime.Now.AddSeconds(20), TimeSpan.Zero,
CacheItemPriority.Default, new CacheItemRemovedCallback(CachedItemRemoveCallBack));
}
DateTime? date2 = (DateTime?)Cache["Date2"];
if (!date2.HasValue) // date2 == null
{
date2 = DateTime.Now;
Cache.Insert("Date2", date2, null, DateTime.Now.AddSeconds(40), TimeSpan.Zero,
CacheItemPriority.Default, new CacheItemRemovedCallback(CachedItemRemoveCallBack));
}
// Set values in labels
lblDate.Text = date1.Value.ToShortDateString();
lblTime.Text = date1.Value.ToLongTimeString();
lblDate1.Text = date2.Value.ToShortDateString();
lblTime1.Text = date2.Value.ToLongTimeString();
}
private void CachedItemRemoveCallBack(string key, object value, CacheItemRemovedReason reason)
{
if (key == "Date1" || key == "Date2")
{
Cache.Remove("Date1");
Cache.Remove("Date2");
}
}

例子中创建了“Date1” 和 “Date2”缓存。“Date1” 在20秒后过期“Date2”为40秒。但是由于我们注册了移除的回调函数,当“Date1” 或 “Date2”其中一个过期都会执行CachedItemRemoveCallBack 方法,在这个方法中移除了两个缓存条目,ASP.NET还提供了处理缓存条目更新时的回调函数CacheItemUpdateCallback