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

mikel阅读(1146)

[转载]在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阅读(1363)

[转载]手把手教你打造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弹出新窗口是获取不到地址,这些问题留着后续连载中

[转载]ASP.NET MVC 3 开发的20个秘诀(六)[20 Recipes for Programming MVC 3]:找回忘记的密码

mikel阅读(1143)

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(六)[20 Recipes for Programming MVC 3]:找回忘记的密码 – O2DS – 博客园.

议题

您或者在您网站的注册的某个用户,无法想起自己的密码了,需要有一种方法能找回它。

解决方案

在AccountController控制器中添加一个新的动作和视图,以便用户可以找回他们的密码。利用Membership类搜索和匹配用户,并发送一个包含密码的电子邮件。

讨论

默认情况下,MVC Internet应用程序使用的是无法逆向转换的单向哈希算法。在下面的例子中,默认是双向散列加密方式。这种方式并不是很安全,但是在用户忘记密码时可以避免强迫用户修改密码。

先在Web.config文件中调整Membership节点的设置:

<?xml version="1.0"?>
<configuration>
    ...
    <system.web>
        ...
        <membership>
            <providers>
                <clear />
                <add name="AspNetSqlMembershipProvider" type=
"System.Web.Security.SqlMembershipProvider"
                connectionStringName="ApplicationServices"
                enablePasswordRetrieval="true" enablePasswordReset=
"false" requiresQuestionAndAnswer="false"
                requiresUniqueEmail="false" passwordFormat=
"Encrypted" maxInvalidPasswordAttempts="5"
                minRequiredPasswordLength="6"
                minRequiredNonalphanumericCharacters="0"
                passwordAttemptWindow="10" applicationName="/" />
            </providers>
        </membership>
        <machineKey
        validationKey=
"2CF9FF841A23366CFA5D655790D9308656B1F7532C0B95B5C067F80C45E59875
E2F3D68DAC63B5024C31D974D4BE151341FB8A31FC4BC3705DF5398B553FC3C3"
        decryptionKey="8E71407B62F47CCA3AAA6546B3880E1A0EF9833700
E0A0C511710F537E64B8B6" validation="SHA1" decryption="AES" />
        ...
    </system.web>
    ...
</configuration>

针对上面的设置代码,我们四个更改:

  1. 将enablePasswordRetrieval更改为True;
  2. 将enablePasswordReset更改为false;
  3. 添加passwordFormat=”Encrypted”;
  4. 生成加密的machineKey;

配置信息修改完毕后,在AccountModels.cs类中添加一个新的动作并创建忘记密码的视图:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Security;

namespace MvcApplication4.Models
{
    public class ChangePasswordModel
    {
        ...
    }
    public class LogOnModel
    {
        ...
    }
    public class RegisterModel
    {
        ...
    }
    public class ForgotPasswordModel
    {
        [Required]
        [DataType(DataType.EmailAddress)]
        [Display(Name = "Email address")]
        public string Email { get; set; }
    }
}

在创建新视图之前,应用程序必须先编译。单击“生成”->“生成解决方案”或按F6。编译完成之后,展开Views文件夹,右键单击 “Account”文件夹,选择“添加”->“视图”(如图1-4)。视图名称更改为“ForgotPassword”,选中“创建强类型视图”选 项,从“模型类”下拉框中选择先前创建的“ForgotPasswordModel”,然后点击“添加”。

视图创建后,添加一个基本的表单,表单接受用户输入注册时填写的电子邮件地址:

@model MvcApplication4.Models.ForgotPasswordModel
@{

    ViewBag.Title = "ForgotPassword";
}

<h2>ForgotPassword</h2>
<p>
    Use the form below to retrieve your password.
</p>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) { 

    @Html.ValidationSummary(true, "Password retrieval was unsuccessful. Please correct the errors and try again.")

    <div>
        <fieldset>
            <legend>Account Information</legend>
            <div class="editor-label">
                @Html.LabelFor(m => m.Email)
            </div>

            <div class="editor-field">
                @Html.TextBoxFor(m => m.Email)
                @Html.ValidationMessageFor(m => m.Email)
            </div>

            <p>
                <input type="submit" value="Retrieve Password" />
            </p>
        </fieldset>
    </div>
}

下面将在之前创建的MailClient类中添加一个新方法,以实现发送用户忘记的密码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;
using System.Net;
using System.Configuration;

namespace MvcApplication4.Utils
{
    public class MailClient
    {
        private static readonly SmtpClient Client;
        static MailClient()
        {
            ...
        }

        private static bool SendMessage(string from, string to,
        string subject, string body)
        {
            ...
        }

        public static bool SendWelcome(string email)
        {
            ...
        }

        public static bool SendLostPassword(string email,
        string password)
        {
            string body = "Your password is: " + password;
            return SendMessage("no-reply@no-reply.com", email,
            "Lost Password", body);
        }
    }
}

这个方法与上一个方法(*译者注:发送欢迎邮件的方法)十分相似,只是又添加了一个参数 — 用户的密码。这个密码将被添加到电子邮件的内容中并发送给用户。

最后在AccountController中添加两个ForgotPassword方法,第一个方法是默认载入时动作,第二是在用户填写完电子邮件地址后,接受回发数据的动作,他会从数据库中通过搜索并匹配电子邮件地址找到用户,然后将用户密码通过这个地址发给用户。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using MvcApplication4.Models;
using MvcApplication4.Utils;

namespace MvcApplication4.Controllers
{
    public class AccountController : Controller
    {
        ...
        //
        // Get: /Account/ForgotPassword
        public ActionResult ForgotPassword()
        {
            return View();
        }

        //
        // Post: /Account/ForgotPassword
        [HttpPost]
        public ActionResult ForgotPassword(
        ForgotPasswordModel model)
        {
            if (ModelState.IsValid)
            {
                MembershipUserCollection users =
                Membership.FindUsersByEmail(model.Email);
                if (users.Count > 0)
                {
                    foreach (MembershipUser user in users)
                    {
                        MailClient.SendLostPassword(model.Email,
                        user.GetPassword());
                    }
                    return RedirectToAction("LogOn");
                }
            }
            // If we got this far, something failed,
            // redisplay form
            return View(model);
        }
        ...
    }

}

在之前的两个秘诀中,只是给用户发送了一些基本信息。将MailMessage的isBodyHtml设置为True,通过简单的扩展增强就可以发送HTML或者更复杂的内容给用户。

参考

Membership.Providers Property 原书地址 书籍源代码

[琐事]博客准备推出源码下载栏目

mikel阅读(1746)

最近准备推出源码分类,把自己这些年积累下来的源码共享出来供大家下载,

主要是网站源码居多,大部分是从网上收集来的,也有朋友送的

希望对各位有所帮助,就算“取之于民,用之于民”吧!

库存不多,希望大家海涵,如果你有好的源码也可以分享出来,发布到下载栏目

图书下载栏目也会陆续更新,我的藏书,主要以开发技术为主,涉及方面比较多,呵呵…毕竟我学得比较杂,什么Delphi、Java、.NET、C#、Python、JavaScriptAndroid、Flash、游戏开发

软件工程类的很多

敬请期待!

[转载]VisualStudio一些可能没用过的调试窗口

mikel阅读(1226)

[转载]一些可能没用过的调试窗口 – LoveJenny – 博客园.

首先说明:如果没有进入调试模式的话,默认的调试窗口如下:

image

开始前的准备:

新建控制台程序DebugWindowDemo:

修改Program.cs 的代码为:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

class S
    {
       public static void Main()
        {
            pcount = Environment.ProcessorCount;
            Console.WriteLine("Proc count = " + pcount);
            ThreadPool.SetMinThreads(4, -1);
            ThreadPool.SetMaxThreads(4, -1);

            t1 = new Task(A, 1);
            t2 = new Task(A, 2);
            t3 = new Task(A, 3);
            t4 = new Task(A, 4);
            Console.WriteLine("Starting t1 " + t1.Id.ToString());
            t1.Start();
            Console.WriteLine("Starting t2 " + t2.Id.ToString());
            t2.Start();
            Console.WriteLine("Starting t3 " + t3.Id.ToString());
            t3.Start();
            Console.WriteLine("Starting t4 " + t4.Id.ToString());
            t4.Start();

            Console.ReadLine();
        }

        static void A(object o)
        {
            B(o);
        }
        static void B(object o)
        {
            C(o);
        }
        static void C(object o)
        {
            int temp = (int)o;

            Interlocked.Increment(ref aa);
            while (aa < 4)
            {
                ;
            }

            if (temp == 1)
            {
                // BP1 - all tasks in C

                Debugger.Break();
                waitFor1 = false;
            }
            else
            {
                while (waitFor1)
                {
                    ;
                }
            }
            switch (temp)
            {
                case 1:
                    D(o);
                    break;
                case 2:
                    F(o);
                    break;
                case 3:
                case 4:
                    I(o);
                    break;
                default:
                    Debug.Assert(false, "fool");
                    break;
            }
        }
        static void D(object o)
        {
            E(o);
        }
        static void E(object o)
        {
            // break here at the same time as H and K

            while (bb < 2)
            {
                ;
            }
            //BP2 - 1 in E, 2 in H, 3 in J, 4 in K

            Debugger.Break();
            Interlocked.Increment(ref bb);

            //after

            L(o);
        }
        static void F(object o)
        {
            G(o);
        }
        static void G(object o)
        {
            H(o);
        }
        static void H(object o)
        {
            // break here at the same time as E and K

            Interlocked.Increment(ref bb);
            Monitor.Enter(mylock);
            while (bb < 3)
            {
                ;
            }
            Monitor.Exit(mylock);

            //after

            L(o);
        }
        static void I(object o)
        {
            J(o);
        }
        static void J(object o)
        {
            int temp2 = (int)o;

            switch (temp2)
            {
                case 3:
                    t4.Wait();
                    break;
                case 4:
                    K(o);
                    break;
                default:
                    Debug.Assert(false, "fool2");
                    break;
            }
        }
        static void K(object o)
        {
            // break here at the same time as E and H

            Interlocked.Increment(ref bb);
            Monitor.Enter(mylock);
            while (bb < 3)
            {
                ;
            }
            Monitor.Exit(mylock);

            //after

            L(o);
        }
        static void L(object oo)
        {
            int temp3 = (int)oo;

            switch (temp3)
            {
                case 1:
                    M(oo);
                    break;
                case 2:
                    N(oo);
                    break;
                case 4:
                    O(oo);
                    break;
                default:
                    Debug.Assert(false, "fool3");
                    break;
            }
        }
        static void M(object o)
        {
            // breaks here at the same time as N and Q

            Interlocked.Increment(ref cc);
            while (cc < 3)
            {
                ;
            }
            //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q

            Debugger.Break();
            Interlocked.Increment(ref cc);
            while (true)
                Thread.Sleep(500); // for ever

        }
        static void N(object o)
        {
            // breaks here at the same time as M and Q

            Interlocked.Increment(ref cc);
            while (cc < 4)
            {
                ;
            }
            R(o);
        }
        static void O(object o)
        {
            Task t5 = Task.Factory.StartNew(P, TaskCreationOptions.AttachedToParent);
            t5.Wait();
            R(o);
        }
        static void P()
        {
            Console.WriteLine("t5 runs " + Task.CurrentId.ToString());
            Q();
        }
        static void Q()
        {
            // breaks here at the same time as N and M

            Interlocked.Increment(ref cc);
            while (cc < 4)
            {
                ;
            }
            // task 5 dies here freeing task 4 (its parent)

            Console.WriteLine("t5 dies " + Task.CurrentId.ToString());
            waitFor5 = false;
        }
        static void R(object o)
        {
            if ((int)o == 2)
            {
                //wait for task5 to die

                while (waitFor5) { ;}

                int i;
                //spin up all procs

                for (i = 0; i < pcount - 4; i++)
                {
                    Task t = Task.Factory.StartNew(() => { while (true);});
                    Console.WriteLine("Started task " + t.Id.ToString());
                }

                Task.Factory.StartNew(T, i + 1 + 5, TaskCreationOptions.AttachedToParent); //scheduled

                Task.Factory.StartNew(T, i + 2 + 5, TaskCreationOptions.AttachedToParent); //scheduled

                Task.Factory.StartNew(T, i + 3 + 5, TaskCreationOptions.AttachedToParent); //scheduled

                Task.Factory.StartNew(T, i + 4 + 5, TaskCreationOptions.AttachedToParent); //scheduled

                Task.Factory.StartNew(T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent); //scheduled

                //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died

                Debugger.Break();
            }
            else
            {
                Debug.Assert((int)o == 4);
                t3.Wait();
            }
        }
        static void T(object o)
        {
            Console.WriteLine("Scheduled run " + Task.CurrentId.ToString());
        }
        static Task t1, t2, t3, t4;
        static int aa = 0;
        static int bb = 0;
        static int cc = 0;
        static bool waitFor1 = true;
        static bool waitFor5 = true;
        static int pcount;
        static S mylock = new S();
    }

F5,开始运行:

由于Debugger.Break();

所以当执行到这里的时候,Debugger会中断。

这个时候再看看调试窗口会发现多了几个窗口:

image

点击调试->窗口->并行任务,界面如下:

image

按下F5,继续运行:

image

双击查看相应等待的任务,就可以知道这个工作线程为什么等待了。

例如:

image

继续按F5,运行,你应该会看到:

image

总之关于当前运行的并行任务你都可以看的到。

关闭程序,重新F5,进入调试模式

选择调试->窗口->并行堆栈

可以看到:

image

其中蓝线代表当前正在执行的线程。

如果右键点击方法视图的S.C ,可以看到右键菜单如下:image

点击切换方法视图

image

可以看到:

image

关闭程序,重新F5,进入调试模式

点击调试->窗口->线程:

可以看到:

image

当前控制台的所有线程都在这里。

在线程上点击右键可以冻结线程:

image

冻结线程也就是Pause线程,

冻结的线程可以被解冻,也就是Resume。

其他的窗口:

调试->窗口->模块:可以看到当前程序加载的所有模块。

image

调试->窗口->进程:

image

调试->窗口->反汇编:

image

调试->窗口->寄存器:

image

调试->窗口->调用堆栈:

调用堆栈窗口是比较常用的窗口:

image

上图表示先调用A方法,接着B方法,接着C方法。

也可以认为是C方法是B调用,而B方法是A调用的。

其他窗口比较常用,就不介绍了,是不是有一些你没用到的窗口呢?

[转载]博客园人必备工具:消除行号软件

mikel阅读(866)

[转载]博客园人必备工具:消除行号软件 – ma6174 – 博客园.

博客园里都是牛人,写的代码都是神笔,共享一下倒是好事,满屏行号情何以堪!

问题来源很简单,大牛们在博客园里面贴代码时,里面有一个“显示行号”的选项,大牛们轻轻一点,然后行号就显示出来了,代码好看了,我们可愁了,本来想研究一下代码,然后复制到编辑器,就发现每行都有行号。不信大家可以试一下,这里有一个带行号的代码http://www.cnblogs.com/ma6174/archive/2011/12/07/2279614.html。不把行号删除,代码怎么运行?然后就一行一行得删行号,代码短了还好说,可博客园里都是编程神牛啊,代码一写就是几千上万行,考验我们的耐力吗?哈,开个玩笑,为了解决这个问题,小弟花几分钟写了一个小软件,瞬间帮您把行号消除。

源码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void welcome()
{
printf(\t\t\t博客园人必备工具——消除行号\n);
printf(\t\t\t\t  BY ma6174\n);
printf(\t\t\t       ma6174@163.com);
printf(\n使用方法:\n\n);
printf(先将你要消除行号的完整代码复制到这里,\n\n按一下回车键,);
printf(然后按“Ctrl + Z”组合键,再按一下回车,烦人的行号就全没啦\n);
for(int i = 0; i < 80; i++)
printf(=);
printf(\n\n);
}
int main()
{
welcome();
char s[1000];
int i,len,tag;
FILE *fp;
fp=fopen(temp.txt,w);
if(fp==NULL){
printf(文件打开失败!);
exit(1);
}
while(gets(s))
{
len=strlen(s);
i=0;
while(s[i]== )i++;
while(s[i]>=0&&s[i]<=9)i++;
while(i<len-1)
fprintf(fp,%c,s[++i]);
fprintf(fp,\n);
}
fclose(fp);
system(start temp.txt);
}

如果大家懒得自己编译么可以到我的网盘下载可直接运行的软件:http://ma6174.ys168.com/

[转载]ASP.NET MVC 3 开发的20个秘诀(五)[20 Recipes for Programming MVC 3]:发送欢迎邮件

mikel阅读(1001)

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(五)[20 Recipes for Programming MVC 3]:发送欢迎邮件 – O2DS – 博客园.

议题

许多网站在访问内容或者发表评论的时候 要求用户注册或登录。这样的网站越来越多,用户非常难以记住每个他注册过的站点以及信息。而在用户提交注册信息的时候,网站可以发送邮件,提醒用户他们刚刚签署过注册信息,方便他们稍后访问时查询。

解决方案

实现SmtpClient类和MailMessage类在用户注册后发送欢迎邮件。

讨论

发送一份电子邮件,你需要配置SMTP服务器地址、端口、用户名和密码。为了让配置更加简单,建议将这些信息存储到Web.config的AppSettings节点里。

    <appSettings>
 
        <add key="webpages:Version" value="1.0.0.0" />
        <add key="ClientValidationEnabled" value="true" />
        <add key="UnobtrusiveJavaScriptEnabled" value="true" />
        <add key="smtpServer" value="localhost" />
        <add key="smtpPort" value="25" />
        <add key="smtpUser" value="" />
        <add key="smtpPass" value="" />
        <add key="adminEmail" value="no-reply@no-reply.com" />

    </appSettings>

在smtpServer、smtpPort、smtpUser和smtpPass的Value中输入可用的参数。

为了更好的组织项目,我们将创建一个新的文件夹来包含这些发送邮件的类。右键单击项目选择“添加”->“新建文件夹”并将其命名为 “Utils”。现在,右键单击新创建的“Utils”文件夹,选择“添加”->”类”并命名为“MailClient.cs”。

为了方便访问,MailClient类被定义为静态类,未来在与其他系统集成使用时,不需要实例化新的对象,直接使用即可。下面是MailClient类的完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;
using System.Net;
using System.Configuration;

namespace MvcApplication4.Utils
{
    public static class MailClient
    {
        private static readonly SmtpClient Client;

        static MailClient()
        {
            Client = new SmtpClient
            {
                Host =
                ConfigurationManager.AppSettings["SmtpServer"],
                Port =
                Convert.ToInt32(
                ConfigurationManager.AppSettings["SmtpPort"]),
                DeliveryMethod = SmtpDeliveryMethod.Network
            };

            Client.UseDefaultCredentials = false;
            Client.Credentials = new NetworkCredential(
            ConfigurationManager.AppSettings["SmtpUser"],
            ConfigurationManager.AppSettings["SmtpPass"]);
        }

        private static bool SendMessage(string from, string to,
        string subject, string body)
        {
            MailMessage mm = null;
            bool isSent = false;

            try
            {
                // Create our message
                mm = new MailMessage(from, to, subject, body);
                mm.DeliveryNotificationOptions =
                DeliveryNotificationOptions.OnFailure;
                // Send it
                Client.Send(mm);
                isSent = true;
            }
            // Catch any errors, these should be logged and
            // dealt with later
            catch (Exception ex)
            {
                // If you wish to log email errors,
                // add it here...
                var exMsg = ex.Message;
            }
            finally
            {
                mm.Dispose();
            }

            return isSent;
        }

        public static bool SendWelcome(string email)
        {
            string body = "Put welcome email content here...";

            return SendMessage(
            ConfigurationManager.AppSettings["adminEmail"],
            email, "Welcome message", body);
        }
    }
}

在类开始的时候,从Web.Config中引用值并创建SmtpClient类实例。下面定义的SendMessage 方法是私有方法,不能从类外部直接调用,此方法的是实际执行发送功能的方法。这个方法创建一个新的MailMessage实例,并通过先前创建 SmtpClient对象发送。最后,创建了一个SendWelcome功能来接受用户的电子邮件地址参数,通过调用SendMessage方法来发送欢 迎邮件。

在Account控制器中,用户通过Register方法注册或更新之后立即调用SendWelcome方法发送电子邮件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using MvcApplication4.Models;
using MvcApplication4.Utils;

namespace MvcApplication4.Controllers
{
    public class AccountController : Controller
    {
            …
        //
        // POST: /Account/Register
        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                MembershipCreateStatus createStatus;
                Membership.CreateUser(model.UserName,
                model.Password, model.Email, null, null,
                true, null, out createStatus);
                if (createStatus ==
                MembershipCreateStatus.Success)
                {
                    // Send welcome email
                    MailClient.SendWelcome(model.Email);
                    FormsAuthentication.SetAuthCookie(
                    model.UserName,
                    false /* createPersistentCookie */);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError("",
                    ErrorCodeToString(createStatus));
                }
            }
            // If we got this far, something failed,
            // redisplay form
            return View(model);
        }

    }
}

上面的代码是一个基本的例子,实现了用户在注册过程中向用户发送一封欢迎邮件。在现代社会中,自动处理表单是非常棒的点子,在以后的使用,我们可以在邮件中加入“确认您的电子邮件地址”的链接消息,并要求用户必须通过点击欢迎电子邮件中的链接激活账户之后,才可以登录。

参考

StmpClient MailMessage 原书地址 书籍源代码

[转载]ASP.NET MVC 3实现类似于Server.Transfer的效果

mikel阅读(977)

[转载]ASP.NET MVC 3实现类似于Server.Transfer的效果 – L.Qiu – 博客园.

ASP.NET MVC的页面跳转

当我们在使用ASP.NET MVC实现页面跳转的时候,常用的应该是:

  • Redirect
  • RedirectToAction
  • RedirectToRoute
  • 或者在前台使用脚本跳转。

但这几种跳转方式都是基于Get请求的,在某些特定场景下可能并不适用。例如需要传递大数据量参数、或者复杂对象类型参数的场景,get方式肯定是有限制的。

在webform里面,有一种服务器端跳转方式:Server.Transfer,相信大家一定都还记得。这种方式是中止当前页面执行,并将执行流程转入一个新的页面,并使用上一个页面创建的应答流。 这种方式具有如下的特点:
1,地址栏URL不会发生变化。
2,上一个页面后台产生的参数和对象可以直接传递到新的页面。
3,减少客户端对服务器的请求。

我们知道,ASP.NET MVC有一个核心思想,就是“约定胜于配置” ,例如在执行完一个action后,会到view目录下根据controller名称查找对应的view来进行渲染,但是 约定的做法并不意味着不能改变。

对于ASP.NET MVC而言,可以通过动态改变当前Action所渲染的view路径,来实现类似的效果。

渲染非常规路径的View

第一步,先实现一个自定义的ViewEngine:
public class ChangeViewEngine : System.Web.Mvc.RazorViewEngine
    {
        public ChangeViewEngine(string controllerPathName,string viewName)
        {
            this.ViewLocationFormats = new[] {"~/Views/" + controllerPathName + "/" + viewName  + ".cshtml" };
            
        }
    }
第二步,实现一个ActionAttribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class ChangeViewPathAttribute : ActionFilterAttribute
    {
        private string _controllerPath;
        private string _viewName;
        public ChangeViewPathAttribute(string controllerPath,string viewName)
        {
            this._controllerPath = controllerPath;
            this._viewName = viewName;
        }
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            //base.OnResultExecuting(filterContext);
            //ViewEngines.Engines.Clear();
            
            ViewEngines.Engines.Add(new ChangeViewEngine(_controllerPath,_viewName));
        }
    }

在该段代码里面,ChangeViewPathAttribute类继承于ActionFilter,并重写其中的OnResultExecuting方法,将自定义的ViewEngine加入到全局ViewEngine集合里面来。

第三步,在需要渲染不同路径的action加上Attribute
     [HttpPost]
        [Filter.ChangeViewPath("Invoice","Create")]
        public ActionResult PreInvoice(string strIds,bool flag)

在做完以上步骤后,我们就可以随意指定action所要渲染的view,在服务器端进行跳转,实现类似Server.Transfer的效果。 当然,以上只是一个简单的示例,你完全可以做的更优雅一点,实现更灵活的路径配置:)

[转载]Jquery easyui 界面设置存档edit.js

mikel阅读(1127)

[转载]Jquery easyui 界面设置存档edit.js_小豆号_百度空间.

$(function() {
    // 数值录入
    $("#salary").numberbox({
        min: 100,
        max: 1000.5,
        precision: 2
    });


    // 日期处理
    // FIXME: 日历控件不支持时分秒, 这在项目中的确是需要的
    // FIXME: 日历控件每周是从星期日开始, 而我们习惯是星期一, 最好能够设置
    $("#birthday").datebox({
        required: true,
        formatter: dateFormatter,
        parser: dateParser
    });


    // 下拉控件 combobox
    // FIXME: 不支持汉字的拼音检索, 最好提供回调接口, 我们来实现
    // 为了支持多音字, 我们会返回数组, 比如: 银行["YH","YX"]
    // FIXME: 空项目显示高度太窄, 需要添加"[请选择]"这样的占位项目(感觉不太好)
    // FIXME: 下拉弹出内容被屏幕遮挡时, 应该改为向上弹出
    // FIXME: 支持多选(每个项目加一个checkbox选项用于多选,用于提交的input里存的是id列表:01,02,03)
    $("#gender").combobox({
        url: '../data/gender.json',
        valueField: 'id',
        textField: 'text',
        width: 155,
        listWidth: 200,
        editable: true,
        checkbox: true // FIXME: 不支持checkbox多选?
    });

    // FIXME: 下拉弹出内容被屏幕遮挡时, 应该改为向上弹出
    // FIXME: 支持多选(每个项目加一个checkbox选项用于多选,用于提交的input里存的是id列表:01,02,03)
    $("#station").combotree({
        url: 'treeDate.aspx?flag=left',
        width: 155,
        checkbox: true, // FIXME: 不支持checkbox多选?
        treeWidth: 200
    });

    $('#save').click(function() {
        alert($('#parentid').text());
        $('#form').submit();
    });

    //提交弹出添加职位框
    $('#form').form({
        url: "Default.aspx?flag=adduser&pid=" + $('#parentid').text(),
        onSubmit: function() {
            return $(this).form('validate');
        },
        success: function(data) {
            alert(data);
            if (data == "yes") {
                $('#w').window('close');
                $('#jobText').val() = "";
                //                $('#w').tree('append', {
                //                    parent: pid,
                //                    data: nodes
                //                });
                $.messager.show({
                    title: "提示",
                    msg: "保存成功",
                    timeout: 3000
                })
            }

        }
    });


    // 自定义录入验证
    $.extend($.fn.validatebox.defaults.rules, {
        minLength: {
            validator: function(value, param) {
                return value.length >= param[0];
            },
            message: '至少输入 {0} 个字符.'
        }
    });


    // 只读测试
    var readonly = false;
    $('#readonly').click(function() {
        // 有这样的方法就好了
        //$('#form').readonly();

        if (readonly == false) {
            $('form input,form textarea').attr({ readonly: "false" });

            // FIXME: 应该支持readonly, disabled对用户不够友好: 不能提交到后台, 不能拷贝值出来
            $("form .easyui-validatebox").validatebox("disable"); // FIXME: 无此方法? 只读状态也提示非空等,不太好
            $("form .easyui-numberbox").numberbox("disable");
            $("form .easyui-datebox").datebox("disable");
            $("form .easyui-combobox").combobox("disable");
            $("form .easyui-combotree").combotree("disable");

        } else {
            $('form input,form textarea').removeAttr("readonly");

            $("form .easyui-validatebox").validatebox("enable"); // FIXME: 无此方法?
            $("form .easyui-numberbox").numberbox("enable");
            $("form .easyui-datebox").datebox("enable");
            $("form .easyui-combobox").combobox("enable");
            $("form .easyui-combotree").combotree("enable");
        }
        readonly = !readonly;
    });

    // TIP: 配合body解决页面跳动和闪烁问题
    $("body").css({ visibility: "visible" });
});

[转载]组织好你的Asp.Net MVC解决方案

mikel阅读(1115)

[转载]【译】组织好你的Asp.Net MVC解决方案 – CareySon – 博客园.

最近,Twitter上发起了一个一个关于“你最爱的ASP.NET MVC项目组织方式”,我自己研究了一些组织项目文件的方法。而我现在一直喜欢用的方式是一个几句灵活性的方式,此外,这个方式还非常简单。

如上图,整个解决方案里只有两个项目,首先让我们来看UI项目,UI项目只包含了那些和Website相关的文件并且不包含任何后台代码,这包括:

  • 没有Controller
  • 没有Model
  • 没有Global.asa.cs
  • 不包含任何后台代码

为什么不包含后台代码?因为UI项目只包含那些和UI相关的文件,而我的UI项目和部署项目相匹配,这包括

  • Views
  • Css
  • Images
  • Global.asax
  • Web.Config
  • 引用Core项目

因为我的UI项目和部署结构相匹配,所以弄清楚开发是怎样工作的将会变得容易.反之,将Model和Controller混合起来将会使得开发中将各部分分开变得困难。所以我的组织方法会将代码和内容这两部分分开。

我们的代码部分怎么办呢?放到另一个项目中,我喜欢称之为”Core”,当然名字你可以随便起,所有的代码文件都会放到这个项目中,包括持久层 Model,View Model,Controller,repositories,ORM相关文件以及任何和后台代码相关的部分。

组织好你的代码

对于组织好的代码,我喜欢用简单的方式,如果可以的话,我宁愿不编译UI项目–这仅仅是一个用于存放内容的文件夹,而这个文件夹下的bin目录里只有来自于”Core”项目的dll。

另外,我喜欢用文件夹来组织代码。利用项目的方式组织代码是可以的,但是这种缺乏灵活性的做法会将你禁锢在很难改变的层和结构当中。我已经在大型项目中因 为错误的组织代码而犯过许多错误,而最后我发现最好的做法并不是大兴土木,因为我曾经呆过的一个Team里动辄就是上百个项目,但是只有不到一半的项目被 实际部署.所以要牢记,编译所需的时间很大程度上取决于你的解决方案中项目的多寡。在一个含有1000个文件的项目和100个文件放入10个项目中比较, 前者所需的编译时间要大大的小于后者。至少在我见过的项目中是这样。

而另一个实际的问题是当你需要重新组织你现有的代码时,你会发现对于包含N个项目的解决方案来说,发现你被禁锢在各个不同项目的结构中会让你捶胸顿足。此 外,这种方式在Ctrl+F5进行调试时,超慢的速度更会让你欲哭无泪。而这种方式在你的代码被源码管理软件管理时,更会显出不足,仅仅将一个文件从一个 项目中移到另一个项目中就会让你的历史记录暴涨。在我最近的一个项目中,情况已经发展到甚至我们连一个简单的源码控制指令都无法执行从而导致我们所有的源 码控制历史记录全部丢失,最后我们不得不重新手动创建整个项目。移动文件夹要比在项目中移动文件让人惬意很多…

如果你想将你的代码进行很好的组织和分层,使用项目的方式或许有些帮助,因为这强制让你遵循你自己一开始设定的规则,但是一旦你开始这样做,你设立 了”Common”项目,”Configuration”项目”,”mapping”项目等,在后面的开发中最好考虑将这些代码回归到同一个项目当中.

所以,为什么不考虑将所有代码放入一个Core项目,所有UI放入一个项目,从而能给你极大的灵活性和最快的编译速度呢?无论你准备何种构架来组织你的代码,请确保不要将后台代码放入UI项目中,这样才能避免将代码和内容的关注点分离结构混为一谈。

—————————————————–

原文链接:http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/12/08/organizing-asp-net-mvc-solutions.aspx