在使用WCF服务时,通常都是用svcutil生成代理类和配置,用生成的默认配置就可以调用服务。先来看看生成的默认的配置内容:

View Code
<client>
            <endpoint address="http://localhost:8732/ConfigNameService/Service1/"
                binding
="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract
="IService1" name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>

其中contract就是代理类中指向接口或契约类,假如contract的定义如下,那么默认情况下是contract接口的名字

View Code
1 [System.ServiceModel.ServiceContract]
2 public interface IService1
3 {
4 }

但实际上存在多个服务或者需要用命名空间去标记contract时,需要修改客户端配置中endpoint的中contract的名字,需要其包含命名空间,比如改成如下:

contract="ConfigNameService.IService1"。

按通常的理解,以为系统会按照类型名称去找代理类中的接口,但实际上并非这样,仅仅在执行以下代码时就会提示找不到默认终结点的错误。

 

View Code

 

1  using (var client = new Service1Client())
2             {
3                 Console.WriteLine(client.GetData(1));
4             }

 而将contract的值改回"IService1"即可正常调用。

原因是代理类中的contract定义时设置的ConfigurationName决定了配置时需要配置的值,这个属性很容易被忽视,尤其是通过svcutil生成的代理类,比如以下代码:

View Code
 1 [System.ServiceModel.ServiceContractAttribute(ConfigurationName="IService1")]
 2 public interface IService1
 3 {
 4     
 5     [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/GetData", ReplyAction="http://tempuri.org/IService1/GetDataResponse")]
 6     string GetData(int value);
 7     
 8     [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/GetDataUsingDataContract", ReplyAction="http://tempuri.org/IService1/GetDataUsingDataContractResponse")]
 9     ConfigNameService.CompositeType GetDataUsingDataContract(ConfigNameService.CompositeType composite);
10 }

ConfigurationName就是原服务接口的名字,因此需要手动修改ConfigurationName为需要的值,这里只能改客户代理类中的接口定义的ConfigurationName。然后在配置endpoint时的contract的值跟ConfigurationName的值保持一致即可。

posted @ 2012-02-04 11:11 神八 阅读(91) 评论(0) 编辑

今日碰到了这么一个异常,异常信息如下:

Type : System.InvalidOperationException, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : 使用 JSON JavaScriptSerializer 进行序列化或反序列化时出错。字符串
的长度超过了为 maxJsonLength 属性设置的值。
Source : System.Web.Extensions
Help link : 
Data : System.Collections.ListDictionaryInternal
TargetSite : Void Serialize(System.Object, System.Text.StringBuilder,
SerializationFormat)
Stack Trace :    在
System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj,
StringBuilder output, SerializationFormat serializationFormat)
   在 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object
obj, SerializationFormat serializationFormat)
   在 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object
 

这个异常是在执行MVC中的JsonResult的时抛出的,根据异常的Message得知是序列化的字符串超出了maxJsonLength的限制。并得知这个属性是由JavaScriptSerializer提供的,因为MVC内置的JsonResult是用JavaScriptSerializer进行序列化的。在网上快速搜索了一下,碰到这个问题的童鞋不少,大部分推荐的解决的方法都是在web.config中加入以下配置,设置maxJsonLength的长度即可。

View Code
<system.web.extensions>
       <scripting>
           <webServices>
               <jsonSerialization maxJsonLength="20971520"/>
           </webServices>
       </scripting>
   </system.web.extensions>

在自动提示下,很顺畅的加上了几行代码,以为这是个简洁的解决方案,但是运行网站之后报以下错误:

分析器错误消息: 无法识别的配置节 system.web.extensions。
这似乎又是碰到了一家人不认识的情况,既然无法识别为什么能带智能感知的方式输出,而且是已system.web开头的,按道理不会识别不出的。以为是拼写错误,经过进一步搜索之后,原来是缺乏了声明,加上对节点的声明即可(很大一串的内容)。

 

View Code
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
          <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
              <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
              <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
                  <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
                  <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                  <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
                  <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
              </sectionGroup>
          </sectionGroup>
      </sectionGroup>

 加入了声明之后,运行正常,但是问题依旧还在,而且不管maxJsonLength设置成多大都无效,就算改成1个字符,居然还能跑起来。碰到这个问题只能进一步的搜索。在这篇文章中找到了原委http://weblogs.asp.net/rashid/archive/2009/03/23/submitting-my-first-bug-after-asp-net-mvc-1-0-rtm-release.aspx

原来MVC框架内置的JsonResult代码中,在使用JavaScriptSerializer时,都是采用的默认值,没有从maxJsonLength读取值,即忽略了这个配置。要想使用配置中的值,只能自定义一个JsonResult,重写原JsonResult的ExecuteResult方法,于是定义一个ConfigurableJsonResult,代码如下:

ConfigurableJsonResult
 1  public class ConfigurableJsonResult : JsonResult
 2     {
 3         public override void ExecuteResult(ControllerContext context)
 4         {
 5             if (context == null)
 6             {
 7                 throw new ArgumentNullException("context");
 8             }
 9             if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
10                 String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
11             {
12                 throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.");
13             }
14 
15             HttpResponseBase response = context.HttpContext.Response;
16 
17             if (!String.IsNullOrEmpty(ContentType))
18             {
19                 response.ContentType = ContentType;
20             }
21             else
22             {
23                 response.ContentType = "application/json";
24             }
25             if (ContentEncoding != null)
26             {
27                 response.ContentEncoding = ContentEncoding;
28             }
29             if (Data != null)
30             {
31                 JavaScriptSerializer serializer = new JavaScriptSerializer();
32 
33                 ScriptingJsonSerializationSection section = ConfigurationManager.GetSection("system.web.extensions/scripting/webServices/jsonSerialization") as ScriptingJsonSerializationSection;
34            
35                 if (section != null)
36                 {
37                     serializer.MaxJsonLength = section.MaxJsonLength;
38                     serializer.RecursionLimit = section.RecursionLimit;
39                 }
40 
41                 response.Write(serializer.Serialize(Data));
42             }
43         }
44     }

关键在红色标记的代码,读取配置的值。JavaScriptSerializer还有其他属性可配置,没有列出与实现,暂时够用。

这样在返回长字符内容的json结果时,直接替换原JsonResult即可,同时也兼顾了可配置的灵活性。

 

posted @ 2012-02-03 14:38 神八 阅读(590) 评论(0) 编辑

昨天在用IIS部署一个WCF服务时,碰到了如下错误:

理解了文档内容,但无法进行处理。
  - WSDL 文档包含无法解析的链接。
  - 下载“http://admin-pc/IISHostService/Service1.svc?xsd=xsd0”时出错。
  - 基础连接已经关闭: 接收时发生错误。
  - 无法从传输连接中读取数据: 远程主机强迫关闭了一个现有的连接。。
  - 远程主机强迫关闭了一个现有的连接。
元数据包含无法解析的引用:“http://admin-pc/IISHostService/Service1.svc?wsdl”。
元数据包含无法解析的引用:“http://admin-pc/IISHostService/Service1.svc?wsdl”。
如果该服务已在当前解决方案中定义,请尝试生成该解决方案,然后再次添加服务引用。

该错误是在使用svcutil生成client代码时报的错误,服务是部署在IIS7上,部署的过程都是完全教科书式的进行。服务也正常启动了,显示如下内容

已创建服务。

若要测试此服务,需要创建一个客户端,并将其用于调用该服务。可以使用下列语法,从命令行中使用 svcutil.exe 工具来进行此操作:

svcutil.exe http://leo-pc/IISHostService/Service1.svc?wsdl

按照提示直接用svcutil.exe http://admin-pc/IISHostService/Service1.svc?wsdl命令去生成代码,就出现了开头说的那个错误。而如果用visual studio的webdevserver启动,则一切正常。

经过一轮谷百之后,发现网上有很多类似的情况,有的说是因为用了wsHttpBinding协议引起的,或者是元数据没有正确公开,但都不是他们说的情况。后来找到了一篇文章,说的是添加WCF引用的一个陷阱。里面提到的情形跟我遇到的一致,原来问题出在权限,难怪用webdevserver可以很正常的运行。原来在下载http://admin-pc/IISHostService/Service1.svc?xsd=xsd0时的权限不足,在浏览器直接访问这个url会提示找不到页面。原因就是IIS进程的用户没有访问Windows\Temp目录的权限。找到Temp目录,然后找到IIS_USER用户,授权即可。

具体可参考:http://merill.net/2008/04/wcf-add-service-reference-gotcha-with-windows-server/

 

posted @ 2012-01-06 09:32 神八 阅读(408) 评论(0) 编辑

   任何程序都离不开对异常的处理,良好的异常处理方式可加快寻找出异常的根源,同时也需要避免暴露敏感信息到异常中。WCF这种典型的服务端和客户端交互的程序,服务端的异常更需要适当的处理。下面以一个简单的服务为例,说明WCF中处理异常的方式。

WCF服务定义如下,很明显方法Divide在divisor为0的时候将会抛出异常

View Code
public class CalculateService : ICalculateService
    {
        public int Divide(int dividend, int divisor)
        {
            return dividend / divisor;
        }

        public int Add(int a, int b)
        {
            return a + b;
        }
    }

客户端调用如下:

View Code
 using (var client = new CalculateServiceClient())
            {
                try
                {
                    Console.WriteLine(client.Divide(200));
                }
                catch (FaultException ex)
                {
                    Console.WriteLine(ex.Reason);
                }

              }

首先需要知道的是,WCF的异常信息默认是以FaultException的形式返回到客户端,FaultException的关键属性Reason是对客户端反馈的最重要信息之一。以上客户端代码调用之后,默认的FaultException返回的Message信息如下:

由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults (从 ServiceBehaviorAttribute 或从 <serviceDebug> 配置行为)以便将异常信息发送回客户端,或在打开每个 Microsoft .NET Framework 3.0 SDK 文档的跟踪的同时检查服务器跟踪日志。

根据异常的提示,意思说如果要在客户端看到详细的Exception信息,那么请将ServiceBehavior对应的IncludeExceptionDetailInFaults属性设置为True,通常在配置中表现为如下设置:

View Code
1 <serviceBehaviors>
2         <behavior>
3           <serviceMetadata httpGetEnabled="True" httpGetUrl="http://localhost:8733/CalculateService/"/>
4           <serviceDebug includeExceptionDetailInFaults="True" />
5         </behavior>
6       </serviceBehaviors>

通过以上设置之后,客户端输出的内容为“尝试除以零”,这个提示信息跟原始的异常信息是一致,即返回的FaultException中的Reason包含原始异常的Message的值,但是这样处理之后服务端所报出的异常信息直接传到了客户端,比如一些保密信息也可能输出到了客户端,因此对于异常信息必须进行一个封装。最直接的形式莫过于在服务端就把异常给捕获了,并重新throw一个FaultException

服务端的代码改进如下,经过以下改进,那么客户端得到的信息仅仅是"操作失败",同时服务端也记录了异常信息(这时IncludeExceptionDetailInFaults是设置为False的)。

View Code
1 try
2             {
3                 return dividend / divisor;
4             }
5             catch (Exception ex)
6             {
7                 Console.WriteLine(ex.Message);
8                 throw new FaultException("操作失败");
9             }

当然这是FaultException的默认用法,FaultException还支持强类型的异常错误信息,返回更加丰富和精确的错误提示。假设定义如下通用的一个FaultContract类型,将出错时的用户名和线程名字记录到异常信息中,因为异常信息也是通过SOAP格式传输的,因此跟定义其他DataContract的方式一样。

CommonFaultContract
1     [DataContract]
2     public class CommonFaultContract
3     {
4         [DataMember]
5         public string UserName { getset; }
6         [DataMember]
7         public string  ThreadName { getset; }
8     }

那么服务方法的接口需要增加如下标记,如果不这样标记,那么客户端得到的异常类型依然是FaultException,而不是强类型的异常信息。

 [FaultContract(typeof(CommonFaultContract))]
 int Divide(int dividend, int divisor)

实现方法中抛出异常的部分代码改成如下:

异常处理
1 catch (Exception ex)
2             {
3                 Console.WriteLine(ex.Message);
4                 throw new FaultException<CommonFaultContract>(new CommonFaultContract 
5                 {
6                     UserName = Environment.UserName,
7                     ThreadName = System.Threading.Thread.CurrentThread.Name
8                 }, "操作失败");
9             }

 这时候重新生成客户端的代理类,然后更新客户端的代码如下,红色部分即获取强类型的异常错误信息。

View Code
 1 try
 2                 {
 3                     Console.WriteLine(client.Divide(200));
 4                 }
 5                 catch (FaultException<CommonFaultContract> ex)
 6                 {
 7                     Console.WriteLine(ex.Detail.ThreadName);
 8                     Console.WriteLine(ex.Detail.UserName);
 9                     Console.WriteLine(ex.Reason);
10                 }

当然在具体应用中还需要根据需求,返回不同的信息,构建不同的FaultContract。

  以上服务端捕获的异常方法,适用于方法比较少的情况,如果有十多个方法,一个个去写try catch然后做标记等,那么工作量会很大,而且代码也不利于重用。尝试寻找像MVC Controller那样的统一处理Exception的方式,将异常处理都放在基类中,那么只要继承与这个基类的方法都不需要去写try catch去捕获异常。但WCF中似乎没有这样的机制,放弃了这种做法。

  最近在研究Enterprise Lib中对WCF的支持时,发现Exception Block中还特地有针对WCF程序异常处理的解决方案,而且满足以上说道的需求,即可记录异常,又可对异常信息进行封装。更重要的时,自动处理运行时的异常信息,不需要挨个方法的去写Try catch。秉承企业库的优秀传统,大部分工作还是通过配置就可以完成了,非常好的解决方案。下面介绍具体的使用步骤。

步骤一:

引用以下dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.dll

Microsoft.Practices.EnterpriseLibrary.Common.dll

Microsoft.Practices.ObjectBuilder2.dll

步骤2:

在具体的实现类中,增加如下属性标记,其中WcfException为企业库中Exception Block中的一个异常处理策略,具体如何配置异常处理策略,请参考企业库的帮助文档。

[ExceptionShielding("WcfException")]
public class CalculateService : ICalculateService

那么只要增加了[ExceptionShielding("WcfException")]这个属性标记之后,所有运行时的异常都将交给策略名为WcfException的异常处理block来处理,在这里就可以执行一些异常记录以及异常封装的操作。

步骤3:

将异常信息封装为FaultException,这个动作也是通过配置来完成。在Exception节点中添加一个Fault Contract Exception Handler。

Fault Contract Exception Handler需要设置以下两个属性值

exceptionMessage:所有异常封装后的错误信息

faultContractType:即返回异常的faltContract类型,这个类型必须指定一个,哪怕方法中没有用到也要,如果方法中有用到,那么客户端那边就能得到强类型FaultException,否则就是普通的FaultException。这里指定为之前定义的CommonFaultContract

对于faultContract类型的值,还可以通过PropertyMappings来自定义需要从原始异常信息中映射到faultContract的属性中,这个属性可选。

  经过以上步骤配置之后,服务端的程序就具备了自动处理异常的功能。客户端还是跟往常那样调用,不过具体是用FaultException捕获异常还是FaultException<T>去捕获异常,还得根据定义方法中是否标记了FaultContract。之后若定义了其他服务接口,同样也仅仅需要在实现类上加上[ExceptionShielding("WcfException")]标记即可。

(图片后续补上)

 

posted @ 2011-12-31 21:41 神八 阅读(79) 评论(0) 编辑

上接方法一

实现的第二种方法是利用企业库提供的针对WCF程序的validation block,如果之前有用过企业库的validation模块,那么在WCF中用起来就非常简单,在WCF中要做的大部分都是配置工作。

步骤一,引用相关的dll

Microsoft.Practices.EnterpriseLibrary.Common.dll

Microsoft.Practices.EnterpriseLibrary.Validation.dll

Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF.dll

步骤二,在指定的接口中设置一个ValidationBehavior属性标记,该标记的目的是表明方法参数中包含需要验证逻辑。当然ValitionBehavior也可以通过配置文件形式注入,具体可参考上一篇博客内容

View Code

1     [ServiceContract]
2     [ValidationBehavior]
3     public interface ITradeService

步骤三,在需要验证的参数上标记验证属性,以及给方法标记FaultContract。如果有多个参数,以此给指定参数加标记即可。对于FaultContract中必须指定,这样才能将验证信息返回。

View Code
1 [FaultContract(typeof(ValidationFault))]
2         string TradeSomething(
3             [StringLengthValidator(10, MessageTemplate = "ths length of qutoeName must less than {0}")]
4             string quoteName);

 到此,服务单的配置就完成了。

接下来说一下客户端的如何获取错误信息。

类似其他服务,通过svcutil生成得到一个客户端类,然后如果按往常那样处理FaultException,将得不到正确的错误提示。如下代码得到的信息是“此错误的创建者没有指定原因”。

View Code
 using (var client = new TradeServiceClient())
            {
                try
                {
                    Console.WriteLine(client.TradeSomething("ztejlkfjdslakfjdlskag"));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }

对于企业库返回的FaultException的错误信息,需要特殊处理,对catch部分的代码做如下修改即可

View Code
using (var client = new TradeServiceClient())
            {
                try
                {
                    Console.WriteLine(client.TradeSomething("ztejlkfjdslakfjdlskag"));
                }
                catch (System.ServiceModel.FaultException<ValidationFault> ex)
                {
                    foreach (var item in ex.Detail.Details)
                    {
                        Console.WriteLine(item.Message);
                    }
                }
            }

这里需要强调的是,ValidationFault的类型必须跟服务用的一致,要么使用svcutil中自动生成的类型,或者直接引用企业库的dll。当引用企业库的dll之后,就不能使用svcutil生成的ValidationFault类型,否则无法正确接收错误提示。

 

 

 

posted @ 2011-12-29 22:30 神八 阅读(131) 评论(0) 编辑

WCF中支持自定义behavior,可通过配置的方式给endpoint或者service设置behavior。配置方法就是在system.serviceModel/extensions/behaviorExtensions节点下注册自定义的behavior,之后就可以在behaviors节点中使用。注册的配置如下

注册自定义Behavior
1 <extensions>
2           <behaviorExtensions>
3               <add name="validation"
4                type="Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF.ValidationElement,
5                  Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF,
6                  Version=4.1.0.0, Culture=neutral, 
7                  PublicKeyToken=31bf3856ad364e35" />
8           </behaviorExtensions>
9       </extensions>

这里用的是企业库中用于支持WCF验证的behavior扩展。

按照enterpise library 4.1版本中的帮助文档描述是这样配置的,但是运行时却出现以下错误:

参数名: element (E:\aspnet\WcfService\TestParameterValidate\bin\Debug\TestParameterValidate.dll.config line 34) ---> System.ArgumentException: 无法将扩展元素“validation”添加到此元素中。请验证该扩展是否已在 system.serviceModel/extensions/behaviorExtensions 中的扩展集合中注册。

出现这种错误的只可能是拼写错误了,但那段配置可是从企业库的帮助文档中拷贝的,难道WCF不认企业库的dll,那可都是一家的产品。回想起之前写过一个自定义的behavior,也有这个问题,那么确定不是企业库的问题,问题还是在于WCF的配置。

经过搜索,在园子里的一篇文章提到了这个问题(http://www.cnblogs.com/hxw/archive/2009/09/17/1331641.html),说这是WCF的bug,配置中对于type的字符串必须严格按照规范来写,而且不能有换行等。照着这个思路,把type工工整整地写了一遍,还是报同样的错误,真让人崩溃。

这时想到了用WCF的配置工具,这个工具是.NET提供的,专门用于配置config中的WCF配置内容。调出的方法是对着配置文件,点击右键,然后选择"编辑WCF配置"。打开之后,所有内容都在掌控之中了。

配置extensions如下步骤:

第一步选择行为元素扩展:

第二步点击右下方的"新建"按钮,在弹出的界面中选择自定义的类型

点击确定之后,保存即可。

经过配置之后,工具生成的配置代码如下,咋一看没什么区别,但重要的是,能运行起来而且不会报错。

用工具生成的extensions
1 <behaviorExtensions>
2         <add name="validation" type="Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF.ValidationElement, Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
3       </behaviorExtensions>

 

posted @ 2011-12-29 18:38 神八 阅读(93) 评论(0) 编辑
摘要: WCF中对方法参数的校验实现方式阅读全文
posted @ 2011-12-28 21:55 神八 阅读(113) 评论(0) 编辑
摘要: 昨天在调试WCF服务时,碰到个异常,异常信息都让人看不懂。之前也没碰到过,折腾了一会才弄明白原因。 像往常一样创建一个WCF库,就是可直接调试的那种WCF服务。然后加入了一个服务,该服务还用到了复杂自定义类型作为DataContract,并像往常一样启动调试,居然蹦出了一个错误提示框,里面有一些错误信息,命名空间“TestNamespace.System”中不存在类型或命名空间名称“Runtime”(是缺少程序集引用吗?),如下图:其中TestNamespace.System命名空间下是复杂类型所在的命名空间。出现这样的错误,一时让人毫无头绪。为什么回去自定义类型中去寻找Runtime和...阅读全文
posted @ 2011-12-20 09:48 神八 阅读(100) 评论(0) 编辑
摘要: 最近在做一个搜索程序的优化改进,将搜索结果按照查询的参数不同进行缓存。缓存的Key很自然的就想到了用查询字符串,而获取查询字符串的最简单方式是通过Request.QueryString.ToString()方法。查看了QueryString的定义类型是NameValueCollection,就误以为这是NameValueCollection的重写了ToString()的方法,于是放心地将代码转移到了业务逻辑层。因为还要重构查询参数,因此重新构建了一个NameValueCollection,并想当然地用ToString()的结果作为Key。但实际运行之后发现,每次的结果都一样的,都是第一次的..阅读全文
posted @ 2011-12-08 23:04 神八 阅读(176) 评论(0) 编辑
摘要: const和readonly关键字也是面试中经常考到的问题,通常都是用来表示一个不可变的变量成员,那么具体区别是什么?从用法上说,const只能以inline代码的形式定义,而readonly既可以以inline代码形式定义也可以通过构造方法定义。CLR中定义,readonly的变量只能在构造方法中赋值,而C#中inline代码实际上是构造方法调用的一部分,因此readonly的变量可以以inline的方式赋值。以上是语法方面的应用,那在实际上的用法上,还是有些微妙的变化,通常不易发觉,请看下面的代码在程序集ConstLib.dll中有一个类MyClass,定义了一个公开的静态变量MaxCou阅读全文
posted @ 2011-11-29 21:04 神八 阅读(251) 评论(0) 编辑