[转载]由"说说JSON和JSONP"博文想到的MVC 扩展

mikel阅读(1142)

[转载]由【说说JSON和JSONP..】博文,想到的MVC 扩展 – DotDot – 博客园.

前言

今天看到随它去吧大牛的 【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例 文章,利用JSONP的跨域令人倍感狂喜。于是想,自己动手针对ASP.NET MVC 进行一些扩展,让其更好的支持Jsonp。

关于 JSONP 的详情这里就不介绍了,请看 ——随它去吧: 【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例

扩展要点

  1. 默认约定 Callback 方法名为 Action名,当然也可以提供覆盖。
  2. 自定义JsonpResult,让其返回Js文件类型的响应,看过MVC 源码的同学都知道,这其实很简单(请打开MVC 源码中的JavaScriptResult.cs文件查看)。
  3. 为JsonpResult提供Json序列化器。同样参考MVC源码。
  4. 定义一个JsonpController 继承自Controller 方便使用JsonpResult。

其他:

除了上面3点外,我还定义了一个JsonpViewResult,故名思议就是可以支持View。
核心源码:

JsonpResult 
   public class JsonpResult : ActionResult
     {
 
 
         public string Json
         {
             get;
             set;
         }
 
         public string Callback
         {
             get;
             set;
         }
 
         public override void ExecuteResult(ControllerContext context)
         {
             if (context == null)
             {
                 throw new ArgumentNullException("context");
             }
             HttpResponseBase response = context.HttpContext.Response;
             response.ContentType = "application/x-javascript";
 
             if (Callback == null)
             {
                 Callback = context.RouteData.Values["action"].ToString();
             }
 
             response.Write(string.Format("{0}({1})", Callback, Json));
 
         }
     }

JsonpResult 里主要是设置Respone 里的 ContentType 类型, 同时判断Callback是否为null ,如果是则表示使用约定:以Action名为回调函数名。

JsonpViewResult

  public class JsonpViewResult : ViewResult
    {
        public override void ExecuteResult(ControllerContext context)
        {
            base.ExecuteResult(context);
            //if
            //context.HttpContext.Request.UrlReferrer.Host=="YourDomain"
            //return ContentType="text/Html"
            //else:
            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "application/x-javascript";
        }
    }

JsonpViewResult 只需要简单的继承ViewResult ,让后同样设置返回类型即可。

public class JsonpController : Controller
    {

        protected internal virtual ActionResult Jsonp(string json)
        {
            return new JsonpResult { Json = json };
        }

        protected internal virtual ActionResult Jsonp(string json, string callback)
        {
            return new JsonpResult { Json = json, Callback = callback };
        }

        protected internal virtual ActionResult Jsonp(object data)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return new JsonpResult { Json = serializer.Serialize(data) };
        }

        protected internal virtual ActionResult Jsonp(object data, string callback)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return new JsonpResult { Json = serializer.Serialize(data), Callback = callback };
        }

        protected internal virtual ActionResult Jsonp(object data, string callback, JavaScriptTypeResolver resolver)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer(resolver);
            return new JsonpResult { Json = serializer.Serialize(data), Callback = callback };
        }

        protected internal virtual ActionResult Jsonp(object data, JavaScriptTypeResolver resolver)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer(resolver);
            return new JsonpResult { Json = serializer.Serialize(data) };
        }

        protected internal ViewResult JsonpView(object model)
        {
            return JsonpView(null /* viewName */, model);
        }

        protected internal ViewResult JsonpView(string viewName, object model)
        {
            if (model != null)
            {
                ViewData.Model = model;
            }

            return new JsonpViewResult
            {
                ViewName = viewName,
                ViewData = ViewData,
                TempData = TempData
            };
        }


    }

JsonpController 对 JsonpResult 和 JsonpViewResult 进行了封装方便使用。
使用示例

新建两个MVCApplictiong 项目,我这里为ASP.NET MVC 3 的项目

在第一个项目为本地域,主要进行Jsonp的跨域请求:

View

<h2>
    Index</h2>
<script>
    var GetCustomers = function (data) {
        alert(data[0].Address );
    };


    var url = "http://localhost:1853/home/GetCustomers";

    var script = document.createElement('script');
    script.setAttribute('src', url);
    document.getElementsByTagName('head')[0].appendChild(script); 
</script>


<script>
    var GetCustomer = function (data) {
        alert(data.Address+" "+data.Name+" "+data.ID);
    };


    var url = "http://localhost:1853/home/Customer/2";

    var script = document.createElement('script');
    script.setAttribute('src', url);
    document.getElementsByTagName('head')[0].appendChild(script); 
</script>

<script>
    var GetPI = function (data) {
        alert(data);
    };


    var url = "http://localhost:1853/home/Calculate?callback=GetPI";

    var script = document.createElement('script');
    script.setAttribute('src', url);
    document.getElementsByTagName('head')[0].appendChild(script); 
</script>

第二个项目为远程域,它负责提供请求的数据封装:

Controller

   public ActionResult GetCustomers()
        {
            var customers = new[]{
                 new Customer{ Address="长安街1", Id=1, Name="张三"},
                 new Customer{ Address="长安街2", Id=2, Name="李四"},
                 new Customer{ Address="长安街3", Id=3, Name="dudu"},
                 new Customer{ Address="长安街4", Id=4, Name="DotDot"},
                 new Customer{ Address="长安街5", Id=5, Name="随它去吧"}

            };

            return Jsonp(customers);
        }



        public ActionResult Customer(int id)
        {
            var customers = new[]{
                 new Customer{ Address="长安街1", Id=1, Name="张三"},
                 new Customer{ Address="长安街2", Id=2, Name="李四"},
                 new Customer{ Address="长安街3", Id=3, Name="dudu"},
                 new Customer{ Address="长安街4", Id=4, Name="DotDot"},
                 new Customer{ Address="长安街5", Id=5, Name="随它去吧"}

            };

            var customer = customers.FirstOrDefault(c => c.Id == id);

            return JsonpView(customer);

        }


        public ActionResult Calculate(string callback)
        {
            var PI = Math.PI;
            return Jsonp(PI, callback);
        }

Controller中使用了 JsonpViewResult 因此可以对应的建立一个 View视图。在视图里直接指定Callback和Json数据(当然也可以利用绑定)

@model MvcApplication3.Models.Customer
@{
Layout = null;
}
GetCustomer( { Address:’@Model.Address’, ID:’@Model.Id’, Name:’@Model.Name’ } )

一些未考虑的事情

1、callback 方法为JS全局的

2、JsonpViewResult仍旧可以扩展成当为本域请求时显示Html,其他域则返回 Js,(可扩展对外服务)

示例源码:

https://github.com/IndexKey/JsonpExtengForASP.NET-MVC

[转载]说说JSON和JSONP

mikel阅读(1082)

[转载]【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例 – 随它去吧 – 博客园.

前言:

 

由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现。

当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的 Socket通讯和本地数据库功能,又或者通过HTML5的WebSocket也可以实现与服务器的通讯和服务端推功能,但这两种方式都有其局限性,前者 需要PhoneGap支持,后者要求用户设备必须支持WebSocket,因此都不能算是ST2的原生解决方案,原生的只有AJAX。

 

说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域可以通过服务器端代理来解决。

但到目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。而这就是本文将要讲述的内容。

 

JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事 儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比 方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格 式,一个是信息传递双方约定的方法。

 

既然随便聊聊,那我们就不再采用教条的方式来讲述,而是把关注重心放在帮助开发人员理解是否应当选择使用以及如何使用上。

小小的广告一下,该篇文章是在自己群里与Sencha Touch 2的开发者们一起探讨ST2数据交互模型时有感而发写出来的因此如果您对Mobile Web App开发有兴趣的话,欢迎加入Sencha Touch 交流 QQ 群 213119459 

 

 什么是JSON?

 

前面简单说了一下,JSON是一种基于文本的数据交换方式,或者叫做数据描述格式,你是否该选用他首先肯定要关注它所拥有的优点。

 

JSON的优点:

1、基于纯文本,跨平台传递极其简单;

2、JavaScript原生支持,后台语言几乎全部支持;

3、轻量级数据格式,占用字符数量极少,特别适合互联网传递;

4、可读性较强,虽然比不上XML那么一目了然,但在合理的依次缩进之后还是很容易识别的;

5、容易编写和解析,当然前提是你要知道数据结构;

JSON的缺点当然也有,但在作者看来实在是无关紧要的东西,所以不再单独说明。

 

JSON的格式或者叫规则:

JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。

1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号””是定义符。

2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。

3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。

4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号””,以便于不同语言的解析。

5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期 时间直接作为字符串传递就好,可以省去很多麻烦。

 

JSON实例:

// 描述一个人

var person = {
"Name": "Bob",
"Age": 32,
"Company": "IBM",
"Engineer": true
}

// 获取这个人的信息

var personAge = person.Age;

// 描述几个人

var members = [
{
"Name": "Bob",
"Age": 32,
"Company": "IBM",
"Engineer": true
},
{
"Name": "John",
"Age": 20,
"Company": "Oracle",
"Engineer": false
},
{
"Name": "Henry",
"Age": 45,
"Company": "Microsoft",
"Engineer": false
}
]

// 读取其中John的公司名称

var johnsCompany = members[1].Company;

// 描述一次会议

var conference = {
"Conference": "Future Marketing",
"Date": "2012-6-1",
"Address": "Beijing",
"Members":
[
{
"Name": "Bob",
"Age": 32,
"Company": "IBM",
"Engineer": true
},
{
"Name": "John",
"Age": 20,
"Company": "Oracle",
"Engineer": false
},
{
"Name": "Henry",
"Age": 45,
"Company": "Microsoft",
"Engineer": false
}
]
}

// 读取参会者Henry是否工程师

var henryIsAnEngineer = conference.Members[2].Engineer;

关于JSON,就说这么多,更多细节请在开发过程中查阅资料深入学习。

 

 什么是JSONP?

 

先说说JSONP是怎么产生的:

其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作 JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住 JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。

JSONP的客户端具体实现:

不管JQuery也好,extjs也罢,又或者是其他支持jsonp的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下jsonp在客户端的实现:

 

1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。

远程服务器remoteserver.com根目录下有个remote.js文件代码如下:

alert('我是远程文件');

本地服务器localserver.com下有个jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>

</body>
</html>

毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。

2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript">
    var localHandler = function(data){
        alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
    };
    </script>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>

</body>
</html>

remote.js文件代码如下:

localHandler({“result”:”我是远程js带来的数据”});

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。

3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

看jsonp.html页面的代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript">
    // 得到航班信息查询结果后的回调函数
    var flightHandler = function(data){
        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
    };
    // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
    // 创建script标签,设置其属性
    var script = document.createElement('script');
    script.setAttribute('src', url);
    // 把script标签加入head,此时调用开始
    document.getElementsByTagName('head')[0].appendChild(script); 
    </script>
</head>
<body>

</body>
</html>

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。

我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。

OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flightHandler({
“code”: “CA1998”,
“price”: 1780,
“tickets”: 5
});

我们看到,传递给flightHandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

什么?你用的是JQuery,想知道jQuery如何实现jsonp调用?好吧,那我就好人做到底,再给你一段jQuery使用jsonp的代码(我们依然沿用上面那个航班信息查询的例子,假定返回jsonp结果不变):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" >
 <head>
     <title>Untitled Page</title>
      <script type="text/javascript" src=jquery.min.js"></script>
      <script type="text/javascript">
     jQuery(document).ready(function(){ 
        $.ajax({
             type: "get",
             async: false,
             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
             dataType: "jsonp",
             jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
             jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
             success: function(json){
                 alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');
             },
             error: function(){
                 alert('fail');
             }
         });
     });
     </script>
     </head>
  <body>
  </body>
 </html>

是不是有点奇怪?为什么我这次没有写flightHandler这个函数呢?而且竟然也运行成功了!哈哈,这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时(还是忍不住吐槽,虽然jquery也把jsonp归入了ajax,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供success属性方法来调用,是不是很爽呀?

好啦,写到这里,我已经无力再写下去,又困又累,得赶紧睡觉。朋友们要是看这不错,觉得有启发,给点个“推荐”呗!由于实在比较简单,所以demo源码就不再奉上了。

注意:此文章属博主原创,转载请注明作者信息和原始链接,谢谢合作。

Sencha Touch 交流 QQ 群 213119459 欢迎您的加入。

[转载]让asp.net mvc的Action支持jQuery直接提交的javascript对象

mikel阅读(1091)

[转载]让asp.net mvc的Action支持jQuery直接提交的javascript对象 – 路人已 – 博客园.

在某些ajax应用中,我们可能会用到如下的场景:

$.post('/Test/PostTest', { values: [1, 2, 3, 4] }, function(result){
//TODO:
}, 'json' );

我们希望提交一个数组给服务器。

于是我们创建了一个如下的Controller,来负责处理上面的ajax请求:

public class TestController : Controller
{
[HttpPost]
public JsonResult PostTest( int[] values )
{
//TODO:
return Json( new { success = true });
}
}

可是当我们充满期待的去测试我们刚才的代码时,却发现了一个问题。

1_thumb18

值并没有被正确的传过来。

于是我们打开了浏览器的开发人员工具,来看看到底JQuery提交了什么内容给我们的服务器。

2_thumb19

我们发现,表单名称被设置成为了 values[],而不是values。莫非是是mvc不能将values[]看成一个数组并自动转化么?

于是我们打开ILSpy,找到了System.Web.Mvc.FormValueProviderFactory的源代码,并将它复制出来,作了一些扩展,以支持我们想要的功能。

public sealed class FormValueProviderFactoryEx
: ValueProviderFactory
{
private readonly UnvalidatedRequestValuesAccessor _unvalidatedValuesAccessor;
public FormValueProviderFactoryEx()
: this(null)
{

}
internal FormValueProviderFactoryEx(UnvalidatedRequestValuesAccessor unvalidatedValuesAccessor)
{
if (unvalidatedValuesAccessor == null)
{
unvalidatedValuesAccessor = ((ControllerContext cc) =&gt; new UnvalidatedRequestValuesWrapper(cc.HttpContext.Request.Unvalidated()));
}
this._unvalidatedValuesAccessor = unvalidatedValuesAccessor;
}
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
return new FormValueProviderEx(controllerContext, this._unvalidatedValuesAccessor(controllerContext));
}
}

下面这几个是原来的FormValueProviderFactory用到的,但在System.Web.Mvc.dll中被声明为internal,所以不得已复制了出来。

internal interface IUnvalidatedRequestValues
{
NameValueCollection Form { get; }
NameValueCollection QueryString {get;}
string this[string key]{ get; }
}

internal delegate IUnvalidatedRequestValues UnvalidatedRequestValuesAccessor(ControllerContext controllerContext);

internal sealed class UnvalidatedRequestValuesWrapper : IUnvalidatedRequestValues
{
private readonly UnvalidatedRequestValues _unvalidatedValues;
public NameValueCollection Form
{
get
{
return this._unvalidatedValues.Form;
}
}
public NameValueCollection QueryString
{
get
{
return this._unvalidatedValues.QueryString;
}
}
public string this[string key]
{
get
{
return this._unvalidatedValues[key];
}
}
public UnvalidatedRequestValuesWrapper(UnvalidatedRequestValues unvalidatedValues)
{
this._unvalidatedValues = unvalidatedValues;
}
}

下面是用于支持FormValueProviderFactoryEx的另外几个对象的定义

public sealed class FormValueProviderEx : NameValueCollectionValueProvider
{
public FormValueProviderEx(ControllerContext controllerContext)
: this(controllerContext, new UnvalidatedRequestValuesWrapper(controllerContext.HttpContext.Request.Unvalidated()))
{

}
internal FormValueProviderEx(ControllerContext controllerContext, IUnvalidatedRequestValues unvalidatedValues)
: base(controllerContext.HttpContext.Request.Form, unvalidatedValues.Form, CultureInfo.CurrentCulture)
{

}

public override ValueProviderResult GetValue(string key, bool skipValidation)
{
var result = base.GetValue(key, skipValidation);
if (result == null)
{
var subKeys = base.GetKeysFromPrefix(key);
if (subKeys.Count &gt; 0)
{
var firstItem = subKeys.First();
if (subKeys.Count == 1 &amp;&amp; firstItem.Value == key + "[]")
{
return GetValue(firstItem.Value, skipValidation);
}
int n;
if( int.TryParse(firstItem.Key, out n) )
{
var indexList = new List(subKeys.Count);
if (subKeys.Keys.All(v =&gt;
{
if (int.TryParse(v, out n))
{
indexList.Add(n);
return true;
}
return false;
}))
{
var arraySize = indexList.Max() + 1;
var elements = new ValueProviderResult[arraySize];
foreach (var i in indexList)
{
elements[i] = GetValue(subKeys[i.ToString()]);
}
return new ArrayValueProviderResult(elements);
}
}

var properties = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var item in subKeys)
{
properties[item.Key] = GetValue(item.Value);
}
return new ObjectValueProviderResult(properties);
}
}
return result;
}
}

public class ArrayValueProviderResult
: ValueProviderResult
{
private ValueProviderResult[] _Elements;
public ArrayValueProviderResult(ValueProviderResult[] elements)
{
_Elements = elements;
base.RawValue = elements.Select( v =&gt; v.RawValue ).ToArray();
base.AttemptedValue = "[" + string.Join(", ", elements.Select(v =&gt; v.AttemptedValue)) + "]";
}

public override object ConvertTo(Type type, CultureInfo culture)
{
if (type.IsArray)
{
var elementType = type.GetElementType();
var array = Array.CreateInstance(elementType, _Elements.Length);
int l = _Elements.Length;
if (elementType == typeof(object))
{
Array.Copy(_Elements, array, l);
}
else
{
for (int i = 0; i &lt; l; i++)
{
var v = _Elements[i];
if (v != null)
{
try
{
array.SetValue(v.ConvertTo(elementType, culture), i);
}
catch
{
}
}
}
}
return array;
}
return null;
}
}

public class ObjectValueProviderResult
: ValueProviderResult
{
private IDictionary _Properties;

public ObjectValueProviderResult(IDictionaryproperties)
{
_Properties = properties;
base.RawValue = properties.ToDictionary(v =&gt; v.Key, v =&gt; v.Value.RawValue);
base.AttemptedValue = "{" + string.Join(", ", properties.Select(v =&gt; string.Format("{0}: {1}", v.Key, v.Value.AttemptedValue ))) + "}";
}

public override object ConvertTo(Type type, CultureInfo culture)
{
if (!type.IsPrimitive &amp;&amp; !type.IsArray)
{
var constructor = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).OrderBy(v =&gt; v.GetParameters().Length).FirstOrDefault();
if (constructor != null)
{
var args = constructor.GetParameters()
.Where(v =&gt; !v.IsOptional)
.Join(_Properties.DefaultIfEmpty(), v =&gt; v.Name, v =&gt; v.Key, (l, r) =&gt; r.Value).ToArray();
var obj = Activator.CreateInstance(type, args);
foreach( var property in type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty ))
{
if (property.GetIndexParameters().Length &gt; 0) continue;
ValueProviderResult propertyValue;
if (_Properties.TryGetValue(property.Name, out propertyValue) &amp;&amp; propertyValue != null )
{
try
{
if (property.PropertyType == typeof(object))
{
property.SetValue(obj, propertyValue.RawValue, null);
}
else
{
property.SetValue(obj, propertyValue.ConvertTo(property.PropertyType, culture), null);
}
}
catch
{

}
}
}
return obj;
}
}
return null;
}
}

在做完上面的事情之后,我们就可以考虑把FormValueProviderFactory替换成为FormValueProviderFactoryEx了。

于是我们在Application_Start中,添加如下的代码:

for (int i = 0; i &lt; ValueProviderFactories.Factories.Count; i++)
{
if (ValueProviderFactories.Factories[i] is FormValueProviderFactory)
{
ValueProviderFactories.Factories[i] = new FormValueProviderFactoryEx();
break;
}
}

现在我们再来测试之前的代码:

3_thumb

很高兴的看到,我们的值,已经正确的解析出来了!

我们再来测试传递一个对象:

$.post('/Test/PostTest', { obj: { Id: 1, Values: ['aa', 'bb', 'cc']} }, function (result) {
//TODO:
}, 'json');

我们把Action的代码也稍作修改:

[HttpPost]
public JsonResult PostTest( MyObject obj )
{
//TODO:
return Json( new { success = true });
}

MyObject的定义如下:

public class MyObject
{
public int Id { get; set; }
public string[] Values { get; set; }
}

如预想中的一样,我们得到了下面的结果:

4_thumb

OK,大功告成。

得于某种目的,上面的代码中有两处需要说明一下:

ArrayValueProviderResult  类中的

if (elementType == typeof(object))

{

Array.Copy(_Elements, array, l);

}

当数组类型为object[]时,复制把原始的ValueProviderResult过去了,这里看个人需要可自己修改。

PS:只是很肤浅的实现了这种直接使用JQuery来提交对象给ASP.NET mvc的支持,代码未做优化,未作任何合理性的设计。

如果有需要的猴子,可以参考自己实现一个。

[转载]jQuery Mobile 1.1

mikel阅读(1150)

[转载]jQuery Mobile 1.1 : 更流畅,更快捷,更实用 – gbin1 – 博客园.

jQuery Mobile 1.1 : 更流畅,更快捷,更实用

最新版本的JQuery Mobile 1.1本月13号刚刚发布,带给我们了最新的强大特和代码提升。在这篇文章中,我们将要快速的介绍JQuery mobile的新特性及其移动开发人员需要了解的所有内容。准备好和我们一起体验JQuery mobile吧!

在这个文章中,我们将要介绍如下内容:

  • 改良的固定header和footer
  • 提高的页面过渡效果,和俩个新开发的过渡效果
  • 加载“spinner”和文本提升
  • 漂亮的迷你表单元素
  • 更漂亮的翻转开关切换
  • 更好的滑动选择
  • 禁止为整个页面标签优化和AJAX处理
  • 其它相关的提升和修改

在本篇文章结尾,你会看到一些jQuery Mobile 1.2的新特性和组件。

首先我们开始介绍jQuery mobile的标题特性,大大的提升了header和footer工具条。

Toolbar的巨大改良

固定的toolbar是用来固定页面顶端和低端的工具条,通过添加data-position=”fixed”来添加到页头和页尾。

最简单的方式去创建一个固定的页面元素是使用CSS position:fixed 。因为移动浏览器对于这个属性的支持不是很完整,所以在老版本中通过动态的重新定位来实现功能。虽然可以工作,但是不是非常完美。

很 幸运的是,浏览器的支持在最近已经被大大的增强了。使用version1.1,jQuery mobile团队已经完整的重新设计了toolbar,开始使用position:fixed。这样使得toolbar更加的流畅。对于不支持 position:fixed的浏览器,例如,Safari in iOS4.3。整个框架会fallback到一般的静态位置的toolbar。

如果你希望重新使得它动态,你可以考虑使用一些polyfill

点击下面的演示,看看新版本和旧版本的区别吧。如果需要看到效果,请使用现代浏览器,或者桌面浏览器。

jQuery1.0演示  jQuery1.1演示

而且toolbar现在拥有了一系列的选项方法帮助你自定行为。包括visibleOnPageShow选项来控制是否页面初始的时候显示toolbar。tapToggle选项来打开或者关闭”toggle-on-tap”特性。show,hide和toggle方法可以使用代码控制显示和隐藏toolbar。

这 里还有一个很重要的小变动,关于全屏定位。在jQmobile 1.0中你可以通过添加data-fullscreen=”true“来实现toolbar的”fullscreen”模式。在1.1中,你现在需要添加 这个属性到独立的header和footer容器中而不是页面容器。

更多信息:这里有一些已知的position:fixed和Android 2.2/2.3的问题。请查看详细文档

更多信息:jQMobile1.0使用CSS overflow-scrolling:touch来提升固定的toolbar。因为1.1使用真正的position:fixed,overflow-scrolling:touch已经被删除了。所以这个属性不再有效。

更流畅,更棒的页面过渡效果

jQMobile1.1中最好的一个变化在于页面间更加流畅的过渡效果。因为jQuery Mobile框架工作的方式,需要先滚动到顶端在执行页面过渡效果。下面是整个流程:

  1. 用户向下滚动到目前页面
  2. 点击一个link查看新页面
  3. jQuery mobile滚动到本页面的顶端
  4. jQuery mobile使用过渡效果来实现新页面

这个过程比较不完美,特别对于比较慢的移动浏览器:

更流畅,更棒的页面过渡效果

因为无法避免滚动。所以jquery mobile团队使用了如下方式来处理:

  1. 用户向下滚动到目前页面
  2. 点击一个link查看新页面
  3. jQuery Mobile开始执行过渡效果
  4. 在一个合适的时候淡出fadeout页面,jQuery mobile执行滚动到本页面的顶端(页面为空),然后在淡入fadein页面。
  5. jQuery mobile完成过渡效果来实现新页面

大家看到不同的地方在于过渡时机,页面淡出后内容变少,这个时候滚动速度就更快。因为没有内容,所以基本用户看不到滚动效果。

大家可以看看如下的对比演示。

jQuery1.0演示  jQuery1.1演示

或者到jQuery Mobile网站上看演示:

更多信息:一个使用这种新的fade机制的例外过渡效果是slide,这个过渡效果使用老的非淡出式的方式。因为不使用fade效果更好,所以你可以考虑使用slidefade来得到更好的性能

更多信息:因为一些移动端浏览器的限制,例如,Android2.x,fade是唯一支持的特效。

两个新添加的页面过渡效果

除了1.0支持的slide, slideup, slidedown, pop, fade, 和 flip,1.1添加了2个过渡效果:

turn 和 flow

效果如下:

过渡效果turn演示  过渡效果flow演示

两个新添加的页面过渡效果

更好更灵活的”loading“旋转效果

“加载中…”效果已经被重新设计,缺省依旧没有文字。你可以设置更多选项:

$mobile.loadingMessageTextVisible:是否显示Loading文字(boolean:缺省为false)

$mobile.loadingMessageTheme:是否使用特定的主题显示信息框。缺省使用”a”,前提是 $.mobile.loadingMessageTextVisible为 true。

$mobile.pageLoadErrorMessageTheme:使用错误信息的主题

你可以在mobileinit方法中全局定义,如下:

$(document).bind( "mobileinit", function() {
  $.mobile.loadingMessageTextVisible = true;
} );

更多信息:和前面版本一样,你可以使用$.mobile.loadingMessage显示加载信息,$.mobile.pageLoadErrorMessage显示错误信息。

如果你调用 $.mobile.showPageLoadingMsg()来显示加载信息,你可以传递3个参数在自定义信息和“加载中”:

  • 信息框主题
  • 信息框文字
  • 是否隐藏图片,缺省false

下面是演示
loading演示

更好更灵活的”loading“旋转效果

toolbar中使用的迷你表单元素

现在你可以添加一些data-mini=”true”到表单元素中来创建更小的表单元素,非常适合添加到toolbar中。这个页面可以查看所有支持的元素及其正常版本。

toolbar中使用的迷你表单元素

更漂亮的翻转开关切换

重新定义了开关的样式,看起来更紧凑。更接近移动平台上的本地开关样式。

新样式的开关选择

jQuery1.0演示  jQuery1.1演示

更好的滑动选择

区域选择现在支持了step属性。你可以添加step=5到<input type=”range”/>来生成一个滑动选择。

另外一个加强是提供了data-hightlight=”true”属性,会提供了一个输入框告诉用户选择的数值,如下图:

更好的滑动选择

查看演示

阻止元素被针对移动设备改良

因 为jQuery Mobile缺省会搜索页面元素,将它们自动改成针对移动或者触摸设备更有好的元素。例如,滑动选择,复选框或者list view等。同时也添加AJAX到link和button,但是有的时候我们不希望它这样处理,例如,我们自己开发自己的页面组件。

以往我们都用一些小技巧,例如, keepNative 选项,initSelector 属性等,在jQuery Mobile1.1中,我们可以使用data-enhance=”false”来阻止jqMobile处理。

<div id="myContainer" data-enhance="false">
<!-- Enhancement and Ajax handling disabled for all elements inside this div -->
</div>

并且在全局选项中指定:

$(document).bind( "mobileinit", function() {
  $.mobile.ignoreContentEnabled = true;
} );

查看演示

针对整个容器关闭AJAX处理

在过去我们可以针对表单或者链接来添加data-ajax=”false”属性,禁止AJAX处理指定的对象表单或者链接。在jQuery mobile 1.1中,我们可以针对一个容易处理,如下:

<div id="myContainer" data-ajax="false">
<!-- Ajax handling disabled for all elements inside this div -->
</div>

和data-enhance属性一样,你也需要在mobileinit中指定ignoreContentEnabled为true。

查看演示

其它变化

  • 更清楚的缺省主题
  • 更容易的主题Roller
  • 支持jQuery1.7.1
  • AMD模块支持
  • 解决缩放bug
  • 可搜索的文档

jQuery mobile 1.2特性

这里列出计划中的几个特性:

弹出组件

允许你讲一个div转化为一个弹出组件,data-role=”popup”,这里查看演示

无新页面加载取得页面内容

帮助你取得链接内容加载到本地的容器中

下载Builder

帮助你下载你需要的组件,效果等等。

总价

这篇文章中我们介绍了jQuery mobile 1.1的新特性,你学习了toolbar的加强,过渡效果的加载和新过渡效果等。还介绍了1.2中的一些计划开发的特性。希望对于大家开发jQuery有帮助。

欢迎访问GBin1.com

[转载]SQL Server 2008 参数化查询

mikel阅读(917)

[转载]SQL Server 2008 参数化查询 – 小máo的专栏 – 博客频道 – CSDN.NET.

我将讨论如果一个查询可以被参数化,那么SQL Server优化器怎样尝试将其参数化,以及你可以怎样建立你自己的参数化查询.

1.什么是参数化查询?

一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数.通过使用不同的参数,一个参数化查询返回不同的结果.要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准.

有两种不同的方式来创建参数化查询.第一个方式是让查询优化器自动地参数化你的查询.另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从而编程一个参数化查询.这篇文章的后面部分将介绍这个方法.

参数化查询的关键是查询优化器将创建一个可以重用的缓存计划.通过自动地或编程使用参数化查询,SQL Server可以优化类似T-SQL语句的处理.这个优化消除了对使用高贵资源为这些类似T-SQL语句的每一次执行创建一个缓存计划的需求.而且通过创建一个可重用计划,SQL Server还减少了存放过程缓存中类似的执行计划所需的内存使用.

2.现在让我们看看使得SQL Server创建参数化查询的不同方式.

参数化查询是怎样自动创建的?

微软编写查询优化器代码的人竭尽全力地优化SQL Server处理你的T-SQL命令的方式.我想这是查询优化器名称的由来.这些尽量减少资源和最大限度地提高查询优化器执行性能的方法之一是查看一个T-SQL语句并确定它们是否可以被参数化.要了解这是如何工作的,让我们看看下面的T-SQL语句:

SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

WHERE SalesOrderID = 56000;

GO

在这里,你可以看到这个命令有两个特点.第一它简单,第二它在WHERE谓词中包含一个用于SalesOrderID值的指定值.查询优化器可以识别这个查询比较简单以及SalesOrderID有一个参数(“56000”).因此,查询优化器可以自动地参数化这个查询.

如果你使用下面的SELECT语句来查看一个只包含用于上面语句的缓存计划的,干净的缓冲池,那么你会看到查询优化器将T-SQL查询重写为一个参数化T-SQL语句:

SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], . AS [plan_text]

FROM sys.dm_exec_cached_plans p

OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

GO

当我在一个SQL Server 2008实例上运行这个命令时,我得到下面的输出,(注意,输出被重新格式化了,以便它更易读):

如果你看看上面输出中的plan_text字段,你会看到它不像原来的T-SQL文本.如前所述,查询优化器将这个查询重新编写为一个参数化T-SQL语句.在这里,你可以看到它现在有一个数据类型为(int)的变量(@1),它在之前的SELECT语句中被定义的.另外在plan_text的末尾,“56000”被替换为变量@1.既然这个T-SQL语句被重写了,而且被存储为一个缓存计划,那么如果未来一个T-SQL命令和它大致相同,只有SalesOrderID字段被赋的值不同的话,它就可以被用于重用.让我们在动作中看看它.

如果我在我的机器上运行下面的命令:

DBCC FREEPROCCACHE

GO

SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

WHERE SalesOrderID = 56000;

GO

SELECT * FROM AdventureWorks.Sales.SalesOrderHeader

WHERE SalesOrderID = 56001;

GO

SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], . AS [plan_text]

FROM sys.dm_exec_cached_plans p

OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

GO

我从最后的SELECT语句得到下面的输出,(注意,输出被重新格式化以便它更易读):

在这里,我首先释放过程缓存,然后我执行两个不同、但却类似的非参数化查询来看看查询优化器是会创建两个不同的缓存计划还是创建用于这两个查询的一个缓存计划.在这里,你可以看到查询优化器事实上很聪明,它参数化第一个查询并缓存了计划.然后当第二个类似、但有一个不同的SalesOrderID值的查询发送到SQL Server,优化器可以识别已经缓存了一个计划,然后重用它来处理第二个查询.你可以这么说是因为“cnt”字段现在表明这个计划被用了两次.

3.数据库配置选项PARAMETERIZATION可以影响T-SQL语句怎样被自动地参数化.对于这个选项有两种不同的设置,SIMPLEFORCED.PARAMETERIZATION设置被设置为SIMPLE,只有简单的T-SQL语句才会被参数化.要介绍这个,看下下面的命令:

SELECT SUM(LineTotal) AS LineTotal

FROM AdventureWorks.Sales.SalesOrderHeader H

JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

WHERE H.SalesOrderID = 56000

这个查询类似于我前面的示例,除了在这里我添加了一个额外的JOIN标准.当数据库AdventureWorksPARAMETERIZATION选项被设置为SIMPLE,这个查询不会被自动地参数化.SIMPLE PARAMETERIZATION设置告诉查询优化器只参数化简单的查询.但是当选项PARAMETERIZATION被设置为FORCED,这个查询将被自动地参数化.

当你设置数据库选项为使用FORCE PARAMETERIZATION,查询优化器试图参数化所有的查询,而不仅仅是简单的查询.你可能会认为这很好.但是在某些情况下,当数据库设置PARAMETERIZATIONFORCED,查询优化器将选择不是很理想的查询计划.当数据库设置PARAMETERFORCED,它改变查询中的字面常量.这可能导致当查询中涉及计算字段时索引和索引视图不被选中参与到执行计划中,从而导致一个无效的计划.FORCED PARAMETERIZATION选项可能是改进具有大量类似的、传递过来的参数稍有不同的查询的数据库性能的一个很好的解决方案.一个在线销售应用程序,它的客户对你的产品执行大量的类似搜索, 产品值不同,这可能是一个能够受益于FORCED PARAMETERIZATION的很好的应用程序类型.

不是所有的查询从句都会被参数化.例如查询的TOPTABLESAMPLE HAVINGGROUP BYORDER BYOUTPUT…INTOFOR XML从句不会被参数化.

4.使用sp_execute_sql来参数化你的T-SQL

你不需要依赖于数据库的PARAMETERIZATION选项来使得查询优化器参数化一个查询.你可以参数化你自己的查询.你通过重新编写你的T-SQL语句并使用”sp_executesql”系统存储过程执行重写的语句来实现.正如已经看到的,上面包括一个“JOIN”从句的SELECT语句在数据库的PARAMETERIZATION设置为SIMPLE时没有被自动参数化.让我重新编写这个查询以便查询优化器将创建一个可重用的参数化查询执行计划.

为了说明,让我们看两个类似的、不会被自动参数化的T-SQL语句,并创建两个不同的缓存执行计划.然后我将重新编写这两个查询使得它们都使用相同的缓存参数化执行计划.

让我们看看这个代码:

DBCC FREEPROCCACHE

GO

SELECT SUM(LineTotal) AS LineTotal

FROM AdventureWorks.Sales.SalesOrderHeader H

JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

WHERE H.SalesOrderID = 56000

GO

SELECT SUM(LineTotal) AS LineTotal

FROM AdventureWorks.Sales.SalesOrderHeader H

JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

WHERE H.SalesOrderID = 56001

GO

SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], . AS [plan_text]

FROM sys.dm_exec_cached_plans p

OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

GO

在这里,我释放了过程缓存,然后运行这两个包含一个JOIN的、不同的非简单的T-SQL语句.然后我将检查缓存计划.这是这个使用DMV SELECT语句的输出(注意,输出被重新格式化了,以便它更易读):

正如你从这个输出看到的,这两个SELECT语句没有被查询优化器参数化.优化器创建了两个不同的缓存执行计划,每一个都只被执行了一次.我们可以通过使用sp_executesql系统存储过程来帮助优化器为这两个不同的SELECT语句创建一个参数化执行计划.

下面是上面的代码被重新编写来使用sp_executesql 系统存储过程:

DBCC FREEPROCCACHE

GO

EXEC sp_executesql N’SELECT SUM(LineTotal) AS LineTotal

FROM AdventureWorks.Sales.SalesOrderHeader H

JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

WHERE H.SalesOrderID = @SalesOrderID’, N’@SalesOrderID INT’, @SalesOrderID = 56000

GO

EXEC sp_executesql N’SELECT SUM(LineTotal) AS LineTotal

FROM AdventureWorks.Sales.SalesOrderHeader H

JOIN AdventureWorks.Sales.SalesOrderDetail D ON D.SalesOrderID = H.SalesOrderID

WHERE H.SalesOrderID = @SalesOrderID’, N’@SalesOrderID INT’, @SalesOrderID = 56001

GO

SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], . AS [plan_text]

FROM sys.dm_exec_cached_plans p

OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql

JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle

GO

如同你所看到的,我重新编写了这两个SELECT语句,使它们通过使用”EXEC sp_executesql”语句来执行.对这些EXEC语句中的每一个,我都传递三个不同的参数.第一个参数是基本的SELECT语句,但是我将SalesOrderID的值用一个变量(@SalesOrderID)替代.在第二个参数中,我确定了@SalesOrderID的数据类型,在这个例子中它是一个integer.然后在最后一个参数中,我传递了SalesOrderID的值.这个参数将控制我的SELECT根据SalesOrderID值所生成的结果.sp_executesql的每次执行中前两个参数都是一样的.但是第三个参数不同,因为每个都有不同的SalesOrderID.

现在当我运行上面的代码时,我从DMV SELECT语句得到下面的输出(注意,输出被重新格式化了,以便它更易读):

从这个输出,你可以看出,我有一个参数化缓存计划,它被执行了两次,为每个EXEC语句各执行了一次.

使用参数化查询来节省资源和优化性能

在语句可以被执行之前,每个T-SQL语句都需要被评估,而且需要建立一个执行计划.创建执行计划会占用宝贵的CPU资源.当执行计划被创建后,它使用内存空间将它存储在过程缓存中.降低CPU和内存使用的一个方法是利用参数化查询.尽管数据库可以被设置为对所有查询FORCE参数化,但是这不总是最好的选择.通过了解你的哪些T-SQL语句可以被参数化然后使用sp_executesql存储过程,你可以帮助SQL Server节省资源并优化你的查询的性能.

[转载]QQ群发工具开发所用到工具和方法

mikel阅读(1284)

[转载]开发所用到工具和方法 – rolends1986 – 博客园.

  1. 关于windows窗体
  2. 简介:大家都知道基于windows系统的交互都基于窗体(window)的,所以window是交互界面,或者交互入口,在windows中基础编程中句柄表示各种类,对象,资源的实例,那么窗体也是有句柄的,窗体的中的控件一定有句柄?(答案下面揭晓)
  3. 窗体的分析方式
    1. Spy++:Spy++是一个基于 Win32(最新版本支持Win64) 的实用工具,它提供系统的进程、线程、窗口和窗口消息的图形视图(百度抄的),这个工具是微软自家,微软自家的没什么说的,功能很强大,它可以分析窗体句柄,捕获消息,查看线程等功能,
      1. 使用方法:
        image
      2. 分解示例
        image
    2. 通过上述示例我们可以得到winrar界面的窗体(句柄)结构,以及属性信息,通过这些属性信息就可以在筛选出我们需要的窗体了
    1. inspect32:inspect32也是一个窗体信息查看的,它和spy不同的是它可以查看菜单
    2. image
  4. 关于无句柄窗体(DUI)
    1. 最近这个几年发展起来的技术,大家可以百度”无句柄窗体”,这种窗体的好处很多,我比较喜欢的性能好,保密性好这个两方面的
    2. DUI窗体没有子控件句柄,要想获取信息就必须实现屏幕取词,HOOK ExtTextOut,TextOut,DrawText等字符绘制函数,获取相应的位置的字符串,然后筛选获取相应的信息
      image
  5. 按钮点击的实现
    1. 通过窗体消息

    SendMessage(hWnd,WM_LBUTTONDOWN, 0x0000001, lprama);按下鼠标左键

    SendMessage (hWnd,WM_LBUTTONUP, 0x0000000, lprama);放开鼠标左键

    其中lprama为位置参数,构造方法 int lprama = ((y << 16) | (x & 0xffff));

    1. 移动鼠标,发送点击事件

      WinAPIHelper.SetCursorPos(x, y); 设置鼠标位置 WinAPIHelper.mouse_event(WinAPIHelper.MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);按下鼠标左键

      WinAPIHelper.mouse_event(WinAPIHelper.MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);

      放开鼠标左键

  6. 文本设置实现
    1. 通过窗体消息

      WinAPIHelper.SendMessage(hwnd, WinAPIHelper.WM_SETTEXT, IntPtr.Zero, new StringBuilder(text));

    2. 模拟键盘

      WinAPIHelper.SetForegroundWindow(hwnd);//设置焦点

      SendKeys.SendWait(text);//发送键盘

     

  7. .Net 全局钩子的实现
    1. 简介:.net由于是托管代码,基于fx运行时(虚拟机),无法实现全局钩子,由于.netdll没有dllMain函数,所以无法实现远程注入
    2. DLL注入
      1. 上面说的无法实现远程,我这里来说P话?不是的,我这里说的注入混合型dll,大家可以百度,非托管dll可以通过DllMain,调用托管Dll实现托管到非托管的连接调用
    3. EasyHook

/Files/Rolends/开发所用到工具和方法.doc 后缀为docx,由于不能上传docx故改之

 

QQ 讨论组广告群发工具(已开发完成)索引

[转载]QQ登陆功能的实现2

mikel阅读(1030)

[转载]QQ登陆功能的实现2 – rolends1986 – 博客园.

QQ登陆功能的实现2

由于看到园子里有朋友说需要讲解和剖析实现的步骤,前面的QQ登陆实现只有代码,所以这篇补上

   1.  分析

1). 当运行QQ.exe后会出现qq登陆界面的窗体
image

2). 我们用spy++分析窗体可以看到窗体有2个子控件,一个是账号输入框,一个密码框
image

3). 再用spy++做事件分析,让spy++挂载hook后点解登陆按钮可以发现,按钮点击的坐标
image

  2.  上面我们详细分析了登陆过程的操作步骤需要的信息,现在来实现

1). 首先运行QQ,使用 Process pro = Process.Start(qqPath);这个方法来运行QQ

2). 然后我们需要找到登陆窗体的句柄,需要EnumDesktopWindows这个API来实现
   函数原型BOOL WINAPI EnumDesktopWindows(

 __in_opt HDESK hDesktop,

 __in WNDENUMPROC lpfn,

 __in LPARAM lParam

    );
大家都知道.net调用API比较烦,那么我们需要一个工具来简化我们的操作,在这里推荐2个工具,pinvoke.net(http://www.pinvoke.net/)和P/Invoke Interop     Assistant(http://clrinterop.codeplex.com/)后者是一个开源项目,个人来说喜欢前者一些,工具很简单一看就会,就不讲解了,接上

     我们需要EnumDesktopWindows来找到登陆窗体,这个api 的声明如下
   public delegate bool EnumDesktopWindowsDelegate(IntPtr hWnd, uint lParam);
[DllImport(“user32.dll”, EntryPoint = “EnumDesktopWindows”, ExactSpelling = false, CharSet =     CharSet.Auto, SetLastError = true)]

   [return: MarshalAs(UnmanagedType.Bool)]
   public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsDelegate lpEnumCallbackFunction, IntPtr lParam);
   使用这个API,然后根据pid就可以找到登陆窗口了

3).  找到登陆窗口之后我们需要找到登陆窗口里面的控件,这过程我们需要使用另一个API就是EnumChildWindows,函数原型:
BOOL EnumChildWindows(
HWND hWndParent,
WNDENUMPROC lpEnumFunc,
LPARAM lParam);
.net API声明
[DllImport(“user32.Dll”)]
  [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr parentHandle, EnumChildWindowsDelegate callback, IntPtr lParam);
  public delegate bool EnumChildWindowsDelegate(IntPtr hwnd, IntPtr lParam);
  找控件方法如下

 public static List<IntPtr> FindControl(IntPtr hwnd, string className, string title = null)
 {
            List<IntPtr> controls = new List<IntPtr>();
            IntPtr handle = IntPtr.Zero;
            while (true)
            {
                IntPtr tmp = handle;
                handle = WinAPIHelper.FindWindowEx(hwnd, tmp, className, title);
                if (handle != IntPtr.Zero)
                {
                    controls.Add(handle);
                }
                else
                    break;
            }
            return controls;
  }

4).  经过上面我们找到2个子控件,现在需要为2个子控件设置值,对于一般的文本框我们可以用SETText消息来实现,对于特殊的输入框,我们只能模拟按键实现

首先设置QQ号码WinAPIHelper.SendMessage(cons[0], WinAPIHelper.WM_SETTEXT, IntPtr.Zero, new StringBuilder(mainQQ)),其中cons[0]为输入框的句柄,这样就输入了QQ号码,输入密码需要以下步骤

A)窗体置顶,b)窗体获取焦点,c)输入控件获取焦点,d)发送按键
WinAPIHelper.BringWindowToTop(hLogonWnd)
,使用BringWindowToTop这个API来实现置顶,WinAPIHelper.SetForegroundWindow(hLogonWnd)使用SetForegroundWindow这个API来实现获取焦点,WinAPIHelper.SendMessage(cons[0], WinAPIHelper.WM_SETFOCUS, 0x001a0494, 0)使用SETFocus来设置控件焦点,SendKeys.SendWait(mainQQPwd)这个类来实现密码输入模拟

5).  经过上面步骤我们实现信息的输入,现在我们需要点解按钮
通过spy我们的到了按钮的坐标信息,关于点击的实现参照http://www.cnblogs.com/Rolends/archive/2012/04/18/2454964.html

6).  登陆操作过程完成,接下来就等待登陆结果,我们需要使用EnumDesktopWindows不断的来获取窗体进行辨别

word里写还是不行,发出来乱完了

QQ 讨论组广告群发工具(已开发完成)索引

[转载]SQLServer ROW_NUMBER()函数的使用

mikel阅读(1016)

[转载](原创)ROW_NUMBER()函数的使用 – InSky – 博客园.

ROW_NUMBER()函数是SQL 2005中新添的一个函数。通常它被用在分页的SQL语句中。

微软官方的对此函数的描述是:返回结果集分区内行的序列号,每个分区的第一行从 1 开始。

我对此的理解:利用此函数可以为表中的某个字段建立序列,从1开始。就是说,根据已存在的某列,利用此函数可建立一新列,新列是数字,按照已存在列的顺序从1开始。

   上边可能说的不是很清楚,请结合下边这个例子来看吧!

1USE Northwind
2GO
3
4--
5SELECT OrderID,
6 CustomerID,
7 OrderDate
8FROM dbo.Orders WITH(NOLOCK)
9
10SELECT OrderID,
11 CustomerID,
12 OrderDate,
13 ROW_NUMBER() OVER(ORDER BY orderID DESC) AS 字段编号
14FROM dbo.Orders WITH(NOLOCK)

第一个查询只是一个普通的查询,查询出Orders表中的3个字段的记录。其结果为:

第二个查询相比第一个查询仅仅是多了“ ROW_NUMBER() OVER(ORDER BY orderID DESC) AS 字段编号 这段语句。我们来分析一下这段语句。

 

这段语句的作用就是利用ROW_NUMBER()函数根据OrderID这列来生成一个新的数据列,这列的名称为字段编号。然后我们根据ORDER BY orderID DESC来指定字段编号这列按照OrderID的逆序来生成,结果如下:

 

 

 

比较两个结果集,会发现第二个结果集是按照OrderID字段逆序的记录,其实也可看成是字段编号这个字段的正序排序,ROW_NUMBER()函数相当于为SELECT语句末尾加了ORDER BY子句,第二个SELECT语句其实等价于:

1SELECT OrderID, 
2    CustomerID, 
3    OrderDate, 
4    ROW_NUMBER() OVER(ORDER BY OrderID DESC) AS 字段编号
5FROM dbo.Orders WITH(NOLOCK)
6ORDER BY 字段编号ASC

下边来看一个对ROW_NUMBER()函数的简单分页应用。

 1--Partition Page Demo
 2/* 取出第- 200条的记录(记录须按OrderID排序)*/
 3
 4WITH OrdersByOrderIDASC AS 
 5(
 6    SELECT OrderID, 
 7        CustomerID, 
 8        OrderDate, 
 9        ROW_NUMBER() OVER(ORDER BY orderID ASC) AS 字段编号
10    FROM dbo.Orders WITH(NOLOCK)
11)
12
13SELECT * 
14FROM OrdersByOrderIDASC WITH(NOLOCK)
15WHERE 字段编号BETWEEN 100 AND 110

首先把应用ROW_NUMBER()函数后的结果集存在一张临时表中,然后以字段编号这个字段为条件,使用BETWEEN关键字过滤相应的记录。

以上就是我对ROW_NUMBER()函数的一点粗浅认识,希望可以帮到大家!

[转载]SQL Server 2005 中的Row_Number()函数

mikel阅读(1048)

[转载]SQL Server 2005 中的Row_Number()函数 – 挖土. – 博客园.

我们知道,SQL Server 2005和SQL Server 2000 相比较,SQL Server 2005有很多新特性。这篇文章我们要讨论其中的一个新函数Row_Number()。数据库管理员和开发者已经期待这个函数很久了,现在终于等到了!

通常,开发者和管理员在一个查询里,用临时表和列相关的子查询来计算产生行号。现在SQL Server 2005提供了一个函数,代替所有多余的代码来产生行号。

我们假设有一个资料库[EMPLOYEETEST],资料库中有一个表[EMPLOYEE],你可以用下面的脚本来产生资料库,表和对应的数据。

USE [MASTER]
GO

IF EXISTS (SELECT NAME FROM SYS.DATABASES WHERE NAME = N'EMPLOYEE TEST')
DROP DATABASE [EMPLOYEE TEST]
GO

CREATE DATABASE [EMPLOYEE TEST]
GO

USE [EMPLOYEE TEST]
GO

IF EXISTS SELECT * FROM SYS.OBJECTS HERE OBJECT_ID = OBJECT_ID(N'[DBO].[EMPLOYEE]') AND TYPE IN (N'U'))
DROP TABLE [DBO].[EMPLOYEE]
GO

CREATE TABLE EMPLOYEE (EMPID INT, FNAME VARCHAR(50),LNAME VARCHAR(50))
GO

INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (2021110, 'MICHAEL', 'POLAND')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (2021110, 'MICHAEL', 'POLAND')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (2021115, 'JIM', 'KENNEDY')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (2121000, 'JAMES', 'SMITH')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (2011111, 'ADAM', 'ACKERMAN')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (3015670, 'MARTHA', 'LEDERER')
INSERT INTO EMPLOYEE (EMPID, FNAME, LNAME) VALUES (1021710, 'MARIAH', 'MANDEZ')
GO

 

 SELECT EMPID, RNAME, LNAME FROM EMPLOYEE

 这个查询的结果应该如图1.0

2021110

MICHAEL

POLAND

2021110

MICHAEL

POLAND

2021115

JIM

KENNEDY

2121000

JAMES

SMITH

2011111

ADAM

ACKERMAN

3015670

MARTHA

LEDERER

1021710

MARIAH

MANDEZ

图1.0

 在SQL Server 2005,要根据这个表中的数据产生行号,我通常使用下面的查询。

SELECT ROWID=IDENTITY(int,1,1) , EMPID, FNAME, LNAME INTO EMPLOYEE2 FROM EMPLOYEE ORDER BY EMPID

 这个查询创建了一个新的表,用identify函数来产生行号。我们用下面的查询来看看这个表的数据。

SELECT ROWID, EMPID, FNAME, LNAME FROM EMPLOYEE2

 上面的查询结果如图1.1

1

1021710

MARIAH

MANDEZ

2

2011111

ADAM

ACKERMAN

3

2021110

MICHAEL

POLAND

4

2021110

MICHAEL

POLAND

5

2021115

JIM

KENNEDY

6

2121000

JAMES

SMITH

7

3015670

MARTHA

LEDERER

图1.1

 这个查询结果很明显EMP=2021110的行是重复的数据。

 要删除EMPID=2021110的重复数据,我们必须在EMPLOYEE2表中删除,不能直接在EMPLOYEE中删除。

 SQL Server 2005提供了一个新的函数(Row_Number())来产生行号。我们可以使用这个新函数来删除原来表中的重复数据,只用通常的表达方式再加上Row_Number()函数。

 让我们用Row_Number()函数根据EMPID来产生ROWID。

 SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE

 上面的查询结果如图1.2

1

1021710

MARIAH

MANDEZ

2

2011111

ADAM

ACKERMAN

3

2021110

MICHAEL

POLAND

4

2021110

MICHAEL

POLAND

5

2021115

JIM

KENNEDY

6

2121000

JAMES

SMITH

7

3015670

MARTHA

LEDERER

图1.2

 在这个结果中,我们可以区别EMPID是2021110的重复数据。

 我们可以用通用表查询表达式和Row_Numner()函数来选出重复的那行数据。

 WITH [EMPLOYEE ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE)
SELECT * FROM [EMPLOYEE ORDERED BY ROWID] WHERE ROWID =4

上面的查询结果如图1.3

4

2021110

MICHAEL

POLAND

图1.3

 这一行重复的数据可以用下面这个通用表和Row_Number()函数来删除。

 WITH [EMPLOYEE ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE)
DELETE FROM [EMPLOYEE ORDERED BY ROWID] WHERE ROWID =4

 删除以后,我们可以用下面的查询语句看一下结果。

 SELECT * FROM EMPLOYEE

 这个查询结果如图1.4

2021110

MICHAEL

POLAND

2021115

JIM

KENNEDY

2121000

JAMES

SMITH

2011111

ADAM

ACKERMAN

3015670

MARTHA

LEDERER

1021710

MARIAH

MANDEZ

图 1.4

 这里我们可以看到,重复的数据已经被删除了。

 总结

在这篇文章中,我们讨论了SQL Server 2005 的新特性Row_Number()函数,还有通常的表表达式,然后如何使用这两个来删除重复的行。

______________________________________________

原文:http://www.databasejournal.com/features/mssql/article.php/3577481

[转载]支持定位当前页自定义排序的分页SQL(拒绝动态SQL)

mikel阅读(1107)

[转载]支持定位当前页,自定义排序的分页SQL(拒绝动态SQL) – 牟向阳 – 博客园.

1,场景:根据学生编号查询,返回该学生所在班级的所有学生。支持分页、自定义排序及结果集自动定位到查询条件的学生编号所在页。

CREATE PROC [dbo].[Sp_testpagerandsorting] (@GroupID     INT,
                                            @CurrentId   INT,
                                            @TimeFrom    DATETIME,
                                            @TimeTo      DATETIME,
                                            @OrderBy     CHAR(50),
                                            @PageSize    INT,
                                            @CurrentPage INT)
AS
  SET nocount ON

  BEGIN
      DECLARE @StartNumber        INT,
              @EndNumber          INT,
              @CurrentIdRowNumber INT,
              @RecordCount        INT,
              @EndPageIndex       INT
      DECLARE @RowNumberTable TABLE (
        rownumber INT IDENTITY (1, 1),
        id        INT )

      --step 1: Build sort id list -------------------------------------------------------       
      INSERT INTO @RowNumberTable
                  (id)
      SELECT sm.id AS id
      FROM   dbo.test sm WITH (nolock)
      WHERE  indate BETWEEN Coalesce(@TimeFrom, indate) AND
                            Coalesce(@TimeTo, indate)
             AND sm.groupid = @GroupID
      ORDER  BY CASE
                  WHEN @OrderBy = 'InDate desc' THEN ( Row_number() OVER (ORDER BY indate DESC))
                  WHEN @OrderBy = 'InDate asc' THEN (Row_number() OVER (ORDER BY indate ASC))
                  WHEN @OrderBy = 'Id asc' THEN (Row_number() OVER (ORDER BY sm.id ASC))
                  WHEN @OrderBy = 'Id desc' THEN (Row_number() OVER (ORDER BY sm.id DESC))
                  WHEN @OrderBy = 'Name asc' THEN (Row_number() OVER (ORDER BY sm.name ASC))
                  WHEN @OrderBy = 'Name desc' THEN (Row_number() OVER (ORDER BY sm.name DESC) )
                END

      --step 2: Reset page index with current id -----------------------------------------  
      IF @CurrentIdNumber > 0
        BEGIN
            SELECT TOP 1 @CurrentIdRowNumber = rownumber
            FROM   @RowNumberTable
            WHERE  id = @CurrentIdNumber

            IF @CurrentIdRowNumber > 0
              BEGIN
                  IF @CurrentPage = 0
                    BEGIN
                        SET @CurrentPage = Ceiling(CAST(@CurrentIdRowNumber AS DECIMAL) / CAST (@PageSize AS DECIMAL))
                    END
              END
        END
      ELSE
        BEGIN
            IF @CurrentPage = 0
              BEGIN
                  SET @CurrentPage = 1
              END
        END

      --step 3: Set recordCount -----------------------------------------      
      SELECT @RecordCount = COUNT(1)
      FROM   @RowNumberTable

      --step 4: Calc startNumber & endNumber -----------------------------------------   
      SELECT @StartNumber = @PageSize * ( @CurrentPage - 1 ),
             @EndNumber = @PageSize * ( @CurrentPage - 1 ) + @pageSize,
             @EndPageIndex = Ceiling(CAST(@RecordCount AS DECIMAL) / CAST(@PageSize AS DECIMAL))

      IF @CurrentPage = @EndPageIndex
        BEGIN
            SET @EndNumber = @RecordCount
        END
      --step 5: Get sorted id of current page -----------------------------------------           
      ;WITH a
           AS (SELECT TOP (@EndNumber - @StartNumber) id,
                                                      rownumber
               FROM   (SELECT TOP (@EndNumber) id,
                                               rownumber
                       FROM   @RowNumberTable) AS b
               ORDER  BY rownumber DESC)
      --step 6: Return current page idList -------------------------------------------------------        
      SELECT [ID],
             [GroupID] [Name],
             [Address]
      FROM   dbo.test sm WITH(nolock)
             INNER JOIN a
               ON a.id = sm.id
      ORDER  BY a.rownumber

      -- step 7:return current page & record count ----------------------------------  
      SELECT @CurrentPage AS currentpage,
             @RecordCount AS recordcount
  END  

2,简单条件的,动态where语句(关于Like查询的动态where,建议使用笨办法做)

CREATE PROC [dbo].[Getstudentlistbycondition] @Name  NVARCHAR(20),
                                              @Class INT
AS
  SET nocount ON 
  BEGIN
      BEGIN
          SELECT [Name],
                 [class]
          FROM   [testtable]
          WHERE  [Class] = CASE
                             WHEN @Class > 0 THEN @Class ELSE [Class] END
                 AND [name] = CASE
                                WHEN @Name <> '' THEN @Name ELSE [Name] END
      END
  END