[转载]SqlServer怎样获取查询语句的成本

mikel阅读(1358)

[转载]SqlServer怎样获取查询语句的成本 – zzj8704 – 博客园.

在有些大系统中,客户端查询非常复杂,需要显示很多字段和过滤很多条件,例如TFS中的条件过滤,或则用户希望客户端查询能像EXCEL过滤那 样方便

这样可能导致查询语句非常复杂,此时的效率也比较低.

TFS查询条件界面:

如果可以获取查询语句的成本,当SQL语句执行成本很大(可能执行时间会很长时),系统自动提示用户是否继续或则大概需要多长时间,这样会提高用户 体验.

下举一例:

查询SQL Server中的所有列信息表:sys.all_columns

SQL server 2k8 management studio 中先后执行下面每行语句即可得到Sql语句的成本, 即为第二个语句执行结果中第一行中TotalSubtreeCost字段值.

set showplan_all on
select * from sys.all_columns

第二个语句(select * from sys.all_columns)执行结果:

StmtText TotalSubtreeCost StmtId NodeId Parent PhysicalOp LogicalOp EstimateRows EstimateIO EstimateCPU AvgRowSize
select * from sys.all_columns 0.1736313 1 1 0 NULL NULL 5270 NULL NULL NULL
|–Concatenation 0.1736313 1 2 1 Concatenation Concatenation 5270 0 0.000527 303
0.01454595 1 3 2 Filter Filter 659 0 8.44E-04 303
0.01370243 1 4 3 Compute Scalar Compute Scalar 659 0 6.59E-05 305
0.01363653 1 5 4 Clustered Index Scan Clustered Index Scan 659 0.01275463 0.0008819 179
0.1585583 1 36 2 Compute Scalar Compute Scalar 4611 0 0.0004611 303
0.1523828 1 37 36 Compute Scalar Compute Scalar 4611 0 0.0011905 164
0.1511923 1 38 37 Clustered Index Scan Clustered Index Scan 4611 0.1379398 0.0132525 167

再执行 set showplan_all off结束查询计划分析结果.

此时可以应用showplan_all来获取Sql查询的成本.

如下为C#简单实现上述应用,在VS2k8中编译通过,只需正确更改Data Source即可运行:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Diagnostics;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SqlConnectionStringBuilder connectionBuilder = new SqlConnectionStringBuilder();
connectionBuilder.Add(“Data Source”, “your server name or ip“);
connectionBuilder.Add(“Initial Catalog”, “master”);
connectionBuilder.Add(“Integrated Security”, “True”);

string sqlStr = “select * from sys.all_columns”;
Console.WriteLine(GetSqlCost(connectionBuilder.ToString(), sqlStr).ToString());
Console.Read();
}

static double GetSqlCost(string connectStr, string strSQL)
{
double sqlCost;
int timeOut;
SqlDataAdapter adapter;
SqlConnection connection = new SqlConnection(connectStr);
SqlCommand command;
DataTable table;
timeOut = 180;
adapter = null;

try
{
connection.Open();
command = new SqlCommand(“set showplan_all on “, connection);
command.ExecuteNonQuery();
command.CommandText = strSQL;
adapter = new SqlDataAdapter(strSQL, connection);
adapter.SelectCommand.CommandTimeout = timeOut;
table = new DataTable();
adapter.Fill(table);
command.CommandText = “set showplan_all off”;
command.ExecuteNonQuery();
sqlCost = System.Convert.ToDouble(table.Rows[0][“TotalSubtreeCost”]);
}
finally
{
command = null;
connection.Close();
}
return sqlCost;
}
}

}

[转载]logparser使用精华

mikel阅读(1145)

[转载]logparser使用精华_多少年华似水逝_百度空间.

logparser下载地址:
http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en
-=zhumoqing=-
1、导出为execl能打开的格式
logparser -i:evt -o:csv “select * from c:\sec.evt” > d:sec.csv
logparser -i:evt -o:csv “select * from security” > d:sec.csv
logparser -i:evt -o:nat “select * into a.txt from security”
logparser -i:evt -o:csv “select TimeGenerated,EventID,Message from c:\sec.evt” > d:sec.csv
logparser -i:evt -o:TPL -tpl:EventLogs.tpl “select * into b.html from d:\sec.evt”
使用条件语句:
SELECT TimeGenerated, EventTypeName, SourceName FROM System
WHERE ( SourceName = ‘Service Control Manager’ AND EventID >= 7024) OR
( SourceName = ‘W32Time’)
SELECT * FROM Security
WHERE Message LIKE ‘%logon%’
A、在iis日志中搜索特殊链接
LogParser -o:csv “SELECT * into a.csv FROM iis.log where EXTRACT_EXTENSION(cs-uri-stem) LIKE ‘asp'”
B、最经典的例子,对日志中的url进行归并统计
LogParser -o:csv “SELECT cs-uri-stem, COUNT(*) into a.csv FROM iis.log GROUP BY cs-uri-stem”
c、统计所有日志
LogParser -o:csv “SELECT cs-uri-stem, COUNT(*)into a.csv FROM ex*.log GROUP BY cs-uri-stem”
LogParser -i:iisw3c -o:csv “SELECT cs-uri-stem, COUNT(*)into a.csv FROM *.log GROUP BY cs-uri-stem”
d、对文件后缀进行排名
LogParser -i:iisw3c -o:csv “SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, COUNT(*) into a.cssv FROM *.log GROUP BY PageType”
e、得到所有的不重复的链接
LogParser -i:iisw3c -o:csv “SELECT distinct cs-uri-stem into a.csv FROM *.log”
2、生成百分比饼图
LogParser “SELECT EventID, COUNT(*) AS Times INTO Chart.gif FROM d:\tmp\sec.evt GROUP BY EventID ORDER BY Times DESC” -chartType:PieExploded3D -chartTitle:”Status Codes”
3、http日志
LogParser file:querytop.SQL -o:chart -chartType:Bar3d -chartTitle:”TOP 10 URL”
querytop.SQL
SELECT TOP 10 cs-uri-stem AS Url,
COUNT(*) AS Hits
INTO Urls.gif
FROM <1>
GROUP BY Url
ORDER BY Hits DESC
4、在html页面里找关键字
Return the lines in an HTML document that contain links to other pages:
LogParser “SELECT Text FROM http://www.microsoft.adatum.com WHERE Text LIKE ‘%href%'” -i:TEXTLINE
5、MD5 Hashes of System Files
LogParser “SELECT Path, HASHMD5_FILE(Path) into a.txt FROM C:\Windows\System32\*.exe” -i:FS -recurse:0
6、Print the 10 largest files on the C: drive:
LogParser “SELECT TOP 10 Path, Name, Size FROM C:\*.* ORDER BY Size DESC” -i:FS
7、获得本机登陆帐户的查看
LogParser.exe -o:nat “SELECT RESOLVE_SID(Sid) AS Account FROM Security WHERE EventID IN (540; 528)”
8、获得系统日志的分类详细信息
LogParser “SELECT DISTINCT SourceName, EventID,SourceName,message INTO Event_*.csv FROM security” -i:EVT -o:CSV
LogParser “SELECT DISTINCT SourceName, EventID,SourceName,message INTO Event_*.csv FROM System” -i:EVT -o:CSV
根据id分类
LogParser “SELECT DISTINCT eventid, EventID,SourceName,message INTO Event_*.csv FROM System” -i:EVT -o:CSV
LogParser “SELECT DISTINCT eventid, EventID,SourceName,message INTO Event_*.csv FROM security” -i:EVT -o:CSV
9、生成图形界面日志
LogParser “SELECT ‘Event ID:’, EventID, SYSTEM_TIMESTAMP(),message FROM security” -i:EVT -o:datagrid
10、生成一个Web页面
LogParser file:d:\EventLogs.SQL?EventLog=security -o:TPL -tpl:d:\EventLogs.tpl
LogParser file:d:\EventLogs.sql?EventLog=system -o:TPL -tpl:d:\EventLogs.tpl
11、在iis日志里查看返回代码分布饼图
LogParser “SELECT sc-status, COUNT(*) AS Times INTO Chart.gif FROM iis.log GROUP BY sc-status ORDER BY Times DESC” -chartType:PieExploded3D -chartTitle:”Status Codes”
12、在所有日志中手机前10位的排名
LogParser file:querytop.sql -o:chart -chartType:Bar3d -chartTitle:”TOP 10 URL”
querytop.sql:
SELECT TOP 10 cs-uri-stem AS Url,
COUNT(*) AS Hits
INTO Urls.gif
FROM ex*.log
GROUP BY Url
ORDER BY Hits DESC
13、检索目录下所有文件的所有的信息
logparser “select * into a.csv from c:\x-scan\*.*” -i:fs -o:csv
查看每个源IP发了多少个包
LogParser “SELECT srcip ,count(*) into a.csv FROM a.cap group by srcip” -fmode:tcpip -o:csv
查看每个源端口的包的个数
LogParser “SELECT srcport ,count(*) into a.csv FROM a.cap group by srcport” -fmode:tcpip -o:csv
归并所有srcip,dstip,srcport一样的包,得到个数
LogParser “SELECT srcip,dstip,srcport ,count(*) into a.csv FROM a.cap group by srcip,dstip,srcport” -fmode:tcpip -o:csv
归并所有tcpflags的包
LogParser “SELECT srcip,srcport,dstip,dstport,tcpflags,count(*) into a.csv FROM a.cap where tcpflags=’AF’ group by srcip,srcport,dstip,dstport,tcpflags” -fmode:tcpip -o:csv
tcpflags的分布饼图
LogParser “SELECT tcpflags,count(*) into a.gif FROM a.cap group by tcpflags ” -fmode:tcpip -chartType:PieExploded3D -chartTitle:”Status Codes”
LogParser “SELECT tcpflags,count(*) into a.csv FROM a.cap group by tcpflags ” -fmode:tcpip -o:csv

[转载]ASP.NET MVC 路由规则XML化 - 汤家大院 - 博客园

mikel阅读(918)

[转载]ASP.NET MVC 路由规则XML化 – 汤家大院 – 博客园.

很久没写文章了,不是懒得写,是写不出。。

最近由于工作关系,重新回顾了ASP.NET MVC 的 1.0 版本。2.0版本还没有研究。

由于MVC框架发展不久,还有很多不足的地方。其中关于路由规则配置这一块问题比较大。首先路由规则是在全局配置问价 Global.asax 的 Application_Start()事件中注册的。

01 public static void RegisterRoutes(RouteCollection routes)
02 {
03 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
04
05 routes.MapRoute(
06 "User", // Route name
07 "{controller}/{action}/{id}", // URL with parameters
08 new { controller = "User", action = "Show", id = "0" } // Parameter defaults
09 );
10 }
11
12 protected void Application_Start()
13 {
14
15 RegisterRoutes(RouteTable.Routes);
16
17 }

默认硬编码的方式使得以后可维护程度大大降低。MVC 1.0 似乎没有提供很好的基于配置文件的路由规则设置。所以只好自己实现了。直到写这篇文章时,才找到了一个比较好的解决方案。

以下是 自定义的XML 格式

1 <?xml version="1.0" encoding="utf-8" ?>
2 <MapRoutes>
01 <!--默认规则-->
02 <MapRoute name="Default" url="{controller}/{action}">
03 <Params>
04 <Item key="controller" default="Article"/>
05 <Item key="action" default="Index"/>
06 </Params>
07 </MapRoute>
08
09 <!--显示新闻列表的路由规则 -->
10 <MapRoute name="ShowArticleList" url="{controller}/{action}/{typeId}/{pageIndex}/{pageSize}">
11 <Params>
12 <Item key="controller" default="Article"/>
13 <Item key="action" default="Index"/>
14 <Item key="typeId" default="1"/>
15 <Item key="pageIndex" default="1"/>
16 <Item key="pageSize" default="10"/>
17 </Params>
18 </MapRoute>
19
20 </MapRoutes>

一下是全部代码

001 /* ***********************************************
002 * 作者 :汤晓华/tension 任何转载请务必保留此头部信息 版权所有 盗版必究
003 * Email:tension1990@hotmail.com
004 * 描述 :
005 * 创建时间:2010-3-9 15:17:26
006 * 修改历史:
007 * ***********************************************/
008 using System;
009 using System.Collections.Generic;
010 using System.Linq;
011 using System.Text;
012 using System.Web.Routing;
013 using System.Web.Mvc;
014 using System.Xml.Linq;
015 using Microsoft.CSharp;
016 using System.CodeDom.Compiler;
017
018 namespace Tension.Mvc
019 {
020 public static class RouteHelper
021 {
022 /// <summary>
023 /// 从XML文件中注册路由规则
024 /// </summary>
025 /// <param name="routes"></param>
026 /// <param name="cfgFile"></param>
027 public static void Register(this RouteCollection routes, string cfgFile)
028 {
029
030 IList<Route> Routes = GetRoutes(cfgFile);
031
032 foreach (var item in Routes)
033 {
034 //路由规则对象
035 object obj = CreateObjectFormString(item.ToString(), item.Name);
036 routes.MapRoute(
037 item.Name, // Route name
038 item.Url, // URL with parameters
039 obj // Parameter defaults
040 );
041
042 }
043 }
044
045 /// <summary>
046 ///  从XML文件中注册路由规则 默认文件为网站根目录下MapRoute.config
047 /// </summary>
048 /// <param name="routes"></param>
049 public static void Register(this RouteCollection routes)
050 {
051 Register(routes, string.Format("{0}\\MapRoute.config", Tension.ServerInfo.GetRootPath()));
052 }
053
054
055 /// <summary>
056 /// 从string动态创建类对象
057 /// </summary>
058 /// <param name="codeString"></param>
059 /// <param name="className"></param>
060 /// <returns></returns>
061 private static object CreateObjectFormString(string codeString, string className)
062 {
063 CSharpCodeProvider ccp = new CSharpCodeProvider();
064 CompilerParameters param = new CompilerParameters(new string[] { "System.dll" });
065 CompilerResults cr = ccp.CompileAssemblyFromSource(param, codeString);
066 Type type = cr.CompiledAssembly.GetType(className);
067 return type.GetConstructor(System.Type.EmptyTypes).Invoke(null);
068 }
069
070 /// <summary>
071 /// 从XML文件中解析路由规则
072 /// </summary>
073 /// <param name="configFile"></param>
074 /// <returns></returns>
075 private static IList<Route> GetRoutes(string configFile)
076 {
077 StringBuilder sb = new StringBuilder();
078
079
080 Console.WriteLine(sb.ToString());
081 IList<Route> Routes = new List<Route>();
082
083 XElement xe = XElement.Load(configFile);
084
085 #region MyRegion
086 foreach (var item in xe.Elements("MapRoute"))
087 {
088
089 //名称 属性
090 XAttribute xaName = item.Attribute("name");
091 if (xaName == null || string.IsNullOrEmpty(xaName.Value))
092 {
093 throw new ArgumentNullException("name!说明:路由配置文件中某规则缺少name属性或name属性的值为空字符串");
094 }
095
096 //URL 属性
097 XAttribute urlName = item.Attribute("url");
098 if (urlName == null || string.IsNullOrEmpty(urlName.Value))
099 {
100 throw new ArgumentNullException("url!说明:路由配置文件中某规则缺少url属性或url属性的值为空字符串");
101 }
102
103
104 Dictionary<string, string> DictParams = new Dictionary<string, string>();
105
106
107
108 #region MyRegion
109 foreach (var pItem in item.Element("Params").Elements("Item"))
110 {
111 XAttribute itemKey = pItem.Attribute("key");
112 if (itemKey == null || string.IsNullOrEmpty(itemKey.Value))
113 {
114 throw new ArgumentNullException("Item->key!说明:路由配置文件中某规则缺少Item->key属性或 Item->key属性的值为空字符串");
115 }
116
117 XAttribute itemDefault = pItem.Attribute("default");
118 if (itemDefault == null || string.IsNullOrEmpty(itemDefault.Value))
119 {
120 throw new ArgumentNullException("Item->default!说明:路由配置文件中某规则缺少Item->default属 性或Item->default属性的值为空字符串");
121 }
122 DictParams.Add(itemKey.Value, itemDefault.Value);
123 }
124 #endregion
125
126 Routes.Add(new Route() { Name = xaName.Value, Url = urlName.Value, Params = DictParams });
127
128
129 }
130 #endregion
131
132 return Routes;
133 }
134 }
135
136
137 /// <summary>
138 /// 路由规则
139 /// </summary>
140 public class Route
141 {
142 public string Name { get; set; }
143 public string Url { get; set; }
144 public Dictionary<string, string> Params { get; set; }
145
146 /// <summary>
147 /// 重写ToString 方法 产生需要动态代码段
148 /// </summary>
149 /// <returns></returns>
150 public override string ToString()
151 {
152 StringBuilder sb = new StringBuilder();
153 sb.AppendFormat("public class {0}", Name);
154 sb.Append("{");
155 foreach (var item in Params)
156 {
157 sb.AppendFormat("public string {0}", item.Key);
158 sb.Append("{get{return \"");
159 sb.Append(item.Value);
160 sb.Append("\";}} ");
161 }
162
163 sb.Append("}");
164 return sb.ToString();
165 }
166 }
167 }

在实现过程中遇到的最大问题就是 参数列表的动态装载 看一下以下代码

1 routes.MapRoute(
2 "User", // Route name
3 "{controller}/{action}/{id}", // URL with parameters
4 new { controller = "User", action = "Show", id = "0" } // Parameter defaults
5 );

这是硬编码实现的路由规则注册

其中 第三个参数(new { controller = “User”, action = “Show”, id = “0” } ) 是一个匿名对象

该对象如何动态构建成了难题。(才疏学浅)

尝试着传入一个 Dictionary<K,T> 但是没有用,ASP.NET 解析这个参数的时候是以反射形式读取的对象属性。

后来想到了使用代码段 在运行时动态创建对象。

我们将类似代码段

1 public class Default{public string controller{get{return "Article";}} public str
2 ing action{get{return "Index";}} public string id{get{return "0";}} public strin
3 g page{get{return "1";}} public string size{get{return "10";}} }

传入方法

1 private static object CreateObjectFormString(string codeString, string className)
2 {
3 CSharpCodeProvider ccp = new CSharpCodeProvider();
4 CompilerParameters param = new CompilerParameters(new string[] { "System.dll" });
5 CompilerResults cr = ccp.CompileAssemblyFromSource(param, codeString);
6 Type type = cr.CompiledAssembly.GetType(className);
7 return type.GetConstructor(System.Type.EmptyTypes).Invoke(null);
8 }

即可有运行时动态的创建我们需要的参数对象。

以后就可以方便的在XML注册路由了。

public static void Register(this RouteCollection routes)  对 RouteCollection 对象添加了扩展方法

引入对应的命名空间后就方便的注册了。

改进后的注册方法

01 public static void RegisterRoutes(RouteCollection routes)
02 {
03 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
04
05 }
06
07 protected void Application_Start()
08 {
09
10 RegisterRoutes(RouteTable.Routes);
11
12 //执行 RouteCollection的扩展方法 用来注册XML文件中的路由配置信息
13 RouteTable.Routes.Register();
14 }

代码下载地址

/tandly/MvcRouteHelper.rar

苏州 晴

汤晓华 QQ 1881597 MSN tension1990@hotmail.com

2010 03 10

[转载]asp.net mvc页面优化的基本用法(十)

mikel阅读(1012)

[转载]asp.net mvc(十) – ASP.NET2.0 – 博客园.

这篇文章我来讲两个关于页面优化的基本用法,下篇分析下静态页面缓存的用法。现在虽然大家的上网环境好了很多,但网站越来越流利胖客户端,使得页面加载速 度并没有提高多少,所以如何提高响应速度也就成了大家各显身手的地方了。

第一:OutputCacheAttribute,这个页面级的缓存我想大家用过web form开发的程序员都知道,它可以将整个页面全部缓存下来,同时支持多种参数形式以及过期策略。在ASP.NET mvc 1.0之前的预览版中,好像没有发现这东西,预览版本太多,如有不实还请谅解,OutputCacheAttribute到了1.0后正式加入框架。先看 下它的源码,主要是OnResultExecuting这个方法,Duration属性表示过期时间,VaryByParam属性表示缓存是否与参数有关 系。

代码

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)]
public class OutputCacheAttribute : ActionFilterAttribute
{
// Fields
private OutputCacheParameters _cacheSettings;

// Methods
public OutputCacheAttribute();
public override void OnResultExecuting(ResultExecutingContext filterContext);

// Properties
public string CacheProfile { get; set; }
internal OutputCacheParameters CacheSettings { get; }
public int Duration { get; set; }
public OutputCacheLocation Location { get; set; }
public bool NoStore { get; set; }
public string SQLDependency { get; set; }
public string VaryByContentEncoding { get; set; }
public string VaryByCustom { get; set; }
public string VaryByHeader { get; set; }
public string VaryByParam { get; set; }

// Nested Types
private sealed class OutputCachedPage : Page
{
// Fields
private OutputCacheParameters _cacheSettings;

// Methods
public OutputCachedPage(OutputCacheParameters cacheSettings);
protected override void FrameworkInitialize();
}
}

OutputCacheAttribute用法非常简单:
1:直接写在Controller中,例如:
但不推荐这样写,因为缓存参数写在代码中,不方便以后更改。

[OutputCache(Duration = 10, VaryByParam = none)]
public ActionResult Index()
{
return View();
}

2:可以写在页面中:<%@ OutputCache Duration=”10″ VaryByParam=”None” %>。

3:可以写在配置文件中:需要两个步骤。
1>:在Controller代码是加上缓存特性。

[OutputCache(CacheProfile = MyProfile)]
public ActionResult Index()

2>:Web.Config配置如下:name就是Controller代码中的CacheProfile。

代码

<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name=MyProfile duration=60 varyByParam=* />
</outputCacheProfiles>
</outputCacheSettings>
</caching>

第二:CompressAttribute。一般大型网站在做优化时,除了程序端的优化外,还有服 务器端。常见方法之一是启用gzip压缩。在程序中我们也可以实现,且难度不大。原理就是在
Response.Filter Stream 上加个 GZipStream/DeflateStream。

代码

public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var acceptEncoding
= filterContext.HttpContext.Request.Headers[Accept-Encoding];
if (!string.IsNullOrEmpty(acceptEncoding))
{
acceptEncoding
= acceptEncoding.ToLower();
var response
= filterContext.HttpContext.Response;

if (acceptEncoding.Contains(gzip))
{
response.AppendHeader(
Content-encoding, gzip);
response.Filter
= new GZipStream(response.Filter, CompressionMode.Compress);
}
else if (acceptEncoding.Contains(deflate))
{
response.AppendHeader(
Content-encoding, deflate);
response.Filter
= new DeflateStream(response.Filter, CompressionMode.Compress);
}
}
}
}

我们来看下启用压缩前后的效果:我们用firebug观察下页面大小。

这是没有启用压缩时的图。

这是启用压缩后的效果图。尽管我这个页面本来就较小,尽管在加载时间上看不出问题,但页面很明显变小了,大的页面在时间加载上会有更加明显的效果。

[转载]AOP解析——含简单AOP框架实现(包括Proxy模式实现与Attribute实现)

mikel阅读(1119)

[转载]AOP解析——含简单AOP框架实现(包括Proxy模式实现与Attribute实现) – pangxiaoliang[北京]流浪者 – 博客园.

AOP简介:

AOP(Aspect Oriented Programming)“面向切面编程”,其实和OOP(Object Oriented Programming)“面向对象编程”一样是一种编程思路,而且个人以为翻译为“切面导向编程”更为妥当,OOP 也应翻译为“对象导向编程”。因为正是有了“切面”和“对象”的想法和概念才产生了“Aspect Oriented Programming”和“Object Oriented Programming”这些编程方法,所以“导向”更为贴近些

以下想法均为个人揣摩得出,具体官方概念请Google,Bing,Baidu.

AOP,个人以为是一种行为(Behavior)的注入,在不改变原有逻辑(original logic)的基础上,将一些可重用的其他逻辑(other logic)注入到原有逻辑(original logic)中。切面(Aspect)即为其他逻辑(other logic),是一些特殊的业务关注点,包括“事务处理”,“异常捕捉”,“日志记录”,“身份验证”等方面。

这种“切面”逻辑一般贯彻整个解决方案,AOP方式将这种切面提取出来,实现了解耦和代码简洁化。

简单例子:

下面举个简单的例子来说明为什么要使用AOP:

1)没有使用AOP的Castle ActiveRecord“事务处理”

代码

class AOPTest
{
// ……

/// <summary>
/// Save the retire user info.
/// </summary>
/// <param name=”site”>The user will be retire user.</param>
public void SaveRetireUserInfo(User user)
{
using(TransactionScope transaction = new TransactionScope())
{
try
{
RetireUser retireUser
= new RetireUser();
retireUser.Name
= user.Name;
retireUser.Department
= user.Department;
//
user.Delete();
retireUser.Save();
transaction.VoteCommit();
//完成事务
}
catch(Exception ex)
{
Log.Write(ex.Message);
transaction.VoteRollBack();
//事务回滚
}
}
}

// ……
}

2)使用AOP的Castle ActiveRecord“事务处理”, 里边使用了我自己写的精简AOP框架,与原来的文章一样,看不懂没关系,这里只是个宏观的概念。重点在demo分析。

代码

/// <summary>
/// The test class for AOP use attribute for transaction.
/// </summary>
[AOPProxy(Interception = typeof(TransactionInterception))]
class AOPTest : ContextBoundObject
{
// ……

/// <summary>
/// Save the retire user info.
/// </summary>
/// <param name=”site”>The user will be retire user.</param>
public void SaveRetireUserInfo(User user)
{
RetireUser retireUser
= new RetireUser();
retireUser.Name
= user.Name;
retireUser.Department
= user.Department;
//
user.Delete();
retireUser.Save();
}

// ……
}

/// <summary>
/// The interception of the AOP for trasaction.
/// </summary>
class TransactionInterception : IInterception
{
private TransactionScope transaction = null;

#region IInterception Members

public void ExceptionHandle()
{
transaction.VoteRollBack();
//事务回滚
}

public void PostInvoke()
{
transaction.VoteCommit();
//完成事务
}

public void PreInvoke()
{
transaction
= new TransactionScope(); //初始化事务
}

#endregion
}

由以上可见,加入AOP后(AOPProxy Attribute实现),“其他逻辑”注入“原始逻辑”使得代码更加简洁,同时也将“切面”的逻辑和“业务”的逻辑分离开来,实现了解耦。 TransactionInterception是拦截器,实现了“其他逻辑”,由AOPProxy Attribute通过type将其注入。在处理“原始逻辑”的时候会同时处理注入的“其他逻辑”。ExceptionHandle方法实现出现 Exception时的“其他逻辑”注入;PreInvoke方法实现在调用“主题”方法前的“其他逻辑”注入;PostInvoke方法实现在调用“主 题”方法后的“其他逻辑”注入。

简单来说执行流程如下:

1. PreInvoke();

2. 主题Method();

3.

if(调用”主题Method()”时出现Exception)
{

ExceptionHandle();
}

else

{

PostInvoke();
}

有下面的SaveRetireUserInfo方法可以看到“业务逻辑”变得简洁,在“业务逻辑”已经见不到扰乱代码可读性的“事务代码”和 “try…catch…”语句块了,只需要给该类加上“AOPProxy”特性注入相应的“拦截器”并继承ContextBoundObject 即可。

public void SaveRetireUserInfo(User user)
{
RetireUser retireUser
= new RetireUser();
retireUser.Name
= user.Name;
retireUser.Department
= user.Department;
//
user.Delete();
retireUser.Save();
}

Proxy基础:

代理模式,说白了就是设置一个“中间层”(proxy),把对实际要操作的“主题”保护在里边,并且在操作时进行额外的操作,实现注入。对“主题” 的操作均需要通过proxy来完成,就如同经济人。

比如:你是著名歌星SexyBaby,你有一个经纪人叫Underdog,你只负责唱歌,各种演唱会的举办和电视台对你邀请一律由Underdog 负责,你SexyBaby就是“主题”而你的经纪人Underdog就是proxy。

具体的代理模式可以看我以前写的关于Dota的设计模式随笔和各种设计模式书籍。

下面介绍个简单的例子来了解下代理模式。

假设一个场景:IT界优秀人士均喜欢看AV,而由于为了保护我们国家未成年人幼小的心灵不受 到不良网站侵害,国家屏蔽了AV站点,那我们这些IT界寂寞哥又有这样的需求,那该怎么办那?我们需要通过Online Web Proxy来解决这个问题,我们通过这样的“在线代理网站”(“在线代理网站”服务器放在不屏蔽AV站点的国家,由“在线代理网站”服务器间接访问AV站 点。因为在线代理网站不直接提供AV信息,所以我们是可以访问的)来访问AV站点就可以了。这时我们是通过Proxy(“在线代理网站”)访问到“主题” (AV站点),对Proxy(“在线代理网站”)的操作和直接操作“主题”(AV站点)没有什么区别。

下面就开始我们的AV之旅吧,由于上次有同学说我代码里用中文,怪怪的,这次都用英文,顺便让大家复习下英文了:

1) 首先我们必须有一台链接到Internet的电脑“MyComputer”。

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// My PC.
/// </summary>
public class MyComputer
{
/// <summary>
/// Visit the site.
/// </summary>
/// <param name=”site”>The site.</param>
public void Visit(ISite site)
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
Site Url:{0}, site.Url);
Console.ForegroundColor
= ConsoleColor.White;
IFilter filter
= new ChinaDNSFilter(); // Server in China, comply with the rule of China.
site = filter.Parse(site); // Parse the site.
Console.ForegroundColor = ConsoleColor.Gray;
site.ShowInformation();
// Show the information of the site.
Console.ForegroundColor = ConsoleColor.White;
}
}
}

“MyComputer”有一个Visit方法,可以访问到传入的ISite(抽象出的站 点接口),Visit方法流程如下:

1) 输出当前站点ISite的Url。下面的Code是ISite的定义。

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The interface of site.
/// </summary>
public interface ISite
{
/// <summary>
/// The url of site.
/// </summary>
string Url
{
get;
}

/// <summary>
/// Show the information about this site.
/// </summary>
void ShowInformation();
}
}

另外我们设计了几个站点以供访问,代码如下:

首先是传说中的AVSite(http://www.avsite.com/), 看看介绍多么吸引人“The site is so hot, all AV stars.”。

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The site contains AV information.
/// </summary>
public class AVSite : ISite
{
#region ISite Members

/// <summary>
/// The url of site.
/// </summary>
public string Url
{
get { return http://www.AVSite.com; }
}

/// <summary>
/// Show the information about this site.
/// </summary>
public void ShowInformation()
{
Console.WriteLine(
The site is so hot, all AV stars.);
}

#endregion
}
}

然后是我们经常上的博客园“http://www.cnblogs.com”, 介绍“The technology site, the home of coders.”。

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The site of cnblogs.
/// </summary>
public class CnblogsSite : ISite
{
#region ISite Members

/// <summary>
/// The url of site.
/// </summary>
public string Url
{
get { return http://www.cnblogs.com; }
}

/// <summary>
/// Show the information about this site.
/// </summary>
public void ShowInformation()
{
Console.WriteLine(
The technology site, the home of coders.);
}

#endregion
}
}

下面该我们的主角出场了,“在线代理网站”“http://www.myproxy.com”。 我们可以通过构造函数传入想要代理访问的站点地址,传入站点地址后会通过DNS解析访问到相应的网站。“在线代理网站”和“DNS(Domain Name Resolution)”的实现(执行“在线代理网站”的ShowInformation方法时实际是通过代理显示要代理网站(AV站点)的信息)。

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The online web proxy site.
/// </summary>
public class MyProxySite : ISite
{
private ISite _site = null;

public MyProxySite(string url)
{
_site
= DNS.GetSite(url);
}

#region ISite Members

/// <summary>
/// The url of site.
/// </summary>
public string Url
{
get { return http://www.myproxy.com; }
}

/// <summary>
/// Show the information about this site.
/// </summary>
public void ShowInformation()
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
Proxy start!);
Console.WriteLine(
Real url:{0}, _site.Url);
Console.ForegroundColor
= ConsoleColor.Gray;
IFilter filter
= new USADNSFilter(); // Server in USA, comply with the rule of USA.
_site = filter.Parse(_site);
_site.ShowInformation();
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
Proxy end!);
Console.ForegroundColor
= ConsoleColor.Gray;
}

#endregion
}
}

DNS

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// Domain name resolution.
/// </summary>
class DNS
{
/// <summary>
/// Get the site instance by the url.
/// </summary>
/// <param name=”url”>The visited url.</param>
/// <returns>The site instance.</returns>
public static ISite GetSite(string url)
{
ISite site
= null;
if (url == http://www.AVSite.com)
{
site
= new AVSite();
}
else if (url == http://www.cnblogs.com)
{
site
= new CnblogsSite();
}
else
{
site
= new NullSite();
}
return site;
}
}
}

另外当DNS中不包含传入的网址时要返回“NullSite”,用“MyComputer”直接访问“AVSite”时,会被 “ChinaFilter”过滤,返回“WarnningSite”提醒访问者触犯法律。“NullSite”和“WarnningSite”实现如下。

NullSite:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The site with nothing.
/// </summary>
public class NullSite : ISite
{
#region ISite Members

/// <summary>
/// The url of site.
/// </summary>
public string Url
{
get { return String.Empty; }
}

/// <summary>
/// Show the information about this site.
/// </summary>
public void ShowInformation()
{
Console.WriteLine(
No site, please check the url.);
}

#endregion
}
}

WarnningSite:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The site for the warnning of gov.
/// </summary>
public class WarnningSite : ISite
{
#region ISite Members

/// <summary>
/// The url of site.
/// </summary>
public string Url
{
get { return String.Empty; }
}

/// <summary>
/// Show the information about this site.
/// </summary>
public void ShowInformation()
{
Console.WriteLine(
For the gov rule, this site can’t be visited.);
}

#endregion
}
}

2) 根据中国法律由ISP(Internat Service Provider)服务器过滤网站,返回相应的站点对象。

过滤接口实现如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// The interface of the site visiting filter.
/// </summary>
public interface IFilter
{
/// <summary>
/// Parse the site.
/// </summary>
/// <param name=”site”>The site.</param>
/// <returns>The new site.</returns>
ISite Parse(ISite site);
}
}

中国过滤器,过滤掉AVSite,过滤后返回WarnningSite,实现如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// ISP(Internet Service Provider),filter the visited site information for comply the rule of China.
/// </summary>
public class ChinaDNSFilter : IFilter
{
#region IFilter Members

/// <summary>
/// Parse the site.
/// </summary>
/// <param name=”site”>The site.</param>
/// <returns>The new site.</returns>
public ISite Parse(ISite site)
{
// For the children, no AV information.
ISite returnSite = site;
if (site is AVSite)
{
returnSite
= new WarnningSite();
}
return returnSite;
}

#endregion
}
}

美国过滤器,不过滤AVSite,直接返回AVSite,实现如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.ProxyPattern
{
/// <summary>
/// ISP(Internet Service Provider),filter the visited site information for comply the rule of USA.
/// </summary>
class USADNSFilter : IFilter
{
#region IFilter Members

/// <summary>
/// Parse the site.
/// </summary>
/// <param name=”site”>The site.</param>
/// <returns>The new site.</returns>
public ISite Parse(ISite site)
{
// Freedom, no filter.
return site;
}

#endregion
}
}

3) 通过_site.ShowInformation显示站点信息。

虚拟Internat已经搭建起来,可以进行测试啦。

首先用“MyComputer”直接访问AVSite,返回 WarnningSite:

MyComputer myComputer = new MyComputer();
ISite site = new AVSite();
myComputer.Visit(site); // Visit AV site will be return warning.

结果:

然后用“MyComputer”通过 “MyProxySite”在线代理网站,代理访问AVSite,返回AVSite站点信息,偶也~~~~

site = new MyProxySite(“http://www.avsite.com/“); // Use online web proxy.
myComputer.Visit(site);

结果:

最后看完AV,我们再会博客园逛一逛

site = new CnblogsSite();
myComputer.Visit(site);

结果:

花了很大的功夫说代理,现在该切入正题了,我们的AOP,分为代理模式实现和Attribute实现。

AOP框架实现

首先AOP实现了逻辑注入,即在调用方法之前进行了逻辑注入。我们使用System.Runtime.Remoting中呃Message机制来实 现Message的截获和注入,有点像MFC框架中的钩子函数。

首先,为了实现逻辑注入,我们先要设计出逻辑注入的接口(IInterception)。为了简单起见逻辑注入我们只设计了3个Method,包括 PreInvoke,PostInvoke和ExceptionHandle,分别用来注入“调用主题Method前的逻辑”,“调用主题Method后 的逻辑”和“调用主题Method时发生Exception的逻辑”。具体实现如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;

namespace Landpy.AOP
{
/// <summary>
/// Description of IInterception.
/// </summary>
public interface IInterception
{
/// <summary>
/// Pre the method invoke.
/// </summary>
void PreInvoke();

/// <summary>
/// Post the method invoke.
/// </summary>
void PostInvoke();

/// <summary>
/// Handling the exception which occurs when the method is invoked.
/// </summary>
void ExceptionHandle();
}
}

逻辑注入的接口(IInterception)永远不为null,根据Null Object模式设计了NullInterception(关于Null Object模式及其意义可以看我原来的文章),实现如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.AOP
{
/// <summary>
/// Null Object pattern for interception.
/// </summary>
public class NullInterception : IInterception
{

#region IInterception Members

/// <summary>
/// Before invoke the real instance to do something.
/// </summary>
public virtual void PreInvoke()
{
// Do nothing.
}

/// <summary>
/// End invoke the real instance to do something.
/// </summary>
public virtual void PostInvoke()
{
// Do nothing.
}

/// <summary>
/// Handling the exception which occurs when the method is invoked.
/// </summary>
public void ExceptionHandle()
{
// Do nothing.
}

#endregion

}
}

为了实现Message拦截,我们必须实现一个继承了“RealProxy”的类“AOPRealProxy”,这样我们就可以重写 System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)方法来注入自己的逻辑。

有的时候代码能更清楚的表达,所以先将AOPRealProxy的实现附上:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Services;
using System.Runtime.Remoting.Activation;

namespace Landpy.AOP
{
/// <summary>
/// RealProxy is a abstract class, which is a class in Framework to provide the function about base proxy.
/// The Invoke method like the hook of MFC, it intercept the message, inject the custom logic and generate a new message
/// for system to performance.
/// </summary>
class AOPRealProxy : RealProxy, IProxyDI
{
private MarshalByRefObject _target = null;
private IInterception _interception = null;

public AOPRealProxy(Type targetType, MarshalByRefObject target)
:
base(targetType)
{
_target
= target;
_interception
= new NullInterception();
}

/// <summary>
/// Overridden the method “Invoke” of the base class, invokes the method that is specified
// in the provided System.Runtime.Remoting.Messaging.IMessage on the remote
// object that is represented by the current instance.
/// </summary>
/// <param name=”msg”>A System.Runtime.Remoting.Messaging.IMessage that contains a System.Collections.IDictionary
// of information about the method call.
// </param>
/// <returns>The message returned by the invoked method, containing the return value and
// any out or ref parameters.
// </returns>
public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
{
IMethodReturnMessage methodReturnMessage
= null;
IMethodCallMessage methodCallMessage
= msg as IMethodCallMessage;//Check whether the message is method call message.
if (methodCallMessage != null)
{
IConstructionCallMessage constructionCallMessage
= methodCallMessage as IConstructionCallMessage;
if (constructionCallMessage != null) //Constructor Method.
{
RealProxy defaultProxy
= RemotingServices.GetRealProxy(_target);
defaultProxy.InitializeServerObject(constructionCallMessage);
methodReturnMessage
= EnterpriseServicesHelper.CreateConstructionReturnMessage(constructionCallMessage, (MarshalByRefObject)GetTransparentProxy()); //Create the message about constructor.
}
else //Other method except constructor method.
{
_interception.PreInvoke();
//Inject PreInvoke method.
try
{
methodReturnMessage
= RemotingServices.ExecuteMessage(_target, methodCallMessage); //Invoke subject method.
}
catch
{
}
if (methodReturnMessage.Exception != null)
{
_interception.ExceptionHandle();
//Occur exception and then inject ExceptionHandle method.
}
else
{
_interception.PostInvoke();
//Inject PostInvoke method.
}
}
}
return methodReturnMessage;
}

#region IProxyDI Members

/// <summary>
/// Dependency injection the interception into proxy class.
/// </summary>
/// <param name=”interception”>The interception.</param>
public void InterceptionDI(IInterception interception)
{
_interception
= interception; //The pattern of interface inject, inject the interception.
}

#endregion
}
}

使用接口注入方式,将Interception“拦截器”注入到“AOPRealProxy”类。

注入接口“IProxyDI”设计如下:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.AOP
{
interface IProxyDI
{
void InterceptionDI(IInterception interception);
}
}

1) 实现Proxy模式AOP:

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Collections.Generic;
using System.Text;

namespace Landpy.AOP
{
public class ProxyFactory
{
public static T CreateProxyInstance<T>(IInterception interception) where T : new()
{
Type serverType
= typeof(T);
MarshalByRefObject target
= Activator.CreateInstance(serverType) as MarshalByRefObject;
AOPRealProxy aopRealProxy
= new AOPRealProxy(serverType, target);
aopRealProxy.InterceptionDI(interception);
return (T)aopRealProxy.GetTransparentProxy();
}
}
}

实现了interception参数注入AOPRealProxy,将包装完成的传输代理类返回。此时返回的代理类对象操作起来如同直接操作“主 题”。

2)实现Attribute模式AOP

代码

//
// Authors:
// Xiaoliang Pang (mailto:mv@live.cn)
//
// Copyright (c) 2010 Landpy Software
//
// http://www.cnblogs.com/pangxiaoliang
//
using System;
using System.Runtime.Remoting.Proxies;

namespace Landpy.AOP
{
/// <summary>
/// Description of AOPProxyAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class AOPProxyAttribute : ProxyAttribute
{
private IInterception _interception;

public Type Interception
{
get
{
return _interception.GetType();
}
set
{
IInterception interception
= Activator.CreateInstance(value) as IInterception;
_interception
= interception;
}
}

public AOPProxyAttribute()
{
_interception
= new NullInterception();
}

public override MarshalByRefObject CreateInstance(Type serverType)
{
MarshalByRefObject target
= base.CreateInstance(serverType);
AOPRealProxy aopRealProxy
= new AOPRealProxy(serverType, target);
aopRealProxy.InterceptionDI(_interception);
return aopRealProxy.GetTransparentProxy() as MarshalByRefObject;
}
}
}

实现了interception的Attribute用type注入AOPRealProxy,将包装完成的传输代理类返回。此时返回的代理类对象 操作起来如同直接操作“主题”。

测试AOP框架

(一)测试代理模式AOP。

// Proxy class to implement the AOP.
IInterception interception = new MyInterception();
AOPTestWithProxyClass aopTestTwo = ProxyFactory.CreateProxyInstance<AOPTestWithProxyClass>(interception) as AOPTestWithProxyClass;
aopTestTwo.Show();

首先实例化了一个MyInterception的拦截器,然后用ProxyFactory将Interception作为传入注入“主题”。

MyInterception实现如下:

代码

/// <summary>
/// The interception of the AOP.
/// </summary>
class MyInterception : IInterception
{

#region IInterception Members

/// <summary>
/// Before invoke the real instance to do something.
/// </summary>
public void PreInvoke()
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
===Pre MyInterception.===);
Console.ForegroundColor
= ConsoleColor.Gray;
}

/// <summary>
/// End invoke the real instance to do something.
/// </summary>
public void PostInvoke()
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
===Post MyInterception.===);
Console.ForegroundColor
= ConsoleColor.Gray;
}

/// <summary>
/// Handling the exception which occurs when the method is invoked.
/// </summary>
public void ExceptionHandle()
{
Console.ForegroundColor
= ConsoleColor.Red;
Console.WriteLine(
There is a exception!);
Console.ForegroundColor
= ConsoleColor.Gray;
}

#endregion
}

AOPTestWithProxyClass实现如下:

代码

/// <summary>
/// The test class for AOP use proxy class.
/// </summary>
class AOPTestWithProxyClass : ContextBoundObject
{
public void Show()
{
Console.ForegroundColor
= ConsoleColor.Gray;
Console.WriteLine(
Hello, I am AOPTestTwo.);
Console.ForegroundColor
= ConsoleColor.Gray;
}
}

结果:

可见===Pre MyInterception.===”和”===Post MyInterception.===“已经成功实现AOP注入。

(二)测试Attribute实现AOP。

// Attribute to implement the AOP.
AOPTestWithAttribute aopTest = new AOPTestWithAttribute();
aopTest.Show();

此时的代码要比Proxy实现更为简单,直接用类的构造函数实现类的实例化,不用ProxyFactory生成实例,Interception同样 还是使用了MyInterception。

AOPTestWithAttribute类的实现如下:

代码

/// <summary>
/// The test class for AOP use attribute..
/// </summary>
[AOPProxy(Interception = typeof(MyInterception))]
class AOPTestWithAttribute : ContextBoundObject
{
public void Show()
{
Console.ForegroundColor
= ConsoleColor.Gray;
Console.WriteLine(
Hello, I am AOPTest.);
Console.ForegroundColor
= ConsoleColor.Gray;
}
}

结果:

(三)测试Attribute实现AOP[事务的提交和回滚]。

AOPTestWithAttributForTrasaction aopTestWithAttributForTrasaction = new AOPTestWithAttributForTrasaction();
// Execute the transaction successfully.
aopTestWithAttributForTrasaction.ExecuteTransactionSuccessfully();

WriteSplitLine();

// Execute the transaction unsuccessfully.
try
{
aopTestWithAttributForTrasaction.ExecuteTransactionUnsuccessfully();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(ex.Message);
Console.ForegroundColor = ConsoleColor.Gray;
}

在ExecuteTransactionUnsuccessfully方法中加入throw Excetpion代码,实现执行不成功的情况。当执行ExecuteTransactionSuccessfully方法时实现事务的提交,当执行 ExecuteTransactionUnsuccessfully实现事务的回滚,此时的例子可以对照前面所将的Castle ActiveRecord的事务实现AOP的例子。

AOPTestWithAttributForTrasaction实现如下:

代码

[AOPProxy(Interception = typeof(TransactionInterception))]
class AOPTestWithAttributForTrasaction : ContextBoundObject
{
public void ExecuteTransactionSuccessfully()
{
Console.WriteLine(
Execute the transaction successfully:));
}

public void ExecuteTransactionUnsuccessfully()
{
Console.WriteLine(
Execute the transaction unsuccessfully:();
throw new AOPNullException();
}
}

Interception使用了新的TransactionInterception,TransactionInterception的实现如 下:

代码

/// <summary>
/// The interception of the AOP for trasaction.
/// </summary>
class TransactionInterception : IInterception
{
#region IInterception Members

public void ExceptionHandle()
{
Console.ForegroundColor
= ConsoleColor.Red;
Console.WriteLine(
☆☆☆Rollback transaction☆☆☆);
Console.ForegroundColor
= ConsoleColor.Gray;
}

public void PostInvoke()
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
☆☆☆Commit transaction☆☆☆);
Console.ForegroundColor
= ConsoleColor.Gray;
}

public void PreInvoke()
{
Console.ForegroundColor
= ConsoleColor.Green;
Console.WriteLine(
☆☆☆Begin transaction☆☆☆);
Console.ForegroundColor
= ConsoleColor.Gray;
}

#endregion
}

结果:

注意:在Debug下执行到“throw new AOPNullException();”时会停止,继续F5即可执行,如果非Debug状态(如直接双击exe执行),则可以执行到结束。

这是由Debug机制造成的,不必在意。


<<完整Demo下 载>>

敬告
作者:pangxiaoliang
出 处:http://www.cnblogs.com/pangxiaoliang
本文版权归pangxiaoliang和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,谢谢合 作。

[转载]让windows日志记录web程序的异常-EnterpriseLibrary.ExceptionHandling在asp.net mvc中的初步应用

mikel阅读(830)

[转载]让windows日志记录web程序的异常-EnterpriseLibrary.ExceptionHandling在asp.net mvc中的初步应用 – 陷空岛 – 博客园.

实现的功能很简单,就是用企业库的异常处理模块和日志模块,利用windows的系统日志,记录web应用的异常。

我的企业库是4.1版

1.添加对Microsoft.Practices.EnterpriseLibrary.ExceptionHandling和 Microsoft.Practices.EnterpriseLibrary.Logging的引用

2.重写Application_Error

protected void Application_Error(Object sender, EventArgs e)
{
try {
Exception objErr = Server.GetLastError().GetBaseException();
Application[“errorPage”] = Request.Url.ToString();
Application[“errorMsg”] = objErr.Message;
Server.ClearError();
ExceptionPolicy.HandleException(objErr, “global expcetion”);  //第一个参数是异常,第二个可以理解为异常名或者异常类型
}
catch
{ }
}

3.在配置文件中加入

<exceptionHandling>
<exceptionPolicies>
<add name=”global expcetion”> //这就是你定义的异常名或者异常类型
<exceptionTypes>
<add type=”System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″
postHandlingAction=”NotifyRethrow” name=”Exception”>
<exceptionHandlers>
<add logCategory=”General” eventId=”100″ severity=”Error” title=”Enterprise Library Exception Handling”
formatterType=”Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″
priority=”0″ useDefaultLogger=”false” type=”Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″
name=”Logging Handler” />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>

4.在程序中加入下列代码测试异常

throw new Exception(“Test”);

5.注意:

需要将异常抛至全局处理的action,不要加异常过滤器IExceptionFilter,否则的话,异常无法传递到 Application_Error。

如果一定要使用IExceptionFilter,则将ExceptionPolicy.HandleException(objErr, “global expcetion”); 加入至过滤器中即可

[转载]性能优化总结。

mikel阅读(1351)

[转载]性能优化总结。 – 交流空间 – 博客园.

最近在领导的要求下做了一下项目的优化,总结如下:

1.    使用存储过程, (如果在程序里用exec 存储过程 参数,这样执行似乎并没有快多少)

在数据库里是预编译的,也不需要在字符串传输上花费大量时间。  防SQL注入攻击。

2.     尽量优化数据库语句,使逻辑尽量简单。      @  还有就是在使用函数时    charindex   >like > padindex  效率依次递减。   @查询字段是否包含在以,分隔的字段串时,最好不要用in  速度非常慢。  还有好多,可以总结的,这里就不再描述了。

3.   EnableViewState(页面的视图状态)。如果无特殊要求设置为false。

使用ViewState ,每个对象都必须先序列化到 ViewState 中,然后再通过回传进行反序列化,因此使用 ViewState是没有代价的。尽量减少使用对象,如果可能,尽量减少放入 ViewState 中的对象的数目。下面情况基本上可以禁用viewstate:

(1)页面控件 (.ascx)

(2)页面不回传给自身。

(3)无需对控件的事件处理。

(4)控件没有动态的或数据绑定的属性值(或对于每个postpack都在代码中处理)

单个页面或每个页面都禁用 ViewState,如下所示:

单个页面:<%@ Page EnableViewState=”False” %>

每个页面:在 web.config 中 <Pages EnableViewState=”false” />

EnableSessionState保持默认值即可(如果页面用到sessionstate它才会占用资源)。

EnableViewStateMac如果无安全上的特殊要求,保持默认值。

4.   Pagelayout.页面布局模型。建议使用Flowlayout(元素不带绝对定位属性添加).Gridlayout(绝对定位属性)由于采用绝对定 位,将会比Flowlayout生产更多的代码,主要是控件的定位信息。  radiobuttonlist   和  checkboxlist等

5.   项目发布的时候切记解除页面的Debug状态

6.   尽量选择html控件。能在客户端实现的功能就在客户端实现(熟练掌握JavaScript),减少服务器的压力。数据控件选择顺序:Repeater、 DataList、DataGrid

7.   在建立数据库连接后只有在真正需要操作时才打开连接,使用完毕后马上关闭,从而尽量减少数据库连接打开的时间,避免出现超出连接限制的情况

8.   字符串操作性能优化

使用值类型的ToString方法
在连接字符串时,经常使用”+”号直接将数字添加到字符串 中。这种方法虽然简单,也可以得到正确结果,但是由于涉及到不同的数据类型,数字需要通过装箱    操 。作转化为引用类型才可以添加到字符串中。但是装箱操作对性能影响较大,因为在进行这类处理时,将在托管堆中分配一个新的对象,原有的值复制到新创建 的对象中。使用值类型的ToString方法可以避免装箱操作,从而提高应用程序性能。

运用StringBuilder类
String类对象是不可改变的,对于String对象的重新赋值在本质上是重新创建了一个String对象并将新值赋予该对象,其方法 ToString对性能的提高并非很显著。在处理字符串时,最好使用StringBuilder类,其.NET 命名空间是System.Text。该类并非创建新的对象,而是通过Append,Remove,Insert等方法直接对字符串进行操作,通过 ToString方法返回操作结果。

9.只要可能就缓存数据或页输出

ASP.NET 提供了一些简单的机制,它们会在不需要为每个页请求动态计算页输出或数据时缓存这些页输出或数据。另外,通过设计要进行缓存的页和数据请求(特别是在站点 中预期将有较大通讯量的区域),可以优化这些页的性能。与 .NET Framework 的任何 Web 窗体功能相比,适当地使用缓存可以更好的提高站点的性能,有时这种提高是超数量级的。使用 ASP.NET 缓存机制有两点需要注意。首先,不要缓存太多项。缓存每个项均有开销,特别是在内存使用方面。不要缓存容易重新计算和很少使用的项。其次,给缓存的项分配 的有效期不要太短。很快到期的项会导致缓存中不必要的周转,并且经常导致更多的代码清除和垃圾回收工作。若关心此问题,请监视与 ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate 性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称作内存压力。

10. 使用 HttpServerUtility.Transfer 方法在同一应用程序的页面间重定向
采用 Server.Transfer 语法,在页面中使用该方法可避免不必要的客户端重定向。但要根据情况区分response.redirect .response.execute的使用方法。 区别对待。
11.适当地使用公共语言运行库的垃圾回收器和自动内存管理
小心不要给每个请求分配过多内存,因为这样垃圾回收器将必须更频繁地进行更多的工作。另外,不要让不必要的指针指向对象,因为它们将使对象保持活动状 态,并且应尽量避免含 Finalize 方法的对象,因为它们在后面会导致更多的工作。特别是在 Finalize 调用中永远不要释放资源,因为资源在被垃圾回收器回收之前可能一直消耗着内存。最后这个问题经常会对 Web 服务器环境的性能造成毁灭性的打击,因为在等待 Finalize 运行时,很容易耗尽某个特定的资源。

12.不要依赖代码中的异常

因为异常大大地降低性能,所以您不应该将它们用作控制正常程序流程的方式。如果有可能检测到代码中可能导致异常的状态,请执行这种操作。不要在处理该状态 之前捕获异常本身。常见的方案包括:检查 null,分配给将分析为数字值的 String 一个值,或在应用数学运算前检查特定值。下面的示例演示可能导致异常的代码以及测试是否存在某种状态的代码。

13.使用 HttpResponse.Write 方法进行字符串串联
该方法提供非常有效的缓冲和连接服务。但是,如果您正在 执行广泛的连接,请使用多个 Response.Write 调用。下面示例中显示的技术比用对 Response.Write 方法的单个调用连接字符串更快。

Response.Write(“atest”);
Response.Write(strString);
Response.Write(“boxbig”);
14.除非有特殊的原因要关闭缓冲,否则使其保持打开
禁用 Web 窗体页的缓冲会导致大量的性能开销。
15.避免到服务器的不必要的往返过程
使用 Page.IsPostBack 避免对往返过程执行不必要的处理
虽然您很可能希望尽量多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些情况下却不宜使用 ASP.NET 服务器控件和回发事件处理。通常,只有在检索或存储数据时,您才需要启动到服务器的往返过程。多数数据操作可在这些往返过程间的客户端上进行。
16.ASP.NET应用程序性能测试
在对ASP.NET 应用程序进行性能测试之前,应确保应用程序没有错误,而且功能正确。具体的性能测试可以采用以下工具进行:Web Application Strees Tool (WAS)是Microsoft发布的一个免费测试工具,可以从http://webtool.rte.microsoft.com/上 下载。它可以模拟成百上千个用户同时对web应用程序进行访问请求,在服务器上形成流量负载,从而达到测试的目的,可以生成平均TTFB、平均TTLB等 性能汇总报告。 Application Center Test (ACT) 是一个测试工具,附带于Visual Studio.NET的企业版中,是Microsoft正式支持的web应用程序测试工具。它能够直观地生成图表结果,功能比WAS多,但不具备多个客户 机同时测试的能力。服务器操作系统”管理工具”中的”性能”计数器,可以对服务器进行监测以了解应用程序性能。
17.  压缩js,js在页面中调用的大小写要保持一致,免得缓存了不同的文件,页面的js可以的话,写成单位的文件进行调用 。图片少用jpeg,使用gzip 对网页进行压缩. 加快页面展示速度。
18.  把调用js,尽量写在页面底部, 还有viewstate 状态也可以重写到页面低部, 也可以把viewstate进行压缩。条件是viewstate必要要用的情况之下。
里面好多都是从网上搜集的。

[转载]《制作有价值的软件才是中国程序员内在的目标》

mikel阅读(950)

[转载]《制作有价值的软件才是中国程序员内在的目标》(2010/03/04) – EOM-企业经营模型 – 博客园.

上一篇从两个方面谈到了程序员的自身价值:收入价值和市场价值。 其主要目的是要提醒程序员不要埋头编程序,要树立市场意识,计算一下自己的自身价值,通过自身价值的计算可以客观地评价自己的技术能力,评价自身编制程序 的价值,对未来有一个更科学预计。当然价值问题是一个仁者见仁智者见智的问题。这里我就不在评判回帖中的一些看法了。

很多程序员 不关心自己编制程序的价值,一些新手沉迷于“学习”,编制一些“学习”程序,以达到练兵学习的目的;有的程序员天天忙于公司布置的工作,也不清楚这个程序 用在什么地方,这个程序能卖多少钱,他们认为这些都是公司的事,一点市场意识都没有,因为他们认为在市场方面他们是毫无话语权的。也有一些程序员虽然市场 意识很强,通过各种关系去找项目,但是项目也仅仅是杯水车薪,远远达不到挣大钱的目标,而且是饥一顿饱一顿,永远处于项目的“有”和“无”之间。

那到底什么是有价值的软件呢?可以从以下几个角度来看。

1、 软件层次

我们可以从软件的应用层次上来看,软件是分层的,从低到高:操作 系统软件、各种驱动软件、工具类软件、办公软件、开发工具、数据库、BI、应用软件等等,这些软件的价值可以通过他们的使用价值和软件 的销售价格就能知道它们的价值了。象操作系统、开发工具、数据库、BI等低层的核心的软件基本由国外公司所垄断,虽然其价值很高,但 是中国很少有公司有能力与其竞争的。因此,在中国这种急功近利的现状下,鲜有程序员去研发这些重要的基础软件。但是,不排除未来我们会补习这些重要功课 的。另一方我们看到应用软件是中国软件公司和程序员最可发挥才华的地方,而应用软件的价值有高有低,不可一概而论。

2、 软件数量

我们也可以从软件的数量上来看,有的软件至此一套,称之为定制软 件。有的软件可以拷贝无数,称之为通用软件或商品化软件。当然通用软件套数是受到这个软件的可能用户数所限制的。例如一个杀毒软件,它最高不可能超过电脑 用户数。一个财务软件可售套数它最高不可能超过企业总数。一般情况下,软件可销售的套数约多,软件的价值就越大。当然也有一套吃天下的定制软件的。

3、 软件售价

我们也可以从软件的售价上来看,有的软件(含项目)价格极高,有的甚至达到亿计,有的软件价格很低,甚至只有几十 元。我们一方面可以从价格上来给这个软件作个价值判断,比如这个软件在100万上就感觉价值很高了。另一方面我们要通过计算软件数量*软件单价来计算这个软件的价值。例如,软件A的单价是1万元,软件预计销售套数为5,则软件A的价值约为5万元。软件B的单价是100万,软件预计销售套数为1,则软件B的价值约为100万。软件C的单价是1万,软件预计销售套数为1000,则软件C的价值为1000万。就软件ABC来说,软件C价值要大于软件B,软件B价值大于软件A

4、 销售形式

我们也可以从软件销售形式上来看,有的软件通过产品方式提供给客 户,软件公司收入的软件的产品销售价。有的软件是通过收取服务费方式提供可客户使用的。通过产品方式的价值比较容易计算,有一算一的。而通过服务方式,尤 其是通过网上服务方式,则相对比较复杂一些。服务方式的价值往往取决于用户数和用户使用的年限。另外,服务方式的各种折扣也先对多些,例如10用户会收取90%的服务费,1000用户可能会收取70%的服务费。从发展的角度上看软件服务方式比重将逐步增加,其价 值也在逐步增大。

5、 是否收费

我们也可以从软件的是否收费上来看,绝大部分软件是要收费的,但 是也有一些软件被称之为绿色软件,可以供用户免费使用的。关于免费软件的价值情形比较复杂,有的免费软件是软件公司的赠品,有的免费软件是程序员自身能力 表现,有的免费软件是其他原因所致。但是,本文所谈的重点都是收费软件。

软件的价值无论大小,都体现的软件有用性的大小,软件的有用性则 又受制于软件用户数多少和软件的功能多少。中国的程序员无论在单位或是在家,都要注重自己所编制软件的价值,只有编制有价值的软件中国的程序员自身收入才能提高,程序员 社会地位才会提高,程序员自我实现度才能提高。而有价值的软件的增加说明软件在国民经济和居民生活中的应用程度的大大提高。

我们可能听到一些程序员说出这样的话:

1、 我们编什么程序我们 作不了主,公司叫我们编什么我们就编什么。我们才不管软件有什么价值不价值呢。

2、 我们想知道自己编程 的价值,但是我们无法获得自己编制软件的销售价格和套数。

3、 我们每天忙死,加班 加点,知道价值怎么样,不知道价值又怎么样?

4、 我们很想编制有价值 的软件,但我们不知道什么软件是有价值的。

我只能说程序员命运掌握在自己手中还是掌握别人手中,虽然不能完全取决于自己,但是要争取取决于自己,放弃自己的选 择只能是听天由命,程序员可以通过编制软件的价值的判断,来判断公司成长性的高低,进而选择自己的未来。程序员职业特点说明程序员有很多的自有空间,这个 空间可以做自己想做的事,而选择有价值的软件制作则是中国程序员内在的和可行的目标。

[转载]《如何计算程序员自身的价值》

mikel阅读(842)

[转载]《如何计算程序员自身的价值》(2010/02/25) – EOM-企业经营模型 – 博客园.

写完了有关优秀程序员素质的文章,我长长舒了一口气!很多时候想要说的话都说了出来!但是,定心一想还 有一个程序员自身价值这个话题呢!不吐不快!还是继续写吧!

“价值”这 个词,很正式,俗一点就是一个“钱”字。可以说这个字是当今社会的一个核心。无论经济活动、社会活动那个能离开这个字呀!“人为财死,鸟为食亡。”可能是 人生的一个真实的写照吧!

回到现实生活之中,我们常常听到程序员抱怨自己的工资很低,抱怨自己的付出和自己的报酬严重背离,有的甚至不考虑自己工作能力和工作贡献,以别人的工资或社会岗位工资来确定自己的报酬。同时我们也常常听到公司的老 板的抱怨软件公司不赚钱,最大的成本都花在员工的工资上了,自己在给员工打工。那么我们怎么能够计算程序员自身的价值呢?我们怎么看待程序员价值值与不值 呢?我感到程序员自身价值有两个方面,一个方面是程序员的收入价值,一方面是程序员市场价值。程序员收入价值比较简单,就是程序员的年收入。例如一个程序 员的年收入是5万。则这个程序员收入价值就是5万。程序员市场价值是指程序员本人编制的软件产品销售价值和预期的销售价值。例如,有5个程序员一年之中为一个客户编制了一个软件,软件售价5万元,假定我们忽略了销售成本和维护成本等,5个程序员水平大致相 同,则一个程序员的市场价值就是1万。

程序员的收入和程序员市场价值往往是不相同的:

1) 当大学生刚刚参加工 作的时候,由于能力的限制,很少能编好程序,大部分时间都花在学习上,但是,他还是获得工资报酬。但是他的市场价值为0。这个时候,程序员是正收入。

2) 当程序员具备编程能 力,投入到编程工作之中,程序员不但获得工资报酬,其劳动成果也变成的公司的收入。假定公司能收支平衡,而且分配比较合理,那么程序员收入价值于程序员的 市场价值相当。

3) 当程序员具备较强的 编程能力,投入到编程工作之中,程序员不但获得工资报酬,其劳动成果也变成公司的收入。假定公司产生利润,而且分配比较合理,那么程序员的收入价值就会低 于程序员市场价值,其差价就是程序员给公司的利润贡献。

4) 当公司出现亏损的时 候,程序员收入价值就会大于程序员的市场价值。

通过以上分析我们可以看出,程序员的市场价值是程序员收入价值的基础,当程序员收入价值远远低于其市场价值的时候,程序员就会感到收入太少, 分配不公,不愿意在公司长期呆下去。当程序员收入价值高于或等于其市场价值时候,公司老板只能维持或靠吃老本来维系公司的运作,维持不下去公司只好倒闭。 另外,程序员的市场价值要小于公司的软件销售收入。

就目前的中国软件企业运行状况而言,中国软件业早已告别了暴利时代,残酷的竞争使得软件的公司利润趋于下降。有的软件公司在竞争中倒闭,大部 分在维持中维持。究其原因很多,但是大部分软件公司都是靠开发项目来维持生计。而项目都是靠用户需求来驱动的,因此,开发出来的软件往往是定制的,不可复 用。一些好的软件公司,往往在一个行业中积累了大量的行业知识和软件开发的程序。通过程序积累系统积累,形成了行业系统的开发工具和通用软件。使得程序的 复用性大大增强,逐步形成了行业软件的优势,这样公司发展很好。

无论从程序 员个人的收入价值或市场价值,还是软件公司的运行状况来看。其核心是软件本身的市场价值。如果一个软件市场价值很高,则程序员的市场价值就会很高,公司的 利润就会很高。一个软件市场价值往往取决于这个软件的复用性或商品化程度。假定一个软件销售价格为1万,预期的销售个数达1000个时,其市场价值就会达1000万,假定这个软件是由5个人开发的,相关销售、维护、管理费用不计,则一个程序员的市 场价值就会达200万,尽管这个程序员年收入只有5万。但是程序员的市场价值是很高的。有了高的市场价值,就有了增加工资、奖金报酬的基础,就会有增加收入的希望。一 般开明的公司都会根据程序员的市场价值的大小来,来定义程序员报酬的。只有这样公司才能留住人才,才能获得更大的利润。

另外一种情况,当一个程序员的市场价值达到一定数值的时候,如50万、100万的时候,而收入价值处在较低的水平,例如,5万,6万,程序员往往会产生单干的念头,以期获取更大的收入价值。但 是,程序员往往会忘记市场价值的实现并不仅仅在于软件本身,还涉及到资金、资质、市场、销售、安装、维护、服务等各种环节。所以个人开软件公司人数挺多 的,但是成功的并不占多数。好的程序员往往不是好的老板,就是这个道理。

现实当中,大学生程序员年收入在2万元比比皆是,普通程序员年收入在4-5万居多,高级程序员在10万以上也不在少数,超过20万年收入的程序员也时有耳闻。

如果我们平均一下程序员的年收入,估计在5万左右。假定公司的 利润率为50%,全部由程序员贡献,那么程序员的市场价值也就在7-8万之间。这就意味着程序员一生(35年计)市场价值也就280万。这与比尔盖茨巨大财富相比是多么的渺小呀!这同时意味着中国程序员市场价值有很大的增长空间。

认识程序员的自身价值的目的。

第一, 就是希望程序员不要 单看自己的收入价值,不要埋怨自己的收入低,而是要更多看看自己的市场价值到底有多高。

第二, 市场价值就是编制满 足用户需求的软件,如果这个软件销售不出去,程序员再辛苦都白费,市场价值为0。程序员应该争做具 有市场价值的软件,同时尽力帮助软件实现销售。

第三, 市场价值最大化就是 多编制可复用的程序,以提高单个软件开发效率,以降低人力成本,提高利润率。

第四, 市场价值最大化就是 多编制商品化软件,在编制定制软件还是通用软件选择中,它可以成倍地提高程序员的市场化价值。

第五, 有了程序员市场化价 值认识后,程序员和公司可以共同地为促进市场化价值作出贡献,并到达员工提高收入,公司提高利润的双赢局面。

中国的程序 员市场化价值相对较低,导致程序员收入不高,程序员技术水平降低,最终导致软件质量下降。我们要关注程序员市场价值的提高,技术水平的提高,使得软件行业 通用能软件所占的比重大大增加,软件公司的收入大大增加,只有这样我们的程序员的收入大大增加,我们的软件才有更高的质量和更大的市场。

[转载]SqlServer性能优化——Partition(创建分区)

mikel阅读(1215)

[转载]SqlServer性能优化——Partition(创建分区) – smjack – 博客园.

和压缩(Compression)相比,数据库分区(Partition)的操作更为复杂繁琐。而且与Compression一次操作,终身保持不 同,分区是一项需要长期维护周期变更的操作。

分区的意义在于将大数据从物理上切割为几个相互独立的小部分,从而在查询时只取出其中一个 或几个分区,减少影响的数据;另外对于置于不同文件组的分区,并行查询的性能也要高于对整个表的查询性能。

事实上,在SQL Server 2005中就已经包含了分区功能,甚至在2005之前,还存在一个叫做“Partitioned Views”的功能,能通过将同样结构的表Union在一个View中,实现类似现在分区表的效果。而在SQL Server 2008中,分区功能得到了显著加强,使得我们不仅能够对表和索引做分区,而且允许对分区上锁,而不是之前的全表上锁

指定分 区列

和Compression一样,在SQL Server 2008中也提供了分区的向导界面。在企业管理器中,需要分区的表上右键选择Storage-》Create Partition:

SqlServer性能优化——Partition(创建分区)

这里会列出该表所有的字 段,包括字段类型、长度、精度及小数位数的信息,可以选择其中的任意一一列作为分区列(Patitioning Column),不仅仅是数字或者日期类型,即使是字符串类型的列,也可以按照字母顺序进行分区。而以下类型的列不可用于分区:text、 ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名、 hierarchyid、空间索引或 CLR 用户定义的数据类型。此外,如果使用计算列作为分区列,则必须将该列设 为持久化列(Persisit)。

在列表下方,提供了两个选项:

  1. 分 配到可用分区表
    这要求在同一数据库下有另一张已分好区的表,同时该表的分区列和当前选中的列的类型完 全一致
    这样的好处是当两张表在查询中有关联时,并且其关联列就是分区列时,使用同样的分区策略会更有效率。
  2. 将非唯一索引和唯一索引的存储空间调整为与索引分区列一致
    这样会将表中的所 有索引也一同分区,实现“对齐”。这是一个重要而麻烦的选项,具体需求请参阅MSDN(已分区索引的特殊指导原则)。
    这样的好处是表和索引的分区一致,一方面查询时利用索引更 为高效,而且在下文提到的移入移出分区也会更为高效。

注意:这里建议使 用聚集索引列作为分区列。一方面索引结构本身就应与查询相关,那么分区列与索引一致会保证查询的最大效率;另一方面,保证索引对齐而且 是聚集索引对齐是保证分区的移入移出操作顺畅的前提,否则可能会出现无法移入移出的情况,而分区的移入移出又是管理大数据的重要策略——滑 动窗口(SlideWindow)策略的基础操作。

分区函数与分区方案

选好分区列后,如果没 有应用“分配到可用分区表”选项,接下来则会进入选择\创建分区函数以及分区方案的 界面。其中分区函数会指定分区边界,而分区方案则规划了每个分区所存储的文件组。

向导操作界面如下:

SqlServer性能优化——Partition(创建分区)

其中Left boundary说明每个分区的边界值被包含在边界值左侧的分区中,也就是每个分区内的数据约束是<=指定的边界值, 相应的,Right boundary则说明每个分区的边界值被包含在边界值右侧的分区中,每个分区内的数据约束是<指定的边界值

在下方的列表中,列出了当前分区方案下现有的分区。其中文件组(Filegroup)指定了每个分区存放的位置,如果将分区放置于位于不同磁盘 中的不同文件组中,由于不同磁盘的读写互不干扰,这将提高分区表并行处理的效率。一般情况下,将所有分区放置在同一个文件组是比较稳妥的做法。关于文件组 的展开阅读可以参阅:SQL Server Filegroups

注意,在这里最后一个分区是 没有指定边界的,用于保存所有>(Left Boundary)或>=(Right boundary)最后一个分区边界的数据。

如果选择时间类型的字段作为分区列,可以通过Set按钮实现按条件分组:

SqlServer性能优化——Partition(创建分区)

这样可以很方便得通过设置起止时间将表按照指 定时间段自动分区,但之后依然需要手动指定每个分区的文件组。

制定好分区方案之后可以通过Estimate sotrage预估每个分区的行数、空间占用情况,不过除非需要以占用空间或行数来规划你的分区策略,一般不建议在这里进行预估, 因为如果对空表来说,预估的结果当然都是0,而如果表中已经包含大量数据,预估则会花费比较长的时间。

创建分区

通 过以上设置,分区已经基本完毕,在向导的最后,可以选择是创建脚本还是立即执行分区操作。

我们可以查看在不同情况下创建分区的脚本的情 况:

1.在表没有索引的情况下:

BEGIN TRANSACTION
CREATE PARTITION FUNCTION [TestFunction](datetime) AS RANGE LEFT FOR VALUES (N'2010-01-01T00:00:00', N'2010-02-01T00:00:00', 
N'2010-03-01T00:00:00', N'2010-04-01T00:00:00', N'2010-05-01T00:00:00', N'2010-06-01T00:00:00')

CREATE PARTITION SCHEME [TestScheme] AS PARTITION [TestFunction] TO ([PRIMARY], [PRIMARY], [PRIMARY], 
[PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY])

CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] 
(
    [birthday]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday])

DROP INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] WITH ( ONLINE = OFF )
COMMIT TRANSACTION

这里先创建Partition Function以及Partition Scheme,之后在分区列上创建聚集索引并按照分区方案分区,最后删除了这一索引。</>

2.在表有索引的情况下:

如果原先没有聚集索引:

CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] 
(
    [birthday]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday])

DROP INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] WITH ( ONLINE = OFF )

这和没有索引的情况一样,如果表原先存在聚集索引,则脚本变为:

CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])

可以看到原有的聚集索引(IX_id)在分区方案上被重建了。

如果选择了“对齐索引”选项,则会对所有索引都应用分区:

CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])

CREATE NONCLUSTERED INDEX [UIX_birthday] ON [dbo].[Account] 
(
    [birthday] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])
CREATE NONCLUSTERED INDEX [UIX_name] ON [dbo].[Account] 
(
    [name] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)

这里不仅对聚集索引IX_id进行了分区,也对非聚集索引UIX_name和UIX_birthday进行了分区。

注意事项

  1. 对一张表分好区后不可以进行再次分区,同时也没有直接取消表分区的方法
  2. 如果要查看已分区表的分区状态以及每个分区中的行数和占用空间,可以通过Storage-》Management Compression查看。同时可以在这里为每个分区指定压缩方式。
  3. 如果分区表索引没有对齐,则不可以对该表进行切入切出(Switch in/out)操作,同样也不能执行滑动窗口操作
  4. 分区实际上是在每个分区表都添加了约束,相应的插入操作的性能也会受到影响。
  5. 即使进行了分区,如果查询的条件字段和分区列并没有关联,性能也未必会得到提升。

附:对分区并行查询的说明

由于我在实际操作中主要考虑并行查询方面的效率,所以文章里只是略略带过,但评论中有人提到,所以摘录整理一些资料在下面:

  1. 并行查询肯定需要多核支持,单核下并行是不可能的。
  2. 在2005中,如果有两个以上的Partition,一个线程对应一个Partition,所以如果有10个线程,却只有3个分区的话,就会 有7个线程被浪费。
  3. 在2008中,这一问题被改进,所有的线程都被投入到所有的Partition中。具体可以参看Partitioning enhancements in SQL Server 2008