ASP.NET ViewState初探

话说在新公司上班快2周了,这里的工作和业务还是有些意思和挑战的。这几天1直在重构公司之前的一个项目,出于保密协议这里不能提项目名称,作为对新人的关爱,我仅负责其中1个模块的1个页面,这个页面的性能有些问题,当大批量进行数据修改的时候,提交和响应会非常的慢,我需要做的是就是改善它的性能。

花了一天时间分析了这个页面的有关代码,实在很头痛,大量的服务端控件、大量的事件、大量的重复代码,发现问题的关键在于该页大量的服务端控件(GridView嵌套大量DropDownList、TextBox、CheckBox)及大量的迭代查询。

于是狠心下来进行重构,越深入发现问题越多,根本无法重构,除非整个模块和某些底层推倒重来,但是工时将纠人心窝(明早开会把情况说明PM估计要跳的了)。

这个项目通过微软传统的WebForm Code Behind编程模型,大量使用事件和服务端控件,说到服务端控件,有个很重要的属性EnableViewState,它保持着页面回转的状态,借这次Code Review的机会,将Asp.Net ViewState进行一次复习。

ASP.NET ViewState 初探


ViewState 到底是什么

与刚接触 ASP.NET 页面的开发人员交谈时,他们通常向我提出的第一个问题就是:“那个 ViewState 到底是什么?”他们的语气中流露出的那种感觉,就象我来到一家异国情调的餐馆,侍者端上一道我从未见过的菜肴时的那种感觉 —— 既疑惑不解,又充满好奇。但肯定有人认为它不错,否则就不会提供了。所以,我会先尝一尝,或许会喜欢上它,尽管它看上去的确很古怪!
对于 ViewState 也是如此,但是如果适应了它的风格,您会发现在许多情况下,您将乐于在自己的 ASP.NET 应用程序中使用 ViewState,因为它可以帮助您使用更少的代码完成更多的工作。但是,有时也会对 ViewState 完全弃之不用。下面我们就这两种情况分别进行阐述,不过,让我们先回答什么是 ViewState 这个问题。
答案:ViewState 用于维护页面的 UI 状态
Web 是没有状态的,ASP.NET 页面也没有状态,它们在到服务器的每个往返过程中被实例化、执行、呈现和处理。作为 Web 开发人员,您可以使用众所周知的技术(如以会话状态将状态存储在服务器上,或将页面回传到自身)来添加状态。下面我们以图 1 中的注册窗体为例进行论述。

Continue reading “ASP.NET ViewState初探”

多线程应用中使用静态方法是否有线程安全问题

类的成员分为两类,静态成员(static member)和实例成员(instance member)。静态成员属于类,实例成员则属于对象,即类的实例。

简单讨论一下在一个类中使用静态字段(static field)和静态方法(static method)是否会有线程安全问题。

我们知道, 静态字段(static field)和静态方法(static method)的调用是通过类来调用。静态方法不对特定的实例操作。实例方法可对特定的实例操作,既能访问静态成员,也能访问实例成员。

那么,在多线程中使用静态方法是否有线程安全问题?这要看静态方法是是引起线程安全问题要看在静态方法中是否使用了静态成员。

因为,在多线程中使用同一个静态方法时,每个线程使用各自的实例字段(instance field)的副本,而共享一个静态字段(static field)。所以说,如果该静态方法不去操作一个静态成员,只在方法内部使用实例字段(instance field),不会引起安全性问题。但是,如果该静态方法操作了一个静态字段,则需要静态方法中采用互斥访问的方式进行安全处理。

静态变量前加volitile,静态函数里面使用Lock控制线程安全。

对于ASP.NET Web应用程序, 多个客户端访问服务端, 就是一个多线程的例子。

C#运算符关键字 – AS运算符

从这一篇开始,我将为大家带来一些C#运算符关键字,我们时常会用到它们,但是可能只知其然不知其所以然,所以整理出来,供大家参考:

as 运算符用于在兼容的引用类型之间执行某些类型的转换。 例如:

 class csrefKeywordsOperators
   {
       class Base
       {
           public override string  ToString()
           {
	             return "Base";
           }
       }
       class Derived : Base 
       { }

       class Program
       {
           static void Main()
           {

               Derived d = new Derived();

               Base b = d as Base;
               if (b != null)
               {
                   Console.WriteLine(b.ToString());
               }

           }
       }
   }

备注


as 运算符类似于强制转换操作。 但是,如果无法进行转换,则 as 返回 null 而非引发异常。 请看下面的表达式:

expression as type

它等效于以下表达式,但只计算一次 expression。

expression is type ? (type)expression : (type)null

注意,as 运算符只执行引用转换和装箱转换。 as 运算符无法执行其他转换,如用户定义的转换,这类转换应使用强制转换表达式来执行。

 

class ClassA { }
class ClassB { }

class MainClass
{
    static void Main()
    {
        object[] objArray = new object[6];
        objArray[0] = new ClassA();
        objArray[1] = new ClassB();
        objArray[2] = "hello";
        objArray[3] = 123;
        objArray[4] = 123.4;
        objArray[5] = null;

        for (int i = 0; i < objArray.Length; ++i)
        {
            string s = objArray[i] as string;
            Console.Write("{0}:", i);
            if (s != null)
            {
                Console.WriteLine("'" + s + "'");
            }
            else
            {
                Console.WriteLine("not a string");
            }
        }
    }
}
/*
Output:
0:not a string
1:not a string
2:'hello'
3:not a string
4:not a string
5:not a string
*/


在.Net中检查Sql注入

SQL 注入是一种攻击方式,在这种攻击方式中,恶意代码被插入到字符串中,然后将该字符串传递到 SQL Server 的实例以进行分析和执行。任何构成 SQL 语句的过程都应进行注入漏洞检查,因为 SQL Server 将执行其接收到的所有语法有效的查询。一个有经验的、坚定的攻击者甚至可以操作参数化数据。

SQL 注入的主要形式包括直接将代码插入到与 SQL 命令串联在一起并使其得以执行的用户输入变量。一种间接的攻击会将恶意代码注入要在表中存储或作为元数据存储的字符串。在存储的字符串随后串连到一个动态 SQL 命令中时,将执行该恶意代码。

注入过程的工作方式是提前终止文本字符串,然后追加一个新的命令。由于插入的命令可能在执行前追加其他字符串,因此攻击者将用注释标记“–”来终止注入的字符串。执行时,此后的文本将被忽略。

Continue reading “在.Net中检查Sql注入”

SQL注入

所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击.

目录

  • sql注入
  • 注入大致方法
  • 常用sql注入语句

sql注入

什么时候最易受到sql注入攻击

当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。相关的SQL注入可以通过测试工具pangolin进行。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码,根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。

Continue reading “SQL注入”

设计模式:单件模式(Singleton Pattern)

概述

Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点。这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任。

从另一个角度来说,Singleton模式其实也是一种职责型模式。因为我们创建了一个对象,这个对象扮演了独一无二的角色,在这个单独的对象实例中,它集中了它所属类的所有权力,同时它也肩负了行使这种权力的职责!

意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

模型图

逻辑模型图:

物理模型图:

生活中的例子

美国总统的职位是Singleton,美国宪法规定了总统的选举,任期以及继任的顺序。这样,在任何时刻只能由一个现任的总统。无论现任总统的身份为何,其头衔”美利坚合众国总统”是访问这个职位的人的一个全局的访问点。

五种实现

1.简单实现

public sealed class Singleton
{
    static Singleton instance=null;

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。实际上在上述代码中,有可能在计算出表达式的值之前,对象实例已经被创建,但是内存模型并不能保证对象实例在第二个线程创建之前被发现。

该实现方式主要有两个优点:

  • 由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能(例如,对子类进行实例化),即使它可能引入不想要的依赖性。
  • 直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton

2.安全的线程

public sealed class Singleton
{
    static Singleton instance=null;
    static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance==null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}
 这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时(instence == null)为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。

3.双重锁定

public sealed class Singleton
{
    static Singleton instance=null;
    static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                lock (padlock)
                {
                    if (instance==null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁,有了我们上面第一部分的里面的分析,我们知道,加锁后还得再进行对象是否已被创建的判断。它解决了线程并发问题,同时避免在每个 Instance 属性方法的调用中都出现独占锁定。它还允许您将实例化延迟到第一次访问对象时发生。实际上,应用程序很少需要这种类型的实现。大多数情况下我们会用静态初始化。这种方式仍然有很多缺点:无法实现延迟初始化。

4.静态初始化

public sealed class Singleton
{
    static readonly Singleton instance=new Singleton();

    static Singleton()
    {
    }

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

看到上面这段富有戏剧性的代码,我们可能会产生怀疑,这还是Singleton模式吗?在此实现中,将在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化。该类标记为sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。

该实现与前面的示例类似,不同之处在于它依赖公共语言运行库来初始化变量。它仍然可以用来解决 Singleton 模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。

由于 Singleton 实例被私有静态成员变量引用,因此在类首次被对 Instance 属性的调用所引用之前,不会发生实例化。

这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。在 Design Patterns 形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此解决方案中由 .NET Framework 负责执行初始化,因此您没有这些选项。在大多数情况下,静态初始化是在 .NET 中实现 Singleton 的首选方法。

5.延迟初始化

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

这里,初始化工作有Nested类的一个静态成员来完成,这样就实现了延迟初始化,并具有很多的优势,是值得推荐的一种实现方式。

实现要点

  • Singleton模式是限制而不是改进类的创建。
  • Singleton类中的实例构造器可以设置为Protected以允许子类派生。
  • Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
  • Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。
  • Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。
  • 理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
  • 可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。

优点

  • 实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
  • 灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程

缺点

  • 开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。
  • 可能的开发混淆:使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
  • 对象的生存期:Singleton 不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于 .NET Framework 的语言),只有 Singleton 类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致 Singleton 类中出现悬浮引用。

适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

应用场景

  • 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。(摘自吕震宇的C#设计模式(7)-Singleton Pattern
  •  PC机中可能有几个串口,但只能有一个COM1口的实例。
  • 系统中只能有一个窗口管理器。
  • .NET Remoting中服务器激活对象中的Sigleton对象,确保所有的客户程序的请求都只有一个实例来处理。

完整示例

这是一个简单的计数器例子,四个线程同时进行计数。

 

using System; 
using System.Threading; 

namespace SigletonPattern.SigletonCounter 
{ 
    /// <summary> 
    /// 功能:简单计数器的单件模式 
    /// 编写:Terrylee 
    /// 日期:2005年12月06日 
    /// </summary> 
    public class CountSigleton 
    { 
        ///存储唯一的实例 
        static CountSigleton uniCounter = new CountSigleton();   

        ///存储计数值 
        private int totNum = 0;   

        private CountSigleton()  

        {  
            ///线程延迟2000毫秒 
            Thread.Sleep(2000); 
        }  

        static public CountSigleton Instance()  

        {  

            return uniCounter;  

        }  

        ///计数加1 
        public void Add() 
        {  
            totNum ++; 
        }   

        ///获得当前计数值 
        public int GetCounter() 
        {  
            return totNum; 
        }  

    } 
}
using System;
using System.Threading;
using System.Text;

namespace SigletonPattern.SigletonCounter
{
    /// <summary>
    /// 功能:创建一个多线程计数的类
    /// 编写:Terrylee
    /// 日期:2005年12月06日
    /// </summary>
    public class CountMutilThread
    {
        public CountMutilThread()
        {

        }

        /// <summary>
        /// 线程工作
        /// </summary>
        public static void DoSomeWork()
        {
            ///构造显示字符串
            string results = "";

            ///创建一个Sigleton实例
            CountSigleton MyCounter = CountSigleton.Instance();

            ///循环调用四次
            for(int i=1;i<5;i++)
            {
                ///开始计数
                MyCounter.Add();

                results +="线程";
                results += Thread.CurrentThread.Name.ToString() + "——〉";
                results += "当前的计数:";
                results += MyCounter.GetCounter().ToString();
                results += "\n";

                Console.WriteLine(results);

                ///清空显示字符串
                results = "";
            }
        }

        public void StartMain()
        {

            Thread thread0 = Thread.CurrentThread; 

            thread0.Name = "Thread 0"; 

            Thread thread1 =new Thread(new ThreadStart(DoSomeWork)); 

            thread1.Name = "Thread 1"; 

            Thread thread2 =new Thread(new ThreadStart(DoSomeWork)); 

            thread2.Name = "Thread 2"; 

            Thread thread3 =new Thread(new ThreadStart(DoSomeWork)); 

            thread3.Name = "Thread 3"; 

            thread1.Start(); 

            thread2.Start(); 

            thread3.Start(); 

            ///线程0也只执行和其他线程相同的工作
            DoSomeWork(); 
       }
    }
}
using System; 
using System.Text; 
using System.Threading; 

namespace SigletonPattern.SigletonCounter 
{ 
    /// <summary> 
    /// 功能:实现多线程计数器的客户端 
    /// 编写:Terrylee 
    /// 日期:2005年12月06日 
    /// </summary> 
    public class CountClient 
    { 
       public static void Main(string[] args) 
       { 
       CountMutilThread cmt = new CountMutilThread(); 

            cmt.StartMain(); 

            Console.ReadLine(); 
        } 
    } 
}

总结

Singleton设计模式是一个非常有用的机制,可用于在面向对象的应用程序中提供单个访问点。文中通过五种实现方式的比较和一个完整的示例,完成了对Singleton模式的一个总结和探索。用一句广告词来概括Singleton模式就是“简约而不简单”。