[转载]ASP.NET MVC3 20个秘方-(16)让你的网站在移动设备上同样精彩!

mikel阅读(1056)

[转载]【译】MVC3 20个秘方-(16)让你的网站在移动设备上同样精彩! – 技术弟弟 – 博客园.

问题

默认情况下,你的网站在移动设备上展现的可能不太好。当然,有的移动设备足够让你的网站在它上显示。但是这也不是稳妥的。可能你不希望为移动电话创建一个全新的网站,成本太高。

解决方案

使用JQuery mobile NuGet包,改变共享的布局和视图,并使你的网站在传统的浏览器和大部分手机展现的都很好。

讨论

首先,如果你一直在阅读关于4 MVC的路线图,你会注意到很多讨论是围绕着移动的增强。特别的是,在这个例子中我们将使用JQuery mobile工具箱。

不幸的是,如很多事情表明这将是MVC中的4中的内容,在这个告诉我们还为时过早。与其“等待”我们不如提供一个非常简单的解决方案,需要最小的努 力来维护移动Web应用程序和一个普通的Web应用程序。此外,Windows8很快出来,它在桌面上支持HTML5 Web应用程序,它也将是一个桌面应用程序。

提示:维护同一网站的多个版本带来风向和额外的时间要求。每次添加新的功能,
你必须首先测试在多种环境的新功能,在多种环境做回归测试。此外,仅仅是因为
这被认为是“简单”并不意味着它不需要动脑筋:必须给花费很多心思来组织页面的结构,以确保它的建成尽可能最好的两个主要平台:台式机浏览器和移动浏览器。

作为开始,我们需要从NuGet package manager 安装JQuery Mobile 包。选Online,再右边搜索框里输入jQueryMobile。

在这之前,我们要注意的是,当前版本的MVC3 默认包括的是1.5X版本的jQuery 。当前最新版本的是JQuery1.7x。所以必须更新Jquery的版本。幸运的是,NuGet 管理工具已经提供了一个简单的方式去实现:

在当前项目:Tools→Library Package Manager→Add Library Package Reference.我们需要更新现有的JQuery 而不是添加新的Jquery。在左边的面板上,选择Update。在更新Jquery之前,需要先更新一些子包,否则会出错哦!我发现了一些正确的更新顺 序,点选每一个并且点击update:Jquery.Validation, Jquery.vs.doc, Jquery.ui.combined最后点JQuery。

一旦所有的包都被更新成功,在搜索框里输入Jquery.Mobile 并且安装。这将安装一些必需的CSS和JavaScript文件。jQuery Mobile的插件是基于HTML5的语法。使用这个语法,各种CSS和JavaScript操作为页面提供所需的外观,很接近一些较受欢迎的智能手机上 的内置应用。

这个例子的目的是演示如何可以更新现有的网站。使用这个新的library,仍然保持一个Web版本,以及一个移动版本。首先,需要更新Shared文件夹下的_Layoutview去匹配jQuery Mobile页面解剖语法。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/jquery.mobile-1.0.min.css")"rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")"type="text/javascript"></script>
    <script type="text/javascript">
                $(document).ready(function () {
                   
                    if (window.innerWidth > 480) {
                        $("link[rel=stylesheet]").attr({ href: '@Url.Content("~/Content/Site.css")' });
                    }
                });
    </script>
    <script src="@Url.Content("~/Scripts/jquery.mobile-1.0.min.js")"type="text/javascript"></script>
    @RenderSection("JavaScriptAndCSS", required: false)
</head>
<body>
    <div class="page" data-role="page" data-theme="a">
        <div id="header" data-role="header" data-theme="e">
            <div id="title">
                <h1>
                    My MVC Application</h1>
            </div>
            <div id="logindisplay" class="ui-bar">
                @Html.Partial("_LogOnPartial")
                [ @Html.ActionLink("English", "ChangeLanguage", "Home", new { language = "en" }, null)
                ] [ @Html.ActionLink("Français", "ChangeLanguage", "Home", new { language = "fr" }, null)
                ]
            </div>
            <div id="menucontainer" class="ui-bar">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home", null, new Dictionary<string, object> { { "data-role", "button" } })</li>
                    <li>@Html.ActionLink("About", "About", "Home", null, new Dictionary<string, object> { { "data-role", "button" } })</li>
                </ul>
            </div>
        </div>
        <div id="main" data-role="content">
            @RenderBody()
        </div>
        <div id="footer" data-role="footer">
        </div>
    </div>
</body>
</html>

这是shared/_layout下项目自动创建的模板。实现JQuery Mobile的功能,已经完成下列事项:

1. 包含了 JQuery Mobile CSS 文件

2. 包含了JQuery Mobile JavaScript 文件

3.添加了多个data-role 属性在已经存在的<div>标签里,包括 page, header, content, 和 footer和一些其他元素。

4. 添加一些 JavaScript 探测去切换 CSS,如果浏览器的size大于480像素,使用default CSS。

提示:

有几种方法来完成的最后一项(例如,在CSS中使用@Media标记目标屏幕尺寸,执行手机和浏览器检测等)。根据您的需要,你将需要确定什么是最好的解决办法。也许你的网站应该执行某种模板,你自己决定。

(译者:我按照例子做是没弄出来的,一定要注意当前_layout 所引用的css 和javascript的版本是否和你项目里的一样。)

如果你运行的应用程序两次(一次在全屏模式下,一次在移动设备或简单调整低于480像素的浏览器),你会看到两个非常不同的网站(参见下图)。

普通版

Mobile版:

(译者:酷么?你怎么觉得我不知道,我是觉得很酷!

如果您没有mobile设备可以测试的话。你也可以注释掉下边代码来查看效果:

 <script type="text/javascript">

                $(document).ready(function () {

                  

                    if (window.innerWidth > 480) {

                        $("link[rel=stylesheet]").attr({ href: '@Url.Content("~/Content/Site.css")' });

                    }

                });

    </script>

你也许会说,仍然有大量的工作要做,使一切看起来不错,但通过添加一些额外的data-role属性的默认布局,90%的工作已经完成了。接下来的步骤是探索特殊功能去让你的网站很有趣。 jQuery Mobile的有以下基本功能:

•导航条(中页眉或页脚,带或不带图标)
•页面过渡
•对话框

•按钮
•表格
•列表视图(在移动平台上提供了典型的手指滚动)
•全面的主题化的支持,以换出完整的外观和感受。

导航条事例:

<div id="logindisplay" class="ui-bar">
@Html.Partial("_LogOnPartial")
@Html.ActionLink("English", "ChangeLanguage", "Home",
new { language = "en" }, null) ]
@Html.ActionLink("Francais", "ChangeLanguage", "Home",
new { language = "fr" }, null) ]
</div>

下面的例子将呈现典型的智能手机的按钮,以及其他链接都将添加相同的样式。

@Html.ActionLink("My Cool Link", "SomeAction", "Home", null,
new Dictionary<string, object>
{{ "data-transition", "slide" }})

以下页面的过渡将在加载完Ajax后显现在新的内容。在我们的标准网站,其他的所有连接也这样做。

Dialog 例子:

<a href=”foo.html” data-rel=”dialog”>Open dialog</a>

就像前面的例子,这将呈现为Web浏览器的通用的链接,但在移动版本,标准的弹出窗口将显示。

Button 例子:

<a href="index.html" data-role="button" data-inline="true">Cancel</a> 
<a href="index.html" data-role="button" data-inline="true" data-theme="b">Save</a>

正如您可能期望的那样,这些按钮放置在Header,他们将呈现在最上面一栏,一个在左边,一个在右边,模仿今天在智能手机的标准Header按钮。

Form例子:

<div class="editor-label">
@Html.LabelFor(model => model.ShortName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ShortName)
@Html.ValidationMessageFor(model => model.ShortName)
</div>

没有什么改变。大多数内置的形式功能将完全呈现jQuery Mobile的预期。

List View例子:

<ul data-role="listview" data-theme="g">
    <li><a href="acura.html">Acura</a></li>
    <li><a href="audi.html">Audi</a></li>
    <li><a href="bmw.html">BMW</a></li>
</ul>

上面的例子将列出所有的books,他们的标题被设置成为一个转向详细页的链接。这个链接在一个标准可滚动的列表中。
更改Theme的示例:
目前,jQuery Mobile的五个内置的主题,从一个字母到E每个
上述项目可以通过追加一个新的属性称为,改变他们的主题。data-theme,用不同的字母(A至E)表示。

(译者:由于书里没有给例子。我在我们的_layout上改变代码如下:

<div class="page" data-role="page" data-theme="a">
        <div id="header" data-role="header" data-theme="e">

我给page 一个主题是a,给header一个主题是e。下图是效果:


是不是有点像某个系列书的风格,囧。

还有太多太多jquery mobile 的例子。想知道更详细的内容,可以去JQuqey Mobile 官网去看看)

另请参见:

JqueryMobile

[转载]ASP.NET MVC 3.0:基于Ajax的表单提交,认证成功调整回原页面

mikel阅读(1193)

[转载]ASP.NET MVC 3.0:基于Ajax的表单提交,A页面认证失败后页面被强转至登录页面,待登录成功将如何回到A页面? – 不觉流年似水 – 博客园.

很多网站的首页都提供信息的输入,而不论您是否有账户且已登录。比如我喜欢逛的42qu(我 跟创始人无任何关系,仅是喜欢该网站且无意广告,有兴趣的可以瞧瞧去)。当我发表自己的”碎碎念”时,会被自动跳转到登录页,而问题是登录成功后能否再回 到原来的页面。听起来这个问题略显乏善可陈,然而它的实现框架是MVC 3.0,而且为了寻求其优雅的实现方式尝试了很多天,现作为记要并分享一下。

先来一张最终效果图吧:

image

上图的页面切分为两个区域,一是碎碎念的发表区(上部分)、另一个是呈现区(下部分),以真分页提取数据;信息提交是基于Ajax.BeginForm()进行的,然后即时无刷新于呈现区。

看看这个基于异步提交的表单是如何陈述的:

值得关注的属性有UpdateTargetId,它预示着异步加载的数据将呈现于哪个Div;另外OnSuccess事件也尤为重要,它担当着回调的处理重任。最后,异步请求的数据想成功加载于指定的Div中,得引用 JQuery.validate.unobtrusive.min.js脚本文件(第3行):

这个js文件的功能类似于ASP.NET AJAX中的”万能”控件UpdatePanel。

现在我想发表一条碎碎念,试图点击”碎碎念”进行发送,由于近一个星期没有登录,其相关Cookie已然过期,页面将被强转至登录页 (Login.cshtml),跳转本身倒是我的设计意图,但页面呈现出的结果却很糟糕,因为整个登录页面全部呈现在id名 为”ChatIntegrationContent”的Div内,其页面大家可以想像得出是很荒唐的,于是我要解决两个问题:

1.默认的认证属性AuthorizeAttribute已经不能满足我的认证需求了,我得自定义认证类LoginAuthorizeAttribute并继承它,且重写(override)相关受保护的虚方法(protected virtual method);

2. 服务器端的任何跳转显然已是徒劳:无论是Redirect还是变向的模拟Server.Transfer模式来曲线跳转,都会乖乖地落入原 UpdateTargetId属性指定的Div中。一开始我想,在服务端我能取消Ajax调用吗?使其转变成普通调用,即形同 @Html.BeginForm()这种普通表单的Post请求。答案是NO!这个请求是Ajax异步产生的,其命运已然注定,故唯一的途径便是通知客户 端脚本作window.location跳转!

祸源于Ajax异步调用,解决之道自然还要从它入手,且看看自定义认证类有如何了不得:

继承AuthorizeAttribute后, 仅重写了虚函数:protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext);这意味着形如:[Authorize(Roles = “Member”)]这样的认证逻辑依旧采用MVC 3.0自己的算法,看了一下它的源码,确实比我自己另起灶炉写得全面而优雅得多,我羞愧的将自己实现的百来行代码统统删除,面向对象的可重用性真是棒啊。 可以看到,上下文对象filterContext的属性Result已被设置成为JsonResult。另外有个内在原因要注意,Authorize属性(Attribute)认证失败后,是因为抛出了401异常,才导致MVC框架跳转到登录页(loginUrl属性所指定):

loginUrl属性显式指定了登录页,按照dudu老大的建议,cookieless也显式指定为 UseCookies,为测试方便Cookie和认证票据(Ticket)都只设置一分钟(timeout=”1″)。

上述代码最重要的一句是:filterContext.HttpContext.Request.IsAjaxRequest()

它 拷问当前请求是否为异步的Ajax请求,之所以这么判断是因为客户端发来的假如是基于@Html.BeginForm()的普通请求,那服务端可以轻松跳 转而刷新整个浏览器,不用担心登录页落入相关Div之内;相反Ajax请求则要加倍呵护,它得通知JS脚本作客户端跳转 (window.location),方能保证登录页是刷新整个浏览器。以JSON格式封装一个键值对传回客户端:

继 承自ActionResult的”JSON格式内容”类JsonResult,有一个专门用来装载数据的Object型属性Data,目前只需要一个键值 对足够。另外JsonRequestBehavior = JsonRequestBehavior.AllowGet也举足轻重:为了避免JSON Hijacking攻击,自MVC2.0起任何以JsonResult类返回的请求都不允许Get型的Ajax请求获取数据,即便数据对象内才一个键值 对,而妥协地采用Post请求也将使数据无法被浏览器缓存,其实这些数据的敏感度又不高,所以采用Get请求是个很好的开发体验!到目前为止,对客户端发来的Ajax请求,服务端在认证失败情况下给出了应答,即通知客户端脚本”我对你验证失败”—-Ajax_UnAuthorized。

还记得Ajax.BeginForm()里的属性OnSuccess吧?现在看看它的JS函数:

客户端的接收也很干脆,如果通知信息两相统一,那么直接赋值给window.location作页面跳转,刷新整个浏览器。采用Request.Url获取到当前页面的路由网址作为跳转路径的参数一并携带过去,结果形如:

http://localhost:1650/Account/LogOn?returnUrl=http%3A%2F%2Flocalhost%3A1650%2F

强转到登录页时,得先让用户输入表单,待提交表单且登录验证通过后,才利用returnUrl参数值跳转回原来的页面,所以我们需要把returnUrl参数值暂存起来,看看LogOn.cshtml的登录表单头:

BeginForm()的第三位参数叫路由参数:object routeValues,我们把”返回页”暂存于此,待将来登录表单提交时,该参数将一并提交到服务器,交由标记了HttpPost特性的Action处理:

路由网址中的参数returnUrl可以在 HttpPost限定的Action作为参数获取:显然第一个是登录表单实体,第二个便是其参数。

只要判断传入参数returnUrl是否为空,便可作出恰当的跳转选择,所以只要returnUrl是一个具体的网址就可以顺利返回,这就是MVC3.0中对返回原页面的较为简洁而优雅的处理方案

[转载]PHP JSON使用 | JavaScript JSON使用

mikel阅读(1074)

[转载]PHP JSON使用 | JavaScript JSON使用.

PHP JSON使用

由于JSON可以在很多种程序语言中使用,所以我们可以用来做小型数据中转,如:PHP输出JSON字符串供JavaScript使用等。在 PHP中可以使用json_decode()由一串规范的字符串解析出JSON对象,使用json_encode()由JSON对象生成一串规范的字符 串。下面就重点介绍这两个函数:
json_decode(),字符转JSON,一般用在接收到JavaScript发送的数据时会用到,

代码

<?php
$s={“webname”:”homehf”,”url”:”www.homehf.com”,”contact”:{“qq”:”744348666″,”mail”:”nieweihf@163.com”,”xx”:”xxxxxxx”}};
$web=json_decode($s);
echo 网站名称:.$web->webname.<br />网址:.$web->url.<br />联系方式:QQ-.$web->contact->qq.&nbsp;MAIL:.$web->contact->mail;
?>

上面的例子中,我们首先定义了一个变量s,然后用json_decode()解析成JSON对象,之后可以按照JSON的方式去使用,从使用情况 看,JSON和XML以及数组实现的功能类似,都可以存储一些相互之间存在关系的数据,但是个人觉得JSON更容易使用,且可以使用JSON和 JavaScript实现数据共享。

json_encode(),JSON转字符,,这个一般在AJAX应用中,为了将JSON对象转成字符串并输出给JavaScript时会用到,而向数据库中存储时也会用到,

<?php
$s={“webname”:”homehf”,”url”:”www.homehf.com”,”contact”:{“qq”:”744348666″,”mail”:”nieweihf@163.com”,”xx”:”xxxxxxx”}};
$web=json_decode($s);
echo json_encode($web);
?>

在做AJAX应用时,JSON是经常用到的,那么JavaScript中有没有类似PHP的json_decode()函数呢?请看下文。

JavaScript JSON使用

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScript(Standard ECMA-262 3rd Edition – December 1999)的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等),这些特性使JSON成为理想的数据交换语言。
在PHP中我们可以使用json_decode()json_encode()来处理JSON对象,那么在JavaScript中呢?
1.创建JSON对象:
var tst={  //创建tst,需要注意JSON的格式
'webname':'homehf',
'url':'www.homehf.com',
'contact':{
'qq':'744348666',
'mail':'nieweihf@163.com'
},
'xx':'xxxxxxx'
}
alert(tst.contact.qq);  //使用JSON数据

2.将字符串转成JSON对象:
var s='{"webname":"homehf","url":"www.homehf.com","qq":"744348666"}';
var tst=eval("("+s+")");  //使用eval()函数,一定要注意这里的双引号
alert(tst.contact.mail);

3.将JSON对象转成字符,如果有嵌套的话,大家也可以自己写个函数递归处理:
//我们使用上面创建的JSON对象

s='{';
for(var x in tst)
{
s
+='"'+x+'":"'+tst[x]+'",';
}
if(s.substring(s.length-1)==',')
{
s
=s.substring(0,s.length-1);
}
s
+='}';
alert(s);


转成字符串后,发送到服务器端,然后保存到数据库中,这样就可以实现JSON数据的保存了。

[转载]在ASP.NET MVC3项目中结合NInject组件实现依赖注入的设计

mikel阅读(946)

[转载]在MVC3项目中结合NInject组件实现依赖注入的设计 – 陈希章 – 博客园.

这是本次MVC3讲座中的一个话题,整理出来给大家参考参考

名词解释

依 赖注入:英文是Dependency Injection。有时候也称为反转控制(Ioc)吧。不管名词怎么讲,它的大致意思是,让我们的应用程序所依赖的一些外部服务,可以根据需要动态注 入,而不是预先在应用程序中明确地约束。这种思想,在当前的软件开发领域,为了保证架构的灵活性,应该还是很有意义的。

在MVC这个框架中,为依赖注入的设计提供了先天的支持。结合一些我们熟知的DI组件,例如NInject,我们可以较为容易地实现上述提到的功能。

场景介绍

我们的应用程序,需要支持各种不同的数据源,而且我们希望日后可以很容易地切换,不会因为数据源的变化而导致对Contoller或者Model,或者View做修改。

本文完整源代码,请通过这里下载 MvcApplicationDISample.rar

演练步骤

第一步:准备一个MVC项目(选择空白模板)

image

第二步:准备一个业务实体类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplicationDISample.Models
{
    public class Employee
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

第三步:准备一个数据访问的接口定义

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MvcApplicationDISample.Models;

namespace MvcApplicationDISample.Services
{
    public interface IDataService
    {
        Employee[] GetEmployee();
    }
}

第四步:创建一个HomeController

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationDISample.Services;
using MvcApplicationDISample.Models;

namespace MvcApplicationDISample.Controllers
{
    public class HomeController : Controller
    {

        IDataService DataService;
        public HomeController(IDataService service)
        {
            DataService = service;
        }

        //
        // GET: /Home/

        public ActionResult Index()
        {
            var data = DataService.GetEmployee();
            return View(data);
        }

    }
}

注意,这里需要为HomeController添加一个特殊的构造函数,传入IDataService这个接口。通常,所有的DI组件都是通过这样的方式注入的。

在设计HomeController的时候,我们不需要关心到底日后会用具体的哪种DataService,我们只是要求要传入一个IDataService的具体实现就可以了,这就是DI的本质了。

到这里为止,我们该做的准备工作基本就绪了。下面来看看如何结合DI组件来实现我们的需求

第五步:引入NInject组件

这是我比较喜欢的一个DI组件。它还针对MVC3专门有一个扩展

image

image

添加这个组件之后,除了自动添加了很多引用之外,还有一个特殊的文件App_Start\NinjectMVC3.cs

image

[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplicationDISample.App_Start.NinjectMVC3), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplicationDISample.App_Start.NinjectMVC3), "Stop")]

namespace MvcApplicationDISample.App_Start
{
    using System.Reflection;
    using Microsoft.Web.Infrastructure.DynamicModuleHelper;
    using Ninject;
    using Ninject.Web.Mvc;

    public static class NinjectMVC3
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
            DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
            bootstrapper.Initialize(CreateKernel);
        }

        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }

        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            RegisterServices(kernel);
            return kernel;
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
        }
    }
}

这个类型很有意思,WebActivator.PreApplicationStartMethod这个方法其实是注册了一个在MVC程序启动之前运行的方法。这些代码大家应该能看懂,它在CreateKernel中,添加一个新的Kernel(用来做注入的容器)。

第六步:创建一个IDataService的具体实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcApplicationDISample.Models;

namespace MvcApplicationDISample.Services
{
    public class SampleDataService:IDataService
    {
        #region IDataService Members

        public Employee[] GetEmployee()
        {
            return new[]{
                new Employee(){ID=1,FirstName="ares",LastName="chen"}};
        }

        #endregion
    }
}

作为举例,我们这里用了一个硬编码的方式实现了该服务。

第七步:实现注入

回到App_Start\NinjectMVC3.cs这个文件,修改RegisterServices方法如下

       /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<Services.IDataService>().To<Services.SampleDataService>();
        }

第八步:测试Controller的功能

image

我们可以看到,数据已经展现出来了。这说明,HomeController中的Index方法,确实调用了我们后期插入的这个SampleDataService。而通过下图,则可以更加清楚看到这一点

image

到这里为止,我们就结合Ninject组件实现了一个简单的依赖注入的实例。Ninject 针对MVC 3有这么一个特殊的文件,可以极大地方便我们的编程。但即便没有这个文件,我们也可以通过另外一些方法来实现需求。

下面介绍两种比较传统的,通过扩展MVC组件实现的方式

第一种:实现自定义ControllerFactory

我们都知道,Controller其实都是由ControllerFactory来生成的,那么,为了给所有新创建从Controller都自动注入我们的服务,那么就可以从ControllerFactory这个地方动动脑筋了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ninject;
using MvcApplicationDISample.Services;

namespace MvcApplicationDISample.Extensions
{
    public class InjectControllerFactory:DefaultControllerFactory
    {

        private IKernel kernel;
        public InjectControllerFactory()
        {
            kernel = new StandardKernel();
            kernel.Bind<IDataService>().To<SampleDataService>();
        }
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            return (IController)kernel.Get(controllerType);
        }
    }
}

要使用这个自定义的 ControllerFactory,我们需要修改Global.ascx文件中的Application_Start方法,添加下面的粗体部分代码

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

           ControllerBuilder.Current.SetControllerFactory(new Extensions.InjectControllerFactory());
        }

这样做好之后,我们可以测试HomeController中的Index这个Action,我们发现它还是能正常工作。

image

第二种:实现自定义的DependencyResolver

顾名思义,这就是MVC框架里面专门来处理所谓的依赖项的处理器。可以说这是MVC专门为DI准备的一个后门。下面是我写好的一个例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ninject;
using MvcApplicationDISample.Services;

namespace MvcApplicationDISample.Extensions
{
    public class InjectDependencyResolver:IDependencyResolver
    {
        private IKernel kernel;

        public InjectDependencyResolver()
        {
            kernel = new StandardKernel();
            kernel.Bind<IDataService>().To<SampleDataService>();
        }

        #region IDependencyResolver Members

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        #endregion
    }
}

那么,如何使用这个自定义的处理器呢?

很简单,我们仍然是修改Global.asax文件中的Application_Start方法

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            //ControllerBuilder.Current.SetControllerFactory(new Extensions.InjectControllerFactory());

            DependencyResolver.SetResolver(new Extensions.InjectDependencyResolver());
        }

请注意,之前那个设置ControllerFactory的代码,我们可以注释掉了

这个解决方案的最终效果和之前是一样的。

image

本文完整源代码,请通过这里下载 MvcApplicationDISample.rar

[转载]ASP.NET 利器:在Global.asax之外实现Application_Start/End,程式元件使用就要这么灵活!

mikel阅读(969)

[转载]ASP.NET 利器:在Global.asax之外实现Application_Start/End,程式元件使用就要这么灵活! – kudy – 博客园.

在System.Web命名空间下, .NET 4.0版本新增了一个 PreApplicationStartMethodAttribute 类,利用它可以Appilcation_Start()前触发的初始化工作。这里主要讲Kudy.Net项目中的System.Kudy.Web.Activating。事实上System.Kudy.Web.Activating也是靠它来运作的,功能很相近但 PreApplicationStartMethodAttribute 有些限制,一是整个Web Application只能加载一次(AllowMultiple=false),如果多个元件都有自己的初始事件就没办法,二是它只提供 Appilcation_Start()前触发的选项,相较之下,System.Kudy.Web.Activating应用起来更方便,因充它允许多次 注册并且还有Appilcation_Start()后触发和Appilcation_End()前触发的选项。

System.Kudy.Web.Activating下提供了四个属性分别是ActivationAttribute、 PreApplicationStartAttribute、PostApplicationStartAttribute和 PreApplicationEndAttribute,利用它们可以灵活地随时在你的程序集中注册一个或多个Appilcation_Start()前/后触发和Appilcation_End()前触发的处理事件。下面来新建一个Web Application项目来测试它们。

下载例子源码:ActivatingWeb.rar ActivatingMvcWeb.rar

请使用vs2010打开项目,本文对你有帮助的话请点下推荐支持,或关注本博客,谢谢。

首先,引用System.Kudy程序集(注意:System.Kudy有.Net3.5和.Net4.0版本的,4.0版本才支持此功能)。新建ActivatingTest类,然后在里面再加上几个测试类,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// 引用命名空间
using System.Kudy.Web.Activating;
// 在程序集注册应用程序触发事件(建议写在:AssemblyInfo.cs)
[assembly: Activation(typeof(ActivatingWeb.MyApplication))]
[assembly: Activation(typeof(ActivatingWeb.MyApplication_2))]
[assembly: PreApplicationStart(typeof(ActivatingWeb.MyStaticApplication), "PreStart")]
[assembly: PostApplicationStart(typeof(ActivatingWeb.MyStaticApplication), "PostStart")]
[assembly: PreApplicationEnd(typeof(ActivatingWeb.MyStaticApplication), "PreEnd")]
namespace ActivatingWeb
{
public class ActivatingTest
{
// 用于显示测试事件调用情况
public static string OutputInfo = "";
}
/// <summary>
/// 方式1:以静态方法来触发事件
/// [assembly: PreApplicationStart(typeof(ActivatingWeb.MyStaticApplication), "PreStart")]
/// [assembly: PostApplicationStart(typeof(ActivatingWeb.MyStaticApplication), "PostStart")]
/// [assembly: PreApplicationEnd(typeof(ActivatingWeb.MyStaticApplication), "PreEnd")]
/// </summary>
public class MyStaticApplication
{
public static void PreStart()
{
ActivatingTest.OutputInfo += "PreStart() in MyStaticApplication<br />";
}
public static void PostStart()
{
ActivatingTest.OutputInfo += "PostStart() in MyStaticApplication<br />";
// 可以在此初始化 ASP.NET MVC 工作或 依赖注入(Dependency Injection),方式2类同
}
public static void PreEnd()
{
ActivatingTest.OutputInfo += "<br />PreEnd() in MyStaticApplication<br />";
// 可以在此做资源清理工作,方式2类同
}
}
/// <summary>
/// 方式2:以实现接口来触发事件
/// [assembly: Activation(typeof(ActivatingWeb.MyApplication))]
/// </summary>
public class MyApplication : ActivationBase
{
public override void PreApplicationStart()
{
ActivatingTest.OutputInfo += "PreApplicationStart() in MyApplication<br />";
}
public override void PostApplicationStart()
{
ActivatingTest.OutputInfo += "PostApplicationStart() in MyApplication<br />";
}
public override void PreApplicationEnd()
{
ActivatingTest.OutputInfo += "PreApplicationEnd() in MyApplication<br />";
}
}
/// <summary>
/// 方式2:以实现接口来触发事件
/// [assembly: Activation(typeof(ActivatingWeb.MyApplication_2))]
/// </summary>
public class MyApplication_2 : IPreApplicationStart
{
public void PreApplicationStart()
{
ActivatingTest.OutputInfo += "PreApplicationStart() in MyApplication_2<br />";
}
}
}

上面写了两种基本使用的方式,我们再新建Global.asax文件来辅助测试工作,因为Application_End的工作无法在页面中显示,所以写在文件ActivatingTest.txt里了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Kudy;
namespace ActivatingWeb
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
ActivatingTest.OutputInfo += "Application_Start() in Global<br />";
}
protected void Application_End(object sender, EventArgs e)
{
ActivatingTest.OutputInfo += "Application_End() in Global<br />";
// 这时程序程序已结束,不可能在网页中显示结果
// 写进根目录下ActivatingTest.txt文件里查看吧
string file = EnvironmentUtil.GetFullPath("ActivatingTest.txt");
string content = ActivatingTest.OutputInfo.Replace("<br />", "\r\n");
System.IO.File.WriteAllText(file, content);
}
}
}

最后在Default.aspx页面输入测试的结果:

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 输出调用结果
Response.Write(ActivatingTest.OutputInfo);
Response.End();
}
}

运行网站,会看到输出结果如下(多刷新几次都是一样的结果):

打开Web.config,然后点击保存,这里网站结束了,可以看到根目录下ActivatingTest.txt的测试结果为:

可能有人会说,有了Global.asax就足够了。是的,如果没有特别需要是足够了。但是,使用 System.Kudy.Web.Activating可以使你的代码结构更清晰,维护方便,而且可以在不修改原来的代码就可以随时添加 Application_Start/Application_End的触发处理工作,使得安装程式元件不需要修改 Application_Start()就可以使用,或多或少降低安装复杂度。还值得说的是,在.Net4.0中提供了一个叫 DynamicModuleUtility的类(位于Microsoft.Web.Infrastructure.dll程序集),里面只有一个方法 RegisterModule(Type moduleType),利用它可以动态的添加HttpModule,MVC3都是动态注册模块的,所以4.0版本的System.Kudy不需要在 Web.config里配置地址重写模块了。当然,你还可以利用System.Kudy.Web.Activating方便的注册HttpModule。

网上还有个老外写的webactivator程序集是实现这样的功能的,本人测试过发现某情况下出现bug,可能有多次触发的情况。

再弄个MVC下的使用例子,把Global.asax删除了,再添加一个MyMvcApplication类,然后启动网站,可以正常运行:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
// 引用命名空间
using System.Kudy.Web.Activating;
// 在程序集注册应用程序触发事件(建议写在:AssemblyInfo.cs)
[assembly: Activation(typeof(ActivatingMvcWeb.MyMvcApplication))]
namespace ActivatingMvcWeb
{
/// <summary>
/// 方式2:以实现接口来触发事件
/// [assembly: Activation(typeof(ActivatingWeb.MyApplication))]
/// </summary>
public class MyMvcApplication : ActivationBase
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
public override void PreApplicationStart()
{
// Mvc 不能在网站启动之前初始化
// 但是可以在这里初始化其它程式元件,例如依赖注入等等
}
public override void PostApplicationStart()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
public override void PreApplicationEnd()
{
}
}
}

运行结果:

另外说明一下:System.Kudy是本人为公司写的一个功能性类库,从之前的KudySharp重新整理并加强的,目前还在整理完善中,大 约在2012年底在公司项目完善时System.Kudy的稳定初版本应该就会发布了,可以免费提供给大家个人或商业使用,但此项目目前是不打算开源的,谢绝索要源码,如果会开源自然就开源的。

下载例子源码:ActivatingWeb.rar ActivatingMvcWeb.rar

请使用vs2010打开项目,本文对你有帮助的话请点下推荐支持,或关注本博客,谢谢。

[转载]从 Linq Queries 快速生成数据 HTML, EXCEL, CSV 报表

mikel阅读(1001)

[转载]从 Linq Queries 快速生成数据 HTML, EXCEL, CSV 报表 – 拥有的都是恩典(宋历) – 博客园.

在CodePlex 上经常可以发现一些好东西, 关键是有没有时间去淘宝.

前几天就发现一个, 并且在实际工作中使用了:

* DoddleReport

你有没有被要求基于来自数据库的数据,生成一个报表? 我们时不时会有类似的需求.

DoddleReport极大的简化了这方面的工作量.

首先你需要下载它的Dll 文件, 可以到 codeplex 中得到http://doddlereport.codeplex.com/

或者直接从这里下载: cnblogs下载地址

得到的是一样的文件, 将它解压到你的一个ASP.NET 网站的bin目录下. 你就可以引用Doddle的类了.

我们来模拟一个场景(本场景是根据DoddleReport提供的sample 代码改编的):

创建一个aspx页面, 加入下面的代码:

创建一个Product 类:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public double Price { get; set; }
    public int OrderCount { get; set; }
    public DateTime LastPurchase { get; set; }
    public int UnitsInStock { get; set; }

}
随机生成1000个Product的数据, 方便我们测试结果:
public static List<Product> GetAll()
{
    var rand = new Random();
    return Enumerable.Range(1, 1000)
        .Select(i => new Product
        {
            Id = i,
            Name = "产品 " + i,
            Description =
                "描述...",
            Price = rand.NextDouble() * 100,
            OrderCount = rand.Next(1000),
            LastPurchase = DateTime.Now.AddDays(rand.Next(1000)),
            UnitsInStock = rand.Next(0, 2000)

        })
        .ToList();
}
下面我们要用到DoddleReport的类:

protected void Page_Load(object sender, EventArgs e)
{
// Get the data for the report (any IEnumerable will work)
var query = GetAll();

// Create the report and turn our query into a ReportSource
var report = new Report(query.ToReportSource());

// Customize the Text Fields
report.TextFields.Title = “报告的标题”;
report.TextFields.SubTitle = “副标题”;
report.TextFields.Footer = “页脚”;
report.TextFields.Header = string.Format(@”制作时间: {0}”, DateTime.Now);

// Render hints allow you to pass additional hints to the reports as they are being rendered
report.RenderHints.BooleanCheckboxes = true;

// Customize the data fields
report.DataFields[“Id”].Hidden = true;
report.DataFields[“Price”].DataFormatString = “{0:c}”;
report.DataFields[“LastPurchase”].DataFormatString = “{0:d}”;

var writer = new HtmlReportWriter();
writer.WriteReport(report, Response.OutputStream);

}
首先用我们的数据生成一个Report的实例; 然后指定Report的一些属性, 例如标题, 页眉, 页脚(很酷,是吗). 再指定一些列的属性, 例如隐藏ID, 和给一些列特殊的格式.

最后调用ReportWriter将数据写出到页面.
下面是生成的截图:

ScreenShot070

如果你要生成Excel 或者CSV(以逗号或者tab为分隔符的文件)可以简单的修改ReportWriter后面的代码为:

string s = Request.PhysicalApplicationPath;

var exWriter = new ExcelReportWriter();
exWriter.WriteReport(report, System.IO.File.Create(s + “fil7.xls”));

或者

var deWriter = new DelimitedTextReportWriter();
deWriter.WriteReport(report, System.IO.File.Create(s + “fil7.csv”));

可以看看效果:

ScreenShot071

ScreenShot072

希望可以帮助到有这方面需要的朋友们.

本文来自于喜乐的ASP.NET(Alex Song) 转贴请注明出处

[转载]ASP.NET MVC 3 开发的20个秘诀(七)[20 Recipes for Programming MVC 3]:对列表进行排序

mikel阅读(835)

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(七)[20 Recipes for Programming MVC 3]:对列表进行排序 – O2DS – 博客园.

议题

现在有一个很大的列表(例如,图书列表),想找到某一项是非常难的。如果针对列表中的某一项进行排序,应该会对查找有所帮助。

解决方案

将书籍清单列表的列标题更新为链接,当链接被点击的时候,将通过Linq针对选中列的内容进行排序(通过再次点击标题链接来切换升序还是降序)。

讨论

与我之前使用过的框架相比添加排序、自动生成视图的过程让我感到有些惊讶。希望在外来的MVC版本中,他可以成为整体框架的一部分。参考 ASP.NET MVC的网站首页上示例,我们需要定义一个Switch语句,每一列排序情况都需要复制一个Case来实现。还好我们的这个案例当中只有五个项需要排序, 情况还不算太坏。如果以后需要针对比如作者或者其他列排序,只需要复制此Case来实现。在下面的示例中,我们将使用Dynamic Linq Library来简化工作。

Linq library 可以从数据库中查询并返回强类型结果。编程工具提供了如智能感知支持和编译时错误检测,我们很多的共奏都将基于这些功能进行操作。

在BooksController控制器和Books、Index视图中添加生成排序支持。以下是Index视图的代码:

@model IEnumerable<MvcApplication4.Models.Book>
<h2>@ViewBag.Title</h2>
<p>
    @Html.ActionLink((string)ViewBag.CreateLink, "Create")
</p>

<table>
    <tr>
        <th>
            @Html.ActionLink((string)ViewBag.TitleDisplay,
            "Index", new { sortOrder = ViewBag.TitleSortParam })
        </th>
        <th>
            @Html.ActionLink((string)ViewBag.IsbnDisplay,
            "Index", new { sortOrder = ViewBag.IsbnSortParam })
        </th>
        <th>
            @ViewBag.SummaryDisplay
        </th>
        <th>
            @Html.ActionLink((string)ViewBag.AuthorDisplay,
            "Index", new { sortOrder = ViewBag.AuthorSortParam })
        </th>
        <th>
            @ViewBag.ThumbnailDisplay
        </th>
        <th>
            @Html.ActionLink((string)ViewBag.PriceDisplay,
            "Index", new { sortOrder = ViewBag.PriceSortParam })
        </th>
        <th>
            @Html.ActionLink((string)ViewBag.PublishedDisplay,
            "Index", new { sortOrder =
            ViewBag.PublishedSortParam })
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Isbn)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Summary)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Author)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Thumbnail)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Published)
            </td>
            <td>
                @Html.ActionLink((string)ViewBag.EditLink,
                "Edit", new { id=item.ID }) |
                @Html.ActionLink((string)ViewBag.DetailsLink,
                "Details", new { id = item.ID }) |
                @Html.ActionLink((string)ViewBag.DeleteLink,
                "Delete", new { id = item.ID })
            </td>
        </tr>   
    }
</table>

在上面这个例子中,修改了以前创建的th标记,使用Html helper将静态文本转换为HTML链接。

接下来,需要修改BookController中的Index方法。此方法将会接受一个新的排序参数,此参数将在Linq执行查询时指定结果排序的列。更会在ViewBag创建一个新的变量存储每个列的排序条件。

Microsoft针对Linq提供了新的免费扩展DynamicQuery类,这个扩展允许在运行的时候动态生成查询语句,可以通过访问 http://msdn2.microsoft.com/en-us/vcsharp/bb894665.aspx 下载到C#版本。下载后解压到硬盘的某个位置,在目录中找到这个文件并添加到项目工程中“~\CSharpSamples\LinqSamples \DynamicQuery\DynamicQuery\Dynamic.cs”。为了更好的组织代码,我们需要在项目工程中创建“Utils”文件夹, 右键单击“Utils”文件夹选择“添加”->“现有项”,然后通过浏览窗口找到这个动态类(或者也可以直接通过拖拽将文件放入“Utils”目 录)。

动态类添加完成后,编辑更新BooksController:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Web;
using System.Web.Mvc;
using MvcApplication4.Models;
using MvcApplication4.Resources;

namespace MvcApplication4.Controllers
{
    public class BooksController : Controller
    {
        private BookDBContext db = new BookDBContext();
        //
        // GET: /Books/
        public ViewResult Index(string sortOrder)
        {
            #region ViewBag Resources
            … 
            #endregion
            #region ViewBag Sort Params
            // Define the sort orders - if the same link is
            // clicked twice, reverse the direction from
            // ascending to descending
            ViewBag.TitleSortParam = (sortOrder == "Title")
            ? "Title desc" : "Title";
            ViewBag.IsbnSortParam = (sortOrder == "Isbn")
            ? "Isbn desc" : "Isbn";
            ViewBag.AuthorSortParam = (sortOrder == "Author")
            ? "Author desc" : "Author";
            ViewBag.PriceSortParam = (sortOrder == "Price")
            ? "Price desc" : "Price";
            ViewBag.PublishedSortParam =
            (String.IsNullOrEmpty(sortOrder))
            ? "Published desc" : "";
            // Default the sort order
            if (String.IsNullOrEmpty(sortOrder))
            {
                sortOrder = "Published desc";
            }
            #endregion
            var books = db.Books.OrderBy(sortOrder);
            return View(books.ToList());
        }
        ...
    }

}

参考

System.Linq.Expressions 名字空间 原书地址 书籍源代码

[转载]T-SQL查询进阶--详解公用表表达式(CTE)

mikel阅读(1275)

[转载]T-SQL查询进阶–详解公用表表达式(CTE) – CareySon – 博客园.

简介


对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果集仅仅需要在存储过程或是用户自定义函数中使用一次的时候,使用视图就显得有些奢侈了.

公用表表达式(Common Table Expression)是SQL SERVER 2005版本之后引入的一个特性.CTE可以看作是一个临时的结果集,可以在接下来的一个 SELECT,INSERT,UPDATE,DELETE,MERGE语句中被多次引用。使用公用表达式可以让语句更加清晰简练.

除此之外,根据微软对CTE好处的描述,可以归结为四点:

  • 可以定义递归公用表表达式(CTE)
  • 当不需要将结果集作为视图被多个地方引用时,CTE可以使其更加简洁
  • GROUP BY语句可以直接作用于子查询所得的标量列
  • 可以在一个语句中多次引用公用表表达式(CTE)

公用表表达式(CTE)的定义


公用表达式的定义非常简单,只包含三部分:

  1. 公用表表达式的名字(在WITH之后)
  2. 所涉及的列名(可选)
  3. 一个SELECT语句(紧跟AS之后)

在MSDN中的原型:

WITH expression_name [ ( column_name [,...n] ) ] 

AS 

( CTE_query_definition )

按照是否递归,可以将公用表(CTE)表达式分为递归公用表表达式和非递归公用表表达式.

非递归公用表表达式(CTE)


非递归公用表表达式(CTE)是查询结果仅仅一次性返回一个结果集用于外部查询调用。并不在其定义的语句中调用其自身的CTE

非递归公用表表达式(CTE)的使用方式和视图以及子查询一致

比如一个简单的非递归公用表表达式:

1

当然,公用表表达式的好处之一是可以在接下来一条语句中多次引用:

2

前面我一直强调“在接下来的一条语句中”,意味着只能接下来一条使用:

3

由于CTE只能在接下来一条语句中使用,因此,当需要接下来的一条语句中引用多个CTE时,可以定义多个,中间用逗号分隔:

4

递归公用表表达式(CTE)


递归公用表表达式很像派生表(Derived Tables ),指的是在CTE内的语句中调用其自身的CTE.与派生表不同的是,CTE可以在一次定义多次进行派生递归.对于递归的概念,是指一个函数或是过程直接或者间接的调用其自身,递归的简单概念图如下:

1

递归在C语言中实现的一个典型例子是斐波那契数列:

long fib(int n)   
{   
     if (n==0) return 0;
   if (n==1) return 1;   
     if (n>1) return fib(n-1)+fib(n-2);
}

上面C语言代码可以看到,要构成递归函数,需要两部分。第一部分是基础部分,返回固定值,也就是告诉程序何时开始递归。第二部分是循环部分,是函数或过程直接或者间接调用自身进行递归.

对于递归公用表达式来说,实现原理也是相同的,同样需要在语句中定义两部分:

  • 基本语句
  • 递归语句

SQL这两部分通过UNION ALL连接结果集进行返回:

比如:在AdventureWork中,我想知道每个员工所处的层级,0是最高级

5

这么复杂的查询通过递归CTE变得如此优雅和简洁.这也是CTE最强大的地方.

当然,越强大的力量,就需要被约束.如果使用不当的话,递归CTE可能会出现无限递归。从而大量消耗SQL Server的服务器资源.因此,SQL Server提供了OPTION选项,可以设定最大的递归次数:

还是上面那个语句,限制了递归次数:

6

所提示的消息:

7

这个最大递归次数往往是根据数据所代表的具体业务相关的,比如这里,假设公司层级最多只有2层.

总结


CTE是一种十分优雅的存在。CTE所带来最大的好处是代码可读性的提升,这是良好代码的必须品质之一。使用递归CTE可以更加轻松愉快的用优雅简洁的方式实现复杂的查询。

[转载]在ASP.NET MVC项目中为用户登录失败次数实现提示

mikel阅读(1139)

[转载]在MVC项目中为用户登录失败次数实现提示 – 陈希章 – 博客园.

这两天在给一个客户讲解MVC 3的架构和在项目中的应用,有提到这样一个问题:

MVC3 默认实现的Forms Authentication机制,可以结合SQL Server做成员管理,而且可以设置在一定时间内如果用户连续输入密码错误达到一定次数的话,就自动将用户锁定。

但是,默认情况下,却没有提供一定的机制,给用户提示,例如你已经输入几次失败了,还最多可以输入几次等等。这在有的时候给用户造成了一些不便。

那么,是否有办法解决这个问题呢?

本文源代码,可以通过这里下载 MvcApplicationSample.rar

首先,我们来看一下默认的一些设置和代码

MemberShip的配置

   <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
             enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>

上面的配置是默认的,意思是在10分钟内,如果连续输入密码5次错误的话,就锁住用户

AccountController中的代码

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if(ModelState.IsValid)
            {
                if(Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if(Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {

                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

这里的代码很简单,只要不通过验证的话,就显示一个错误消息:The user name or password provided is incorrect.

但其实这个消息对用户来说并不是很直观,他不会知道到底是哪里出了错:是用户名不对呢?还是密码不对?甚至说,密码还可以最多输入几次?

所以,默认情况下,如果登录不成功,就会看到如下的界面

image

如果连续输入错误超过5次,就会导致用户再也无法登录,而他没有得到任何提示。我们通过数据库可以看到这个用户已经被锁定了。

image

而且在这个表中确实有一个字段是记录了连续输入密码错误的次数

image

那么,接下来看看是否有什么办法解决?我想到的解决方案如下

添加一个自定义的MembershipProvider

既然默认的Membership Provider提供不了这个功能,那么我们可以尝试自己做一个,想办法去读取这张表应该就能实现

using System.Configuration;
using System.Data.SqlClient;
using System.Web.Security;

namespace MvcApplicationSample.Extensions
{
    /// <summary>
    /// 这是一个自定义的MembershipProvider,添加了一个功能,可以获取用户连续输入密码失败的次数
    /// 作者:陈希章
    /// 反馈:ares@xizhang.com
    /// </summary>
    public class MySQLMemberShipProvider:SqlMembershipProvider
    {

        private string connectionString = string.Empty;

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            //这里获取到默认使用的数据库连接字符串
            connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;
        }

        public int GetFailedPasswordAttemptCount(string userName)
        {
            using(var conn = new SqlConnection(connectionString))
            {
                using(var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "SELECT aspnet_Membership.FailedPasswordAttemptCount FROM aspnet_Membership INNER JOIN aspnet_Users ON aspnet_Membership.UserId = aspnet_Users.UserId INNER JOIN aspnet_Applications ON aspnet_Membership.ApplicationId =aspnet_Applications.ApplicationId AND aspnet_Users.ApplicationId = aspnet_Applications.ApplicationId WHERE (aspnet_Users.UserName = @userName) AND (aspnet_Applications.ApplicationName =@applicationName)";

                    cmd.Parameters.AddWithValue("@userName", userName);
                    cmd.Parameters.AddWithValue("@applicationName", this.ApplicationName);

                    conn.Open();
                    var result = cmd.ExecuteScalar();
                    conn.Close();

                    if(result != null)
                    {
                        return (int)result;
                    }
                }
            }
            //如果用户不存在,则返回-1
            return -1;
        }
    }
}

修改web.config使用这个新的membership Provider

修改部分为下面粗体的部分

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="MvcApplicationSample.Extensions.MySQLMemberShipProvider,MvcApplicationSample" connectionStringName="ApplicationServices"
             enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>

修改AccountController中的代码(请注意粗体部分)

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if(ModelState.IsValid)
            {
                if(Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if(Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {

                    var provider = (Extensions.MySQLMemberShipProvider)Membership.Provider;
                    var count = provider.GetFailedPasswordAttemptCount(model.UserName);
                    var max = provider.MaxInvalidPasswordAttempts;

                    if(count != -1)
                    {

                        ModelState.AddModelError("", count==max?"Your account is locked.":string.Format("You have been continually input the wrong password {0} times, if then enter {1} mistakes, your account will be locked", count, max - count));
                    }
                    else
                        ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

这样的话,我们来测试一下效果看看

image
如果连续输错5次,则提示已经被锁定了

image

本文源代码,可以通过这里下载 MvcApplicationSample.rar

[转载]手把手教你打造c#多线程多页面浏览器(连载一)

mikel阅读(1362)

[转载]手把手教你打造c#多线程多页面浏览器(连载一) – qihangkeji – 博客园.

现在浏览器真的太多了, 微软的Internet Explorer、Mozilla的Firefox、Apple的Safari、Opera、HotBrowser、Google Chrome、  浏览器GreenBrowser浏览器、Avant 浏览器、财猫省钱浏览器、360安全浏览器、360极速浏览器、世界之窗、腾讯TT、搜狗浏览器、傲游浏览器、百度浏览器众多浏览器让人眼花缭乱

作为技术人员,我们知道只有几种内核

基于Gecko排版引擎

Mozilla Firefox(火狐浏览器)

Mozilla Firefox现在是市场占有率第三的浏览器,仅次于微软的internet explorer和google的chrome。

基于WebKit排版引擎

谷歌浏览器(Chrome)

Chrome是由Google公司开发的网页浏览器,浏览速度在众多浏览器中走在前列,属于高端浏览器。采用BSD许可证授权并开放源代码,开源计划名为Chromium。

基于Trident排版引擎

微软Internet Explorer

我们中的大多数人都在使用IE,这要感谢它对web站点强大的兼容性。最新的Internet Explorer 9.0.2包括HTML5、CSS3以及大量的安全更新。
今天我们要做的的就是基于ie内核的C#浏览器

首先我们介绍一下 WebBrowser

使用 WebBrowser 控件可以在 Windows 窗体应用程序中承载网页以及支持浏览器的其他文档。例如,可以使用 WebBrowser 控件在应用程序中提供基于 HTML 的集成用户帮助或 Web 浏览功能。此外,还可以使用 WebBrowser 控件向 Windows 窗体客户端应用程序添加基于 Web 的现有控件。先介绍一下简单操作

private void toolStripButton2_Click(object sender, EventArgs e) //后退
{
webBrowser.GoBack();
}

private void toolStripButton3_Click(object sender, EventArgs e) //前进
{
webBrowser.GoForward();
}

private void toolStripButton4_Click(object sender, EventArgs e) //刷新
{

if (!((CustomTabpage)(this.tabControl1.SelectedTab)).webBrowser.Url.Equals(“about:blank”))
{
webBrowser.Refresh();
}

}

private void toolStripButton5_Click(object sender, EventArgs e) //停止
{
webBrowser.Stop();
}
private void toolStripButton6_Click_1(object sender, EventArgs e) //主页
{
webBrowser.GoHome();
}

//地址跳转

private void Navigate(String address)

{ if (String.IsNullOrEmpty(address))

return;

if (address.Equals(“about:blank”)) return;

if (!address.StartsWith(“http://”) && !address.StartsWith(“https://”))

{ address = “http://” + address;

} try { webBrowser1.Navigate(new Uri(address));

} catch (System.UriFormatException) { return; } }

private void 属性ToolStripMenuItem_Click(object sender, EventArgs e) //菜单栏-属性
{
try
{
webBrowser.ShowPropertiesDialog();
}
catch (System.Exception ex)
{
throw ex;
}

}

private void 页面设置ToolStripMenuItem_Click(object sender, EventArgs e) //菜单栏-页面设置
{
try
{
webBrowser.ShowPageSetupDialog();
}
catch (System.Exception ex)
{
throw ex;
}

}

private void 打印预览ToolStripMenuItem_Click(object sender, EventArgs e) //菜单栏-打印预览
{
try
{
webBrowser.ShowPrintPreviewDialog();
}
catch (System.Exception ex)
{
throw ex;
}
}

private void 打印ToolStripMenuItem_Click(object sender, EventArgs e) //菜单栏-打印
{
try
{
webBrowser.ShowPrintDialog();
}
catch (System.Exception ex)
{
throw ex;
}

}

WebBrowser 主要事件

BeforeNavigate2
在导航之前发生. (该事件并不在不刷新页面的时候发生)

DocumentComplete
当整个文档完全完成装载时发生.如果你刷新页面, 此事件并不激发.

DownloadBegin
当一个下载项目开始时候发生

DownloadComplete
当整个下载项目完成是发生该事件也发生在完成刷新页面.

NavigateComplete2
当整个导航完成. 该事件对应于 BeforeNavigate2.

NewWindow2
在一个新的窗口被创建以显示Web页或者其他资源的时候发生。譬如你在页面中以新建窗口的方式打开一个连接

OnStatusBar
当StatusBar 属性被改变的时候发生。

我们要做的是多线程多页面浏览器,今天只是一个基础,WebBrowser有个很大的缺点,就是对于window.open弹出新窗口是获取不到地址,这些问题留着后续连载中