好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ASP.NET的一些坑

ASP.NET的一些坑

ASP.NET的一些坑

阅读目录

开始 HttpContext.Current并非无处不在 Application_Start的异常与IIS经典模式 QueryString,Form允许重复的KEY ashx的重用问题 当前登录用户信息有时获取不到 Timer可能会不起作用 Session与复杂数据类型 DateTime的JSON序列化 招聘信息

前段时间碰到一个问题:为什么在ASP.NET程序中定时器有时候会不工作?

这个问题看起来很奇怪,代码好像也没错,但就是结果与预期不一致。
其实这里是ASP.NET程序的一个陷阱,我习惯说成坑。 后来想想,其实ASP.NET的坑何止这一个,我今天就把我能想到的各种坑都写出来,希望大家小心这些问题。

想到我以前的博客中也零散的说过了一些坑,所以这篇博客中也把它们列出来了, 不过,对于以前谈过的内容,这里将只会简略地说明。

回到顶部

HttpContext.Current并非无处不在

这个问题是我上个月的博客中提到的问题, 原文链接: http://www.cnblogs.com/fish-li/archive/2013/04/06/3002940.html

在以下情形中访问HttpContext.Current将会返回null
1. 定时器的回调。
2. Cache的移除通知。
3. APM模式下异步完成回调。
4. 主动创建线程或者将任务交给线程池来执行。

所以,在写类库时,请注意这个问题。

回到顶部

Application_Start的异常与IIS经典模式

在IIS6或者II7的经典模式下运行ASP.NET程序时,如果Application_Start事件中抛出了未捕获异常, 那么  这个异常将显示一次。

关于这个问题的更多细节介绍请点击: http://www.cnblogs.com/fish-li/archive/2013/03/24/2979780.html

回到顶部

QueryString,Form允许重复的KEY

我们经常见到的集合,例如:Hashtable, Dictionary,它们都要求KEY是唯一的,然而, HttpRequest的QueryString,Form集合实例却  允许KEY重复 ,当遇到KEY重复时,通过索引器访问集合时,  会将KEY对应的所有元素值用逗号拼接起来。

为什么会这样,因为这二个集合的类型是NameValueCollection,类似的,Headers集合也是这样。

由于这个特殊性与我们常见的情形不一致,所以我们需要注意这个差别,当然了,有些时候我们还可以利用这个行为实现一些特殊的需求, 关于这个细节的更多介绍请参考: http://www.cnblogs.com/fish-li/archive/2011/12/06/2278463.html , 在这篇博客中,还介绍了HttpRequest的二个索引器也是值得我们注意的。

回到顶部

ashx的重用问题

很多ASP.NET的开发人员都应该创建过ashx文件,例如下面这个:

 public class   Handler1  :  IHttpHandler  {
     public bool  IsReusable {
         get  {
             return false ;
        }
    }

我想不少人会对IsReusable这个属性感到好奇,于是去查一下IHttpHandler的定义,找到这个解释,

 // 摘要:
//     获取一个值,该值指示其他请求是否可以使用 System.Web.IHttpHandler 实例。
//
// 返回结果:
//     如果 System.Web.IHttpHandler 实例可再次使用,则为 true;否则为 false。
  bool  IsReusable {  get ; }

看到可以重用,有些对性能关注的人可能会将它修改为返回true, 其实改成什么都一样,因为它不起作用。

不起作用的原因在这篇博客中有说明: http://www.cnblogs.com/fish-li/archive/2012/01/29/2331477.html

回到顶部

当前登录用户信息有时获取不到

在ASP.NET中,提供了以下方法让我们获取当前用户的信息,例如:

 if (  HttpContext  . Current  !=   null  ) {

     // 检查当前用户是否已为一个已登录用户
      bool  isAuthenticated  =   HttpContext  . Current . Request . IsAuthenticated;

     // 获取当前请求的用户名
      string  userName  =   HttpContext  . Current . User . Identity . Name;
}

不过,这段代码放在不同的地方,效果却截然不同。

最近就遇到一个问题:有人问我为什么总是取不当前用户的用户名。
网站采用的是Windows身份认证,因此,所有的请求都是经过IIS认证过的, 理论上说,变量isAuthenticated应该返回true,而userName应该是当前请求的用户名(Windows登录名),然而呢,在调试时, isAuthenticated的值是false, 后面的代码直接抛出一个空引用异常,因为User对象为null,太奇怪了,是吗?

当出现这种情况时,我们应该检查代码在哪里被调用的。
结果在我的追问下,发现代码是在一个HttpModule中调用的,且发生在订阅HttpApplication的BeginRequest事件中。 找到这里,原因也就找到了, 此时(在这个阶段)还没有经过ASP.NET的身份认证检查, User对象对象根本就没有构造出来, 现在去访问,当然取不到结果。

凭良心说,这个还真算不上ASP.NET留给我们的坑,只怪一些人对管线事件不了解。

回到顶部

Timer可能会不起作用

有时候我们会遇到一些诸如执行定时任务的需求,于是有些人可能会想到用定时器来实现, 在.net framework中,有二个Timer类型可以用于ASP.NET环境中,不过,Timer有可能会不起作用, 具体表现情况也会让你难以描述:不知道在什么时候定时器就停止工作了。

这个问题很奇怪:当你在调试模式下,定时器是一直能正常工作的,但当你把网站部署起来, 运行时间久一些,便会发现定时器没有正常工作。

为什么会这样呢?
答案是: 当网站在一段时间没有请求后,进程会被IIS回收(释放)。
所以,在ASP.NET程序不适合执行【长久性】的定时任务,除非你能接受定时器会停止工作。
类似的问题还有:在ASP.NET程序中将某个方法做为回调方法传给Win32程序,发现回调没有响应。

正是由于这个原因,建议将长久性的定时任务或者接收Win32回调的程序用Windows Service的程序来实现

回到顶部

Session与复杂数据类型

Session有三种工作模式,拿ASPX页面来说,EnableSessionState指令有三个可选值:true, false, ReadOnly

EnableSessionState="false",这个容易理解:不使用Session。

EnableSessionState="ReadOnly",从字面上来说,就是Session是只读的。
只读的控件不允许用户修改,然而Session的只读模式是说:你可以改,但我不保存你的修改。 这样理解没有问题吧。

EnableSessionState="true",表示Session支持可读可写。
当你更新了Session的内容之后,当前会话的所有Session数据将会被重新保存。

进程内Session容易丢失,且不支持多台Web服务器共享数据,因此选择这种保存方法的人不多, 大多数人会选择状态服务或者SQL Server来保存,那么这里就有一个问题需要关注了: 当Session模式是EnableSessionState="true"时,如果你访问了一个复杂对象(不是系统值类型也不是字符串), 不管你有没有修改它,Session的保存操作都会执行。 对于进程外Session,保存操作意味着需要执行序列化,还可以会有网络传输的开销,它们会影响性能。

如果上面的描述不容易理解的话,请看下面的示例代码:

 string  sessionValue  =  Session[ "s2" ]  as string ;
 if ( sessionValue  ==   null  ) {
    Session[ "s2" ]  =   "Fish Li" ;
    sessionValue  =  Session[ "s2" ]  as string ;
}

当这个页面首次运行时,Session被修改了,因此会有保存的操作发生。但是后面的访问就不会有保存的动作。

再看另一段代码:

 // TestData是一个自定义类型。
  TestData  sessionValue  =  Session[ "s1" ]  as   TestData ;
 if ( sessionValue  ==   null  ) {
    Session[ "s1" ]  =   new   TestData  { IntValue  =   5 , StrValue  =   "Fish Li"  };
    sessionValue  =  Session[ "s1" ]  as   TestData ;
}

此时每次执行这段代码时,都会有保存操作发生(只要是EnableSessionState="true")。

我再重申一遍:这个问题只有当EnableSessionState="true",且访问复杂对象(不是系统值类型也不是字符串)才会发生。 对于进程内Session来说,这个问题的影响不大,但是对于进程外Session来说,会对性能产生一些影响, 到底有多大的影响,要根据Session的数据量以及用户的并发度来决定。

列出这个问题只是想告诉大家:如果你确实需要使用Session,请尽量在Session中保存【不可变】的简单数据, 尤其是不要保持Session的默认设置(EnableSessionState="true")。

检验这个问题的方法是:实现一个自定义的SessionStateStoreProviderBase派生类,然后调试观察。 SessionStateItemCollection的二个索引器也会给你一个答案。

回到顶部

DateTime的JSON序列化

在SP.NET3.5中,微软为ASP.NET为设计了一个JSON序列化的工具类, System.Web.Script.Serialization.JavaScriptSerializer,这个类的使用很广泛,而且比WCF的那个JSON序列化类的兼容性要好。 不过,这个类有一个问题,在序列化DataTime类型时,它生成的结果会让所有人感觉别扭, 其实序列化的结果表现形式还个小问题,在前端写个转换函数就能解决, 然而,如果你需要  用序列化和反序列化的方法来做对象的持久化 ,就会遇到问题,例如下面的代码:

 DateTime  dt1  =   DateTime  . Now;
 JavaScriptSerializer  jss  =   new   JavaScriptSerializer ();

 string  json  =  jss . Serialize(dt1);
 DateTime  dt2  =  jss . Deserialize <  DateTime  > (json);

context . Response . Write(dt1  ==  dt2);

浏览器显示的结果会让人感到很意外,竟然是:False

出现这个原因与JavaScript的时间格式有关,它使用了UTC时间, 不过,这个理由让人感到难以接受,毕竟其它的反序列化都能还原对象,例如二进制序列化和XML都能正确的还原对象。 没办法,这里只能算是个坑了,所以,如果你要做对象的持久化操作,尽量不要选择JSON序列化。

回到顶部

招聘信息

我所在的部门(平台开发部),现需要若干名 .net 方面的高级开发人员,要求熟悉以下技术领域:
1. .net framework
2. ASP.NET
3. SQL SERVER (T-SQL, SP)
4. JavaScript, jQuery
5. CSS
6. 常见的设计模式。

说明:
1. 公司名称: 明源软件  
2. 工作地点: 武汉 。
3. 关于招聘相关的具体细节请发邮件给我: liqifeng0503@163.com  
4. 咨询招聘相关的疑问也请发邮件给我,评论中的疑问一律不回复!

如果你希望与 Fish Li 一起并肩奋斗,就给我发邮件吧。

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的 【 推荐 】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下右下角的 【 关注 Fish Li 】。
因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是Fish Li 。

 

 

分类:  Asp.net

亲们,拿到DateTime.Now你是否也是这样比较的?

 

项目中有了 找回密码 功能,我很高兴是由我做的,嘿嘿。

测试了很多遍没有问题。结果还是无意中被别人发现了,“ 数据库 中的 失效时间(ExpiredDate) 与 当前时间(DateTime.Now)  存在着  4 分多钟  的差距 ”,我一直是取的 DateTime.Now  当前时间比较的啊!

首先我们看看,下面的这行代码,

  var  modelCode = LoadRepository.GetEntities<Entity.Verification_Code>().Where(i => i.Code.Equals(code) && i.ExpiredDate >=   DateTime.Now  ).FirstOrDefault();

根据Sql Server Profiler查看SQL,有了惊人的发现:=》 [Extent1].[ExpiredDate] >= GetDate() :

也许你认为可能没有什么问题,但是我看了看查询分析器解析后的Sql,拿到的Sql中的DateTime.Now 却变成了GetDate(),这什么情况;

exec sp_executesql N '  SELECT TOP (1)  
 [Extent1].[VerificationID] AS [VerificationID], 
[Extent1].[UserID] AS [UserID], 
[Extent1].[Code] AS [Code], 
[Extent1].[AddDate] AS [AddDate], 
[Extent1].[ExpiredDate] AS [ExpiredDate], 
[Extent1].[Status] AS [Status]
FROM [dbo].[Verification_Code] AS [Extent1]
WHERE ([Extent1].[Code]  = @p__linq__0) AND ([Extent1].[ExpiredDate] >= (GetDate()))
' ,N ' @p__linq__0 varchar( 8000 ) ' ,
@p__linq__0= ' f784222508ce4e859426fd0c8b95df8f '

于是我试了试 在 select getdate(); 发现真的 getdate() 与 当前时间存在着误差

我可能明白了, DateTime.Now 放在Where中不能进行比较。因为Linq只能与定值进行比较。所以先要转成值。

修改如下:

   var    dateTime   =    DateTime.Now  ;
    var  modelCode = LoadRepository.GetEntities<Entity.Verification_Code>().Where(i => i.Code.Equals(code) && i.ExpiredDate >=  dateTime ).FirstOrDefault();

根据Sql Server Profiler查看SQL,问题解决了,DateTime.Now回来了。 @p__linq__1= ' 2013 - 05 - 28   15 : 25 : 37.8470000 '

exec sp_executesql N '  SELECT TOP (1)  
 [Extent1].[VerificationID] AS [VerificationID], 
[Extent1].[UserID] AS [UserID], 
[Extent1].[Code] AS [Code], 
[Extent1].[AddDate] AS [AddDate], 
[Extent1].[ExpiredDate] AS [ExpiredDate], 
[Extent1].[Status] AS [Status]
FROM [dbo].[Verification_Code] AS [Extent1]
WHERE ([Extent1].[Code]  = @p__linq__0) AND ([Extent1].[ExpiredDate] >= @p__linq__1) '  ,
N ' @p__linq__0 varchar( 8000 ),@p__linq__1 datetime ' ,
@p__linq__0= ' f784222508ce4e859426fd0c8b95df8f ' ,
@p__linq__1= ' 2013 - 05 - 28 15 : 25 : 37.8470000 '

总结: 希望亲们不要像我这样菜,也可能这个错误只有我一个人犯,细节真的让人觉得学习是很快乐的一件事!

在寂寞的日子里沉淀自己,在程序的日子里找到自己,我为梦想而坚持!

作者: Leo_wl

    

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

    

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

版权信息

查看更多关于ASP.NET的一些坑的详细内容...

  阅读:52次