<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[ewan_study_space]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[文章都是转贴  拒绝评论  概不答复     ewan_study &copy; 2006-2008]]></description>
<link>http://hi.baidu.com/godblessewan</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[context attribute]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/84fb631e3f97bcf61bd576ac.html]]></link>
        <description><![CDATA[
		
		.NET Framework拦截机制的设计中，在客户端和对象之间，存在着多种消息接收器，这些消息接收器组成一个链表，客户端的调用对象的过程以及调用返回实行拦截，你可以定制自己的消息接收器，把它们插入了到链表中，来完成你对一个调用的前处理和后处理。那么调用拦截是如何构架或者说如何实现的呢？ 
<p><font size="2">在.NET中有两种调用，一种是跨应用域（App Domain)，一种是跨上下文环境（Context)，两种调用均通过中间的代理（proxy)，代理被分为两个部分：透明代理和实际代理。透明代理暴露同对象一样的公共入口点，当客户调用透明代理的时候，透明代理把堆栈中的帧转换为消息（上一节提到的实现IMessage接口的对象），消息中包含了方法名称和参数等属性集，然后把消息传递给实际代理，接下去分两种情况：在跨应用域的情况下，实际代理使用一个格式化器对消息进行序列化，然后放入远程通道中；在跨上下文环境的情况下，实际代理不必知道格式化器、通道和Context拦截器，它只需要在向前传递消息之前对调用实行拦截，然后它把消息传递给一个消息接收器（实现IMessageSink的对象），每一个接收器都知道自己的下一个接收器，当它们对消息进行处理之后（前处理），都将消息传递给下一个接收器，一直到链表的最后一个接收器，最后一个接收器被称为堆栈创建器，它把消息还原为堆栈帧，然后调用对象，当调用方法结果返回的时候，堆栈创建器把结果转换为消息，传回给调用它的消息接收器，于是消息沿着原来的链表往回传，每个链表上的消息接收器在回传消息之前都对消息进行后处理。一直到链表的第一个接收器，第一个接收器把消息传回给实际代理，实际代理把消息传递给透明代理，后者把消息放回到客户端的堆栈中。从上面的描述我们看到穿越Context的消息不需要格式化，CLR使用一个内部的通道，叫做CrossContextChannel，这个对象也是一种消息接收器。</font></p>
<p><font size="2">有几种消息接收器的类型，一个调用拦截可以在服务器端进行也可以在客户端进行，服务器端接收器拦截所有对服务器上下文环境中对象的调用，同时作一些前处理和后处理。客户端的接收器拦截所有外出客户端上下文环境的调用，同时也做一些前处理和后处理。服务器负责服务器端接收器的安装，拦截对服务器端上下文环境访问的接收器称为服务器上下文环境接收器，那些拦截调用实际对象的接收器是对象接收器。通过客户安装的客户端接收器称为客户端上下文环境接受器，通过对象安装的客户端接收器则称为特使（Envoy）接收器，特使接收器仅拦截那些和它相关的对象。客户端的最后一个接收器和服务器端的第一个接收器是CrossContextChannel类型的实例。不同类型的接收器组成不同的段，每个段的端点都装上称为终结器的接收器，终结器起着把本段的消息传给下一个段的作用。在服务器上下文环境段的最后一个终结器是ServerContextTerminatorSink。如果你在终结器调用NextSink，它将返回一个null，它们的行为就像是死端头，但是在它们内部保存有下一个接收器对象的私有字段。</font></p>
<p><font size="2">我们大致介绍了.NET Framework的对象调用拦截的实现机制，目的是让大家对这种机制有一个认识，现在是实现我们代码的时候了，通过代码的实现，你可以看到消息如何被处理的过程。首先是为我们的程序定义一个接收器CallTraceSink:</font></p>
<br>
<pre style="BACKGROUND-COLOR: silver">//TraceContext.cs

using System;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Activation;

namespace NiwalkerDemo
{
&nbsp;&nbsp;&nbsp; public class CallTraceSink : IMessageSink //实现IMessageSink
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private IMessageSink nextSink;&nbsp;&nbsp; //保存下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在构造器中初始化下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public CallTraceSink(IMessageSink next)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextSink=next;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //必须实现的IMessageSink接口属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessageSink NextSink
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return nextSink;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实现IMessageSink的接口方法，当消息传递的时候，该方法被调用
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessage SyncProcessMessage(IMessage msg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //拦截消息，做前处理
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Preprocess(msg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //传递消息给下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMessage retMsg=nextSink.SyncProcessMessage(msg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用返回时进行拦截，并进行后处理
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Postprocess(msg,retMsg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return retMsg;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //IMessageSink接口方法，用于异步处理，我们不实现异步处理，所以简单返回null,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //不管是同步还是异步，这个方法都需要定义
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //我们的前处理方法，用于检查库存，出于简化的目的，我们把检查库存和发送邮件都写在一起了，
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在实际的实现中，可能也需要把Inventory对象绑定到一个上下文环境，
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //另外，可以将发送邮件设计为另外一个接收器，然后通过NextSink进行安装
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void Preprocess(IMessage msg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检查是否是方法调用，我们只拦截Order的Submit方法。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMethodCallMessage call=msg as IMethodCallMessage;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call==null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call.MethodName=="Submit")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string product=call.GetArg(0).ToString(); //获取Submit方法的第一个参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int qty=(int)call.GetArg(1); //获取Submit方法的第二个参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用Inventory检查库存存量
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(new Inventory().Checkout(product,qty))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Order availible");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Order unvailible");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SendEmail();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //后处理方法，用于记录订单提交信息，同样可以将记录作为一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //我们在这里处理，仅仅是为了演示
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void Postprocess(IMessage msg,IMessage retMsg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMethodCallMessage call=msg as IMethodCallMessage;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call==null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Log order information");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void SendEmail()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Send email to manager");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp; 
</pre>
<font size="2">接下来我们定义上下文环境的属性，上下文环境属性必须根据你要创建的接收器类型来实现相应的接口，比如：如果创建的是服务器上下文环境接收器，那么必须实现IContributeServerContextSink接口。</font> <br>
<pre style="BACKGROUND-COLOR: silver">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
public class CallTraceProperty : IContextProperty, IContributeObjectSink
{
&nbsp;&nbsp;&nbsp; public CallTraceProperty()
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContributeObjectSink的接口方法，实例化消息接收器
&nbsp;&nbsp;&nbsp; public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new CallTraceSink(next);
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口方法，如果该方法返回ture,在新的上下文环境中激活对象
&nbsp;&nbsp;&nbsp; public bool IsNewContextOK(Context newCtx)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口方法，提供高级使用
&nbsp;&nbsp;&nbsp; public void Freeze(Context newCtx)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口属性
&nbsp;&nbsp;&nbsp; public string Name
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return "OrderTrace";}
&nbsp;&nbsp;&nbsp; }
}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
</pre>
<font size="2">最后是ContextAttribute</font> 
<pre style="BACKGROUND-COLOR: silver">&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Class)]
&nbsp;&nbsp;&nbsp; public class CallTraceAttribute : ContextAttribute
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public CallTraceAttribute():base("CallTrace")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //重载ContextAttribute方法，创建一个上下文环境属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ctorMsg.ContextProperties.Add(new CallTraceProperty());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}
</pre>
<br>
为了看清楚调用Order对象的Submit方法如何被拦截，我们稍微修改一下Order类，同时把它设计为ContextBoundObject的派生类： <br>
<pre style="BACKGROUND-COLOR: silver">//Inventory.cs

//Order.cs
using System;

namespace NiwalkerDemo
{
&nbsp;&nbsp;&nbsp; [CallTrace]
&nbsp;&nbsp;&nbsp; public class Order : ContextBoundObject
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void Submit(string product, int quantity)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.product=product;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.quantity=quantity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; }
}

</pre>
<br>
<font size="2">客户端调用代码：</font> <br>
<pre style="BACKGROUND-COLOR: silver">...
public class AppMain
{
&nbsp;&nbsp;&nbsp; static void Main()
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order1=new Order(100);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order1.Submit("Item1",150);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order2=new Order(101);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order2.Submit("Item2",150);
&nbsp;&nbsp;&nbsp; }
}
...
</pre>
<p><font size="2">运行结果表明了我们对Order的Sbumit成功地进行了拦截。需要说明的是，这里的代码仅仅是作为对ContextAttribute应用的演示，它是粗线条的。在具体的实践中，大家可以设计的更精妙。</font> </p>
<p><font size="2">后记：本来想对Attribute进行更多的介绍，发现要讲的东西实在是太多了。请允许我在其他的专题中再来讨论它们。十分感谢大家有耐心读完这个系列。如果这里介绍的内容在你的编程生涯有所启迪的话，那么就是我的莫大荣幸了。再一次谢谢大家。<br>
</font></p> <a href="http://hi.baidu.com/godblessewan/blog/item/84fb631e3f97bcf61bd576ac.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/84fb631e3f97bcf61bd576ac.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-08  11:44 A.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/84fb631e3f97bcf61bd576ac.html</guid>
</item>

<item>
        <title><![CDATA[用Visual C#调用Windows API函数]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/928ecea2878183afcbefd0d5.html]]></link>
        <description><![CDATA[
		
		<table width="100%">
<tbody>
    <tr>
        <td class="a14">Api函数是构筑Windws应用程序的基石，每一种Windows应用程序开发工具，它提供的底层函数都间接或直接地调用了Windows API函数，同时为了实现功能扩展，一般也都提供了调用WindowsAPI函数的接口， 也就是说具备调用动态连接库的能力。Visual C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows API函数。它能够定位和调用输出函数,根据需要，组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">下面以C#为例简单介绍调用API的基本过程： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>动态链接库函数的声明 </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　动态链接库函数使用前必须声明，相对于VB,C#函数声明显得更加罗嗦，前者通过 Api Viewer粘贴以后，可以直接使用，而后者则需要对参数作些额外的变化工作。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　动态链接库函数声明部分一般由下列两部分组成，一是函数名或索引号，二是动态链接库的文件名。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　 譬如，你想调用User32.DLL中的MessageBox函数，我们必须指明函数的名字MessageBoxA或MessageBoxW，以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本，单字节字符的ANSI版本和双字节字符的UNICODE版本。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　下面是一个调用API函数的例子： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true, </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">CharSet=CharSet.Unicode, ExactSpelling=true, </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">CallingConvention=CallingConvention.StdCall)] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static extern bool MoveFile(String src, String dst); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　其中入口点EntryPoint标识函数在动态链接库的入口位置，在一个受管辖的工程中，目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且，你还可以把这个入口点映射为一个不同的名字，也就是对函数进行重命名。重命名可以给调用函数带来种种便利，通过重命名，一方面我们不用为函数的大小写伤透脑筋，同时它也可以保证与已有的命名规则保持一致，允许带有不同参数类型的函数共存，更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本，ExactSpelling＝false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助.</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　在C#中，你可以在EntryPoint域通过名字和序号声明一个动态链接库函数，如果在方法定义中使用的函数名与DLL入口点相同，你不需要在EntryPoint域显示声明函数。否则，你必须使用下列属性格式指示一个名字和序号。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("dllname", EntryPoint="Functionname")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("dllname", EntryPoint="#123")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">值得注意的是，你必须在数字序号前加“＃” </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">下面是一个用MsgBox替换MessageBox名字的例子： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[C#] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">using System.Runtime.InteropServices; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public class Win32 { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("user32.dll", EntryPoint="MessageBox")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static extern int MsgBox(int hWnd, String text, String caption, uint type); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数，譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员，这时你必须提供额外的信息格式化这个类型，以保持参数原有的布局和对齐。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">C#提供了一个StructLayoutAttribute类，通过它你可以定义自己的格式化类型，在受管辖代码中，格式化类型是一个用StructLayoutAttribute说明的结构或类成员，通过它能够保证其内部成员预期的布局信息。布局的选项共有三种：</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>布局选项 </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>描述 </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">LayoutKind.Automatic </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">为了提高效率允许运行态对类型成员重新排序。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">注意：永远不要使用这个选项来调用不受管辖的动态链接库函数。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">LayoutKind.Explicit </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">对每个域按照FieldOffset属性对类型成员排序 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">LayoutKind.Sequential </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">传递结构成员 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">下面的例子说明如何在受管辖代码中定义一个点和矩形类型，并作为一个参数传递给User32.dll库中的PtInRect函数， </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">函数的不受管辖原型声明如下： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">BOOL PtInRect(const RECT *lprc, POINT pt); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">注意你必须通过引用传递Rect结构参数，因为函数需要一个Rect的结构指针。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[C#] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">using System.Runtime.InteropServices; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[StructLayout(LayoutKind.Sequential)] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public struct Point { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public int x; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public int y; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[StructLayout(LayoutKind.Explicit] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public struct Rect { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[FieldOffset(0)] public int left; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[FieldOffset(4)] public int top; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[FieldOffset(8)] public int right; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[FieldOffset(12)] public int bottom; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">class Win32API { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("User32.dll")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static extern Bool PtInRect(ref Rect r, Point p); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">类似你可以调用GetSystemInfo函数获得系统信息： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">? using System.Runtime.InteropServices; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[StructLayout(LayoutKind.Sequential)] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public struct SYSTEM_INFO { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwOemId; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwPageSize; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint lpMinimumApplicationAddress; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint lpMaximumApplicationAddress; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwActiveProcessorMask; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwNumberOfProcessors; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwProcessorType; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwAllocationGranularity; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwProcessorLevel; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public uint dwProcessorRevision; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("kernel32")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">static extern void GetSystemInfo(ref SYSTEM_INFO pSI); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">SYSTEM_INFO pSI = new SYSTEM_INFO(); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">GetSystemInfo(ref pSI); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>类成员的传递 </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">同样只要类具有一个固定的类成员布局，你也可以传递一个类成员给一个不受管辖的动态链接库函数，下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数, 函数用C/C++调用规范如下:</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">void GetSystemTime(SYSTEMTIME* SystemTime); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">不像传值类型,类总是通过引用传递参数. </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[C#] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[StructLayout(LayoutKind.Sequential)] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public class MySystemTime { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wYear; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wMonth; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wDayOfWeek; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wDay; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wHour; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wMinute; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wSecond; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public ushort wMilliseconds; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">class Win32API { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("User32.dll")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static extern void GetSystemTime(MySystemTime st); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>回调函数的传递: </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义，然后调用它即可,这个过程非常直接。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">如果一个动态链接库函数需要一个函数指针作为参数，你还需要做以下几步： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">首先，你必须参考有关这个函数的文档，确定这个函数是否需要一个回调；第二，你必须在受管辖代码中创建一个回调函数；最后，你可以把指向这个函数的指针作为一个参数创递给DLL函数,.</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>回调函数及其实现: </strong></td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32 API 中的EnumFontFamilies(字体枚举), EnumPrinters(打印机), EnumWindows (窗口枚举)函数. 下面以窗口枚举为例,谈谈如何通过调用EnumWindow 函数遍历系统中存在的所有窗口</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">分下面几个步骤: </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">1. 在实现调用前先参考函数的声明 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam) </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">显然这个函数需要一个回调函数地址作为参数. </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄，第二个参数由应用程序定义，两个参数均为整形。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　　当这个回调函数返回一个非零值时，标示执行成功，零则暗示失败，这个例子总是返回True值，以便持续枚举。 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">3. 最后创建以代表对象(delegate)，并把它作为一个参数传递给EnumWindows 函数，平台会自动地 把代表转化成函数能够识别的回调格式。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[C#] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">using System; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">using System.Runtime.InteropServices; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public delegate bool CallBack(int hwnd, int lParam); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public class EnumReportApp { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("user32")] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static extern int EnumWindows(CallBack x, int y); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static void Main() </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">{ </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">CallBack myCallBack = new CallBack(EnumReportApp.Report); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">EnumWindows(myCallBack, 0); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">public static bool Report(int hwnd, int lParam) { </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">Console.Write("窗口句柄为"); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">Console.WriteLine(hwnd); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">return true; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">} </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"><strong>指针类型参数传递：</strong> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　在Windows API函数调用时，大部分函数采用指针传递参数，对一个结构变量指针，我们除了使用上面的类和结构方法传递参数之外，我们有时还可以采用数组传递参数。</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　下面这个函数通过调用GetUserName获得用户名 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">BOOL GetUserName( </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">LPTSTR lpBuffer, // 用户名缓冲区 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">LPDWORD nSize // 存放缓冲区大小的地址指针 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　 </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[DllImport("Advapi32.dll", </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">EntryPoint="GetComputerName", </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">ExactSpelling=false, </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">SetLastError=true)] </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">static extern bool GetComputerName ( </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　 [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize ); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　这个函数接受两个参数，char * 和int *,因为你必须分配一个字符串缓冲区以接受字符串指针，你可以使用String类代替这个参数类型，当然你还可以声明一个字节数组传递ANSI字符串，同样你也可以声明一个只有一个元素的长整型数组，使用数组名作为第二个参数。上面的函数可以调用如下：</td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14"> </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">byte[] str=new byte[20]; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">Int32[] len=new Int32[1]; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">len[0]=20; </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">GetComputerName (str,len); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">MessageBox.Show(System.Text.Encoding.ASCII.GetString(str)); </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　最后需要提醒的是，每一种方法使用前必须在文件头加上： </td>
    </tr>
</tbody>
</table>
<table width="100%">
<tbody>
    <tr>
        <td class="a14">　using System.Runtime.InteropServices; </td>
    </tr>
</tbody>
</table> <a href="http://hi.baidu.com/godblessewan/blog/item/928ecea2878183afcbefd0d5.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/928ecea2878183afcbefd0d5.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-08  11:17 A.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/928ecea2878183afcbefd0d5.html</guid>
</item>

<item>
        <title><![CDATA[Attribute在.NET编程的应用（六）]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/cbfda945581a2d24cffca38b.html]]></link>
        <description><![CDATA[
		
		<div class="postbody">Posted on 2005-07-06 11:24 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(234) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/187070.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=187070"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=187070"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/187070.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/187070.html?webview=1" width="1"> </div>
<div class="postbody"></div>
<div class="postbody">(承上节） .NET Framework拦截机制的设计中，在客户端和对象之间，存在着多种消息接收器，这些消息接收器组成一个链表，客户端的调用对象的过程以及调用返回实行拦截，你可以定制自己的消息接收器，把它们插入了到链表中，来完成你对一个调用的前处理和后处理。那么调用拦截是如何构架或者说如何实现的呢？ </div>
<p class="postbody"><font size="2">在.NET中有两种调用，一种是跨应用域（App Domain)，一种是跨上下文环境（Context)，两种调用均通过中间的代理（proxy)，代理被分为两个部分：透明代理和实际代理。透明代理暴露同对象一样的公共入口点，当客户调用透明代理的时候，透明代理把堆栈中的帧转换为消息（上一节提到的实现IMessage接口的对象），消息中包含了方法名称和参数等属性集，然后把消息传递给实际代理，接下去分两种情况：在跨应用域的情况下，实际代理使用一个格式化器对消息进行序列化，然后放入远程通道中；在跨上下文环境的情况下，实际代理不必知道格式化器、通道和Context拦截器，它只需要在向前传递消息之前对调用实行拦截，然后它把消息传递给一个消息接收器（实现IMessageSink的对象），每一个接收器都知道自己的下一个接收器，当它们对消息进行处理之后（前处理），都将消息传递给下一个接收器，一直到链表的最后一个接收器，最后一个接收器被称为堆栈创建器，它把消息还原为堆栈帧，然后调用对象，当调用方法结果返回的时候，堆栈创建器把结果转换为消息，传回给调用它的消息接收器，于是消息沿着原来的链表往回传，每个链表上的消息接收器在回传消息之前都对消息进行后处理。一直到链表的第一个接收器，第一个接收器把消息传回给实际代理，实际代理把消息传递给透明代理，后者把消息放回到客户端的堆栈中。从上面的描述我们看到穿越Context的消息不需要格式化，CLR使用一个内部的通道，叫做CrossContextChannel，这个对象也是一种消息接收器。</font></p>
<p class="postbody"><font size="2">有几种消息接收器的类型，一个调用拦截可以在服务器端进行也可以在客户端进行，服务器端接收器拦截所有对服务器上下文环境中对象的调用，同时作一些前处理和后处理。客户端的接收器拦截所有外出客户端上下文环境的调用，同时也做一些前处理和后处理。服务器负责服务器端接收器的安装，拦截对服务器端上下文环境访问的接收器称为服务器上下文环境接收器，那些拦截调用实际对象的接收器是对象接收器。通过客户安装的客户端接收器称为客户端上下文环境接受器，通过对象安装的客户端接收器则称为特使（Envoy）接收器，特使接收器仅拦截那些和它相关的对象。客户端的最后一个接收器和服务器端的第一个接收器是CrossContextChannel类型的实例。不同类型的接收器组成不同的段，每个段的端点都装上称为终结器的接收器，终结器起着把本段的消息传给下一个段的作用。在服务器上下文环境段的最后一个终结器是ServerContextTerminatorSink。如果你在终结器调用NextSink，它将返回一个null，它们的行为就像是死端头，但是在它们内部保存有下一个接收器对象的私有字段。</font></p>
<p class="postbody"><font size="2">我们大致介绍了.NET Framework的对象调用拦截的实现机制，目的是让大家对这种机制有一个认识，现在是实现我们代码的时候了，通过代码的实现，你可以看到消息如何被处理的过程。首先是为我们的程序定义一个接收器CallTraceSink:</font></p>
<div class="postbody"><br>
</div>
<div>
<pre style="BACKGROUND-COLOR: silver">//TraceContext.cs

using System;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Activation;

namespace NiwalkerDemo
{
&nbsp;&nbsp;&nbsp; public class CallTraceSink : IMessageSink //实现IMessageSink
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private IMessageSink nextSink;&nbsp;&nbsp; //保存下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在构造器中初始化下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public CallTraceSink(IMessageSink next)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextSink=next;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //必须实现的IMessageSink接口属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessageSink NextSink
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return nextSink;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //实现IMessageSink的接口方法，当消息传递的时候，该方法被调用
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessage SyncProcessMessage(IMessage msg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //拦截消息，做前处理
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Preprocess(msg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //传递消息给下一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMessage retMsg=nextSink.SyncProcessMessage(msg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用返回时进行拦截，并进行后处理
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Postprocess(msg,retMsg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return retMsg;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //IMessageSink接口方法，用于异步处理，我们不实现异步处理，所以简单返回null,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //不管是同步还是异步，这个方法都需要定义
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //我们的前处理方法，用于检查库存，出于简化的目的，我们把检查库存和发送邮件都写在一起了，
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在实际的实现中，可能也需要把Inventory对象绑定到一个上下文环境，
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //另外，可以将发送邮件设计为另外一个接收器，然后通过NextSink进行安装
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void Preprocess(IMessage msg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检查是否是方法调用，我们只拦截Order的Submit方法。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMethodCallMessage call=msg as IMethodCallMessage;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call==null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call.MethodName=="Submit")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string product=call.GetArg(0).ToString(); //获取Submit方法的第一个参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int qty=(int)call.GetArg(1); //获取Submit方法的第二个参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用Inventory检查库存存量
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(new Inventory().Checkout(product,qty))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Order availible");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Order unvailible");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SendEmail();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //后处理方法，用于记录订单提交信息，同样可以将记录作为一个接收器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //我们在这里处理，仅仅是为了演示
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void Postprocess(IMessage msg,IMessage retMsg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMethodCallMessage call=msg as IMethodCallMessage;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(call==null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Log order information");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void SendEmail()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Send email to manager");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp; 
</pre>
</div>
<div><font size="2">接下来我们定义上下文环境的属性，上下文环境属性必须根据你要创建的接收器类型来实现相应的接口，比如：如果创建的是服务器上下文环境接收器，那么必须实现IContributeServerContextSink接口。</font> <br>
</div>
<div>
<pre style="BACKGROUND-COLOR: silver">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
public class CallTraceProperty : IContextProperty, IContributeObjectSink
{
&nbsp;&nbsp;&nbsp; public CallTraceProperty()
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContributeObjectSink的接口方法，实例化消息接收器
&nbsp;&nbsp;&nbsp; public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new CallTraceSink(next);
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口方法，如果该方法返回ture,在新的上下文环境中激活对象
&nbsp;&nbsp;&nbsp; public bool IsNewContextOK(Context newCtx)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口方法，提供高级使用
&nbsp;&nbsp;&nbsp; public void Freeze(Context newCtx)
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; //IContextProperty接口属性
&nbsp;&nbsp;&nbsp; public string Name
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return "OrderTrace";}
&nbsp;&nbsp;&nbsp; }
}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
</pre>
</div>
<div><font size="2">最后是ContextAttribute</font> </div>
<div>
<pre style="BACKGROUND-COLOR: silver">&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Class)]
&nbsp;&nbsp;&nbsp; public class CallTraceAttribute : ContextAttribute
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public CallTraceAttribute():base("CallTrace")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //重载ContextAttribute方法，创建一个上下文环境属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ctorMsg.ContextProperties.Add(new CallTraceProperty());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}
</pre>
</div>
<div><br>
为了看清楚调用Order对象的Submit方法如何被拦截，我们稍微修改一下Order类，同时把它设计为ContextBoundObject的派生类： <br>
</div>
<div>
<pre style="BACKGROUND-COLOR: silver">//Inventory.cs

//Order.cs
using System;

namespace NiwalkerDemo
{
&nbsp;&nbsp;&nbsp; [CallTrace]
&nbsp;&nbsp;&nbsp; public class Order : ContextBoundObject
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void Submit(string product, int quantity)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.product=product;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.quantity=quantity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp; }
}

</pre>
</div>
<div><br>
<font size="2">客户端调用代码：</font> <br>
</div>
<div>
<pre style="BACKGROUND-COLOR: silver">...
public class AppMain
{
&nbsp;&nbsp;&nbsp; static void Main()
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order1=new Order(100);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order1.Submit("Item1",150);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order2=new Order(101);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order2.Submit("Item2",150);
&nbsp;&nbsp;&nbsp; }
}
...
</pre>
</div>
<p><font size="2">运行结果表明了我们对Order的Sbumit成功地进行了拦截。需要说明的是，这里的代码仅仅是作为对ContextAttribute应用的演示，它是粗线条的。在具体的实践中，大家可以设计的更精妙。</font> </p>
<p><font size="2">后记：本来想对Attribute进行更多的介绍，发现要讲的东西实在是太多了。请允许我在其他的专题中再来讨论它们。十分感谢大家有耐心读完这个系列。如果这里介绍的内容在你的编程生涯有所启迪的话，那么就是我的莫大荣幸了。再一次谢谢大家。<br>
（全文完）<br>
</font></p> <a href="http://hi.baidu.com/godblessewan/blog/item/cbfda945581a2d24cffca38b.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/cbfda945581a2d24cffca38b.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:31 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/cbfda945581a2d24cffca38b.html</guid>
</item>

<item>
        <title><![CDATA[Attribute在.NET编程中的应用（五）]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/b06b86222ebca3f1d7cae28a.html]]></link>
        <description><![CDATA[
		
		Posted on 2005-07-06 09:37 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(222) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/187004.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=187004"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=187004"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/187004.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/187004.html?webview=1" width="1"> 
<div class="postbody">Attribute在拦截机制上的应用 <br>
从这一节开始我们讨论Attribute的高级应用，为此我准备了一个实际的例子：我们有一个订单处理系统，当一份订单提交的时候，系统检查库 
<p>存，如果库存存量满足订单的数量，系统记录订单处理记录，然后更新库存，如果库存存量低于订单的数量，系统做相应的记录，同时向库存</p>
<p>管理员发送邮件。为了方便演示，我们对例子进行了简化：</p>
<p>//Inventory.cs<br>
using System;<br>
using System.Collections;</p>
<p>namespace NiwalkerDemo<br>
{<br>
&nbsp;&nbsp;&nbsp; public class Inventory<br>
&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private Hashtable inventory=new Hashtable();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Inventory()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inventory["Item1"]=100;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inventory["Item2"]=200;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool Checkout(string product, int quantity)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int qty=GetQuantity(product);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return qty&gt;=quantity;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int GetQuantity(string product)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int qty=0;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(inventory[product]!=null)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qty = (int)inventory[product];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return qty;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void Update(string product, int quantity)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int qty=GetQuantity(product);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inventory[product]=qty-quantity;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
}</p>
<p>//Logbook.cs<br>
using System;</p>
<p>namespace NiwalkerDemo<br>
{<br>
&nbsp;&nbsp;&nbsp; public class Logbook<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void Log(string logData)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("log:{0}",logData);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
}</p>
<p>//Order.cs<br>
using System;</p>
<p>namespace NiwalkerDemo<br>
{<br>
&nbsp;&nbsp;&nbsp; public class Order<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int orderId;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string product;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int quantity;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Order(int orderId)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.orderId=orderId;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void Submit()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Inventory inventory=new Inventory(); //创建库存对象<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检查库存<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(inventory.Checkout(product,quantity))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Logbook.Log("Order"+orderId+" available");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inventory.Update(product,quantity);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Logbook.Log("Order"+orderId+" unavailable");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SendEmail();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string ProductName<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get{ return product; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set{ product=value;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int OrderId<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get{ return orderId; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int Quantity<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get{ return quantity;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set{ quantity=value; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void SendEmail()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Send email to manager");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
}</p>
<p> </p>
<p>下面是调用程序：</p>
<p>//AppMain.cs</p>
<p>using System;</p>
<p>namespace NiwalkerDemo<br>
{<br>
&nbsp;&nbsp;&nbsp; public class AppMain<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order1=new Order(100);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order1.ProductName="Item1";<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order1.Quantity=150;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order1.Submit();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Order order2=new Order(101);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order2.ProductName="Item2";<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order2.Quantity=150;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; order2.Submit();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
}</p>
<p><br>
程序看上去还不错，商务对象封装了商务规则，运行的结果也符合要求。但是我好像听到你在抱怨了，没有吗？当你的客户的需求改变的时候</p>
<p>（客户总是经常改变他们的需求），比如库存检查的规则不是单一的检查产品的数量，还要检查产品是否被预订的多种情况，那么你需要改变</p>
<p>Inventory的代码，同时还要修改Order中的代码，我们的例子只是一个简单的商务逻辑，实际的情况比这个要复杂的多。问题在于Order对象同</p>
<p>其他的对象之间是紧耦合的，从OOP的观点出发，这样的设计是有问题的，如果你写出这样的程序，至少不会在我的团队里面被Pass.</p>
<p>你说了：“No problem! 我们可以把商务逻辑抽出来放到一个专门设计的用来处理事务的对象中。”嗯，好主意，如果你是这么想的，或许我</p>
<p>还可以给你一个提议，使用Observer Design Pattern（观察者设计模式）：你可以使用delegate，在Order对象中定义一个BeforeSubmit和</p>
<p>AfterSubmit事件，然后创建一个对象链表，将相关的对象插入到这个链表中，这样就可以实现对Order提交事件的拦截，在Order提交之前和提</p>
<p>交之后自动进行必要的事务处理。如果你感兴趣的话，你可以自己动手来编写这样的一个代码，或许还要考虑在分布式环境中（Order和</p>
<p>Inventory不在一个地方）如何处理对象之间的交互问题。</p>
<p>幸运的是，.NET Framework中提供了实现这种技术的支持。在.NET Framework中的对象Remoting和组件服务中，有一个重要的拦截机制，在对</p>
<p>象Remoting中，不同的应用程序之间的对象的交互需要穿越他们的域边界，每一个应用域也可以细分为多个Context(上下文环境），每一个应</p>
<p>用域也至少有一个默认的Context，即使在同一个应用域，也存在穿越不同Context的问题。NET的组件服务发展了COM+的组件服务，它使用</p>
<p>Context Attribute来实现象COM+一样的拦截功能。通过对调用对象的拦截，我们可以对一个方法的调用进行前处理和后处理，同时也解决了上</p>
<p>述的跨越边界的问题。</p>
<p>需要提醒你，如果你在MSDN文档查ContextAttribute，我可以保证你得不到任何有助于了解ContextAttribute的资料，你看到的将是这么一句</p>
<p>话：“This type supports the .NET Framework infrastructure and is not intended to be used directly from your code.”——“本</p>
<p>类型支持.NET Framework基础结构，它不打算直接用于你的代码。”不过，在msdn站点，你可以看到一些有关这方面的资料（见文章后面的参</p>
<p>考链接）。</p>
<p>下面我们介绍有关的几个类和一些概念，首先是：</p>
<p>ContextAttribute类</p>
<p>ContextAttribute派生自Attribute,同时它还实现了IContextAttribute和IContextProperty接口。所有自定义的ContextAttribute必须从这个</p>
<p>类派生。<br>
构造器：<br>
ContextAttribute:构造器带有一个参数，用来设置ContextAttribute的名称。</p>
<p>公共属性：<br>
Name:只读属性。返回ContextAttribute的名称</p>
<p>公共方法：<br>
GetPropertiesForNewContext：虚拟方法。向新的Context添加属性集合。<br>
IsContextOK:虚拟方法。查询客户Context中是否存在指定的属性。<br>
IsNewContextOK:虚拟方法。默认返回true。一个对象可能存在多个Context,使用这个方法来检查新的Context中属性是否存在冲突。<br>
Freeze:虚拟方法。该方法用来定位被创建的Context的最后位置。</p>
<p>ContextBoundObject类</p>
<p>实现被拦截的类，需要从ContextBoundObject类派生，这个类的对象通过Attribute来指定它所在Context,凡是进入该Context的调用都可以被</p>
<p>拦截。该类从MarshalByRefObject派生。</p>
<p>以下是涉及到的接口：</p>
<p>IMessage:定义了被传送的消息的实现。一个消息必须实现这个接口。</p>
<p>IMessageSink:定义了消息接收器的接口，一个消息接收器必须实现这个接口。</p>
<p>参考文章：Decouple Components by Injecting Custom Services into Your Object's Interception Chain&nbsp;&nbsp;<br>
</p>
</div> <a href="http://hi.baidu.com/godblessewan/blog/item/b06b86222ebca3f1d7cae28a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/b06b86222ebca3f1d7cae28a.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:30 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/b06b86222ebca3f1d7cae28a.html</guid>
</item>

<item>
        <title><![CDATA[Attribute在.NET编程中的应用（四）]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/88eae31f90853808304e1589.html]]></link>
        <description><![CDATA[
		
		Posted on 2005-07-06 09:33 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(214) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/186999.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=186999"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=186999"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/186999.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/186999.html?webview=1" width="1"> 
<div class="postbody">
<div class="postbody">
<p><strong>SqlCommandGenerator类的设计</strong> </p>
<p><font size="2">SqlCommandGEnerator类的设计思路就是通过反射得到方法的参数，使用被SqlCommandParameterAttribute标记的参数来装配一个Command实例。 </font></p>
<p><font size="2">引用的命名空间：</font>
<pre style="BACKGROUND-COLOR: silver">//SqlCommandGenerator.cs

using System;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
using Debug = System.Diagnostics.Debug;
using StackTrace = System.Diagnostics.StackTrace;&nbsp;&nbsp; 

</pre>
<font size="2">类代码：</font> 
<pre style="BACKGROUND-COLOR: silver">namespace DataAccess
{
&nbsp;&nbsp;&nbsp; public sealed class SqlCommandGenerator
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //私有构造器，不允许使用无参数的构造器构造一个实例
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private SqlCommandGenerator()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new NotSupportedException();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //静态只读字段，定义用于返回值的参数名称
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static readonly string ReturnValueParameterName = "RETURN_VALUE";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //静态只读字段，用于不带参数的存储过程
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static readonly object[] NoValues = new object[] {};
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static SqlCommand GenerateCommand(SqlConnection connection,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MethodInfo method, object[] values)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果没有指定方法名称，从堆栈帧得到方法名称
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (method == null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method = (MethodInfo) (new StackTrace().GetFrame(1).GetMethod());

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取方法传进来的SqlCommandMethodAttribute
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 为了使用该方法来生成一个Command对象，要求有这个Attribute。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommandMethodAttribute commandAttribute = 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (SqlCommandMethodAttribute) Attribute.GetCustomAttribute(method, typeof(SqlCommandMethodAttribute));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(commandAttribute != null);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure ||
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commandAttribute.CommandType == CommandType.Text);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 创建一个SqlCommand对象，同时通过指定的attribute对它进行配置。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand command = new SqlCommand();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.Connection = connection;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.CommandType = commandAttribute.CommandType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取command的文本，如果没有指定，那么使用方法的名称作为存储过程名称 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (commandAttribute.CommandText.Length == 0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.CommandText = method.Name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.CommandText = commandAttribute.CommandText;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用GeneratorCommandParameters方法，生成command参数，同时添加一个返回值参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GenerateCommandParameters(command, method, values);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.Parameters.Add(ReturnValueParameterName, SqlDbType.Int).Direction 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =ParameterDirection.ReturnValue;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return command;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void GenerateCommandParameters(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand command, MethodInfo method, object[] values)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 得到所有的参数，通过循环一一进行处理。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParameterInfo[] methodParameters = method.GetParameters();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int paramIndex = 0;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ParameterInfo paramInfo in methodParameters)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 忽略掉参数被标记为[NonCommandParameter ]的参数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Attribute.IsDefined(paramInfo, typeof(NonCommandParameterAttribute)))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取参数的SqlParameter attribute，如果没有指定，那么就创建一个并使用它的缺省设置。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlParameterAttribute paramAttribute = (SqlParameterAttribute) Attribute.GetCustomAttribute(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paramInfo, typeof(SqlParameterAttribute));
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute == null)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paramAttribute = new SqlParameterAttribute();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //使用attribute的设置来配置一个参数对象。使用那些已经定义的参数值。如果没有定义，那么就从方法 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 的参数来推断它的参数值。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlParameter sqlParameter = new SqlParameter();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsNameDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.ParameterName = paramAttribute.Name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.ParameterName = paramInfo.Name;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!sqlParameter.ParameterName.StartsWith("@"))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.ParameterName = "@" + sqlParameter.ParameterName;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsTypeDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.SqlDbType = paramAttribute.SqlDbType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsSizeDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Size = paramAttribute.Size;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsScaleDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Scale = paramAttribute.Scale;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsPrecisionDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Precision = paramAttribute.Precision;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramAttribute.IsDirectionDefined)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Direction = paramAttribute.Direction;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (paramInfo.ParameterType.IsByRef)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Direction = paramInfo.IsOut ? 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParameterDirection.Output : 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParameterDirection.InputOutput;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Direction = ParameterDirection.Input;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 检测是否提供的足够的参数对象值
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(paramIndex &lt; values.Length);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //把相应的对象值赋于参数。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sqlParameter.Value = values[paramIndex];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.Parameters.Add(sqlParameter);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paramIndex++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检测是否有多余的参数对象值
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(paramIndex == values.Length);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}
</pre>
<p><font size="2">必要的工作终于完成了。SqlCommandGenerator中的代码都加上了注释，所以并不难读懂。下面我们进入最后的一步，那就是使用新的方法来实现上一节我们一开始显示个那个AddCustomer的方法。</font></p>
<p><font size="2"><strong>重构新的AddCustomer代码：</strong></font></p>
<pre style="BACKGROUND-COLOR: silver">[ SqlCommandMethod(CommandType.StoredProcedure) ]
public void AddCustomer( [NonCommandParameter] SqlConnection connection, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(50)] string customerName, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(20)] string country, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(20)] string province, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(20)] string city, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(60)] string address, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [SqlParameter(16)] string telephone,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out int customerId )
{
&nbsp;&nbsp;&nbsp; customerId=0; //需要初始化输出参数
&nbsp;&nbsp; //调用Command生成器生成SqlCommand实例
&nbsp;&nbsp;&nbsp; SqlCommand command = SqlCommandGenerator.GenerateCommand( connection, null, new object[]
{customerName,country,province,city,address,telephone,customerId } );
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; connection.Open();
&nbsp;&nbsp;&nbsp; command.ExecuteNonQuery();
&nbsp;&nbsp;&nbsp; connection.Close();

&nbsp;&nbsp;&nbsp; //必须明确返回输出参数的值
&nbsp;&nbsp;&nbsp; customerId=(int)command.Parameters["@CustomerId"].Value;
}
</pre>
<p><font size="2">代码中必须注意的就是out参数，需要事先进行初始化，并在Command执行操作以后，把参数值传回给它。</font><font size="2">受益于Attribute,使我们摆脱了那种编写大量枯燥代码编程生涯。</font> <font size="2">我们甚至还可以使用Sql存储过程来编写生成整个方法的代码，如果那样做的话，可就大大节省了你的时间了，上一节和这一节中所示的代码，你可以把它们单独编译成一个组件，这样就可以在你的项目中不断的重用它们了。从下一节开始，我们将更深层次的介绍Attribute的应用，请继续关注。（待续）</font></p>
<p>原文: <a href="http://www.csdn.net/Develop/Read_Article.asp?Id=19591"><font color="#1d58d1">http://www.csdn.net/Develop/Read_Article.asp?Id=19591</font></a></p>
</p>
</div>
<h3>Feedback</h3>
<div class="post">
<div class="posthead">
<h2><a title="permalink: re: Attribute在.NET编程中的应用(四)" href="http://www.cnblogs.com/dudu/articles/4452.html#161817"><font color="#1d58d1">#</font></a> <a name="161817"></a>re: Attribute在.NET编程中的应用（四） <a href=" :__doPostBack( Comments1$CommentList$_ctl0$DeleteLink ,  )"></a>&nbsp;&nbsp;<a ></a> </h2>
2005-05-24 23:38 by <a target="_blank"><font color="#1d58d1">wqjch</font></a> </div>
<div class="postbody">你好，我看了你的"Attribute在.NET编程中的应用"一系列文章，受益菲浅。但还有一个问题就是，我想做一下实验，我在程序怎么样调用下面这个方面。我不知第8个参数要传什么的类型。给我一个例子好吗。谢谢。 <br>
[ SqlCommandMethod(CommandType.StoredProcedure) ] <br>
public void AddCustomer( [NonCommandParameter] SqlConnection connection, <br>
[SqlParameter(50)] string customerName, <br>
[SqlParameter(20)] string country, <br>
[SqlParameter(20)] string province, <br>
[SqlParameter(20)] string city, <br>
[SqlParameter(60)] string address, <br>
[SqlParameter(16)] string telephone, <br>
out int customerId ) <br>
{ <br>
customerId=0; //需要初始化输出参数 <br>
//调用Command生成器生成SqlCommand实例 <br>
SqlCommand command = SqlCommandGenerator.GenerateCommand( connection, null, new object[] <br>
{customerName,country,province,city,address,telephone,customerId } ); <br>
<br>
connection.Open(); <br>
command.ExecuteNonQuery(); <br>
connection.Close(); <br>
<br>
//必须明确返回输出参数的值 <br>
customerId=(int)command.Parameters["@CustomerId"].Value; <br>
} <br>
</div>
</div>
</div> <a href="http://hi.baidu.com/godblessewan/blog/item/88eae31f90853808304e1589.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/88eae31f90853808304e1589.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:29 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/88eae31f90853808304e1589.html</guid>
</item>

<item>
        <title><![CDATA[Attribute在.NET编程中的应用（三）]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/2a421c083b45a5d362d98689.html]]></link>
        <description><![CDATA[
		
		Posted on 2005-07-06 09:31 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(207) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/186998.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=186998"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=186998"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/186998.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/186998.html?webview=1" width="1"> 
<div class="postbody"><strong>用于参数的Attribute</strong> 
<p align="left"><font size="+0"><font size="2">在编写多层应用程序的时候，你是否为每次要写大量类似的数据访问代码而感到枯燥无味？比如我们需要编写调用存储过程的代码，或者编写T_SQL代码，这些代码往往需要传递各种参数，有的参数个数比较多，一不小心还容易写错。有没有一种一劳永逸的方法？当然，你可以使用MS的Data Access Application Block，也可以使用自己编写的Block。这里向你提供一种另类方法，那就是使用Attribute。</font></font></p>
<p align="left"><font size="2">下面的代码是一个调用AddCustomer存储过程的常规方法：<br>
</font></p>
<pre style="BACKGROUND-COLOR: silver">public int AddCustomer(SqlConnection connection, 
  string customerName, 
  string country, 
  string province, 
  string city, 
  string address, 
  string telephone)
{
&nbsp;&nbsp;&nbsp; SqlCommand command=new SqlCommand("AddCustomer", connection);
&nbsp;&nbsp;&nbsp; command.CommandType=CommandType.StoredProcedure;

&nbsp;&nbsp;&nbsp; command.Parameters.Add("@CustomerName",SqlDbType.NVarChar,50).Value=customerName;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@country",SqlDbType.NVarChar,20).Value=country;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@Province",SqlDbType.NVarChar,20).Value=province;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@City",SqlDbType.NVarChar,20).Value=city;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@Address",SqlDbType.NVarChar,60).Value=address;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@Telephone",SqlDbType.NvarChar,16).Value=telephone;
&nbsp;&nbsp;&nbsp; command.Parameters.Add("@CustomerId",SqlDbType.Int,4).Direction=ParameterDirection.Output;

&nbsp;&nbsp;&nbsp; connection.Open();
&nbsp;&nbsp;&nbsp; command.ExecuteNonQuery();
&nbsp;&nbsp;&nbsp; connection.Close();

&nbsp;&nbsp;&nbsp; int custId=(int)command.Parameters["@CustomerId"].Value;
&nbsp;&nbsp;&nbsp; return custId;
}  
  </pre>
<p><font size="2">上面的代码，创建一个Command实例，然后添加存储过程的参数，然后调用ExecuteMonQuery方法执行数据的插入操作，最后返回CustomerId。从代码可以看到参数的添加是一种重复单调的工作。如果一个项目有100多个甚至几百个存储过程，作为开发人员的你会不会要想办法偷懒？（反正我会的:-)）。</font> </p>
<p><font size="2">下面开始我们的代码自动生成工程：</font></p>
<p><font size="2">我们的目的是根据方法的参数以及方法的名称，自动生成一个Command对象实例，第一步我们要做的就是创建一个SqlParameterAttribute, 代码如下：<br>
</font></p>
<pre style="BACKGROUND-COLOR: silver">SqlCommandParameterAttribute.cs

using System;
using System.Data;
using Debug=System.Diagnostics.Debug;

namespace DataAccess
{
&nbsp;&nbsp;&nbsp; // SqlParemeterAttribute 施加到存储过程参数
&nbsp;&nbsp;&nbsp; [ AttributeUsage(AttributeTargets.Parameter) ]
&nbsp;&nbsp;&nbsp; public class SqlParameterAttribute : Attribute
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string name;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;     //参数名称
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private bool paramTypeDefined;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否参数的类型已经定义
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private SqlDbType paramType;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int size;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数尺寸大小
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private byte precision;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数精度
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private byte scale;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数范围
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private bool directionDefined;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否定义了参数方向
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private ParameterDirection direction;&nbsp;&nbsp; //参数方向
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Name
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return name == null ? string.Empty : name; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { _name = value; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int Size
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return size; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { size = value; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public byte Precision
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return precision; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { precision = value; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public byte Scale
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return scale; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { scale = value; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ParameterDirection Direction
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(directionDefined);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return direction;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; direction = value; 
  &nbsp;&nbsp;&nbsp;&nbsp; directionDefined = true;
   }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlDbType SqlDbType
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Assert(paramTypeDefined);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return paramType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paramType = value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paramTypeDefined = true;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsNameDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return name != null &amp;&amp; name.Length != 0; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsSizeDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return size != 0; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsTypeDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return paramTypeDefined; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsDirectionDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return directionDefined; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsScaleDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return _scale != 0; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool IsPrecisionDefined
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return _precision != 0; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</pre>
<font size="2">以上定义了SqlParameterAttribute的字段和相应的属性，为了方便Attribute的使用，我们重载几个构造器，不同的重载构造器用于不用的参数：</font> 
<pre style="BACKGROUND-COLOR: silver">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 重载构造器，如果方法中对应于存储过程参数名称不同的话，我们用它来设置存储过程的名称
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 其他构造器的目的类似
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(string name)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name=name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(int size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Size=size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(SqlDbType paramType)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDbType=paramType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(string name, SqlDbType paramType)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name = name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDbType = paramType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(SqlDbType paramType, int size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDbType = paramType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Size = size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(string name, int size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name = name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Size = size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlParameterAttribute(string name, SqlDbType paramType, int size)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name = name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDbType = paramType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Size = size;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}
</pre>
<p><font size="2">为了区分方法中不是存储过程参数的那些参数，比如SqlConnection，我们也需要定义一个非存储过程参数的Attribute:</font> </p>
<pre style="BACKGROUND-COLOR: silver">//NonCommandParameterAttribute.cs

using System;
namespace DataAccess
{
&nbsp;&nbsp;&nbsp; [ AttributeUsage(AttributeTargets.Parameter) ]
&nbsp;&nbsp;&nbsp; public sealed class NonCommandParameterAttribute : Attribute
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; }
}
</pre>
<p><font size="2">我们已经完成了SQL的参数Attribute的定义，在创建Command对象生成器之前，让我们考虑这样的一个事实，那就是如果我们数据访问层调用的不是存储过程，也就是说Command的CommandType不是存储过程，而是带有参数的SQL语句，我们想让我们的方法一样可以适合这种情况，同样我们仍然可以使用Attribute，定义一个用于方法的Attribute来表明该方法中的生成的Command的CommandType是存储过程还是SQL文本，下面是新定义的Attribute的代码：</font> </p>
<pre style="BACKGROUND-COLOR: silver">//SqlCommandMethodAttribute.cs

using System;
using System.Data;

namespace Emisonline.DataAccess
{
&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Method)]
&nbsp;&nbsp;&nbsp; public sealed class SqlCommandMethodAttribute : Attribute
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string commandText;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private CommandType commandType;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlCommandMethodAttribute( CommandType commandType, string commandText)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commandType=commandType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commandText=commandText;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SqlCommandMethodAttribute(CommandType commandType) : this(commandType, null){}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string CommandText
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return commandText==null ? string.Empty : commandText;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commandText=value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public CommandType CommandType
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return commandType;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commandType=value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}
  
</pre>
<p><font size="2">我们的Attribute的定义工作已经全部完成，下一步就是要创建一个用来生成Command对象的类。（待续</font></p>
</div> <a href="http://hi.baidu.com/godblessewan/blog/item/2a421c083b45a5d362d98689.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/2a421c083b45a5d362d98689.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:29 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/2a421c083b45a5d362d98689.html</guid>
</item>

<item>
        <title><![CDATA[Attribute在.net编程中的应用（二）]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/dbcbac1111a31f7cca80c488.html]]></link>
        <description><![CDATA[
		
		Posted on 2005-07-06 09:36 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(264) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/187002.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=187002"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=187002"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/187002.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/187002.html?webview=1" width="1"> 
<div class="postbody">NET Framework中对Attribute的支持是一个全新的功能，这种支持来自它的Attribute类。在你的程序中适当地使用这个类，或者是灵活巧妙 
<p>地利用这个类，将使你的程序获得某种在以往编程中很难做到的能力。我们来看一个例子：<br>
假如你是一个项目开发小组中的成员，你想要跟踪项目代码检查的信息，通常你可以把代码的检查信息保存在数据库中以便查询；或者把信息</p>
<p>写到代码的注释里面，这样可以阅读代码的同时看到代码被检查的信息。我们知道.NET的组件是自描述的，那么是否可以让代码自己来描述它</p>
<p>被检查的信息呢？这样我们既可以将信息和代码保存在一起，又可以通过代码的自我描述得到信息。答案就是使用Attribute.<br>
下面的步骤和代码告诉你怎么做：<br>
首先，我们创建一个自定义的Attribute,并且事先设定我们的Attribute将施加在class的元素上面以获取一个类代码的检查信息。</p>
<p><br>
using System;<br>
using System.Reflection;</p>
<p>[AttributeUsage(AttributeTargets.Class)] //还记得上一节的内容吗？<br>
public class CodeReviewAttribute : System.Attribute //定义一个CodeReview的Attribute<br>
{<br>
private string reviewer; //代码检查人<br>
private string date; //检查日期<br>
private string comment; //检查结果信息</p>
<p>//参数构造器<br>
public CodeReviewAttribute(string reviewer, string date)<br>
{<br>
this.reviewer=reviewer;<br>
this.date=date;<br>
}</p>
<p>public string Reviewer<br>
{<br>
get<br>
{<br>
return reviewer;<br>
}<br>
}</p>
<p>public string Date<br>
{<br>
get<br>
{<br>
return date;<br>
}<br>
}</p>
<p>public string Comment<br>
{<br>
get<br>
{<br>
return comment;<br>
}<br>
set<br>
{<br>
comment=value;<br>
}<br>
}<br>
}</p>
<p> </p>
<p>我们的自定义CodeReviewAttribute同普通的类没有区别，它从Attribute派生，同时通过AttributeUsage表示我们的Attribute仅可以施加到类</p>
<p>元素上。</p>
<p>第二步就是使用我们的CodeReviewAttribute, 假如我们有一个Jack写的类MyClass，检查人Niwalker，检查日期2003年7月9日，于是我们施加</p>
<p>Attribute如下：</p>
<p>[CodeReview("Niwalker","2003-7-9",Comment="Jack的代码")]<br>
public class MyClass<br>
{<br>
//类的成员定义<br>
} </p>
<p>当这段代码被编译的时候，编译器会调用CodeReviewAttribute的构造器并且把"Niwalker"和"2003-7-9"分别作为构造器的参数。注意到参数表</p>
<p>中还有一个Comment属性的赋值，这是Attribute特有的方式，这里你可以设置更多的Attribute的公共属性（如果有的话），需要指出的是.NET </p>
<p>Framework1.0允许向private的属性赋值，但在.NET Framework1.1已经不允许这样做，只能向public的属性赋值。</p>
<p>第三步就是取出我们需要的信息，这是通过.NET的反射来实现的，关于反射的知识，限于篇幅我不打算在这里进行说明，也许我会在以后另外</p>
<p>写一篇介绍反射的文章。</p>
<p>class test<br>
{<br>
static void Main(string[] args)<br>
{<br>
System.Reflection.MemberInfo info=typeof(MyClass); //通过反射得到MyClass类的信息</p>
<p>//得到施加在MyClass类上的定制Attribute <br>
CodeReviewAttribute att=<br>
(CodeReviewAttribute)Attribute.GetCustomAttribute(info,typeof(CodeReviewAttribute)); <br>
if(att!=null)<br>
{<br>
Console.WriteLine("代码检查人:{0}",att.Reviewer);<br>
Console.WriteLine("检查时间:{0}",att.Date);<br>
Console.WriteLine("注释:{0}",att.Comment);<br>
}<br>
}<br>
}</p>
<p>在上面这个例子中，Attribute扮演着向一个类添加额外信息的角色，它并不影响MyClass类的行为。通过这个例子，我们大致可以知道如何写</p>
<p>一个自定义的Attribute，以及如何在应用程序使用它。下一节，我将介绍如何使用Attribute来自动生成ADO.NET的数据访问类的代码。 <br>
</p>
</div> <a href="http://hi.baidu.com/godblessewan/blog/item/dbcbac1111a31f7cca80c488.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/dbcbac1111a31f7cca80c488.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:28 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/dbcbac1111a31f7cca80c488.html</guid>
</item>

<item>
        <title><![CDATA[Attribute的基本概念]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/bb6951c276cd2a37e5dd3b8f.html]]></link>
        <description><![CDATA[
		
		Attribute的基本概念 
<p>Posted on 2005-07-06 11:24 <a href="http://zhanghl.cnblogs.com/"><font color="#223355">追风逐云.NET</font></a> 阅读(234) <a href="http://www.cnblogs.com/zhanghl/archive/2005/07/06/187070.html#Post"><font color="#223355">评论(0)</font></a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/zhanghl/admin/EditPosts.aspx?postid=187070"><font color="#223355">编辑</font></a> <a href="http://www.cnblogs.com/zhanghl/AddToFavorite.aspx?id=187070"><font color="#223355">收藏</font></a> <a href="http://www.cnblogs.com/zhanghl/services/trackbacks/187070.aspx"><font color="#223355">引用</font></a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();"><font color="#223355">网摘</font></a> <img height="1" src="http://www.cnblogs.com/zhanghl/aggbug/187070.html?webview=1" width="1"> </p>
<p>经常有朋友问，Attribute是什么？它有什么用？好像没有这个东东程序也能运行。实际上在.Net中，Attribute是一个非常重要的组成部分，为了帮助大家理解和掌握Attribute，以及它的使用方法，特地收集了几个Attribute使用的例子，提供给大家参考。</p>
<p>在具体的演示之前，我想先大致介绍一下Attribute。我们知道在类的成员中有property成员，二者在中文中都做属性解释，那么它们到底是不是同一个东西呢？从代码上看，明显不同，首先就是它们的在代码中的位置不同，其次就是写法不同（Attribute必须写在一对方括符中）。</p>
<h4>什么是Atrribute </h4>
<p>首先，我们肯定Attribute是一个类，下面是msdn文档对它的描述：<br>
公共语言运行时允许你添加类似关键字的描述声明，叫做attributes, 它对程序中的元素进行标注，如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起，可以用来向运行时描述你的代码，或者在程序运行的时候影响应用程序的行为。</p>
<p>在.NET中,Attribute被用来处理多种问题，比如序列化、程序的安全特征、防止即时编译器对程序代码进行优化从而代码容易调试等等。下面，我们先来看几个在.NET中标准的属性的使用，稍后我们再回过头来讨论Attribute这个类本身。（文中的代码使用C#编写，但同样适用所有基于.NET的所有语言） </p>
<h3>Attribute作为编译器的指令</h3>
<p>在C#中存在着一定数量的编译器指令，如：#define DEBUG, #undefine DEBUG, #if等。这些指令专属于C#,而且在数量上是固定的。而Attribute用作编译器指令则不受数量限制。比如下面的三个Attribute: </p>
<ul>
<li>Conditional：起条件编译的作用，只有满足条件，才允许编译器对它的代码进行编译。一般在程序调试的时候使用。 </li>
    <li>DllImport：用来标记非.NET的函数，表明该方法在一个外部的DLL中定义。 </li>
    <li>Obsolete：这个属性用来标记当前的方法已经被废弃，不再使用了。 </li>
</ul>
    <p>下面的代码演示了上述三个属性的使用：</p>
    <pre style="BACKGROUND-COLOR: silver"> 
#define DEBUG //这里定义条件
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
namespace AttributeDemo
{
&nbsp;&nbsp;&nbsp;&nbsp; class MainProgramClass
&nbsp;&nbsp;&nbsp;&nbsp; {
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [DllImport("User32.dll")]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DisplayRunningMessage();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DisplayDebugMessage();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox(0,"Hello","Message",0);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.ReadLine();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Conditional("DEBUG")]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void DisplayRunningMessage()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("开始运行Main子程序。当前时间是"+DateTime.Now);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Conditional("DEBUG")]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Obsolete]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void DisplayDebugMessage()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("开始Main子程序");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp; }
}&nbsp;&nbsp;&nbsp; 
</pre>
    <p>如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上，前面的代码，[DllImport]施加到MessageBox函数上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法，[Obsolete]施加到DisplayDebugMessage方法上。</p>
    <p>根据上面涉及到的三个Attribute的说明，我们可以猜到程序运行的时候产生的输出：DllImport Attribute表明了MessageBox是User32.DLL中的函数，这样我们就可以像内部方法一样调用这个函数。</p>
    <p>重要的一点就是Attribute就是一个类，所以DllImport也是一个类，Attribute类是在编译的时候被实例化的，而不是像通常的类那样在运行时候才实例化。Attribute实例化的时候根据该Attribute类的设计可以带参数，也可以不带参数，比如DllImport就带有"User32.dll"的参数。Conditional对满足参数的定义条件的代码进行编译，如果没有定义DEBUG,那么该方法将不被编译，读者可以把#define DEBUG一行注释掉看看输出的结果（release版本，在Debug版本中Conditional的debug总是成立的）。Obsolete表明了DispalyDebugMessage方法已经过时了，它有一个更好的方法来代替它，当我们的程序调用一个声明了Obsolete的方法时，那么编译器会给出信息，Obsolete还有其他两个重载的版本。大家可以参考msdn中关于的ObsoleteAttribute 类的描述。 </p>
    <h3>Attribute类</h3>
    <p>除了.NET提供的那些Attribute派生类之外，我们可以自定义我们自己的Attribute，所有自定义的Attribute必须从Attribute类派生。现在我们来看一下Attribute 类的细节： </p>
    <p>protected Attribute(): 保护的构造器，只能被Attribute的派生类调用。</p>
    <p>三个静态方法： </p>
    <p>static Attribute GetCustomAttribute():这个方法有8种重载的版本，它被用来取出施加在类成员上指定类型的Attribute。 </p>
    <p>static Attribute[] GetCustomAttributes(): 这个方法有16种重载版本，用来取出施加在类成员上指定类型的Attribute数组。 </p>
    <p>static bool IsDefined():由八种重载版本，看是否指定类型的定制attribute被施加到类的成员上面。</p>
    <p>实例方法： </p>
    <p>bool IsDefaultAttribute(): 如果Attribute的值是默认的值，那么返回true。 </p>
    <p>bool Match():表明这个Attribute实例是否等于一个指定的对象。</p>
    <p>公共属性： TypeId: 得到一个唯一的标识，这个标识被用来区分同一个Attribute的不同实例。 </p>
    <p>我们简单地介绍了Attribute类的方法和属性，还有一些是从object继承来的。这里就不列出来了。</p>
    <p>下面介绍如何自定义一个Attribute: 自定义一个Attribute并不需要特别的知识，其实就和编写一个类差不多。自定义的Attribute必须直接或者间接地从Attribute这个类派生，如： </p>
    <p>public MyCustomAttribute : Attribute { ... } </p>
    <p>这里需要指出的是Attribute的命名规范，也就是你的Attribute的类名+"Attribute",当你的Attribute施加到一个程序的元素上的时候，编译器先查找你的Attribute的定义，如果没有找到，那么它就会查找“Attribute名称"+Attribute的定义。如果都没有找到，那么编译器就报错。</p>
    <p>对于一个自定义的Attribute，你可以通过AttributeUsage的Attribute来限定你的Attribute 所施加的元素的类型。代码形式如下： [AttriubteUsage(参数设置)] public 自定义Attribute : Attribute { ... } </p>
    <p>非常有意思的是，AttributeUsage本身也是一个Attribute，这是专门施加在Attribute类的Attribute. AttributeUsage自然也是从Attribute派生，它有一个带参数的构造器，这个参数是AttributeTargets的枚举类型。下面是AttributeTargets 的定义： </p>
    <pre style="BACKGROUND-COLOR: silver">public enum AttributeTargets
{
&nbsp;&nbsp;&nbsp;&nbsp; All=16383,
&nbsp;&nbsp;&nbsp;&nbsp; Assembly=1,
&nbsp;&nbsp;&nbsp;&nbsp; Module=2,
&nbsp;&nbsp;&nbsp;&nbsp; Class=4,
&nbsp;&nbsp;&nbsp;&nbsp; Struct=8,
&nbsp;&nbsp;&nbsp;&nbsp; Enum=16,
&nbsp;&nbsp;&nbsp;&nbsp; Constructor=32,
&nbsp;&nbsp;&nbsp;&nbsp; Method=64,
&nbsp;&nbsp;&nbsp;&nbsp; Property=128,
&nbsp;&nbsp;&nbsp;&nbsp; Field=256,
&nbsp;&nbsp;&nbsp;&nbsp; Event=512,
&nbsp;&nbsp;&nbsp;&nbsp; Interface=1024,
&nbsp;&nbsp;&nbsp;&nbsp; Parameter=2048,
&nbsp;&nbsp;&nbsp;&nbsp; Delegate=4096,
&nbsp;&nbsp;&nbsp;&nbsp; ReturnValue=8192
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</pre>
    <p>作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合，如果你没有指定参数，那么默认参数就是All 。 AttributeUsage除了继承Attribute 的方法和属性之外，还定义了以下三个属性：</p>
    <p>AllowMultiple: 读取或者设置这个属性，表示是否可以对一个程序元素施加多个Attribute 。 </p>
    <p>Inherited:读取或者设置这个属性，表示是否施加的Attribute 可以被派生类继承或者重载。 </p>
    <p>ValidOn: 读取或者设置这个属性，指明Attribute 可以被施加的元素的类型。 </p>
    <h3>AttributeUsage 的使用例子： </h3>
    <pre style="BACKGROUND-COLOR: silver">using System; 
namespace AttTargsCS 
{ 

&nbsp;&nbsp;&nbsp;&nbsp; // 该Attribute只对类有效. 
&nbsp;&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Class)]
&nbsp;&nbsp;&nbsp;&nbsp; public class ClassTargetAttribute : Attribute 
&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; // 该Attribute只对方法有效. 
&nbsp;&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Method)]
&nbsp;&nbsp;&nbsp;&nbsp; public class MethodTargetAttribute : Attribute 
&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; // 该Attribute只对构造器有效。
&nbsp;&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Constructor)]
&nbsp;&nbsp;&nbsp;&nbsp; public class ConstructorTargetAttribute : Attribute 
&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; // 该Attribute只对字段有效. 
&nbsp;&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Field)]
&nbsp;&nbsp;&nbsp;&nbsp; public class FieldTargetAttribute : Attribute
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; // 该Attribute对类或者方法有效（组合）. 
&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
&nbsp;&nbsp;&nbsp;&nbsp; public class ClassMethodTargetAttribute : Attribute
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; // 该Attribute对所有的元素有效.
&nbsp;&nbsp;&nbsp;&nbsp; [AttributeUsage(AttributeTargets.All)]
&nbsp;&nbsp;&nbsp;&nbsp; public class AllTargetsAttribute : Attribute 
&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp; //上面定义的Attribute施加到程序元素上的用法
&nbsp;&nbsp;&nbsp;&nbsp; [ClassTarget]&nbsp;&nbsp;&nbsp; //施加到类
&nbsp;&nbsp;&nbsp;&nbsp; [ClassMethodTarget]//施加到类
&nbsp;&nbsp;&nbsp;&nbsp; [AllTargets] //施加到类
&nbsp;&nbsp;&nbsp;&nbsp; public class TestClassAttribute
&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ConstructorTarget] //施加到构造器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [AllTargets] //施加到构造器
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestClassAttribute()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [MethodTarget] //施加到方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ClassMethodTarget] //施加到方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [AllTargets] //施加到方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void Method1()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [FieldTarget] //施加到字段
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [AllTargets] //施加到字段
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int myInt; 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } 
&nbsp;&nbsp;&nbsp;&nbsp; }
} 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
</pre>
    <p> </p>
    <p>至此，我们介绍了有关Attribute类和它们的代码格式。你一定想知道到底如何在你的应用程序中使用Attribute，如果仅仅是前面介绍的内容，还是不足以说明Attribute有什么实用价值的话，那么从后面的章节开始我们将介绍几个Attribute的不同用法，相信你一定会对Attribute有一个新的了解。（待续） </p> <a href="http://hi.baidu.com/godblessewan/blog/item/bb6951c276cd2a37e5dd3b8f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/bb6951c276cd2a37e5dd3b8f.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  01:27 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/bb6951c276cd2a37e5dd3b8f.html</guid>
</item>

<item>
        <title><![CDATA[ContextAttribute类]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/0746802f328e873a1f3089a6.html]]></link>
        <description><![CDATA[
		
		<p><font face="Arial"><font size="2"><strong>ContextAttribute</strong>派生自Attribute,同时它还实现了IContextAttribute和IContextProperty接口。所有自定义的ContextAttribute必须从这个类派生。<br>
构造器：<br>
<strong>ContextAttribute:</strong>构造器带有一个参数，用来设置ContextAttribute的名称。</font></font> </p>
<p><font face="Arial" size="2">公共属性：<br>
<strong>Name</strong>:只读属性。返回ContextAttribute的名称</font> </p>
<p><font face="Arial" size="2">公共方法：<br>
<strong>GetPropertiesForNewContext</strong>：虚拟方法。向新的Context添加属性集合。<br>
<strong>IsContextOK:</strong>虚拟方法。查询客户Context中是否存在指定的属性。<br>
<strong>IsNewContextOK:</strong>虚拟方法。默认返回true。一个对象可能存在多个Context,使用这个方法来检查新的Context中属性是否存在冲突。<br>
<strong>Freeze:</strong>虚拟方法。该方法用来定位被创建的Context的最后位置。</font> </p>
<p><strong><font face="Arial" size="2">ContextBoundObject类</font></strong> </p>
<p><font face="Arial" size="2">实现被拦截的类，需要从ContextBoundObject类派生，这个类的对象通过Attribute来指定它所在Context,凡是进入该Context的调用都可以被拦截。该类从MarshalByRefObject派生。</font> </p>
<p><font face="Arial" size="2">以下是涉及到的接口：</font> </p>
<p><font face="Arial"><font size="2"><strong>IMessage:</strong>定义了被传送的消息的实现。一个消息必须实现这个接口。</font></font> </p>
<p><font face="Arial"><font size="2"><strong>IMessageSink</strong>:定义了消息接收器的接口，一个消息接收器必须实现这个接口。</font></font> </p>
<p><font face="Arial" size="2">还有几个接口，我们将在下一节结合拦截构架的实现原理中进行介绍。<br>
</font></p>
<div class="postDesc">posted on 2004-07-05 18:18 <a href="http://huqingyu.cnblogs.com/">龍龙</a> 阅读(316) <a href="http://www.cnblogs.com/huqingyu/archive/2004/07/05/21466.aspx#Post">评论(0)</a>&nbsp;&nbsp;<a href="http://www.cnblogs.com/huqingyu/admin/EditPosts.aspx?postid=21466">编辑</a> <a href="http://www.cnblogs.com/huqingyu/AddToFavorite.aspx?id=21466">收藏</a> <a href="http://www.cnblogs.com/huqingyu/services/trackbacks/21466.aspx">引用</a> <a href=" :d=document;t=d.selection?(d.selection.type!= None ?d.selection.createRange().text:  ):(d.getSelection?d.getSelection():  );void(saveit=window.open( http://wz.csdn.net/storeit.aspx?t= +escape(d.title)+ &amp;u= +escape(d.location.href)+ &amp;c= +escape(t), saveit , scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes ));saveit.focus();">网摘</a> 所属分类: <a href="http://www.cnblogs.com/huqingyu/category/5569.html">Java&amp;C#</a> </div> <a href="http://hi.baidu.com/godblessewan/blog/item/0746802f328e873a1f3089a6.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Dotnet">Dotnet</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/0746802f328e873a1f3089a6.html#comment">查看评论</a>]]></description>
        <pubDate>2007-08-07  12:35 P.M.</pubDate>
        <category><![CDATA[Dotnet]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/0746802f328e873a1f3089a6.html</guid>
</item>

<item>
        <title><![CDATA[Top Ten Free eBook Websites]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/f9c0c92a3db39a2cd52af141.html]]></link>
        <description><![CDATA[
		
		<font color="#333399" size="3">　　 <br>
　　1. Project Gutenberg: </font><a href="http://www.gutenberg.org/" target="_blank"><font color="#333399" size="3">http://www.gutenberg<wbr></wbr>.org</font></a><font color="#333399" size="3"> <br>
　　 <br>
　　2. The Online Books Page: </font><a href="http://onlinebooks.library.upenn.edu/" target="_blank"><font color="#333399" size="3">http://onlinebooks.l<wbr></wbr>ibrary.upenn.edu/</font></a><font color="#333399" size="3"> <br>
　　Listing over 25,000 free books on the Web. The site is hosted by the University of Pennsylvania Library. <br>
　　 <br>
　　3. Asiaing.com: </font><a href="http://www.asiaing.com/" target="_blank"><font color="#333399" size="3">http://www.asiaing.c<wbr></wbr>om</font></a><font color="#333399" size="3"> <br>
　　Over 2,000 free ebooks &amp; free magazines. Most of them can be downloaded directly. <br>
　　 <br>
　　4. PSU's Electronic Classics Site: </font><a href="http://www2.hn.psu.edu/faculty/jmanis/jimspdf.htm" target="_blank"><font color="#333399" size="3">http://www2.hn.psu.e<wbr></wbr>du/faculty/jmanis/ji<wbr></wbr>mspdf.htm</font></a><font color="#333399" size="3"> <br>
　　Classic works of Literature. <br>
　　 <br>
　　5. PlanetPDF </font><a href="http://www.planetpdf.com/free_pdf_ebooks.asp?CurrentPage=1" target="_blank"><font color="#333399" size="3">http://www.planetpdf<wbr></wbr>.com/free_pdf_ebooks<wbr></wbr>.asp?CurrentPage=1</font></a><font color="#333399" size="3"> <br>
　　Classics works of Literature. <br>
　　 <br>
　　6. University of California, eScholarship Edition: </font><a href="http://content.cdlib.org/escholarship/" target="_blank"><font color="#333399" size="3">http://content.cdlib<wbr></wbr>.org/escholarship/</font></a><font color="#333399" size="3"> <br>
　　 The eScholarship Editions collection includes almost 2000 books from academic presses on a range of topics, including art, science, history, music, religion, and fiction. <br>
　　 <br>
　　7. University of Adelaide Library’s collection of Web books: <br>
　　 </font><a href="http://etext.library.adelaide.edu.au/" target="_blank"><font color="#333399" size="3">http://etext.library<wbr></wbr>.adelaide.edu.au/</font></a><font color="#333399" size="3"> <br>
　　 The collection includes classic works of Literature, Philosophy, Science, and History. <br>
　　 <br>
　　8. AvaxHome.ru: </font><a href="http://www.avaxhome.ru/" target="_blank"><font color="#333399" size="3">http://www.avaxhome.<wbr></wbr>ru</font></a><font color="#333399" size="3"> <br>
　　Some new ebooks. Rapidshare download links. Copyright is a problem. <br>
　　 <br>
　　9. The National Academies Press: </font><a href="http://www.nap.edu/" target="_blank"><font color="#333399" size="3">http://www.nap.edu</font></a><font color="#333399" size="3"> <br>
　　Read more than 3,000 books online FREE! <br>
　　 <br>
　　10.You! Everyone has his own favorite ebook website. Maybe It’s already on the list. Maybe not. It doesn’t matter. The most important thing is that you love eBook. <br>
</font> <a href="http://hi.baidu.com/godblessewan/blog/item/f9c0c92a3db39a2cd52af141.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/f9c0c92a3db39a2cd52af141.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-26  10:52 A.M.</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/f9c0c92a3db39a2cd52af141.html</guid>
</item>

<item>
        <title><![CDATA[An Introduction to Clustered and Non-Clustered Index Data Structures]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/bcbbcf58717ad480810a1864.html]]></link>
        <description><![CDATA[
		
		<a href="http://www.sql-server-performance.com/gv_index_data_structures.asp">http://www.sql-server-performance.com/gv_index_data_structures.asp</a> 
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Sql">Sql</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/bcbbcf58717ad480810a1864.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-23  05:14 P.M.</pubDate>
        <category><![CDATA[Sql]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/bcbbcf58717ad480810a1864.html</guid>
</item>

<item>
        <title><![CDATA[SQL Server Database Coding Conventions,  2]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/c5e4f9110e75cec7a7ef3fb1.html]]></link>
        <description><![CDATA[
		
		<font face="Verdana" color="#000080" size="2">To reproduce the above problem, use the following commands:<br>
<br>
</font><font color="#000080"><font face="Courier New" size="2">sp_addlogin 'dSQLuser'<br>
GO<br>
sp_defaultdb 'dSQLuser', 'pubs'<br>
USE pubs<br>
GO<br>
sp_adduser 'dSQLUser', 'dSQLUser'<br>
GO<br>
CREATE PROC dSQLProc<br>
AS<br>
BEGIN<br>
SELECT * FROM titles WHERE title_id = 'BU1032' --This works<br>
DECLARE @str CHAR(100)<br>
SET @str = 'SELECT * FROM titles WHERE title_id = ''BU1032'''<br>
EXEC (@str) --This fails<br>
END<br>
GO<br>
GRANT EXEC ON dSQLProc TO dSQLuser<br>
GO</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">Now login to the pubs database using the login dSQLuser and execute the procedure dSQLproc to see the problem.<br>
<br>
</font></font>
<li><font face="Verdana" color="#000080" size="2">Consider the following drawbacks before using the IDENTITY property for generating primary keys. IDENTITY is very much SQL Server specific, and you will have problems porting your database application to some other RDBMS. IDENTITY columns have other inherent problems. For example, IDENTITY columns can run out of numbers at some point, depending on the data type selected; numbers can't be reused automatically, after deleting rows; and replication and IDENTITY columns don't always get along well. </font><blockquote>
<p><font face="Verdana" color="#000080" size="2">So, come up with an algorithm to generate a primary key in the front-end or from within the inserting stored procedure. There still could be issues with generating your own primary keys too, like concurrency while generating the key, or running out of values. So, consider both options and go with the one that suits you best.<br>
</font></p>
</blockquote>
<ul>
<li><font face="Verdana" color="#000080" size="2">Minimize the use of NULLs, as they often confuse the front-end applications, unless the applications are coded intelligently to eliminate NULLs or convert the NULLs into some other form. Any expression that deals with NULL results in a NULL output. ISNULL and COALESCE functions are helpful in dealing with NULL values. Here's an example that explains the problem:<br>
<br>
Consider the following table, Customers which stores the names of the customers and the middle name can be NULL.<br>
<br>
</font><font color="#000080"><font face="Courier New" size="2">CREATE TABLE Customers<br>
(<br>
FirstName varchar(20),<br>
MiddleName varchar(20),<br>
LastName varchar(20)<br>
)</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">Now insert a customer into the table whose name is Tony Blair, without a middle name:<br>
<br>
</font></font><font face="Courier New" color="#000080" size="2">INSERT INTO Customers <br>
(FirstName, MiddleName, LastName) <br>
VALUES ('Tony',NULL,'Blair')<br>
<br>
The following SELECT statement returns NULL, instead of the customer name:<br>
<br>
SELECT FirstName + ' ' + MiddleName + ' ' + LastName FROM Customers<br>
<br>
To avoid this problem, use ISNULL as shown below:<br>
<br>
SELECT FirstName + ' ' + ISNULL(MiddleName + ' ','') + LastName FROM Customers<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Use Unicode datatypes, like NCHAR, NVARCHAR, or NTEXT, if your database is going to store not just plain English characters, but a variety of characters used all over the world. Use these datatypes only when they are absolutely needed as they use twice as much space as non-Unicode datatypes.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Always use a column list in your INSERT statements. This helps in avoiding problems when the table structure changes (like adding or dropping a column). Here's an example which shows the problem.<br>
<br>
Consider the following table:<br>
<br>
</font><font color="#000080"><font face="Courier New" size="2">CREATE TABLE EuropeanCountries<br>
(<br>
CountryID int PRIMARY KEY,<br>
CountryName varchar(25)<br>
)</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">Here's an INSERT statement without a column list , that works perfectly:<br>
<br>
</font></font><font color="#000080"><font face="Courier New" size="2">INSERT INTO EuropeanCountries<br>
VALUES (1, 'Ireland')</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">Now, let's add a new column to this table:<br>
<br>
<br>
</font></font><font color="#000080"><font face="Courier New" size="2">ALTER TABLE EuropeanCountries<br>
ADD EuroSupport bit</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">Now run the above INSERT statement. You get the following error from SQL Server:<br>
<br>
</font></font><font color="#000080"><font face="Courier New" size="2">Server: Msg 213, Level 16, State 4, Line 1<br>
Insert Error: Column name or number of supplied values does not match table definition.</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">This problem can be avoided by writing an INSERT statement with a column list as shown below:<br>
<br>
</font></font><font face="Courier New" color="#000080" size="2">INSERT INTO EuropeanCountries<br>
(CountryID, CountryName)<br>
VALUES (1, 'England')<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Perform all your referential integrity checks and data validations using constraints (foreign key and check constraints) instead of triggers, as they are faster. Limit the use triggers only for auditing, custom tasks and validations that can not be performed using constraints. Constraints save you time as well, as you don't have to write code for these validations, allowing the RDBMS to do all the work for you.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Always access tables in the same order in all your stored procedures and triggers consistently. This helps in avoiding deadlocks. Other things to keep in mind to avoid deadlocks are: Keep your transactions as short as possible. Touch as few data as possible during a transaction. Never, ever wait for user input in the middle of a transaction. Do not use higher level locking hints or restrictive isolation levels unless they are absolutely needed. Make your front-end applications deadlock-intelligent, that is, these applications should be able to resubmit the transaction incase the previous transaction fails with error 1205. In your applications, process all the results returned by SQL Server immediately so that the locks on the processed rows are released, hence no blocking.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Offload tasks, like string manipulations, concatenations, row numbering, case conversions, type conversions etc., to the front-end applications if these operations are going to consume more CPU cycles on the database server. Also try to do basic validations in the front-end itself during data entry. This saves unnecessary network roundtrips.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">If back-end portability is your concern, stay away from bit manipulations with T-SQL, as this is very much RDBMS specific. Further, using bitmaps to represent different states of a particular entity conflicts with normalization rules.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Always add a @Debug parameter to your stored procedures. This can be of BIT data type. When a 1 is passed for this parameter, print all the intermediate results, variable contents using SELECT or PRINT statements and when 0 is passed do not print anything. This helps in quick debugging stored procedures, as you don't have to add and remove these PRINT/SELECT statements before and after troubleshooting problems.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Do not call functions repeatedly within your stored procedures, triggers, functions and batches. For example, you might need the length of a string variable in many places of your procedure, but don't call the LEN function whenever it's needed, instead, call the LEN function once, and store the result in a variable, for later use.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Make sure your stored procedures always return a value indicating their status. Standardize on the return values of stored procedures for success and failures. The RETURN statement is meant for returning the execution status only, but not data. If you need to return data, use OUTPUT parameters.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">If your stored procedure always returns a single row resultset, consider returning the resultset using OUTPUT parameters instead of a SELECT statement, as ADO handles output parameters faster than resultsets returned by SELECT statements.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Always check the global variable @@ERROR immediately after executing a data manipulation statement (like INSERT/UPDATE/DELETE), so that you can rollback the transaction in case of an error (@@ERROR will be greater than 0 in case of an error). This is important, because, by default, SQL Server will not rollback all the previous changes within a transaction if a particular statement fails. This behavior can be changed by executing SET XACT_ABORT ON. The @@ROWCOUNT variable also plays an important role in determining how many rows were affected by a previous data manipulation (also, retrieval) statement, and based on that you could choose to commit or rollback a particular transaction.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">To make SQL Statements more readable, start each clause on a new line and indent when needed. Following is an example:<br>
<br>
</font><font face="Courier New" color="#000080" size="2">SELECT title_id, title<br>
FROM titles<br>
WHERE title LIKE '%Computer%' AND<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; title LIKE '%cook%'<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Though we survived the Y2K, always store 4 digit years in dates (especially, when using cCHAR or INT datatype columns), instead of 2 digit years to avoid any confusion and problems. This is not a problem with DATETIME columns, as the century is stored even if you specify a 2 digit year. But it's always a good practice to specify 4 digit years even with DATETIME datatype columns. <br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">As is true with any other programming language, do not use GOTO, or use it sparingly. Excessive usage of GOTO can lead to hard-to-read-and-understand code.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Do not forget to enforce unique constraints on your alternate keys.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Always be consistent with the usage of case in your code. On a case insensitive server, your code might work fine, but it will fail on a case sensitive SQL Server if your code is not consistent in case. For example, if you create a table in SQL Server or a database that has a case-sensitive or binary sort order, all references to the table must use the same case that was specified in the CREATE TABLE statement. If you name the table as 'MyTable' in the CREATE TABLE statement and use 'mytable' in the SELECT statement, you get an 'object not found' error.<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Though T-SQL has no concept of constants (like the ones in the C language), variables can serve the same purpose. Using variables instead of constant values within your queries improves readability and maintainability of your code. Consider the following example: <br>
<br>
</font><font color="#000080"><font face="Courier" size="2">SELECT OrderID, OrderDate<br>
FROM Orders<br>
WHERE OrderStatus IN (5,6)</font> </font><font face="Verdana" size="2"><br>
<br>
<font color="#000080">The same query can be re-written in a mode readable form as shown below:<br>
<br>
</font></font><font face="Courier New" color="#000080" size="2">DECLARE @ORDER_DELIVERED, @ORDER_PENDING<br>
SELECT @ORDER_DELIVERED = 5, @ORDER_PENDING = 6<br>
<br>
SELECT OrderID, OrderDate<br>
FROM Orders<br>
WHERE OrderStatus IN (@ORDER_DELIVERED, @ORDER_PENDING)<br>
<br>
</font></li>
<li><font face="Verdana" color="#000080" size="2">Do not use column numbers in the ORDER BY clause. Consider the following example in which the second query is more readable than the first one:<br>
<br>
</font><font color="#000080"><font face="Courier New" size="2">SELECT OrderID, OrderDate<br>
FROM Orders<br>
ORDER BY 2<br>
<br>
SELECT OrderID, OrderDate<br>
FROM Orders<br>
ORDER BY OrderDate</font> </font><font face="Verdana" size="2"><br>
</font></li>
</ul>
<p><font face="Verdana" size="2"><font color="#000080">Well, this is all for now folks. I'll keep updating this page as and when I have something new to add. I welcome your feedback on this, so feel free to </font><a href="mailto:vyaskn@hotmail.com"><font color="#000080">email me</font></a><font color="#000080">. Happy database programming!<br>
<br>
<br>
</font></font></p>
<p><font face="Verdana" size="2"><font color="#000080"><em>Published with the explicit written permission of the author. Copyright 2001.</em> </font></font></p>
<p><font color="#000080"></font></p>
</li> <a href="http://hi.baidu.com/godblessewan/blog/item/c5e4f9110e75cec7a7ef3fb1.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Sql">Sql</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/c5e4f9110e75cec7a7ef3fb1.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-23  04:11 P.M.</pubDate>
        <category><![CDATA[Sql]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/c5e4f9110e75cec7a7ef3fb1.html</guid>
</item>

<item>
        <title><![CDATA[SQL Server Database Coding Conventions,  1]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/29ce8e52cab8ca0f0cf3e3b1.html]]></link>
        <description><![CDATA[
		
		<font face="Verdana" size="2">by </font><a href="http://www.sql-server-performance.com/vyas_kondreddi.asp"><font face="Verdana" size="2">Vyas Kondreddi</font></a><br>
<br>
<br>
<font face="Verdana" size="2">Databases are the heart and soul of many enterprise applications, and it is very essential to pay special attention to database programming. I've seen in many occasions where database programming is overlooked, thinking that it's something easy that be done by anyone. This is wrong.<br>
<br>
For a better performing database you need a real DBA and a specialist database programmer, let it be for Microsoft SQL Server, Oracle, Sybase, DB2 or whatever! If you don't use database specialists during your development cycle, databases often end up becoming the performance bottleneck. I decided to write this article in order to put together some of the database programming best practices so that my fellow DBAs and database developers can benefit!<br>
<br>
Here are some programming guidelines and best practices, keeping quality, performance and maintainability in mind. This list many not be complete at this moment, and will be constantly updated. BTW, special thanks to Tibor Karaszi (SQL Server MVP) and Linda (lindawie) for taking time to read this article and providing suggestions.<br>
<br>
</font>
<ul>
<li><font face="Verdana" size="2">Decide upon a database naming convention, standardize it across your organization, and be consistent in following it. It helps make your code more readable and understandable. </font><a href="http://vyaskn.tripod.com/object_naming.htm" target="_blank"><font face="Verdana" size="2">Click here to see the database object naming convention that I follow</font></a><font face="Verdana" size="2">.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Make sure you normalize your data at least to the 3rd normal form. At the same time, do not compromise on query performance. A little bit of denormalization helps queries perform faster.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Write comments in your stored procedures, triggers and SQL batches generously, whenever something is not very obvious. This helps other programmers understand your code clearly. Don't worry about the length of the comments, as it won't impact the performance, unlike interpreted languages like ASP 2.0.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Do not use SELECT * in your queries. Always write the required column names after the SELECT statement, like:</font> </li>
</ul>
<blockquote>
<p><font face="Courier New" size="2">SELECT CustomerID, CustomerFirstName, City</font></p>
<p><font face="Verdana" size="2">This technique results in reduced disk I/O and better performance.<br>
</font></p>
</blockquote>
<ul>
<li><font face="Verdana" size="2">Try to avoid server side cursors as much as possible. Always stick to a 'set-based approach' instead of a 'procedural approach' for accessing and manipulating data. Cursors can often be avoided by using SELECT statements instead.</font> </li>
</ul>
<blockquote>
<p><font face="Verdana" size="2">If a cursor is unavoidable, use a WHILE loop instead. I have personally tested and concluded that a WHILE loop is always faster than a cursor. But for a WHILE loop to replace a cursor you need a column (primary key or unique key) to identify each row uniquely. I personally believe every table must have a primary or unique key. <a href="http://vyaskn.tripod.com/code.htm" target="_blank">Click here to see some examples of using a WHILE loop</a>.<br>
</font></p>
</blockquote>
<ul>
<li><font face="Verdana" size="2">Avoid the creation of temporary tables while processing data as much as possible, as creating a temporary table means more disk I/O. Consider using advanced SQL, views, SQL Server 2000 table variable, or derived tables, instead of temporary tables.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Try to avoid wildcard characters at the beginning of a word while searching using the LIKE keyword, as that results in an index scan, which defeats the purpose of an index. The following statement results in an index scan, while the second statement results in an index seek:<br>
<br>
</font><font face="Courier New" size="2">SELECT LocationID FROM Locations WHERE Specialities LIKE '%pples'<br>
SELECT LocationID FROM Locations WHERE Specialities LIKE 'A%s'</font> <font face="Verdana" size="2"><br>
<br>
Also avoid searching using not equals operators (&lt;&gt; and NOT) as they result in table and index scans.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Use 'Derived tables' wherever possible, as they perform better. Consider the following query to find the second highest salary from the Employees table:<br>
<br>
</font><font face="Courier New" size="2">SELECT MIN(Salary) <br>
FROM Employees <br>
WHERE EmpID IN<br>
(<br>
SELECT TOP 2 EmpID <br>
FROM Employees <br>
ORDER BY Salary Desc<br>
)</font> <font face="Verdana" size="2"><br>
<br>
The same query can be re-written using a derived table, as shown below, and it performs twice as fast as the above query:<br>
<br>
</font><font face="Courier New" size="2">SELECT MIN(Salary) <br>
FROM <br>
(<br>
SELECT TOP 2 Salary <br>
FROM Employees <br>
ORDER BY Salary DESC<br>
) AS A</font> <font face="Verdana" size="2"><br>
<br>
This is just an example, and your results might differ in different scenarios depending on the database design, indexes, volume of data, etc. So, test all the possible ways a query could be written and go with the most efficient one.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">While designing your database, design it keeping "performance" in mind. You can't really tune performance later, when your database is in production, as it involves rebuilding tables andindexes, re-writing queries, etc. Use the graphical execution plan in Query Analyzer or SHOWPLAN_TEXT or SHOWPLAN_ALL commands to analyze your queries. Make sure your queries do an "Index seek" instead of an "Index scan" or a "Table scan." A table scan or an index scan is a very bad thing and should be avoided where possible. Choose the right indexes on the right columns.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Prefix the table names with the owner's name, as this improves readability and avoids any unnecessary confusion. Microsoft SQL Server Books Online even states that qualifying table names with owner names helps in execution plan reuse, further boosting performance.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Use SET NOCOUNT ON at the beginning of your SQL batches, stored procedures and triggers in production environments, as this suppresses messages like '(1 row(s) affected)' after executing INSERT, UPDATE, DELETE and SELECT statements. This improves the performance of stored procedures by reducing network traffic.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Use the more readable ANSI-Standard Join clauses instead of the old style joins. With ANSI joins, the WHERE clause is used only for filtering data. Where as with older style joins, the WHERE clause handles both the join condition and filtering data. The first of the following two queries shows the old style join, while the second one shows the new ANSI join syntax:<br>
<br>
</font><font face="Courier New" size="2">SELECT a.au_id, t.title <br>
FROM titles t, authors a, titleauthor ta<br>
WHERE <br>
a.au_id = ta.au_id AND<br>
ta.title_id = t.title_id AND <br>
t.title LIKE '%Computer%'<br>
<br>
SELECT a.au_id, t.title<br>
FROM authors a <br>
INNER JOIN<br>
titleauthor ta <br>
ON <br>
a.au_id = ta.au_id<br>
INNER JOIN<br>
titles t<br>
ON<br>
ta.title_id = t.title_id<br>
WHERE t.title LIKE '%Computer%'<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Do not prefix your stored procedure names with "sp_". The prefix sp_ is reserved for system stored procedure that ship with SQL Server. Whenever SQL Server encounters a procedure name starting with sp_, it first tries to locate the procedure in the master database, then it looks for any qualifiers (database, owner) provided, then it tries dbo as the owner. So you can really save time in locating the stored procedure by avoiding the "sp_" prefix.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Views are generally used to show specific data to specific users based on their interest. Views are also used to restrict access to the base tables by granting permission only on views. Yet another significant use of views is that they simplify your queries. Incorporate your frequently required, complicated joins and calculations into a view so that you don't have to repeat those joins/calculations in all your queries. Instead, just select from the view.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Use User Defined Datatypes if a particular column repeats in a lot of your tables, so that the datatype of that column is consistent across all your tables.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Do not let your front-end applications query/manipulate the data directly using SELECT or INSERT/UPDATE/DELETE statements. Instead, create stored procedures, and let your applications access these stored procedures. This keeps the data access clean and consistent across all the modules of your application, and at the same time centralizing the business logic within the database.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Try not to use TEXT or NTEXT datatypes for storing large textual data. The TEXT datatype has some inherent problems associated with it. For example, you cannot directly write or update text data using the INSERT or UPDATE statements. Instead,&nbsp;&nbsp; you have to use special statements like READTEXT, WRITETEXT and UPDATETEXT. There are also a lot of bugs associated with replicating tables containing text columns. So, if you don't have to store more than 8KB of text, use CHAR(8000) or VARCHAR(8000) datatypes instead.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">If you have a choice, do not store binary or image files (Binary Large Objects or BLOBs) inside the database. Instead, store the path to the binary or image file in the database and use that as a pointer to the actual binary file stored elsewhere on a server. Retrieving and manipulating these large binary files is better performed outside the database, and after all, a database is not meant for storing files.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Use the CHAR data type for a column only when the column is non-nullable. If a CHAR column is nullable, it is treated as a fixed length column in SQL Server 7.0+. So, a CHAR(100), when NULL, will eat up 100 bytes, resulting in space wastage. So, use VARCHAR(100) in this situation. Of course, variable length columns do have a very little processing overhead over fixed length columns. Carefully choose between CHAR and VARCHAR depending up on the length of the data you are going to store.<br>
<br>
</font></li>
<li><font face="Verdana" size="2">Avoid dynamic SQL statements as much as possible. Dynamic SQL tends to be slower than static SQL, as SQL Server must generate an execution plan every time at runtime. IF and CASE statements come in handy to avoid dynamic SQL. Another major disadvantage of using dynamic SQL is that it requires users to have direct access permissions on all accessed objects, like tables and views. Generally, users are given access to the stored procedures which reference the tables, but not directly on the tables. In this case, dynamic SQL will not work. Consider the following scenario where a user named 'dSQLuser' is added to the pubs database and is granted access to a procedure named 'dSQLproc', but not on any other tables in the pubs database. The procedure dSQLproc executes a direct SELECT on titles table and that works. The second statement runs the same SELECT on titles table, using dynamic SQL and it fails with the following error:<br>
<br>
</font><font face="Courier New" size="2">Server: Msg 229, Level 14, State 5, Line 1<br>
SELECT permission denied on object 'titles', database 'pubs', owner 'dbo'.</font> </li>
</ul> <a href="http://hi.baidu.com/godblessewan/blog/item/29ce8e52cab8ca0f0cf3e3b1.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Sql">Sql</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/29ce8e52cab8ca0f0cf3e3b1.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-23  04:10 P.M.</pubDate>
        <category><![CDATA[Sql]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/29ce8e52cab8ca0f0cf3e3b1.html</guid>
</item>

<item>
        <title><![CDATA[Locking in Microsoft SQL Server]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/6ffbcb621ea35ddce7113ab5.html]]></link>
        <description><![CDATA[
		
		<h2><a name="part_1"></a>Introduction</h2>
In this article, I want to tell you about SQL Server 7.0/2000 Transaction Isolation Levels, what kinds of Transaction Isolation Levels exist, and how you can set the appropriate Transaction Isolation Level, about Lock types and Locking optimizer hints, about deadlocks, and about how you can view locks by using the <strong>sp_lock</strong> stored procedure.<br>
<br>
<h2><a name="part_2"></a>Transaction Isolation Levels</h2>
There are four isolation levels:<br>
<br>
<li>READ UNCOMMITTED </li>
<li>READ COMMITTED </li>
<li>REPEATABLE READ </li>
<li>SERIALIZABLE<br>
<br>
Microsoft SQL Server supports all of these Transaction Isolation Levels and can separate <strong>REPEATABLE READ</strong> and <strong>SERIALIZABLE</strong>.<br>
<br>
Let me to describe each isolation level.<br>
<br>
<h3>READ UNCOMMITTED</h3>
When it's used, SQL Server not issue shared locks while reading data. So, you can read an uncommitted transaction that might get rolled back later. This isolation level is also called dirty read. This is the lowest isolation level. It ensures only that a physically corrupt data will not be read.<br>
<br>
<h3>READ COMMITTED</h3>
This is the default isolation level in SQL Server. When it's used, SQL Server will use shared locks while reading data. It ensures that a physically corrupt data will not be read and will never read data that another application has changed and not yet committed, but it not ensures that the data will not be changed before the end of the transaction.<br>
<br>
<h3>REPEATABLE READ</h3>
When it's used, the dirty reads and nonrepeatable reads cannot occur. It means that locks will be placed on all data that is used in a query, and another transactions cannot update the data.<br>
<br>
This is the definition of nonrepeatable read from SQL Server Books Online:<br>
<br>
<table width="100%">
<tbody>
    <tr bgcolor="#dcdcdc">
    <td>
<pre>nonrepeatable read
When a transaction reads the same row more than one time, and between the
two (or more) reads, a separate transaction modifies that row. Because the
row was modified between reads within the same transaction, each read
produces different values, which introduces inconsistency.
</pre>
</td>
</tr>
</tbody>
</table>
<pre> </pre>
<h3>SERIALIZABLE</h3>
Most restrictive isolation level. When it's used, the phantom values cannot occur. It prevents other users from updating or inserting rows into the data set until the transaction will be completed.<br>
<br>
This is the definition of phantom from SQL Server Books Online:<br>
<br>
<table width="100%">
<tbody>
    <tr bgcolor="#dcdcdc">
    <td>
<pre>phantom
Phantom behavior occurs when a transaction attempts to select a row that
does not exist and a second transaction inserts the row before the first
transaction finishes. If the row is inserted, the row appears as a phantom
to the first transaction, inconsistently appearing and disappearing.
</pre>
</td>
</tr>
</tbody>
</table>
<pre> </pre>
You can set the appropriate isolation level for an entire SQL Server session by using the <strong>SET TRANSACTION ISOLATION LEVEL</strong> statement.<br>
This is the syntax from SQL Server Books Online:<br>
<br>
<table width="100%">
<tbody>
    <tr bgcolor="#dcdcdc">
    <td>
<pre>SET TRANSACTION ISOLATION LEVEL 
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; READ COMMITTED 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | READ UNCOMMITTED 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | REPEATABLE READ 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | SERIALIZABLE
&nbsp;&nbsp;&nbsp;&nbsp; }
</pre>
</td>
</tr>
</tbody>
</table>
<pre> </pre>
You can use the <strong>DBCC USEROPTIONS</strong> statement to determine the Transaction Isolation Level currently set. This command returns the set options that are active for the current connection. This is the example:<br>
<br>
<table width="100%">
<tbody>
    <tr bgcolor="#dcdcdc">
    <td>
<pre>SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
DBCC USEROPTIONS
GO
</pre>
</td>
</tr>
</tbody>
</table>
<pre> </pre>
<h2><a name="part_3"></a>Lock types</h2>
There are three main types of locks that SQL Server 7.0/2000 uses:<br>
<br>
</li>
<li>Shared locks </li>
<li>Update locks </li>
<li>Exclusive locks<br>
<br>
<strong>Shared</strong> locks are used for operations that do not change or update data, such as a SELECT statement.<br>
<br>
<strong>Update</strong> locks are used when SQL Server intends to modify a page, and later promotes the update page lock to an exclusive page lock before actually making the changes.<br>
<br>
<strong>Exclusive</strong> locks are used for the data modification operations, such as UPDATE, INSERT, or DELETE.<br>
<br>
<strong>Shared</strong> locks are compatible with other <strong>Shared</strong> locks or <strong>Update</strong> locks.<br>
<br>
<strong>Update</strong> locks are compatible with <strong>Shared</strong> locks only.<br>
<br>
<strong>Exclusive</strong> locks are not compatible with other lock types.<br>
<br>
Let me to describe it on the real example. There are four processes, which attempt to lock the same page of the same table. These processes start one after another, so Process1 is the first process, Process2 is the second process and so on.<br>
<br>
Process1 : SELECT<br>
Process2 : SELECT<br>
Process3 : UPDATE<br>
Process4 : SELECT<br>
<br>
Process1 sets the <strong>Shared</strong> lock on the page, because there are no another locks on this page.<br>
Process2 sets the <strong>Shared</strong> lock on the page, because <strong>Shared</strong> locks are compatible with other <strong>Shared</strong> locks.<br>
Process3 wants to modify data and wants to set <strong>Exclusive</strong> lock, but it cannot make it before Process1 and Process2 will be finished, because <strong>Exclusive</strong> lock is not compatible with other lock types. So, Process3 sets <strong>Update</strong> lock.<br>
Process4 cannot set <strong>Shared</strong> lock on the page before Process3 will be finished. So, there is no <strong>Lock starvation</strong>. <strong>Lock starvation</strong> occurs when read transactions can monopolize a table or page, forcing a write transaction to wait indefinitely. So, Process4 waits before Process3 will be finished.<br>
After Process1 and Process2 were finished, Process3 transfer <strong>Update</strong> lock into <strong>Exclusive</strong> lock to modify data. After Process3 was finished, Process4 sets the <strong>Shared</strong> lock on the page to select data.<br>
<br>
<h2><a name="part_4"></a>Locking optimizer hints</h2>
SQL Server 7.0/2000 supports the following Locking optimizer hints:<br>
<br>
</li>
<li>NOLOCK </li>
<li>HOLDLOCK </li>
<li>UPDLOCK </li>
<li>TABLOCK </li>
<li>PAGLOCK </li>
<li>TABLOCKX </li>
<li>READCOMMITTED </li>
<li>READUNCOMMITTED </li>
<li>REPEATABLEREAD </li>
<li>SERIALIZABLE </li>
<li>READPAST </li>
<li>ROWLOCK<br>
<br>
<strong>NOLOCK</strong> is also known as "dirty reads". This option directs SQL Server not to issue shared locks and not to honor exclusive locks. So, if this option is specified, it is possible to read an uncommitted transaction. This results in higher concurrency and in lower consistency.<br>
<br>
<strong>HOLDLOCK</strong> directs SQL Server to hold a shared lock until completion of the transaction in which HOLDLOCK is used. You cannot use HOLDLOCK in a SELECT statement that includes the FOR BROWSE option. HOLDLOCK is equivalent to SERIALIZABLE.<br>
<br>
<strong>UPDLOCK</strong> instructs SQL Server to use update locks instead of shared locks while reading a table and holds them until the end of the command or transaction.<br>
<br>
<strong>TABLOCK</strong> takes a shared lock on the table that is held until the end of the command. If you also specify HOLDLOCK, the lock is held until the end of the transaction.<br>
<br>
<strong>PAGLOCK</strong> is used by default. Directs SQL Server to use shared page locks.<br>
<br>
<strong>TABLOCKX</strong> takes an exclusive lock on the table that is held until the end of the command or transaction.<br>
<br>
<strong>READCOMMITTED</strong><br>
Perform a scan with the same locking semantics as a transaction running at the READ COMMITTED isolation level. By default, SQL Server operates at this isolation level.<br>
<br>
<strong>READUNCOMMITTED</strong><br>
Equivalent to NOLOCK.<br>
<br>
<strong>REPEATABLEREAD</strong><br>
Perform a scan with the same locking semantics as a transaction running at the REPEATABLE READ isolation level.<br>
<br>
<strong>SERIALIZABLE</strong><br>
Perform a scan with the same locking semantics as a transaction running at the SERIALIZABLE isolation level. Equivalent to HOLDLOCK.<br>
<br>
<strong>READPAST</strong><br>
Skip locked rows. This option causes a transaction to skip over rows locked by other transactions that would ordinarily appear in the result set, rather than block the transaction waiting for the other transactions to release their locks on these rows. The READPAST lock hint applies only to transactions operating at READ COMMITTED isolation and will read only past row-level locks. Applies only to the SELECT statement.<br>
You can only specify the READPAST lock in the READ COMMITTED or REPEATABLE READ isolation levels.<br>
<br>
<strong>ROWLOCK</strong><br>
Use row-level locks rather than use the coarser-grained page- and table-level locks.<br>
<br>
You can specify one of these locking options in a SELECT statement.<br>
This is the example:<br>
<br>
<em>SELECT au_fname FROM pubs..authors (holdlock)</em><br>
<br>
<h2><a name="part_5"></a>Deadlocks</h2>
Deadlock occurs when two users have locks on separate objects and each user wants a lock on the other's object. For example, User1 has a lock on object "A" and wants a lock on object "B" and User2 has a lock on object "B" and wants a lock on object "A". In this case, SQL Server ends a deadlock by choosing the user, who will be a deadlock victim. After that, SQL Server rolls back the breaking user's transaction, sends message number 1205 to notify the user's application about breaking, and then allows the nonbreaking user's process to continue.<br>
<br>
You can decide which connection will be the candidate for deadlock victim by using <strong>SET DEADLOCK_PRIORITY</strong>. In other case, SQL Server selects the deadlock victim by choosing the process that completes the circular chain of locks.<br>
<br>
So, in a multiuser situation, your application should check the error 1205 to indicate that the transaction was rolled back, and if it's so, restart the transaction.<br>
<br>
<strong>Note.</strong> To reduce the chance of a deadlock, you should minimize the size of transactions and transaction times.<br>
<br>
<h2><a name="part_6"></a>View locks (sp_lock)</h2>
Sometimes you need a reference to information about locks. Microsoft recommends using the <strong>sp_lock</strong> system stored procedure to report locks information. This very useful procedure returns the information about SQL Server process ID, which lock the data, about locked database, about locked table ID, about locked page and about type of locking (locktype column).<br>
<br>
This is the example of using the <strong>sp_lock</strong> system stored procedure:<br>
<br>
<table width="100%">
<tbody>
    <tr bgcolor="#dcdcdc">
    <td>
<pre>spid&nbsp;&nbsp;&nbsp; locktype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table_id&nbsp;&nbsp;&nbsp;&nbsp; page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbname
------ ----------------------------------- ----------- ----------- ---------------
11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sh_intent&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 688005482&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; master
11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Ex_extent&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 336&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tempdb
</pre>
</td>
</tr>
</tbody>
</table>
<pre> </pre>
The information, returned by <strong>sp_lock</strong> system stored procedure needs in some clarification, because it's difficult to understand database name, object name and index name by their ID numbers.<br>
<br>
Check the link below if you need to get user name, host name, database name, index name object name and object owner instead of their ID numbers:<br>
<a href="http://www.mssqlcity.com/Articles/Adm/LockView.htm">Detailed locking view: sp_lock2</a><br>
</li> <a href="http://hi.baidu.com/godblessewan/blog/item/6ffbcb621ea35ddce7113ab5.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Sql">Sql</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/6ffbcb621ea35ddce7113ab5.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-23  04:05 P.M.</pubDate>
        <category><![CDATA[Sql]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/6ffbcb621ea35ddce7113ab5.html</guid>
</item>

<item>
        <title><![CDATA[Tips for Reducing SQL Server Deadlocks]]></title>
        <link><![CDATA[http://hi.baidu.com/godblessewan/blog/item/8b5d1d4f72b94f37afc3abe0.html]]></link>
        <description><![CDATA[
		
		<p><font size="3"><strong>Deadlocking</strong> occurs when two user processes have locks on separate objects and each process is trying to acquire a lock on the object that the other process has. When this happens, SQL Server ends the deadlock by automatically choosing one and aborting the process, allowing the other process to continue. The aborted transaction is rolled back and an error message is sent to the user of the aborted process. Generally, the transaction that requires the least amount of overhead to rollback is the transaction that is aborted.</font></p>
<p><font size="3">As you might imagine, deadlocks can use up SQL Server's resources, especially CPU power, wasting it unnecessarily.</font></p>
<p><font size="3">Most well-designed applications, after receiving a deadlock message, will resubmit the aborted transaction, which most likely can now run successfully. This process, if it happens often on your server, can drag down performance. If the application has not been written to trap deadlock errors and to automatically resubmit the aborted transaction, users may very well become confused as to what is happening when they receive deadlock error messages on their computer.</font></p>
<p><font size="3">Here are some tips on how to avoid deadlocking on your SQL Server:</font></p>
<ul>
<li><font size="3">Ensure the database design is properly normalized. </font></li>
    <li><font size="3">Have the application access server objects in the same order each time. </font></li>
    <li><font size="3">During transactions, don't allow any user input. Collect it before the transaction begins. </font></li>
    <li><font size="3">Avoid cursors. </font></li>
    <li><font size="3">Keep transactions as short as possible. One way to help accomplish this is to reduce the number of round trips between your application and SQL Server by using stored procedures or keeping transactions with a single batch. Another way of reducing the time a transaction takes to complete is to make sure you are not performing the same reads over and over again. If you do need to read the same data more than once, cache it by storing it in a variable or an array, and then re-reading it from there. </font></li>
    <li><font size="3">Reduce lock time. Try to develop your application so that it grabs locks at the latest possible time, and then releases them at the very earliest time. </font></li>
    <li><font size="3">If appropriate, reduce lock escalation by using the ROWLOCK or PAGLOCK. </font></li>
    <li><font size="3">Consider using the NOLOCK hint to prevent locking if the data being locked is not modified often. </font></li>
    <li><font size="3">If appropriate, use as low of an isolation level as possible for the user connection running the transaction.&nbsp;&nbsp;&nbsp;</font></li>
    <li><font size="3">Consider using bound connections. </font></li>
</ul>
    <p><font size="3">[6.5, 7.0, 2000, 2005] <em>Updated 6-6-2005</em></font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3">When a deadlock occurs, by default, SQL Server choose a deadlock "victim" by identifying which of the two processes will use the least amount of resources to rollback, and then returns error message 1205.</font></p>
    <p><font size="3"><strong>But what if you don't like default behavior? Can you change it?</strong> Yes, you can, by using the following command:</font></p>
    <p><span class="textCode"><font size="3">SET DEADLOCK_PRIORITY { <em>LOW | NORMAL | @deadlock_var</em> }</font></span></p>
    <p><font size="3">WHERE:</font></p>
    <p><font size="3"><em>Low</em> tells SQL Server that the current session should be the preferred deadlock victim, not the session that incurs the least amount of rollback resources. The standard deadlock error message 1205 is returned.</font></p>
    <p><font size="3"><em>Normal</em> tells SQL Server to use the default deadlock method.</font></p>
    <p><font size="3"><em>@deadlock_var</em> is a character variable specifying which deadlock method you want to use. Specify "3" for low, or "6" for normal.</font></p>
    <p><font size="3">This command is set a runtime for a specified user connection. [2000] <em>Updated 9-1-2005</em></font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3"><strong>To help identify deadlock problems</strong>, use the SQL Server Profiler's Create Trace Wizard to run the "Identify The Cause of a Deadlock" trace. This will provide you with the raw data you need to help isolate the causes of deadlocks in your databases.&nbsp;&nbsp; [7.0]</font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3"><strong>Consider turning on SQL Server deadlock tracing</strong> in order to track deadlocks, when you are having deadlock-related issues. The overhead for doing this is minimal. <br>
<br>
<span class="textCode">DBCC TRACEON (3605,1204,-1)</span></font></p>
    <p><font size="3">Once this is run, then all deadlocking activity will be written to the SQL Server log file. [7.0, 2000] <em>Updated 11-6-2006</em></font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3"><strong>To help identify which tables or stored procedures are causing deadlock problems</strong>, turn on trace flag 1204 (outputs basic trace data) or trace flag 1205 (outputs more detailed trace data). Be sure to turn off this trace flag when you are done, as this trace can eat up SQL Server's resources unnecessarily, hurting performance. [6.5, 7.0, 2000] <em>Updated 11-6-2006</em></font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3">Ideally, deadlocks should be eliminated from your applications. <strong>But if you are unable to eliminate all deadlocks</strong> in your application, be sure to include program logic in your application to deal with killed transactions in a user-friendly way.</font></p>
    <p><font size="3">For example, let's say that two transactions are deadlocked and that SQL Server kills one of the transactions. In this case, SQL Server will raise an error message that your application needs to respond to. In most cases, you will want your application to wait a random amount of time after the deadlock in order to resubmit the killed transaction to SQL Server.</font></p>
    <p><font size="3">It is important that there is a random waiting period because it is possible that another contending transaction could also be waiting, and you don't want both contending transactions to wait the same amount of time and then both try to execute at the same time, causing another deadlock.</font></p>
    <p><font size="3">If your error-trapping code is good, the end user should never know that the deadlock even occurred. [6.5, 7.0, 2000] <em>Updated 9-1-2005</em></font></p>
    <p class="asterisk"><font size="3">*****</font></p>
    <p><font size="3"><strong>One way to help prevent deadlocks is to use the UPDLOCK optimizer hint</strong>. What this hint does is to force SQL Server to use an update lock instead of a shared lock.</font></p>
    <p><font size="3">A shared lock occurs when your query wants to read a row, but does not want to change it. Shared locks can co-exist with other shared locks, which means that multiple shared locks can exist on the same row, page, or table. A shared lock prevents the locked resource from receiving an exclusive lock, so if another user runs a query that need to UPDATE a row, they will not be able to until all of the shared locks on the row are gone. Once a shared lock is no longer needed by a query that is reading it, it is immediately released.</font></p>
    <p><font size="3">An update lock is a hybrid between an shared and an exclusive lock. Update locks are used when a query needs to update one or more records, but until the WHERE clause is complete, the query doesn't know which records it needs to UPDATE. So instead of putting an exclusive lock on every record (which reduces concurrency and your application's performance) it places a shared lock on the requisite records, and only when the WHERE clause is completed, are exclusive locks put on the records that need to be UPDATEDed. The shared locks created by an update lock are held until the WHERE clause is completed running, not immediately released after the row is read, as is normal with a shared lock.</font></p>
    <p><font size="3">So how does using the UPDLOCK hint help to reduce deadlocks? If you force the use of an update lock instead of a shared lock when reading tables, the UPDLOCK hint holds the locks until the end of your statement or transaction. This allows you to read data without blocking other users, and to update data later with the knowledge that the data has not changed since you last read it. This helps to prevent deadlocks because other users can't sneak in and lock a record that you will need before you need it. [6.5, 7.0, 2000] <em>Updated 9-1-2005</em></font></p>
 <a href="http://hi.baidu.com/godblessewan/blog/item/8b5d1d4f72b94f37afc3abe0.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/godblessewan/blog/category/Sql">Sql</a>&nbsp;<a href="http://hi.baidu.com/godblessewan/blog/item/8b5d1d4f72b94f37afc3abe0.html#comment">查看评论</a>]]></description>
        <pubDate>2007-05-23  02:09 P.M.</pubDate>
        <category><![CDATA[Sql]]></category>
        <author><![CDATA[godblessewan]]></author>
		<guid>http://hi.baidu.com/godblessewan/blog/item/8b5d1d4f72b94f37afc3abe0.html</guid>
</item>


</channel>
</rss>