[C#]ASP.NET MVC Training Kit发布了

mikel阅读(731)

Scott Guthrie昨天宣布了ASP.NET MVC 按照Ms-PL协议开源发布,具体内容参见ASP.NET MVC 1.0 has been released,也可以参看Scott Hanselman的新闻稿Microsoft ASP.NET MVC 1.0 is now Open Source MS-PL

微软同时也推出了一套ASP.NET MVC frameworke培训教材, 其中包含了许多范例程序、PPT、实验演练(Labs)教材等,想学习新技术的人不用在等了,赶快下载回来学习吧。

下面是主要内容截图:

image

 image

作者: 自由、创新、研究、探索……
出处:http://shanyou.cnblogs.com/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
个人网站: http://www.openbeta.cn

[Flash]flash跨域名动态加载数据

mikel阅读(753)

    以前开发的一个页面功能是由flash实现的,考虑到用户体验的连续性,flash与后台的交互不能通过普通的getURL的方式实现(否则每次交互都会导致页面刷新,用户体验很差),而必须用AS里面的LoadVars类来实现动态加载。
    通常活动开发的相关flash会放在html/…/promote目录下面(对应promote域名),而cgi会放在cgi-bin /service目录下面(对应service域名)。结果发现这个flash总是加载不到cgi输出的信息。把cgi移到cgi-bin /promote目录(对应promote域名),flash就可以加载到cgi的输出信息。看apache日志也发现:cgi放在promote目录, 就有HTTP请求;cgi放到service目录,则flash居然连访问cgi的请求包都没发送。很奇怪。
    通过HTTP Watch仔细对比两种情况下的请求,发现后者多了下面这个请求:
00:00:00.231 0.525 521 GET 404 text/html: charset=iso-88…  http://service.paipai.com/crossdomain.xml
    看看名字,估计就是flash对跨域加载数据作了安全性限制。baidu一番以后,在html/…/service目录添加crossdomain.xml文件,内容如下:
<?xml version="1.0"?>
<cross-domain-policy>
  <allow-access-from domain="*.paipai.com" />
</cross-domain-policy>
    这个xml文件的格式很简单,就不多解释了。加了这个文件以后,LoadVars类就可以从paipai.com的任何一个子域名访问到service域名的cgi了。
    如果不希望所有cgi都能随便访问,那么需要再作精细化配置,诸位可自行研究。

[Flex]AIR APIs详解 - 文件访问系统 part6

mikel阅读(883)

连载索引:
AIR APIs详解 – 文件访问系统 part1
AIR APIs详解 – 文件访问系统 part2
AIR APIs详解 – 文件访问系统 part3
AIR APIs详解 – 文件访问系统 part4
AIR APIs详解 – 文件访问系统 part5
Part6:
是 时候讨论一下同步和异步的问题了。同步(synchronous)和异步(asynchronous)编程不单单是AIR开发中才会遇到的问题,而是所有 编程中都很重要的一部分。往通俗了说,同步已经可以解决很多问题,但是同步解决不了特定环境下的问题。而异步处理用好了就是幸福,用不好就是灾难。单纯的 从功能角度而言,只要逻辑执行效率够高,速度够快(我们期待的快),很多问题用同步就可以解决,比如文件的操作。但是事实不是这样,我们AIR APIs连载的前五篇中的范例,全部是同步操作,这是基于这些范例的操作逻辑足够简单,操作的目标对象被处理的速度非常快,对于使用Flex(Flash Player单线程机制)而言,处于文件(夹)操作之后的逻辑完全可以等待这些小型的文件操作完成再顺序执行,这就是同步,后面的操作要等前面的逻辑执行 完毕后才会执行。但是如果我们操作的是一个1GB或者更大的文件目录体系呢?用同步的方式,后面的逻辑都要等这个操作完成,才能开始执行,显然这个不是同 步可以解决的问题。这时候需要在AIR里引入异步处理,很高兴,Adobe在AIR的很多APIs操作中都有内置的异步方式支持。
异步操作的最大 特点是没有必要非要在一个逻辑执行完毕后才去执行后续的逻辑,这个逻辑初次听起来还有点困惑,但是AIR中异步的实用价值在于当一个操作花费很长时间的时 候,后面的其他逻辑没必要等待,比如大文件夹的浏览和操作,大数据量文件的读写。不仅仅在AIR中,连平时Flash操作XML文件都可能要根据网络速度 和XML数据的大小来决定是否正确的使用异步操作。我们现在就来看一个异步执行语句逻辑的AS范例(你可以贴在Fla文件的timeline的第一帧上执 行,确认你的sandbox在publish setting是local file access only):

//命名一个URLLoader对象
var myloader:URLLoader;
myloader=new URLLoader();
myloader.addEventListener(Event.COMPLETE,completeLoadingHandler);
//从我的站点可以下载rss.xml,就在首页部分右下角的xml logo上,将rss.xml存储到与swf同一目录
myloader.load(new URLRequest("rss.xml"));
trace(myloader.data);
private function completeLoadingHandler(event:Event):void{
  trace(myloader.data);
}

上 面的代码你在执行后,会发现第一个trace语句的执行永远都是在myloader.load之前,而且是undefined,而真正有load数据的则 是completeLoadingHandler里面的trace部分,这就说明URLLoader类的load()方法本身就是异步的,所以,第一个 trace语句会先于load执行,所以结果是undefined。
那么使用同步和异步的规则很简单,当逻辑执行可以非常快的完成时,你就可以使用同步操作的方式了。而在AIR的文件访问API中,很多文件类的操作方法兼具同步和异步两个,具体使用哪个,可以根据情况而定,而今后的连载,你也将会看到有异步方法的操作了。
好了,part6就到这里。

[C#]蛙蛙推荐:简化基于数据库的DotNet应用程序开发

mikel阅读(808)

分析

  要做一个基于数据库的应用程序,我们有大量的重复劳动要去做,建表,写增删改查的SQL语句,写与数据库表对应的实体类,写执行SQLC#代码,写 添加、修改、列表、详细页面等等。这些活动都是围绕着一个个都数据表来开展的,在.NET领域有很多的OR Mapping的方案,但好多方案用起来好用,但原理很复杂,而且性能也不好把握,所以我们可以做一个轻型的ORM方案。有了ORM框架,根据数据表写 C#实体类这些劳动,其实也可以写一个代码生成器来帮我们生成,甚至代码生成器还能帮我们生成一些界面的代码。我们大概需要解决如下问题
1、我们要有一个通用的数据库操作帮助类,类似微软的DAAB,但最好能支持多种数据库;
2、我们要有一个使用简单的orm框架,能方便的用c#代码来进行数据库存取操作,而且要尽量保证性能,比如使用参数化查询;
3、我们要有一个代码生成器帮助我们解决一些重复性劳动,比如生成实体类,生成调用存储过程的c#代码等;

围绕这3个问题,我们一一来展开

一、通用的数据库吃操作帮助类

  ADO.NET 2.0为我们访问数据库提供了一套与具体数据库无关的模型,其核心类是DbProviderFactory,它遵循了Provider模式,就是把对各种 数据库的操作抽象出一个Provider,再由各种数据库去写与具体数据库相关的Provider,然后通过配置在运行时方便的切换数据库,而尽量少的不 修改业务逻辑层的代码,业务逻辑层依赖的是抽象的Provider。这也是典型的依赖倒置,就是说业务逻辑说我需要哪些接口,我依赖这些接口,而让别人去 实现这些接口,在运行的时候再去加载调用实现这些接口的具体类。
  为了提高性能,减少SQLServer执行计划的重编译,我们尽量使用参数化的查询,而一个固定的语句或者存储过程它的ADO.NET参数是固定的, 所以我们可以把这些参数缓存起来,避免每次执行SQL语句都创新新的参数对象。另外oledb的ado.net provider的参数是不能命名的,所以给参数赋值要按顺序赋值。

  为了使用方便,我们为执行SQL语句提供如下的API

public System.Data.DataSet SqlExecuteDateSet(string sql, string[] paramters, params object[] values)
public System.Data.DataTable SqlExecuteDateTable(string sql, string[] paramters, params object[] values)
public int SqlExecuteNonQuery(string sql, string[] paramters, params object[] values)
public System.Data.Common.DbDataReader SqlExecuteReader(string sql, string[] paramters, params object[] values)
public object SqlExecuteScalar(string sql, string[] paramters, params object[] values)

  当然,为了支持存储过程的执行,以及数据库事务,还需要提供相关的重载的API。大概的使用示例(面向SQLServer)如下:

DbHelper dbhelper = new DbHelper();
string sql = "delete from Citys where CityId = @id";
using (DatabaseTrans trans = new DatabaseTrans(dbhelper))
{
    
try
    {
        dbhelper.SqlExecuteNonQuery(trans, sql, 
new string[] { "@id" }, 1);
        dbhelper.SqlExecuteNonQuery(trans, sql, 
new string[] { "@id" }, 2);
        trans.Commit();
        OutPut(
"ok");
    }
    
catch (Exception)
    {
        trans.RollBack();
        OutPut(
"no ok");
    }
}

 

二、通用的ORM框架

先看如下的代码

 

//1、添加
xxxCase xxxCase = new xxxCase();
xxxCase.Title 
= "abc";
xxxCase.Content 
= "呵呵";
xxxCase.CaseFrom 
= CaseFrom.客服投诉;
xxxCase.PostUser 
= "huhao";
xxxCase.CreateTime 
= DateTime.Now;
xxxCase.CaseType 
= CaseType.生产环境查询;
xxxCase.Priority 
= CasePriority.中;
xxxCase.ReleationServices 
= "aaa,bbb";
xxxCase.ReleationClient 
= "ccc,ddd";
EntityBase.Insert(xxxCase);
//2、修改
xxxCase.ClearInnerData();
xxxCase.CaseId 
= 1;
xxxCase.Title 
= "嘿嘿";
EntityBase.Update(xxxCase);
//3、删除
xxxCase.ClearInnerData();
xxxCase.CaseId 
= 1;
EntityBase.Delete(xxxCase);
//4、复杂条件查询,查询大于昨天的客服投诉或者wawa关闭的问题
WhereCondition condition = new WhereCondition(
    xxxCase.CaseFromColName,SqlOperator.Equal, (
short)CaseFrom.客服投诉)
    .And(
    
new WhereCondition(xxxCase.CreateTimeColName, SqlOperator.GreaterThan ,
        DateTime.Now.AddDays(
1)))
    .Group()
    .Or(
    
new WhereCondition(xxxCase.CloseUserColName, SqlOperator.Equal, "wawa"));
IList
<xxxCase> list = EntityBase.Select<xxxCase>(
    
new string[] {"Title""PostUser"}, condition);
foreach (xxxCase item in list)
{
    Console.WriteLine(
"{0}-{1}",item.Title,item.PostUser);
}
Console.ReadKey();

  上面的代码是以面向对象(请忽略那些关于贫血模型的讨论,说上面的代码不够OO,上面的代码至少相对的面向对象,而且看起来很直观)的方式去执 行一些业务,这应该比到处写SQL语句要强很多吧,而且如果这些操作内部使用的仍然是参数化查询而不是拼sql字符串的话,性能也不会很差(请忽略具体语 句是否能使用索引的讨论,那得具体分析)。

  我们看一下EntityBase.Insert方法的实现,逻辑很简单明了,其他的Update,Delete,Select也是类似的思路。

 

private static DbHelper _db = new DbHelper();
public static void Insert(EntityBase entity) {
    
string sql = GetInsertSql(entity);
    
string[] parameters = GetParameters(entity.InnerData);
    
object[] parameterValues = GetParameterValuess(entity.InnerData);
    _db.SqlExecuteNonQuery(sql, parameters, parameterValues);
}
private static string GetInsertSql(EntityBase entity) {
    
int len = entity.InnerData.Count;
    StringBuilder sql 
= new StringBuilder();
    sql.AppendFormat(
"Insert INTO [{0}]\r\n", entity.TableName);
    sql.Append(
"(\r\n");
    
for (int i = 0; i < len; i++) {
        
if (i != len  1)
            sql.AppendFormat(
"[{0}],", entity.InnerData[i].Key);
        
else
            sql.AppendFormat(
"[{0}]", entity.InnerData[i].Key);
    }
    sql.Append(
")\r\n");
    sql.Append(
"VALUES(\r\n");
    
for (int i = 0; i < len; i++) {
        
if (i != len  1)
            sql.AppendFormat(
"@{0},", entity.InnerData[i].Key);
        
else
            sql.AppendFormat(
"@{0}", entity.InnerData[i].Key);
    }
    sql.Append(
")\r\n");
    
return sql.ToString();
}
private static string[] GetParameters(IList<DbCommonClass<stringobject>> items) {
    
int len = items.Count;
    List
<string> parameters = new List<string>();
    
for (int i = 0; i < len; i++) {
        parameters.Add(
string.Format("@{0}", items[i].Key));
    }
    
return parameters.ToArray();
}
private static object[] GetParameterValuess(List<DbCommonClass<stringobject>> items) {
    
int len = items.Count;
    List
<object> parameters = new List<object>();
    
for (int i = 0; i < len; i++) {
        parameters.Add(items[i].Value);
    }
    
return parameters.ToArray();
}

当然Select方法稍微复杂一些,因为我们要考虑复杂的Where字句,Top字句,OrderBy字句等,我们为Where字句建立了一个 WhereCondition对象,来方便的用c#代码来描述SQL的where语句,但是为了实现简单,我们不去实现表连接,复杂的子语句等支持(我个 人认为向NBear等框架做的过于强大了)。

三、代码生成器

  ADO.NET的各种数据库实现都有获取某个数据库Schema的API,其中最重要的是 SqlConnection.GetSchema(SqlClientMetaDataCollectionNames.Tables)和 SqlCommand.ExecuteReader( CommandBehavior.KeyInfo | CommandBehavior.CloseConnection)方法,有了这两个方法,我们可以枚举一个数据库的所有表,及某个表的所有字段,及每个 字段的类型,长度、可否为空,是否为主键,是否为标识列等信息,有了这些元数据,我们再根据一个模板就可以生成特定格式的代码了。而且我们需要新增加一种 代码生成的格式的话,只需添加一个模板就可以了,这样的代码生成器还有扩展性,而不是一个写死的针对特定框架的代码生成器。
  为了脱离对特定数据库的依赖,我们建立一个代码生成器的元数据模型,如下

public class CodeModel
{
 
public string ClassName;
 
public string TableName;
 
public string Descript;
 
public string Namespace;
 
public string PkColName;
 
public List<CodeProperty> Properties;
}
public class CodeProperty
{
 
public string DbColName;
 
public int? DbLength;
 
public bool DbAllowNull
 
public SqlDbType DbType;
 
public string DbTypeStr;
 
public bool DbIsIdentity;
 
public bool DbIsPk;
 
 
public string Descript;
 
public string PropertyName;
 
public System.Type CSharpType;
 
public string CSharpTypeStr;
 
 
public bool UiAllowEmpty;
 
public bool UiIsShowOn;
 
public long? UiMaxCheck;
 
public long? UiMinCheck;
 
public string UiRegxCheck;
}

得到元数据后,剩下的就是读取模板,然后替换字符串了,比如实体类的模板,如下

using System;
using System.Collections.Generic;
using WawaSoft.Common;
namespace $model.namespace$ {
    
public class $model.classname$ : EntityBase {
$
foreach.prop$
        
public const string $prop.property$ColName = "$prop.dbcolname$";
$endforeach$    
        
private static readonly List<string> _Cols = new List<string>();
        
static $model.classname$()
        {            
$
foreach.prop$
            _Cols.Add($prop.property$ColName);
$endforeach$            
        }
        
public $model.classname$() {
            _tableName 
= "$model.tablename$";
            _PkName 
= "$model.pkcolname$";            
        }
$
foreach.prop$
        
private $prop.csharptype$ $prop.property2$;
$endforeach$
 
$
foreach.prop$
        
public $prop.csharptype$ $prop.property$ {
            
get { return $prop.property2$; }
            
set {
                $prop.property2$ 
= value;
                AddInnerData(
"$prop.property2$", value);
            }
        }
$endforeach$
        
protected override IList<string> Cols
        {
            
get { return _Cols; }
        }
        
public override void ConvertToEntity(IEnumerable<DbCommonClass<stringobject>> items) {
            
foreach (DbCommonClass<stringobject> item in items) {
                
switch (item.Key) {
$
foreach.prop$
                    
case $prop.property$ColName:
                        $prop.property2$ 
= ($prop.csharptype$)item.Value;
                        
break;
$endforeach$
                }
            }
        }
    }
}

生成的实体类,如下

using System;
using System.Collections.Generic;
using WawaSoft.Common;
namespace Entities {
    
public class User : EntityBase {
        
public const string UserIdColName = "UserId";
        
public const string UsernameColName = "Username";
        
public const string NameColName = "Name";
        
public const string PasswordColName = "Password";
        
public const string CreateTimeColName = "CreateTime";
        
public const string IsAdminColName = "IsAdmin";
        
private static readonly List<string> _Cols = new List<string>();
        
static User() {
            _Cols.Add(UserIdColName);
            _Cols.Add(UsernameColName);
            _Cols.Add(NameColName);
            _Cols.Add(PasswordColName);
            _Cols.Add(CreateTimeColName);
            _Cols.Add(IsAdminColName);
        }
        
public User() {
            _tableName 
= "User";
            _PkName 
= "UserId";
        }
        
private Nullable<Int32> userid;
        
private String username;
        
private String name;
        
private String password;
        
private Nullable<DateTime> createtime;
        
private Nullable<Boolean> isadmin;
        
public Nullable<Int32> UserId {
            
get { return userid; }
            
set {
                userid 
= value;
                AddInnerData(
"userid", value);
            }
        }
        
public String Username {
            
get { return username; }
            
set {
                username 
= value;
                AddInnerData(
"username", value);
            }
        }
        
public String Name {
            
get { return name; }
            
set {
                name 
= value;
                AddInnerData(
"name", value);
            }
        }
        
public String Password {
            
get { return password; }
            
set {
                password 
= value;
                AddInnerData(
"password", value);
            }
        }
        
public Nullable<DateTime> CreateTime {
            
get { return createtime; }
            
set {
                createtime 
= value;
                AddInnerData(
"createtime", value);
            }
        }
        
public Nullable<Boolean> IsAdmin {
            
get { return isadmin; }
            
set {
                isadmin 
= value;
                AddInnerData(
"isadmin", value);
            }
        }
        
protected override IList<string> Cols {
            
get { return _Cols; }
        }
        
public override void ConvertToEntity(IEnumerable<DbCommonClass<stringobject>> items) {
            
foreach (DbCommonClass<stringobject> item in items) {
                
switch (item.Key) {
                    
case UserIdColName:
                        userid 
= (Nullable<Int32>)item.Value;
                        
break;
                    
case UsernameColName:
                        username 
= (String)item.Value;
                        
break;
                    
case NameColName:
                        name 
= (String)item.Value;
                        
break;
                    
case PasswordColName:
                        password 
= (String)item.Value;
                        
break;
                    
case CreateTimeColName:
                        
if (item.Value != DBNull.Value)
                            createtime 
= (Nullable<DateTime>)item.Value;
                        
break;
                    
case IsAdminColName:
                        
if (item.Value != DBNull.Value)
                            isadmin 
= (Nullable<Boolean>)item.Value;
                        
break;
                }
            }
        }
    }
}

小结

解决了以上几个问题,再开发数据库应用,应该会提高不少效率。
相关代码下载:code_wawa.zip

[C#]生成器(抽象工厂加存储过程)

mikel阅读(713)

首先建立一个Exam解决方案

在添加下面的类库

打开生成器

填写好后  安生成(注意  是多线程的   速度很快)

 

点  打开文件夹  里面生成的文件夹对应下面的文件 复制到项目里面就可以了

我时间有限  只写了 增 、删、改、查。  自己可以扩展  注释都写好了的

下面是生成的存储过程

下载地址:/Files/Evans/生成器r.rar

[C#]无缝的缓存读取:双存储缓存策略

mikel阅读(795)

最近在做一个WEB的数据统计的优化,但是由于数据量大,执行一次SQL统计要比较长的时间(一般700ms算是正常)。

正常的做法只要加个缓存就好了。

但是同时业务要求此数据最多1分钟就要更新,而且这一分种内数据可能会有较多变化(而且原系统不太易扩展)。

也就是说缓存1分钟就要失效重新统计,而且用户访问这页还很是频繁,如果使用一般缓存那么用户体验很差而且很容易造成超时。

 

看到以上需求,第一个进入我大脑的就是从前做游戏时接触到的DDraw的双缓冲显示方式。

image

在第一帧显示的同时,正在计算第二帧,这样读取和计算就可以分开了,也就避免了读取时计算,提高了用户体验。

我想当然我们也可以将这种方式用于缓存的策略中,但这样用空间换取时间的方式还是得权衡的,因为并不是所有时候都值得这么做,但这里我觉得这样做应该是最好的方式了。

注:为了可以好好演示,本篇中的缓存都以IEnumerable的形式来存储,当然这个文中原理也可以应用在WebCache中。

这里我使用以下数据结构做为存储单元:

namespace CHCache {
/// <summary>
/// 缓存介质
/// </summary>
public class Medium {
/// <summary>
/// 主要存储介质
/// </summary>
public object Primary { get; set; }
/// <summary>
/// 次要存储介质
/// </summary>
public object Secondary { get; set; }
/// <summary>
/// 是否正在使用主要存储
/// </summary>
public bool IsPrimary { get; set; }
/// <summary>
/// 是否正在更新
/// </summary>
public bool IsUpdating { get; set; }
/// <summary>
/// 是否更新完成
/// </summary>
public bool IsUpdated { get; set; }
}
}
   有了这个数据结构我们就可以将数据实现两份存储。再利用一些读写策略就可以实现上面我们讲的缓存方式。

整个的缓存我们使用如下缓存类来控制:

/*
* http://www.cnblogs.com/chsword/
* chsword
* Date: 2009-3-31
* Time: 17:00
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace CHCache {
/// <summary>
/// 双存储的类
/// </summary>
public class DictionaryCache : IEnumerable {
/// <summary>
/// 在此缓存构造时初始化字典对象
/// </summary>
public DictionaryCache()
{
Store = new Dictionary<string, Medium>();
}
public void Add(string key,Func<object> func)
{
if (Store.ContainsKey(key)) {//修改,如果已经存在,再次添加时则采用其它线程
var elem = Store[key];
if (elem.IsUpdating)return;  //正在写入未命中
var th = new ThreadHelper(elem, func);//ThreadHelper将在下文提及,是向其它线程传参用的
var td = new Thread(th.Doit);
td.Start();
}
else {//首次添加时可能也要读取,所以要本线程执行
Console.WriteLine("Begin first write");
Store.Add(key, new Medium {IsPrimary = true, Primary =  func()});
Console.WriteLine("End first write");
}
}
/// <summary>
/// 读取时所用的索引
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object this[string key] {
get {
if (!Store.ContainsKey(key))return null;
var elem = Store[key];
if (elem.IsUpdated) {//如果其它线程更新完毕,则将主次转置
elem.IsUpdated = false;
elem.IsPrimary = !elem.IsPrimary;
}
var ret = elem.IsPrimary ? elem.Primary : elem.Secondary;
var b = elem.IsPrimary ? " from 1" : " form 2";
return ret + b;
}
}
Dictionary<string, Medium> Store { get; set; }
public IEnumerator GetEnumerator() {
return ((IEnumerable)Store).GetEnumerator();
}
}
}

这里我只实现了插入一个缓存,以及读取的方法。

我读取缓存单元的逻辑是这样的

image 

从2个不同缓存读取当然是很容易了,但是比较复杂的就是向缓存写入的过程:

image

这里读取数据以及写入缓存时我使用了一个委托,在其它线程中仅在需要执行时才会执行。

这里除了首次写入缓存占用主线程时间(读取要等待)以外,其它时间都可以无延时的读取,实现了无缝的缓存。

但我们在委托中要操作缓存的元素Medium,所以要传递参数进其它线程,所以我这里使用了一个辅助类来传递参数进入其它线程:

using System;
namespace CHCache {
/// <summary>
/// 一个线程Helper,用于帮助多抛出线程时传递参数
/// </summary>
public class ThreadHelper {
Func<object> Fun { get; set; }
Medium Medium { get; set; }
/// <summary>
/// 通过构造函数来传递参数
/// </summary>
/// <param name="m">缓存单元</param>
/// <param name="fun">读取数据的委托</param>
public ThreadHelper(Medium m,Func<object> fun) {
Medium = m;
Fun = fun;
}
/// <summary>
/// 线程入口,ThreadStart委托所对应的方法
/// </summary>
public void Doit()
{
Medium.IsUpdating = true;
if (Medium.IsPrimary) {
Console.WriteLine("Begin write to 2.");
var ret = Fun.Invoke();
Medium.Secondary = ret;
Console.WriteLine("End write to 2.");
}
else {
Console.WriteLine("Begin write to 1.");
var ret = Fun.Invoke();
Medium.Primary = ret;
Console.WriteLine("End write to 1.");
}
Medium.IsUpdated = true;
Medium.IsUpdating = false;
}
}
}

这样我们就实现了在另个线程读取数据的过程,这样就在任何时候读取数据时都会无延时直接读取了。

最后我们写一个主函数来测试一下效果

/*
* http://www.cnblogs.com/chsword/
* chsword
* Date: 2009-3-31
* Time: 16:53
*/
using System;
using System.Threading;
namespace CHCache
{
class Program
{
public static void Main(string[] args)
{
var cache = new DictionaryCache();
Console.WriteLine("Init...4s,you can press the CTRL+C to close the console window.");
while (true)
{
cache.Add("1", GetValue);
Thread.Sleep(1000);
Console.WriteLine(cache["1"]);
}
}
/// <summary>
/// 获取数据的方法,假设是从数据库读取的,费时约4秒
/// </summary>
/// <returns></returns>
static object GetValue()
{
Thread.Sleep(4000);
return DateTime.Now;
}
}
}

得到如下数据:

image

这样就实现了平滑的读取缓存数据而没有任何等待时间

当然这里还有些问题,比如说传递不同参数时的解决方法,但是由于我仅是在一个统计时需要这种缓存提高性能,所以暂没有考虑通用的传参方式。

如果大家对这个话题感兴趣,欢迎讨论。

源码下载:点击下载

[C#]ASP.NET MVC 1.0浅析

mikel阅读(777)

最近的项目没有升级到ASP.NET MVC 1.0,也没有评论的资格,没详细看过MVC1.0的源码,据说改动挺大,看来需要升级的工作又艰巨了不少,以下转载自:

http://www.cnblogs.com/andy1027/archive/2009/04/01/1427369.html

为什么要用ASP.NET MVC 1.0?当我刚知道1.0发布的时候,经常这样问。

最近正在考虑是否在我们的企业级应用中使用ASP.NET MVC 1.0框架,因此会一直找使用它的理由,希望大家在关注技术的同时,结合企业应用谈谈自己的看法。

1、MVC的组成
Models
:访问数据库,装载数据、处理业务逻辑。在项目中体现为数据实体类加业务代理类。
Views:显示数据,用户界面。在项目中体现为aspx页面,偶尔可以加上code-behind。
Controller:按路由规则将请求的数据传送给指定页面,用于显示;也可以把用户输入的数据传递给逻辑处理类。它可以包含简单的验证逻辑
。不应包含数据访问逻辑。
2、为何使用MVC
提出MVC的目的无非是提高开发效率、提高可测试性。
官方的ASP.NET MVC 1.0指南中指出(以下简称指南),基于MVC的Web应用程序有如下优点:
[1]对复杂的程序管理更方便
It makes it easier to manage complexity by dividing an application into the model, the view, and the ontroller.

[2]在开发上有更高的可控性
It does not use view state or server-based forms. This makes the MVC framework ideal for developers who want full control
over the behavior of an application.
[3]Routing使软件设计有更多灵活性
It uses a Front Controller pattern that processes Web application requests through a single controller. This enables you to
design an application that supports a rich routing infrastructure.
[4]更加适合测试驱动开发
It provides better support for test-driven development (TDD).
[5]团队开发项目中有更高的可控性
It works well for Web applications that are supported by large teams of developers and Web designers who need a high degree
of control over the application behavior.
同时MVC框架还有以下特点:
[1]将应用程序分成各个组成部份,更有利于测试。MVC框架是基于接口的,这样可以利用MOCK方式来替换你的实际类;做单元测试的时候,也
可以不运行Contrllers,这样的测试就更快更灵活。
[2]MVC框架是可扩展的,你可以自己设计并替换视频引擎、URL导向规则、Action的参数序列等等。同时MVC框架也支持依赖注入和控制反转,
你可以从外部注入实例,而不用让类自己创建实例,你还可以通过配置文件的方式创建实例,这样使得测试更方便。
[3]强大的URL映射组件使得你的应用程序的URL更易理解,同时具备搜索能力。你的URL不必包括文件路径,这样的设计很适合自定义查询引擎
和REST架构。
[4]MVC框架仍然支持ASP.NET中的页面、用户控件、母版页作为视图的模板;同时你也还可以使用嵌套母版页、行内表达式(in-line
expressions<%= %>)、服务器端控件、模板、数据绑定、本地化等等属于ASP.NET已有的东西。
[5]同时ASP.NET中的FORM验证、Windows验证、URL授权、Membership、角色、输出、数据缓存、Session、Profile 状态管理、配置、Provider
框架等特性在MVC框架中仍然是可用的。

小结:ASP.NET MVC 1.0框架是基于ASP.NET的,所以他包括了ASP.NET中的几乎所有特性。同时他为设计人员提供了一套测试的方案(当然这是所有语言平台MVC模式的共性)。在安装了框架的VS2008中还增加了不少功能,可以方便地添加Views、Models、Controllers。
3、与三层结构的ASP.NET应用程序比较
与普通ASP.NET比较而言,最大的区别还是在于前台开发,后台包括的数据库访问、逻辑处理与以往的方式没有明显区别,在MVC框架中,这些
统称为Model。而三层结构中,这些可以称为数据访问层与逻辑处理层。
[1]页面开发
用这种模式开发的站点,光看页面的代码的确比以往少一些,但它更多地使用了页面脚本(<% … %>)用于显示数据。在指南中并未提到不推
荐使用服务器端控件,但是它提供了大量的HTML HELPER,而且还允许你自己添加Helper,比如DataGridHelper,所以在MVC框架中使用这些Helper会更方便些,不过这对于熟练工来说应该影响不大,因为实际开发中我们更多使用的是Ctrl+C/Ctrl+V,复制几个标签和复制几个Helper方法所花的时间差不多。可能对于新手来说,如果对标签不熟悉的话,用这些Helper的速度会快些,但是这样会影响新手掌握标签,真是矛盾呐
[2]数据提交
普通的ASP.Net开发,在提交数据的时候可能还需要通过设置数据绑定,或者在code-behind里写封装代码;而在MVC中,框架自动帮助你将页面
上填写的数据封装到事先指定的Model中,数据提交操作在MVC框架挺方便。而且在普通ASP.NET页面中,经常会出现某个属性无法绑回去的情况,这点在MVC中应该可以得到解决。指南中提到了Routing的使用使得MVC框架下的应用程序在操作自定义查询时变得更方便,实际上在查询方面跟普通方式并没有多大区别,都是对封装好的类进行解析。至于“URL更容易理解”,现在应用程序都是从界面上点击来实现操作,很少有人会关注URL本身吧,所以这个优点不算优点。
[3]单元测试
从测试上讲,MVC框架确实做得不错,若用MOCK方式测试可以更方便,一个好的WEB应用程序设计就应该将页面呈现与逻辑分开,这点普通
ASP.NET应用程序也是可以做到的,关键在于设计。
[4]其它
MVC框架在验证、母版页这些地方有几个新特性,但与普通ASP.NET的方式大同小异,因此不仔细说了。

文中分析不对的地方,请指正。

[JQuery]JQuery ajax批量上传图片

mikel阅读(945)

在网上搜索了一下,发现以JQuery+ajax方式实现单张图片上传的代码是有的,但实现批量上传图片的程序却没搜索到,于是根据搜索到的代码,写了一个可以批量上传的。

         先看效果图

点击增加按钮,会增加一个选择框,如下图:

 

选择要上传的图片,效果图如下:

 

上传成功如下图:

 

 

 

下面来看代码:

前台html主要代码:

<button id="SubUpload" class="ManagerButton" onClick="TSubmitUploadImageFile();return false;">确定上传</button>&nbsp;&nbsp;

<button id="CancelUpload" class="ManagerButton" onClick="JavaScript:history.go(-1);">取消</button>&nbsp;&nbsp;

<button id="AddUpload" class="ManagerButton" onClick="TAddFileUpload();return false;">增加</button>

<tr><td class="tdClass">

         图片1

         </td><td class="tdClass">

         <input name="" size="60" id="uploadImg1" type="file" />

         <span id="uploadImgState1"></span>

         </td></tr>

 

 

 

 

 

 

 

 

 

 

因为用了JQuery,所以你完全可以把click事件放在js文件中

增加按钮js代码:

 

var TfileUploadNum=1; //记录图片选择框个数

var Tnum=1; //ajax上传图片时索引

         function TAddFileUpload()

         {

                   var idnum = TfileUploadNum+1;

                   var str="<tr><td class='tdClass'>图片"+idnum+"</td>";

                   str += "<td class='tdClass'><input name='' size='60' id='uploadImg"+idnum+"' type='file' /><span id='uploadImgState"+idnum+"'>";

                   str += "</span></td></tr>";

                   $("#imgTable").append(str);

                   TfileUploadNum += 1;

         }

 

 

 

 

 

 

 

 

 

 

确定上传按钮js代码:

 

function TSubmitUploadImageFile()

         {

                  M("SubUpload").disabled=true;

                   M("CancelUpload").disabled=true;

                   M("AddUpload").disabled=true;

                   setTimeout("TajaxFileUpload()",1000);//此为关键代码

}

 

 

 

 

 

 

 

 

关于setTimeout("TajaxFileUpload()",1000);这句代码:因为所谓的批量上传,其实还是一个一个的上传,给用户的只是一个假象。只所以要延时执行TajaxFileUpload(),是因为在把图片上传到服务器上时,我在后台给图片重新命名了,命名的规则是,如下代码:

 

 

Random rd = new Random();

StringBuilder serial = new StringBuilder();

serial.Append(DateTime.Now.ToString("yyyyMMddHHmmssff"));

serial.Append(rd.Next(0, 999999).ToString());

return serial.ToString();

 

 

 

 

 

即使我命名精确到毫秒,另外再加上随机数,可是还是有上传的第二张图片把上传的第一张图片覆盖的情况出现。所以此处我设置了延时1秒后在上传下一张图片。刚开始做这个东西的时候,用的是for循环,来把所有的图片一个一个的循环地用ajax上传,可是for循环速度太快了,可能第一张图片还没来得及ajax,第二张就被for过来了,还是有第二张覆盖第一张的情况出现。

下面来看TajaxFileUpload()函数,代码如下:

 

function TajaxFileUpload()

         {

                   if(Tnum<TfileUploadNum+1)

                   {

                            //准备提交处理

                            $("#uploadImgState"+Tnum).html("<img src=../images/loading.gif />");

                            //开始提交

                            $.ajax

                            ({

                                     type: "POST",

                                     url:"http://localhost/ajaxText2/Handler1.ashx",

                                     data:{upfile:$("#uploadImg"+Tnum).val(),category:$("#pcategory").val()},

                                     success:function (data, status)

                                     {

                                               //alert(data);

                                               var stringArray = data.split("|");

                                              

                                               if(stringArray[0]=="1")

                                               {

                                                        //stringArray[0]    成功状态(1为成功,0为失败)

                                                        //stringArray[1]    上传成功的文件名

                                                        //stringArray[2]    消息提示

                                                        $("#uploadImgState"+Tnum).html("<img src=../images/note_ok.gif />");//+stringArray[1]+"|"+stringArray[2]);

                                               }           

                                               else

                                               {

                                                        //上传出错

                                                        $("#uploadImgState"+Tnum).html("<img src=../images/note_error.gif />"+stringArray[2]);//+stringArray[2]+"");

                                               }

                                               Tnum++;

                                             setTimeout("TSubmitUploadImageFile()",0);

                                      }

                             });                     

                   }

         }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 上面的代码没什么可说的,很容易看懂。下面来看Handler1.ashx(一般处理程序)如何来处理post过来的图片的(此代码来自网上,具体地址忘记了),下面只给出关键代码,全部代码在附件里。

1

  

string _fileNamePath = "";

            try

            {

                _fileNamePath = context.Request.Form["upfile"];

                //开始上传

                string _savedFileResult = UpLoadFile(_fileNamePath);

                context.Response.Write(_savedFileResult);

            }

            catch

            {

                context.Response.Write("0|error|上传提交出错");

            }

 

 

 

 

 

 

 

 

 

 

 

 

2

 

//生成将要保存的随机文件名

string fileName = GetFileName() + fileNameExt;

//物理完整路径                   

string toFileFullPath = HttpContext.Current.Server.MapPath(toFilePath);

//检查是否有该路径 没有就创建

if (!Directory.Exists(toFileFullPath))

{

     Directory.CreateDirectory(toFileFullPath);

}

///创建WebClient实例      

WebClient myWebClient = new WebClient();

//设定windows网络安全认证   方法1

myWebClient.Credentials = CredentialCache.DefaultCredentials;

//要上传的文件      

FileStream fs = new FileStream(fileNamePath, FileMode.Open, FileAccess.Read);

//FileStream fs = OpenFile();      

BinaryReader r = new BinaryReader(fs);

//使用UploadFile方法可以用下面的格式      

//myWebClient.UploadFile(toFile, "PUT",fileNamePath);      

byte[] postArray = r.ReadBytes((int)fs.Length);

Stream postStream = myWebClient.OpenWrite(toFile, "PUT");

if (postStream.CanWrite)

{

postStream.Write(postArray, 0, postArray.Length);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3、检查是否合法的上传文件

 

private bool CheckFileExt(string _fileExt)

{

     string[] allowExt = new string[] { ".gif", ".jpg", ".jpeg" };

     for (int i = 0; i < allowExt.Length; i++)

     {

         if (allowExt[i] == _fileExt) { return true; }

     }

    return false;

}

4、生成要保存的随即文件名

 

 

 

 

 

 

 

 

  

public static string GetFileName()

{

            Random rd = new Random();

            StringBuilder serial = new StringBuilder();

            serial.Append(DateTime.Now.ToString("yyyyMMddHHmmssff"));

            serial.Append(rd.Next(0, 999999).ToString());

            return serial.ToString();

}

 

 

 

 

 

 

 

Ok,基本上这个批量上传图片的jQuery+ajax方式实现的程序完成了。如果你要上传word文档,pdf文件,只要稍作修改,就可以实现了。

[JQuery]jQuery Calculation Plug-in

mikel阅读(757)

JQuery Calculation Plug-in (v0.4.04)

The Calculation plug-in is designed to give easy-to-use JQuery functions for commonly used mathematical functions.

This plug-in will work on all types of HTML elements—which means you can use it to calculate values in <td> elements or in <input> elements. You can even mix and match between element types.

Numbers are parsed from the element using parseNumber() method—which uses a regular expression (/-?\d+(,\d{3})*(\.\d{1,})?/g) to parse out the numeric value. You can change the regular expression that's used to determine what's consider a number by changing the default regular expression.

Download

Download the plug-in:
jquery.calculation.js
jquery.calculation.min.js

[DBMS]漫谈数据库索引

mikel阅读(903)

一、引言

对数据库索引的关注从未淡出我的们的讨论,那么数据库索引是什么样的?聚集索引与非聚集索引有什么不同?希望本文对各位同仁有一定的帮助。有不少存疑的地方,诚心希望各位不吝赐教指正,共同进步。[最近首页之争沸沸扬扬,也不知道这个放在这合适么,苦劳?功劳?……]

 

二、B-Tree

我们常见的数据库系统,其索引使用的数据结构多是B-Tree或者B+Tree。例如,MsSQL使用的是B+TreeOracleSysbase使用的是B-Tree。所以在最开始,简单地介绍一下B-Tree

B-Tree不同于Binary Tree(二叉树,最多有两个子树),一棵M阶的B-Tree满足以下条件:
1)每个结点至多有M个孩子;
2)除根结点和叶结点外,其它每个结点至少有M/2个孩子;
3)根结点至少有两个孩子(除非该树仅包含一个结点);
4)所有叶结点在同一层,叶结点不包含任何关键字信息;
5)有K个关键字的非叶结点恰好包含K+1个孩子;

另外,对于一个结点,其内部的关键字是从小到大排序的。以下是B-TreeM=4)的样例:

  

对于每个结点,主要包含一个关键字数组Key[],一个指针数组(指向儿子)Son[]。在B-Tree内,查找的流程是:使用顺序查找(数组长度较短时)或折半查找方法查找Key[]数组,若找到关键字K,则返回该结点的地址及KKey[]中的位置;否则,可确定K在某个Key[i]Key[i+1]之间,则从Son[i]所指的子结点继续查找,直到在某结点中查找成功;或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。

接着,我们使用以下图片演示如何生成B-TreeM=4,依次插入1~6):
从图可见,当我们插入关键字4时,由于原结点已经满了,故进行分裂,基本按一半的原则进行分裂,然后取出中间的关键字2,升级(这里是成为根结点)。其它的依类推,就是这样一个大概的过程。

  

 

三、数据库索引

1.什么是索引

在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。
A)索引可以避免全表扫描。多数查询可以仅扫描少量索引页及数据页,而不是遍历所有数据页。
B对于非聚集索引,有些查询甚至可以不访问数据页。
C聚集索引可以避免数据插入操作集中于表的最后一个数据页。
D一些情况下,索引还可用于避免排序操作。

当然,众所周知,虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引。

 

2.索引的存储

一条索引记录中包含的基本信息包括:键值(即你定义索引时指定的所有字段的值)+逻辑指针(指向数据页或者另一索引页)。

  

当你为一张空表创建索引时,数据库系统将为你分配一个索引页,该索引页在你插入数据前一直是空的。此页此时既是根结点,也是叶结点。每当你往表中插入一行数据,数据库系统即向此根结点中插入一行索引记录。当根结点满时,数据库系统大抵按以下步骤进行分裂:
A)创建两个儿子结点
B)将原根结点中的数据近似地拆成两半,分别写入新的两个儿子结点
C)根结点中加上指向两个儿子结点的指针

通常状况下,由于索引记录仅包含索引字段值(以及4-9字节的指针),索引实体比真实的数据行要小许多,索引页相较数据页来说要密集许多。一个索引页可以存储数量更多的索引记录,这意味着在索引中查找时在I/O上占很大的优势,理解这一点有助于从本质上了解使用索引的优势。

 

3.索引的类型

A聚集索引,表数据按照索引的顺序来存储的。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
B非聚集索引,表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,该层紧邻数据页,其行数量与数据表行数据量一致。

在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只可能是一种。如果一张表没有聚集索引,那么它被称为堆集Heap)。这样的表中的数据行没有特定的顺序,所有的新行将被添加的表的末尾位置。

 

4.聚集索引

在聚集索引中,叶结点也即数据结点,所有数据行的存储顺序与索引的存储顺序一致。

  

1)聚集索引与查询操作

如上图,我们在名字字段上建立聚集索引,当需要在根据此字段查找特定的记录时,数据库系统会根据特定的系统表查找的此索引的根,然后根据指针查找下一个,直到找到。例如我们要查询“Green”,由于它介于[Bennet,Karsen],据此我们找到了索引页1007,在该页中“Green”介于[Greane, Hunter],据此我们找到叶结点1133(也即数据结点),并最终在此页中找以了目标数据行。

此次查询的IO包括3个索引页的查询(其中最后一次实际上是在数据页中查询)。这里的查找可能是从磁盘读取(Physical Read)或是从缓存中读取(Logical Read),如果此表访问频率较高,那么索引树中较高层的索引很可能在缓存中被找到。所以真正的IO可能小于上面的情况。

 

2)聚集索引与插入操作

最简单的情况下,插入操作根据索引找到对应的数据页,然后通过挪动已有的记录为新数据腾出空间,最后插入数据。

如果数据页已满,则需要拆分数据页(页拆分是一种耗费资源的操作,一般数据库系统中会有相应的机制要尽量减少页拆分的次数,通常是通过为每页预留空间来实现):
A在该使用的数据段(extent)上分配新的数据页,如果数据段已满,则需要分配新段。
B调整索引指针,这需要将相应的索引页读入内存并加锁。
C大约有一半的数据行被归入新的数据页中。
D
如果表还有非聚集索引,则需要更新这些索引指向新的数据页。

特殊情况:
A如果新插入的一条记录包含很大的数据,可能会分配两个新数据页,其中之一用来存储新记录,另一存储从原页中拆分出来的数据。
B通常数据库系统中会将重复的数据记录存储于相同的页中。
C类似于自增列为聚集索引的,数据库系统可能并不拆分数据页,页只是简单的新添数据页。

 

3)聚集索引与删除操作

删除行将导致其下方的数据行向上移动以填充删除记录造成的空白。

如果删除的行是该数据页中的最后一行,那么该数据页将被回收,相应的索引页中的记录将被删除。如果回收的数据页位于跟该表的其它数据页相同的段上,那么它可能在随后的时间内被利用。如果该数据页是该段的唯一一个数据页,则该段也被回收。

对于数据的删除操作,可能导致索引页中仅有一条记录,这时,该记录可能会被移至邻近的索引页中,原索引页将被回收,即所谓的“索引合并”。

 

5.非聚集索引

非聚集索引与聚集索引相比:
A叶子结点并非数据结点
B叶子结点为每一真正的数据行存储一个指针
C叶子结点中还存储了一个指针偏移量,根据页指针及指针偏移量可以定位到具体的数据行。
D
类似的,在除叶结点外的其它索引结点,存储的也是类似的内容,只不过它是指向下一级的索引页的。

聚集索引是一种稀疏索引,数据页上一级的索引页存储的是页指针,而不是行指针。而对于非聚集索引,则是密集索引,在数据页的上一级索引页它为每一个数据行存储一条索引记录。

对于根与中间级的索引记录,它的结构包括:
A索引字段值
BRowId(即对应数据页的页指针+指针偏移量)。在高层的索引页中包含RowId是为了当索引允许重复值时,当更改数据时精确定位数据行。
C下一级索引页的指针

对于叶子层的索引对象,它的结构包括:
A
索引字段值
BRowId

  

1)非聚集索引与查询操作

针对上图,如果我们同样查找“Green”,那么一次查询操作将包含以下IO3个索引页的读取+1个数据页的读取。同样,由于缓存的关系,真实的IO实际可能要小于上面列出的。

 

2)非聚集索引与插入操作

如果一张表包含一个非聚集索引但没有聚集索引,则新的数据将被插入到最末一个数据页中,然后非聚集索引将被更新。如果也包含聚集索引,该聚集索引将被用于查找新行将要处于什么位置,随后,聚集索引、以及非聚集索引将被更新。

 

3)非聚集索引与删除操作

如果在删除命令的Where子句中包含的列上,建有非聚集索引,那么该非聚集索引将被用于查找数据行的位置,数据删除之后,位于索引叶子上的对应记录也将被删除。如果该表上有其它非聚集索引,则它们叶子结点上的相应数据也要删除。

如果删除的数据是该数所页中的唯一一条,则该页也被回收,同时需要更新各个索引树上的指针。

由于没有自动的合并功能,如果应用程序中有频繁的随机删除操作,最后可能导致表包含多个数据页,但每个页中只有少量数据。

 

6.索引覆盖

索引覆盖是这样一种索引策略:当某一查询中包含的所需字段皆包含于一个索引中,此时索引将大大提高查询性能。

包含多个字段的索引,称为复合索引。索引最多可以包含31个字段,索引记录最大长度为600B。如果你在若干个字段上创建了一个复合的非聚集索引,且你的查询中所需Select字段及Where,Order By,Group By,Having子句中所涉及的字段都包含在索引中,则只搜索索引页即可满足查询,而不需要访问数据页。由于非聚集索引的叶结点包含所有数据行中的索引列值,使用这些结点即可返回真正的数据,这种情况称之为索引覆盖

在索引覆盖的情况下,包含两种索引扫描:
A)匹配索引扫描
B)非匹配索引扫描

 

1)匹配索引扫描

此类索引扫描可以让我们省去访问数据页的步骤,当查询仅返回一行数据时,性能提高是有限的,但在范围查询的情况下,性能提高将随结果集数量的增长而增长。

针对此类扫描,索引必须包含查询中涉及的的所有字段,另外,还需要满足:Where子句中包含索引中的引导列Leading Column),例如一个复合索引包含A,B,C,D四列,则A引导列。如果Where子句中所包含列是BCD或者BD等情况,则只能使用非匹配索引扫描。

 

2)非配置索引扫描

正如上述,如果Where子句中不包含索引的导引列,那么将使用非配置索引扫描。这最终导致扫描索引树上的所有叶子结点,当然,它的性能通常仍强于扫描所有的数据页。

 

[参考]
[1]http://manuals.sybase.com/onlinebooks/group-asarc/asg1200e/aseperf/@Generic__BookTextView/3358
[2]
http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.adref.doc/adref235.htm