[Tools]Windbg下载

mikel阅读(863)

下载地址:
http://www.microsoft.com/whdc/devtools/Debugging/installx86.mspx
Symbols path的设置
   打开windbg之后,按Ctrl+S,然后把这段(带下划线的部分)paste上:srv*c:\symcache*http://msdl.microsoft.com/download/symbols;c:\symcache;
   注意的是,那个c:\symcache是我常用的路径,你可以根据你的习惯,设置为d:\symbols等。

加载sos.dll
   
SOS是为了方便调试.NET程序的一个add-in,不知道哪个牛人写的……在.net framework 1.x下面,直接在windbg的命令行里面,执行.load clr10\sos.dll即可。
   在.net framework 2.0里面,我们可以到%windir%\microsoft.net\framework\v2.0.50727下面,把sos.dll,复制到 windbg安装目录下面。我一般是这样:在windbg目录下面,创建一个目录叫做clr20,然后copy sos.dll %programfiles%\Debugg~1\clr20。搞好后,加载命令是一样的:.load clr20\sos.dll即可。

[C#]使用Action、Func和Lambda表达式 在.NET在,我们经常使用委托,委托的作用

mikel阅读(999)

使用ActionFuncLambda表达式

.NET,我们经常使用委托,委托的作用不必多说,在.NET 2.0之前,我们在使用委托之前,得自定义一个委托类型,再使用这个自定义的委托类型定义一个委托字段或变量。.NET 2.0给我们带来了ActionFunc两个泛型委托,.NET3.0给我们带来了Lambda,这一切使得委托的定义和使用变得简单起来。下面的例子中的委托都使用了Lambda表达式。

.Action系列的泛型委托

Action系列的委托定义的是没有返回值(返回值为void)的委托。它有多个版本包括没有输入参数,1个输入参数,2个输入参数,3个输入参数,4个输入参数共5个版本这几个版本的原型如下:

1.       没有输入参数返回值为void的委托.

Action委托 封装一个方法,该方法不采用参数并且不返回值。

可以使用此委托以参数形式传递一个执行某操作的方法,而不用显式声明一个自定义的委托来封装此方法。该封装的方法必须与此委托定义的方法签名相对应。这意味着该方法不得具有参数和返回值。例:

using System;

using System.Windows.Forms;

public class Name

{

   private string instanceName;

   public Action ShowName;

   public Show()

{

   If(ShowName != null)

    ShowName();

}

   public Name(string name)

   {

      this.instanceName = name;

   }

   public void DisplayToConsole()

   {

      Console.WriteLine(this.instanceName);

   }

   public void DisplayToWindow()

   {

      MessageBox.Show(this.instanceName);

   }

}

public class ActionStudy

{

   public static void Main()

   {

      Name testName = new Name("Koani");

      testName.ShowName  = () => testName.DisplayToWindow();

      testName.Show();

   }

}

2.       1个输入参数返回值为void的委托

Action<T>泛型委托封装一个方法,该方法只采用一个参数并且不返回值。

可以使用此委托以参数形式传递方法,而不用显式声明自定义的委托。该方法必须与此

委托定义的方法签名相对应。也就是说,封装的方法必须具有一个通过值传递给它的参数,并且不能返回值。例:

using System;

using System.Windows.Forms;

 

public class ActionStudy

{

   public static void Main()

   {

      Action<string> messageTarget;

      if (Environment.GetCommandLineArgs().Length > 1)

         messageTarget = s => MessageBox.Show(s);

      else

         messageTarget = s => Console.WriteLine(s);

 

      messageTarget("Hello, World!");

   }

}

下面的示例演示如何使用 Action(T) 委托来打印 List(T) 对象的内容。在此示例中,使用 Print 方法将列表的内容显示到控制台上。此外,C# 示例还演示如何使用匿名方法将内容显示到控制台上。

using System;

using System.Collections.Generic;

 

class Program

{

    static void Main()

    {

        Action<string> PrintInConsole = s => Console.WriteLine(s);

        Action<string> PrintInDialog = s=>MessageBox.Show(s);

        List<String> names = new List<String>();

        names.Add("Bruce");

        names.Add("Alfred");

        names.Add("Tim");

        names.Add("Richard");

        names.ForEach(PrintInConsole);

        names.ForEach(PrintInDialog);      

    }

}

3.       2个输入参数返回值为void的委托

Action<T1,T2> 封装一个方法,该方法具有两个参数并且不返回值。

可以使用 Action(T1, T2) 委托以参数形式传递方法,而不用显式声明自定义的委托。该

方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有两个均通过值传递给它的参数,并且不能返回值。

using System;

using System.IO;

 

public class ActinStudy

{

   public static void Main()

  {

      string message1 = "The first line of a message.";

      string message2 = "The second line of a message.";

      Action<string, string>  concat;

 

      if (Environment.GetCommandLineArgs().Length > 1)

         concat = (s1, s2) =>

{

StreamWriter writer = null; 

      try

      {

         writer = new StreamWriter(Environment.GetCommandLineArgs()[1], false);

         writer.WriteLine("{0}"n{1}", s1, s2);

      }

      catch

      {

         Console.WriteLine("File write operation failed…");

      }

      finally

      {

         if (writer != null) writer.Close();

      }

};

      else

         concat = (s1, s2) => Console.WriteLine("{0}"n{1}", s1, s2);

 

      concat(message1, message2);

   }

4.       3个输入参数返回值为void的委托

Action<T1,T2,T3>委托,封装一个方法,该方法采用三个参数并且不返回值。

可以使用 Action(T1, T2, T3) 委托以参数形式传递方法,而不用显式声明自定义的委托。

该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有三个均通过值传递给它的参数,并且不能返回值。

5.       4个输入参数返回值为void的委托

Action<T1,T2,T3,T4>委托, 封装一个方法,该方法具有四个参数并且不返回值。

可以使用 Action(T1, T2, T3, T4) 委托以参数形式传递方法,而不用显式声明自定义的委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有四个均通过值传递给它的参数,并且不能返回值。

.Func系统的泛型委托

Func系列的委托定义的是返回值的委托。它有多个版本包括没有输入参数,1个输入参数,2个输入参数,3个输入参数,4个输入参数共5个版本这几个版本的原型如下:

1.       没有输入参数有返回值(返回值不为void)的委托

Func<TResult>封装一个不具有参数但却返回 TResult 参数指定的类型值的方法。
可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该

方法必须与此委托定义的方法签名相对应。这意味着封装的方法不得具有参数,但必须返回值。

2.       具有一个输入参数有返回值(返回值不为void)的委托

   Func<T,TResult>封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。

可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有一个通过值传递给它的参数,并且必须返回值。

3.       具有二个输入参数有返回值(返回值不为void)的委托

  Func<T1,T2,TResult>封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。

可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有两个均通过值传递给它的参数,并且必须返回值

4.       具有三个输入参数有返回值(返回值不为void)的委托

   Func<T1,T2,T3,TResut>封装一个具有三个参数并返回 TResult 参数指定的类型值的方法。

可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有三个均通过值传递给它的参数,并且必须返回值。

5.       具有四个输入参数有返回值(返回值不为void)的委托

 Func<T1,T2,T3,TResult>封装一个具有四个参数并返回 TResult 参数指定的类型值的方法。

可以使用此委托构造一个能以参数形式传递的方法,而不用显式声明自定义的委托。该方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有四个均通过值传递给它的参数,并且必须返回值。

[MVC]如何使ASP.NET MVC Controller易测试呢?

mikel阅读(857)

昨天那篇文章如何进行ASP.NET MVC 的测试,虽然通过自己写一个Fake的HttpContext,但是同时也暴露出之所以难于测试,是因为设计的代码不易测试,根据Jeffrey Zhao(老赵)的建议(我没完全看懂如何去做),我又重新试了一下,我把对httpcontext的操作全部放到一个ModelBinder里,因为那里可以处理httpcontext,而测试的时候是不执行ModelBinder

下面是实验的代码

Product类

image

ProductBinder

image

HomeController

image

记得在global.asax里注册

image

测试代码

image

很明显,测试是通过的,因为此时Action没有用到任何httpcontext的内容,处理的只是传进去的Product对象。

现在我的问题如下:

1. 很明显About的Action是通不过测试的,这里我只是使用了一个session值,难道我也需要新建一个ModerBinder,和一个类吗?这样 凡是出现调用httpcontext都的新建一个Binder那Binder是不是太多了,ModelBinder是不是主要处理Form?

2. 如果不用ModelBinder怎么样即可以使用session又易测呢?

3. 虽然Action好测了,但ProductBinder如何测?

注:本文没有技术含量,为何放在首页?因为我没有搜索到相关的内容,而园子里很多人肯定有更好的方案,但是他们的时间可能很紧,我想他们能在评论里给点好的思路,文章+评论=一篇好文, 我这里就是想抛砖引玉,把他们的经验为大家引出来。

[C#]独立主机上实现二级别域名解释(泛解释) (asp.net)

mikel阅读(751)

1\那就是把 *.iloveyou.io解释到服务器默认网站

2\在项目Global的事件protected void Application_BeginRequest(Object sender, EventArgs e)中通过

   string _path = Request.Url.AbsoluteUri;

   读取当前路径,然后做处理就是了!

   把

   _path="http://liangsan.iloveyou.io/ "

   的路径变为

  newurl="love/?toName=liangsan"

然后通过

    HttpContext.Current.RewritePath(newurl);

 就搞定了!就这么简单!

[MVC]如何进行ASP.NET MVC 的测试

mikel阅读(800)

如何进行ASP.NET MVC 的测试

本文参考了http://stephenwalther.com/blog/的内容。

今天需要对ASP.NET MVC的Controller进行测试,我们都知道当我们在测试工程里new一个controller时,这个controller里的httpcontext是空的,也就是session,cookie, form等都是空。

方法一:Mock controller的HttpContext, 暂时失败

那么我们如何对controller进行测试呢,我首先想到的是mock一个httpcontext,这里我用的是Rhino Mocks

public static class MvcMockHelpers

    {

        public static HttpContextBase FakeHttpContext(this MockRepository mocks)

        {

            HttpContextBase context = mocks.PartialMock<HttpContextBase>();

            HttpRequestBase request = mocks.PartialMock<HttpRequestBase>();

            HttpResponseBase response = mocks.PartialMock<HttpResponseBase>();

            HttpSessionStateBase session = mocks.PartialMock<HttpSessionStateBase>();

            HttpServerUtilityBase server = mocks.PartialMock<HttpServerUtilityBase>();

 

            SetupResult.For(context.Request).Return(request);

            SetupResult.For(context.Response).Return(response);               

            SetupResult.For(context.Session).Return(session);

            SetupResult.For(context.Server).Return(server);

 

            mocks.Replay(context);

            return context;

        }

 

        public static HttpContextBase FakeHttpContext(this MockRepository mocks, string url)

        {

            HttpContextBase context = FakeHttpContext(mocks);

            context.Request.SetupRequestUrl(url);

            return context;

        }

 

        public static void SetFakeControllerContext(this MockRepository mocks, Controller controller)

        {

            var httpContext = mocks.FakeHttpContext();

            ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);

            controller.ControllerContext = context;

        }

下面我们建立一个ASP.NET MVC工程

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Mvc.Ajax;

 

namespace MVCTest.Controllers

{

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult TestSession()

        {

            Session["name"] = "Jack Wang";

            ViewData["Title"] = "Home Page";

            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            if (Session["CurrentCulture"] != null)

            {

                ViewData["CurrentCulture"] = Session["CurrentCulture"];

            }

            return View();

        }

 

        public ActionResult TestForm()

        {

            ViewData["Name"] = Request.Form["Name"];

            ViewData["Age"] = Request.Form["Age"];

            ViewData["count"] = Request.Form.Count;

            return View();

        }

 

        public ActionResult TestLogin()

        {

            if (User.Identity.IsAuthenticated)

            {

                ViewData["userName"] = User.Identity.Name;

                return View("Admin");

            }

            else

            {

                return RedirectToAction("Index");

            }

        }

 

        public ActionResult Admin()

        {

            if (User.IsInRole("Admin"))

            {

                return View("Admin");

            }

            else

            {

                return RedirectToAction("Index");

            }

        }

 

        public ViewResult Details()

        {

            ViewData["PageSize"] = Request.QueryString["PageSize"];

            ViewData["CurrentPage"] = Request.QueryString["CurrentPage"];

            ViewData["count"] = Request.QueryString.Count;

 

            return View();

        }

        public ViewResult TestCookie()

        {

            ViewData["key"] = Request.Cookies["key"].Value;

            return View();

        }

    }

}

 

测试代码

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MVCTest;

using MVCTest.Controllers;

using MvcFakes;

using System.Web.SessionState;

using System.Web;

using System.Collections.Specialized;

using Rhino.Mocks;

namespace MVCTest.Tests.Controllers

{

    /// <summary>

    /// Summary description for HomeControllerTestByMock

    /// </summary>

    [TestClass]

    public class HomeControllerTestByMock

    {

        public HomeControllerTestByMock()

        {

            //

            // TODO: Add constructor logic here

            //

        }

        private MockRepository mocks;

        [TestInitialize]

        public void Setup()

        {

            mocks = new MockRepository();

        }

        [TestMethod]

        public void TestSession()

        {

            // Arrange

 

            HomeController controller = new HomeController();

            mocks.SetFakeControllerContext(controller);

            controller.Session.Add("name", "Jack Wang");

            SetupResult.For(controller.Session["CurrentCulture"]).Return("zh-CN");

 

            mocks.ReplayAll();

            // Act

            ViewResult result = controller.TestSession() as ViewResult;

 

            // Assert

            ViewDataDictionary viewData = result.ViewData;

            Assert.AreEqual("Home Page", viewData["Title"]);

            Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);

            Assert.AreEqual(controller.Session["name"], "Jack Wang");

            Assert.AreEqual("zh-CN", viewData["CurrentCulture"]);

        }

 

    }

}

运行,测试

image

从错误信息可以看到是因为代码里Session["name"] = "Jack Wang"出错

本人排查很久,只知道mock的controllercontext的Session里没有这个Key,但现在还没有找到如何解决,哪位高人能帮吗解决?

 

二,自己写个模拟的Fake类,测试通过

 

既然这种方法不行,我们只能换一种方法,还好controller的ControllerContext可以赋值,这样我们就通过继承ControllerContext来Fake

using System;

using System.Collections;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Linq;

using System.Text;

using System.Web;

using System.Web.SessionState;

 

namespace MvcFakes

{

    public class FakeHttpSessionState : HttpSessionStateBase

    {

        private readonly SessionStateItemCollection _sessionItems;

 

        public FakeHttpSessionState(SessionStateItemCollection sessionItems)

        {

            _sessionItems = sessionItems;

        }

 

        public override void Add(string name, object value)

        {

            _sessionItems[name] = value;

        }

 

        public override int Count

        {

            get

            {

                return _sessionItems.Count;

            }

        }

 

        public override IEnumerator GetEnumerator()

        {

            return _sessionItems.GetEnumerator();

        }

 

        public override NameObjectCollectionBase.KeysCollection Keys

        {

            get

            {

                return _sessionItems.Keys;

            }

        }

 

        public override object this[string name]

        {

            get

            {

                return _sessionItems[name];

            }

            set

            {

                _sessionItems[name] = value;

            }

        }

 

        public override object this[int index]

        {

            get

            {

                return _sessionItems[index];

            }

            set

            {

                _sessionItems[index] = value;

            }

        }

 

        public override void Remove(string name)

        {

            _sessionItems.Remove(name);

        }

    }

}

 

using System;

using System.Collections.Specialized;

using System.Web;

 

namespace MvcFakes

{

 

    public class FakeHttpRequest : HttpRequestBase

    {

        private readonly NameValueCollection _formParams;

        private readonly NameValueCollection _queryStringParams;

        private readonly HttpCookieCollection _cookies;

 

        public FakeHttpRequest(NameValueCollection formParams, NameValueCollection queryStringParams, HttpCookieCollection cookies)

        {

            _formParams = formParams;

            _queryStringParams = queryStringParams;

            _cookies = cookies;

        }

 

        public override NameValueCollection Form

        {

            get

            {

                return _formParams;

            }

        }

 

        public override NameValueCollection QueryString

        {

            get

            {

                return _queryStringParams;

            }

        }

 

        public override HttpCookieCollection Cookies

        {

            get

            {

                return _cookies;

            }

        }

 

    }

}

 

using System;

using System.Security.Principal;

 

namespace MvcFakes

{

 

 

    public class FakeIdentity : IIdentity

    {

        private readonly string _name;

 

        public FakeIdentity(string userName)

        {

            _name = userName;

 

        }

 

        public string AuthenticationType

        {

            get { throw new System.NotImplementedException(); }

        }

 

        public bool IsAuthenticated

        {

            get { return !String.IsNullOrEmpty(_name); }

        }

 

        public string Name

        {

            get { return _name; }

        }

 

    }

 

 

}

 

using System;

using System.Linq;

using System.Security.Principal;

 

namespace MvcFakes

{

 

    public class FakePrincipal : IPrincipal

    {

        private readonly IIdentity _identity;

        private readonly string[] _roles;

 

        public FakePrincipal(IIdentity identity, string[] roles)

        {

            _identity = identity;

            _roles = roles;

        }

 

        public IIdentity Identity

        {

            get { return _identity; }

        }

 

        public bool IsInRole(string role)

        {

            if (_roles == null)

                return false;

            return _roles.Contains(role);

        }

    }

}

 

using System;

using System.Collections.Specialized;

using System.Security.Principal;

using System.Web;

using System.Web.SessionState;

 

namespace MvcFakes

{

    public class FakeHttpContext : HttpContextBase

    {

        private readonly FakePrincipal _principal;

        private readonly NameValueCollection _formParams;

        private readonly NameValueCollection _queryStringParams;

        private readonly HttpCookieCollection _cookies;

        private readonly SessionStateItemCollection _sessionItems;

 

        public FakeHttpContext(FakePrincipal principal, NameValueCollection formParams, NameValueCollection queryStringParams, HttpCookieCollection cookies, SessionStateItemCollection sessionItems )

        {

            _principal = principal;

            _formParams = formParams;

            _queryStringParams = queryStringParams;

            _cookies = cookies;

            _sessionItems = sessionItems;

        }

 

        public override HttpRequestBase Request

        {

            get

            {

                return new FakeHttpRequest(_formParams, _queryStringParams, _cookies);

            }

        }

 

        public override IPrincipal User

        {

            get

            {

                return _principal;

            }

            set

            {

                throw new System.NotImplementedException();

            }

        }

 

        public override HttpSessionStateBase Session

        {

            get

            {

                return new FakeHttpSessionState(_sessionItems);

            }

        }

 

    }

}

 

using System;

using System.Collections.Specialized;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

using System.Web.SessionState;

 

namespace MvcFakes

{

 

    public class FakeControllerContext : ControllerContext

    {

        public FakeControllerContext(ControllerBase controller)

            : this(controller, null, null, null, null, null, null)

        {

        }

 

        public FakeControllerContext(ControllerBase controller, HttpCookieCollection cookies)

            : this(controller, null, null, null, null, cookies, null)

        {

        }

 

        public FakeControllerContext(ControllerBase controller, SessionStateItemCollection sessionItems)

            : this(controller, null, null, null, null, null, sessionItems)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, NameValueCollection formParams)

            : this(controller, null, null, formParams, null, null, null)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, NameValueCollection formParams, NameValueCollection queryStringParams)

            : this(controller, null, null, formParams, queryStringParams, null, null)

        {

        }

 

 

 

        public FakeControllerContext(ControllerBase controller, string userName)

            : this(controller, userName, null, null, null, null, null)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, string userName, string[] roles)

            : this(controller, userName, roles, null, null, null, null)

        {

        }

 

 

        public FakeControllerContext

            (

                ControllerBase controller,

                string userName,

                string[] roles,

                NameValueCollection formParams,

                NameValueCollection queryStringParams,

                HttpCookieCollection cookies,

                SessionStateItemCollection sessionItems

            )

            : base(new FakeHttpContext(new FakePrincipal(new FakeIdentity(userName), roles), formParams, queryStringParams, cookies, sessionItems),

            new RouteData(), controller)

        { }

    }

}

 

下面是测试类

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MVCTest;

using MVCTest.Controllers;

using MvcFakes;

using System.Web.SessionState;

using System.Web;

using System.Collections.Specialized;

namespace MVCTest.Tests.Controllers

{

    /// <summary>

    /// Summary description for HomeControllerTest

    /// </summary>

    [TestClass]

    public class HomeControllerTest

    {

        [TestMethod]

        public void TestSession()

        {

            // Arrange

            HomeController controller = new HomeController();

            var sessionItems = new SessionStateItemCollection();

            sessionItems["CurrentCulture"] = "zh-CN";

            controller.ControllerContext = new FakeControllerContext(controller, sessionItems);

            // Act

            ViewResult result = controller.TestSession() as ViewResult;

 

            // Assert

            ViewDataDictionary viewData = result.ViewData;

            Assert.AreEqual("Home Page", viewData["Title"]);

            Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);

            Assert.AreEqual(sessionItems["name"], "Jack Wang");

            Assert.AreEqual("zh-CN", viewData["CurrentCulture"]);

        }

        [TestMethod]

        public void TestFakeFormParams()

        {

 

            var controller = new HomeController();

 

            var formParams = new NameValueCollection { { "Name", "Jack" }, { "Age", "28" } };

            controller.ControllerContext = new FakeControllerContext(controller, formParams);

 

            var result = controller.TestForm() as ViewResult;

            Assert.AreEqual("Jack", result.ViewData["Name"]);

            Assert.AreEqual("28", result.ViewData["Age"]);

            Assert.AreEqual(formParams.Count, result.ViewData["count"]);

        }

 

        [TestMethod]

        public void TestFakeQueryStringParams()

        {

            var controller = new HomeController();

 

            var queryStringParams = new NameValueCollection { { "PageSize", "10" }, { "CurrentPage", "5" } };

            controller.ControllerContext = new FakeControllerContext(controller, null, queryStringParams);

 

            var result = controller.Details() as ViewResult;

            Assert.AreEqual("10", result.ViewData["PageSize"]);

            Assert.AreEqual("5", result.ViewData["CurrentPage"]);

            Assert.AreEqual(queryStringParams.Count, result.ViewData["count"]);

        }

 

        [TestMethod]

        public void TestFakeUser()

        {

            var controller = new HomeController();

 

            controller.ControllerContext = new FakeControllerContext(controller, "Jack Wang");

            var result = controller.TestLogin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(ViewResult));

            ViewDataDictionary viewData = ((ViewResult)result).ViewData;

            Assert.AreEqual("Jack Wang", viewData["userName"]);

 

 

            controller.ControllerContext = new FakeControllerContext(controller);

            result = controller.TestLogin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

        }

 

 

        [TestMethod]

        public void TestFakeUserRoles()

        {

 

            var controller = new HomeController();

 

 

            controller.ControllerContext = new FakeControllerContext(controller, "Jack Wang", new string[] { "Admin" });

            var result = controller.Admin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(ViewResult));

 

 

            controller.ControllerContext = new FakeControllerContext(controller);

            result = controller.Admin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

        }

 

 

 

        [TestMethod]

        public void TestCookies()

        {

 

            var controller = new HomeController();

 

 

            var cookies = new HttpCookieCollection();

            cookies.Add(new HttpCookie("key", "a"));

            controller.ControllerContext = new FakeControllerContext(controller, cookies);

            var result = controller.TestCookie() as ViewResult;

 

 

            Assert.AreEqual("a", result.ViewData["key"]);

        }

    }

}

测试完全通过

image 

 

总结: 个人感觉ASP.NET MVC的测试是很不友好的,和ruby on rails相比,在测试这一块还有很大的距离,希望正式版里这一块能够加强

 

王德水 写于2008-01-05 00:30分

 

本文源码下载

[Javascript]javascript中setTimeout 和setInterval的区别

mikel阅读(680)

window对象有两个主要的定时方法,分别是setTimeout 和 setInteval 他们的语法基本上相同,但是完成的功能取有区别。
  setTimeout方法是定时程序,也就是在什么时间以后干什么。干完了就拉倒。
  setInterval方法则是表示间隔一定时间反复执行某操作。

  如果用setTimeout实现setInerval的功能,就需要在执行的程序中再定时调用自己才行(setTimeout( ) 預設只是執行一次, 但我們可以使用一個循環方式, 使到一個setTimeout( ) 再啟動自己一次, 就會使到第二個 setTimeout( ) 執行, 第二個又啟動第三個, 這樣循環下去, 這 setTimeout( ) 就會不斷執行)。如果要清除计数器需要根据使用的方法不同,调用不同的清除方法:

  1. var timeID = null;   
  2.   
  3. //timeout方法   
  4. timeID=setTimeout('northsnow()',1000);   
  5. clearTimeout(timeID);     
  6.   
  7. //interval方法   
  8. timeID=setInterval('northsnow()',1000);   
  9. clearInteval(timeID);  

 eg:

  1. <script type="text/JavaScript">   
  2. var count = 0;   
  3. var timeID = null;   
  4. function counter() {   
  5.     count++;       
  6.     $("#count").html(count + '%');   
  7.     timeID = setTimeout("counter()"1000);//注意:执行的函数需要加引号,否则会报错的
  8.     if (count > 10) {   
  9.         clearTimeout(timeID);   //注意:clearTimeout(timeID)必须位于setTimeout()之后,不能是之前,原因很容易呦!   
  10.     }   
  11. }   
  12. $(function() {   
  13.     counter();   
  14. });   
  15. </script>  

 

  1. <script type="text/JavaScript">   
  2. var count = 0;   
  3. var timeID = null;   
  4. function counter() {   
  5.     count++;       
  6.     $("#count").html(count + '%');   
  7.     if (count > 10) {   
  8.         clearInterval(timeID);   
  9.     }       
  10. }   
  11. $(function() {       
  12.     timeID = setInterval('counter()', 1000);  //注意:执行的函数需要加引号,否则会报错的 
  13.        
  14. });   
  15. </script>  

    好了,基本原理明白了,剩下的就是自己灵活运用了,可以开发一些定时器或延时器之类的东东^_^.

[Javascript]关闭浏览器的事件

mikel阅读(872)

在做图片刷新功能的时候,会出现IE图片缓存的问题,试了N多方法都没有效果,最后只有使用每刷新一次修改图片文件名加随机数的方法才勉强解决。但是这样 的方法是下次打开浏览器读取图片的时候,上次产生的随机数是动态的,这时候只有让用户关闭浏览器的时候捕捉关闭事件然后将图片更名为一个统一的名字。由于 浏览器是无状态的,在这时候捕捉浏览器关闭会出现两种情况:1.真正的关闭浏览器 2.刷新浏览器。如何判断区分这两种动作呢。
一. JavaScript代码处理方法:
      function window.onbeforeunload()  
      {        
        //用户点击浏览器右上角关闭按钮
        if(event.clientX>document.body.clientWidth&&event.clientY<0||event.altKey)  
        {   
                document.getElementById("btnCompelete").click();
//              window.event.returnValue="确定要退出本页吗?";  
        }
        //用户点击任务栏,右键关闭
        else if(event.clientY > document.body.clientHeight || event.altKey)
        {
            document.getElementById("btnCompelete").click();
//            window.event.returnValue="确定要退出本页吗?";  
        }
         else//其他情况为刷新   
         {   
              alert("你在刷新");
         }  
      } 
其中 event.clientX   鼠标光标X坐标     document.body.clientWidth窗体工作区宽度     event.clientY鼠标光标Y坐标     event.altKey   是否按下alt键
二. 事件捕捉方法:
<body scroll="no" onbeforeunload="return CloseEvent();" onunload="UnLoadEvent()" >
</body>

<script language="JavaScript" type="text/javascript">
 
  
    var DispClose = true;
    function CloseEvent()
    {
        if (DispClose)
        {
            return "是否离开当前页面?";
        }
    }
   
    function UnLoadEvent()
    {
        DispClose = false;
        //在这里处理关闭页面前的动作
    }
在页面卸载之前引发onbeforeunload事件,如果用户选择“是”即确定卸载页面将引发onunload事件,否则返回页面不做任何操作。

[C#]static变量与asp.net

mikel阅读(976)

 在C#中,static变量表示该变量属于类,而不是类的实例。可以说是该类的所有实例共享一个static变量。
ASP.NET的页面就是一个类,我们访问一个页面。就会在服务器上实例化一个该类的实例,来响应我们的请求。
“所有实例共享一个static变量” 这就意味着,所有的客户端访问到的ASP.NET页面中static变量都是同一个变量。

由于我们每次访问asp.net页面都是一个全新的对象,而不是我们上一次访问的对象。所以上次页面访问时我们对页面中变量的改动都没有保留。遇到 这个问题的时候,很多初学者的直觉就是将这个变量申明为static,自己在测试的时候发现还真的保留住了页面的状态。窃喜之余没有发现这又有引入了另外 一个错误。因为你要的只是页面能保留住状态,而这个状态是针对一个客户端的(session的效果)。而得到的结果是只要一个客户端改变了该值所有的其他 客户端都受到了影响(如同Applicatin的效果)。这种情况下,需要的极有可能就是个ViewState或者是Session。

Application与static变量
Application是通过一个集合保存所有的对象。

强类型:
Application中保存的是object,对对象的保存和使用需要作cast动作。对于值类型更需要Box&UnBox。对性能的影响较大。
而static变量是强类型的对象。

线程同步:
Application将所有的对象放到一个集合,这样对访问集合中的任何对象都会锁定这个集合。
假如有Application["A"]、Application["B"]、Application["C"],有线程访问Application["A"]其他线程不能访问Application["B"] and Application["C"]。
而static变量,可以根据他们的作用分别放在不同的class当中。这样可以并行访问不同的static变量,而不存在线程安全问题。

友情提示:
1. 对static变量,做lock时。可以通过lock(typeof(classname))来锁定该变量所在的类的类型,达到线程同步的目的。
2. 由于Aplication,static member是全局变量,而我们是在多线程服务器环境写程序,对他们的使用需要注意线程安全的问题

[MVC]ASP.Net MVC Framework - Create your own IRout

mikel阅读(841)

In my previous post I wrote about how we can create our own IControllerFactory for the ASP.NET MVC Framework; in this post I will show you how we can create our own IRouteHandler.
The RouteHandler in this post will replace the IControllerFactory with my own Controller Factory, and also set a default ControllerFactory and a ViewFactory specified in the web.config. This is something the current preview bits of the MVC Framework can’t.
It’s quite easy to create our own RouteHandler, we only need to implement the IRouteHandler interface and implement the GetHttpHandler method 😉
public class N2MVCRouteHandler : IRouteHandler
{
   public IHttpHandler GetHttpHandler(RequestContext requestContext)
   {
       N2MVCHandler handler = new N2MVCHandler();
       handler.RequestContext = requestContext;
       return handler;
    }
}

The GetHttpHandler returns an IHttpHandler. I have created my own IHttpHandler (N2MVCHandler). The N2MVCHandler inherits the MvcHandler shipped with the ASP.NET MVC Framework. The reason why I inherit the MvcHandler is because I don’t want to rewrite stuff that isn’t necessarily. In my IHttpHandler I override the ProcessRequest method and added my own code to create a Controller for the current request.

public class N2MVCHandler : MvcHandler
{
    protected override void ProcessRequest(IHttpContext httpContext)
    {
       if (this.RequestContext == null)
          throw new InvalidOperationException("No RequestContext");

       string controllerName = this.GetRequiredString(this.RequestContext.RouteData, "controller");

       IController controller = this.CreateController(controllerName);
       ControllerContext controllerContext = new ControllerContext(base.RequestContext, controller);

       controller.Execute(controllerContext);
    }

    private IController CreateController(string controllerName)
    {
       N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

       IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

       IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

       return controller;
    }
}

In the ProcessRequest method I also create a Controller for the current request by calling the CreateController method. The CreateController method in the N2MvcHandler instantiates a ControllerFactroy which is specified in the web.config file. When the factory is instantiated I make a call to its CreateController method to create an instance of the Controller.

In this example I have replace the IControllerFactory with my own interface, the reason is that I want to pass the name of the Controller and not the Type. The MvcHandler shipped with the MVC Framework will in the ProcessRequest method try to locate the Controller within the references assemblies and create the Type of the Controller and pass it to the IController.CreateController method. But I think it’s up to the ControllerFactory to look up the Controller. Here is my IControllerFactory interface:

public interface IN2ControllerFactory
{
    IController CreateController(RequestContext context, string controllerName);
}

Note: You can still create your own ControllerFactory, but you need to implement my interface instead of IControllerFactory, and you don’t need to make a call to the ControllerBuilder.Current.SetDefaultControllerFactory method in the Application_Start event in Global.asax to specify which ControllerFactory you want to use, instead you can do it in web.config. You will see how to do it later in this post.

I decided to use Spring.Net in this post also to create my Controller within the ControllerFactory.

public class N2ControllerFactory : IN2ControllerFactory
{
    public IController CreateController(RequestContext context, string controllerName)
    {
        IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
        IObjectFactory factory = new XmlObjectFactory(input);

        IController controller = (IController)factory.GetObject(controllerName, typeof(IController));

        if (typeof(Controller).IsAssignableFrom(controller.GetType()))
        {
          N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;
          ((Controller)controller).ViewFactory = N2ViewFactoryBuilder.GetViewFactory(config.ViewFactory);
        }
        return controller;
    }
}
It's the ControllerFactory's responsibility to create and fill the Controller with all it information it needs. In this case the ViewFactory.

The IViewFactory is used to create a factory which has the responsibility to create a View. Because a Controller don’t need to implement the Controller base class I will in my code use a "code policy". I will check if the created Controller inherits the Controller class, if so I will create a IViewFactory and inject it to the Controller. The IController interface don’t have the ViewFactory property, it’s something we will get from the Controller base class. 

If we take a look again at the CreateContoller method in the IHttpHandler (N2MVCHandler), we can see how I get the ControllerFactory from the web.config

private IController CreateController(string controllerName)
{
    N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

    IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

    IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

    return controller;
}

I use some other helper classes in my code to create an instance of the Controller- and ViewFactory specified in the web.config, the code of the helper methods is not relevant for this post. When the public CTP of the MVC Framework is released, you can drop me an e-mail and I can send you my source code.

Here is the config section in the web.config where a conrollerFactory is specified and also a viewFactory. So we can now easy specify our ControllerFactory and ViewFactory in web.config.

<Nsquared2>
    <N2MVCSection
        controllerFactory="MvcApplication.Models.N2ControllerFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        viewFactory="MvcApplication.Models.N2ViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</Nsquared2>

By using the Route objects’ RouteHandler, we can easy change the RouteHandler the MVC Framework should use.

RouteTable.Routes.Add(new Route
{
    Url = "[controller]/[action]/[id]",
    Defaults = new { action = "Index", id = (string)null },
    RouteHandler = typeof(N2MVCRouteHandler)
});

In this post you have seen how we can create our own IRouteHandler, and also replace the ControllerFactory with our own.

[C#]提高软件开发效率三板斧之二----利用CMP模式

mikel阅读(897)

提高软件开发效率三板斧之二

利用CMP模式

上一章给大家总体介绍了一些提高软件开发效率的技术和技巧,在这里将给大家演示一个软件体系结构的应用,我们引用《ASP.NET电子商务高级编程》的一句话来解释“体系结构”这个词,因为演示的这个架构正是这本书中提到的。

“我们通常将体系结构看作一系列服务的集合,用来执行其他代码,这些服务应当提供有效的功能和特性来完成一些特定目标。”建立体系结构时必须牢记几个目标

1.      尽量创建可重用的代码

2.       加强设计模式和最佳实践

3.       提供通用功能

4.       提供开发基础

了解了设计体系结构的目标后,还应该明确体系结构应该提供的服务,在深入讨论之前建议大家先研究一下IbuySpy,Nile,Pet Shop等站点的体系结构,这些站点都非常好,也很正确。但这个架构要构建一个易于维护,升级,增强功能的架构。先列举一下需要架构提供的服务列表。

1.      数据服务

这里用到的时托管容器式持久性模型(CMP),它是一种设计模式,这种模式下业务对象不考虑数据从何处来,以及如何存放。业务对象可以是纯粹的业务对象,它们维护业务数据并根据业务规则判断这些数据是否正确,并且和数据库访问层没有丝毫关系。关于CMP的更多详细的信息,可以去买一本清华出版的《ASP.NET电子商务高级编程》以及相关网站资料,这里给出两个相关链接。

2.       监测服务

这里提供一套标准的服务来完成软件的监测任务。这里的监测是指记录,跟踪,错误处理,还有其它与跟踪系统正在做什么以及谁正在使用系统等相关的任务。这些服务包括跟踪变量,记录不同来源的错误和其它信息,以及使用自定义异常等

3.       安全服务

设计安全服务是为了提供足够的安全,不过这里用到的安全服务非常简单,仅仅实现了身份验证的功能,其实安全在开发过程中是一项不容忽视且相当艰巨的任务,包括防止脚本注入,数据库注入,实现数字签名,混淆代码以防止代码斜路以及web服务中的一些安全实现等。最近我买了一本《ASP.NET安全性高级编程》,那本书对在asp.net开发过程中如何实现安全性这个问题有很完整的阐述,建议大家可以买一本看看。

4.       配置服务

配置服务是用来控制应用程序所需要的配置选项的。这里的配置服务也是比较简单的,不过可以以后根据需求改进设计并加强体系结构中的这个部分。

因为篇幅有限,不可能对架构中的每个项目的每个类都逐一分析讨论,这里给出一个简单的类的功能列表,以方便你理解和使用这个架构,当然还有好多更吸引人和有用的技巧在里面,建议你下载源代码后慢慢研究一下,就这个架构我零零碎碎的看了3个多月才开始真正的使用它并把它用到实际开发中去,源代码可以去wrox网站去下载,也可以在本篇文章提供的地址去下载,前面说了,这个架构有足够的扩展性,你完全可以根据你的需要来写一个针对accessoracleSQLPersistenceContainer类,或者在实现安全服务的

1.       CMPServices项目

CMPServices项目

功能

CMPConfigurationHandler.cs

它负责从web.config文件的自定义配置部分加载元数据配置信息

ContainerMappingSet.cs

元数据配置的顶层是XML根结点,包含一个容器映射的列表,这个容器映射的集合保存在ContainerMappingSet类的一个实例中。它具有基于XML结点进行自身初始化的功能,以及用于调试的手动装载功能

ContainerMapping.cs

实现容器映射,对单个对象关系映射的抽象。它允许一个容器(如SQL持久性容器或XML容器等)获得对象实例的足够信息,该对象实例可用于执行对象关系映射,它可以让对象实例保持对底层数据源的持久性。一个容器映射最多包含4个命令映射(限于CRUD标准的4个命令)。

CommandMapping.cs

命令映射是CRUD动作的表现,可以被底层数据源执行,对于SQL容器,定义了4CommandMapping,分别实现Insert,Update,Select,Delete方法的支持.它还包含一个参数列表,并使属性总是显示命令的名称(可以映射到某个存储过程)和提供者提示,并允许在同一个容器和数据源中从不同的数据库得到不同的命令。

CommandParameter.cs

它包含的信息可以将类示例的公共属性和字段映射到数据源上使用的特定命令参数,它包括下列属性:类属性,参数名称,数据类型,大小,参数方向。

PersistableObject.cs

这是一个基类,它提供一系列让对象参与托管容器式持久性的基本功能,任何希望它的实例参与托管容器式持久性的类都必须继承这个类,它提供单条需要保持的数据。如果需要从数据库或者其它数据源获得多条数据查询结果,可以利用这个类。

PersistableObjectSet.cs

这个类继承自PersistableObject类如果不能提前返回数据集的大小,就需要用这个类来维护一个内部数据集,这个内部数据集相当于容器执行命令后的返回数据的存储处。容器可以事先知道它处理的是PersistableObject类还是PersistableObjectSet类可以动态决定命令执行后将返回的数据结果保存在哪里。

StdPersistenceContainer.cs

StdPersistenceContainer类是一个基类,它定义了所有容器必须提供的核心功能。这里采用类而不是接口,所以可以实现一些默认的功能,并且可以在整个架构中利用它的多态性使问题简化。

SqlPersistenceContainer.cs

SqlPersistenceContainer是持久性容器的一个实现,是专门为SQLServer设计的,可以针对SQLServer数据库的select,insert,update,delete操作方法。该类的其它内部容器映射来分析对象的关系映射,在对象实例和数据源之间交换数据。

2.       ConfigurationServices项目

ConfigurationServices项目

功能

SiteProfile.cs

在这里主要提供了一些程序运行的所有对象可以用到的静态属性,比如加载元数据,CMP数据引擎的数据库类型集合,指明默认数据源的连接字符串等。这些静态成员在应用程序启动时被加载起来,也就是在Global.asax文件里实现的,下面我会讲的。

 

3.       MonitorServices项目

MonitorServices项目

功能

GWTrace.cs

GWTrace.类负责提供标准的跟踪功能。该类统一了跟踪信息的存放位置。它知道web.config文件里的一个跟踪级别开关,但跟踪不同等级详细信息时会进行识别。合理应用该类的方法可以大大方便你在开发中对代码运行的跟踪。

MonitorUtilities.cs

该类提供了一些工具类方法,在监测系统中对其它方法非常有用,例如包含了返回机器名,应用程序名称,进程信息的方法它还提供了一个SafeFormat方法,它提供了一个安全,不引起错误的办法把对象参数转换成标准格式的字符串。

4.       MonitorServicesLogging项目

MonitorServicesLogging项目

功能

DbErrorEntry.cs

该类是一个PersistableObject类,在存储一条错误信息记录到数据库时,它用作与数据库的接口。它只是写入数据库或者读出的错误记录信息的一个简单的占位符号。

DbErrorEntrySet.cs

该类时一个PersistableObjectSet类,包含了在一定数据范围内返回一列错误所需的参数信息。为了管理而检查一个特定时间段的错误记录的时候,该类非常有用

DbErrorLog.cs

该类负责在数据库中维护错误记录信息,这些错误记录包含错误来源,运行时返回的进程等详细信息,以及实际的异常内容

FileErrorLog.cs

该类只是简单的把错误信息记录到一个文件,虽然该类可以单独使用,但开发者不应该单独调用它,而应该调用ErrorLog类。ErrorLog类会把错误先记录到数据库,只把文本文件作为一种数据库出问题时的备份

ErrorLog.cs

该类用于筛选错误记录,它首先尝试把错误记录送到数据库中。如果失败,他会把错误信息写入文件;如果成功,他会在文件中留下信息指出错误记录已经成功保存在数据库,并附带一个合适的错误ID

GWException.cs

这是一个自定义异常类,当实例化改类时,类实例可获得的错误信息就会发送给ErrorLog类,以将其持久保存到数据库或相应的文本文件中,这样就保证了开发者在任何时候实例化改类或其子类对应的异常时,错误信息都能够以可靠的形式保存下来。它还有个方法将基于一个数字键在内部资源文件ErrorMessages.resx中检索异常信息。这个数字键由GWExceptionMessages的枚举项提供

GWExceptionMessages.cs

改类时包含了证书值的枚举项,在加载错误信息字符串时,这些证书值在资源文件内被用做键。

架构已经有了,现在想办法把这个架构利用到实际的开发中,这里演示一个简单的留言板程序,其中也实现了对数据库的添加,删除,修改,选择等基本功能。其实一个大的项目也无非是由这样小的独立的功能模块构建起来的,这里只是抛砖引玉。闲话不说,我们来一步一步进行。

1.       构建项目

新建立一个web项目,这里是wawabook.然后把CMPServicesConfigurationServicesMonitorServicesMonitorServicesLogging4个文件夹拷贝到项目根目录下,并在解决方案里添加已有项目,把那这个项目添加进去。另外还有SharedAssemblyInfo.csGadgetsWarehouse.snk也拷贝到根目录下,一个是版权信息,一个是程序集的强名键。不用改动直接复制过来就可以。完成后解决方案管理器大致如下图。注意一下各个项目之间的依赖性,因为各个项目之间有交叉引用,关于各个项目之间的关系可以在源代码中看出来。

2.       建立数据库和相关表,存储过程

按实际的需求在SQLServer里建立数据库,表以及相应的存储过程。这里的数据库名叫wawabook,表名叫GuestBook,在创建表时最好把字段的描述信息写上,这时很有用的,方便以后查看和理解,完成后如下图。


分别建立对GuestBook表的Insert,Select,Update,Delete的存储过程如图

这里只给出insert_GuestBook的存储过程源码供参考,其它存储过程可以在本文附带的数据库源码中查看


Create PROCEDURE [insert_GuestBook]

(
@UserName     [nvarchar](10),

 
@Mail    [varchar](50),

 
@Title    [nvarchar](50),

 
@Content        [ntext],

 
@AddDate       [datetime],

 
@IPAddress     [varchar](50))

 

AS Insert INTO [wawabook].[dbo].[GuestBook] 

 ( 
[UserName],

 
[Mail],

 
[Title],

 
[Content],

 
[AddDate],

 
[IPAddress]

 

VALUES 

@UserName,

 
@Mail,

 
@Title,

 
@Content,

 
@AddDate,

 
@IPAddress)

GO

另外把SystemErrors表,GW_Insert_ERRORLOG_SP存储过程复制到你新建的数据库里,因为程序出错的时候会用到这个表和存储过程来记录错误日志。 你可以自己写一个GW_GET_ERRORLOG_SP存储过程来在出错误的时候查看错误日志以分析错误的原因所在。

3.       配置web.config

这里要配置几个地方,数据库连接字符串,跟踪级别开关,元数据等,这里只说一下额外需要特殊配置的地方,具体的web.config配置根据你的程序需要而定,比如说安全小节,身份验证小节,http小节等。

1)因为元数据在web.config文件保存,而web.config是个固定格式的文件,这就要指定这个自定义节点。

    <configSections>

         
<section name="GWConfig" type="GW.CMPServices.CMPConfigurationHandler, GW.CMPServices" />

</configSections>

2)为了让跟踪项目能够合理的工作,需要定义项目跟踪级别,下面的小节定义了项目的跟踪级别,具体可以根据你项目实际需求来设置。要了解TraceSwitch更多的消息请查阅MSDN文档

  <system.diagnostics>

         
<switches>

              
<add name="GWTrace" value="4" />

         
</switches>

</system.diagnostics>

需要注意的是上面两个小节都在system.web节点上面

       3)刚才定义了GwCfonfig自定义节点,这里用来放置CMP元数据,因为架构里的错误处理功能需要把错误信息保存到数据库里,所以错误处理所需要的元数据是必须的。另外这里给出了GuestBook容器的insert的元数据。你可以看一下元数据的结构,理解一下。因为很简单,相信你能很快写出相应的update,select,delete的元数据,记着,这些元数据和你刚刚写的存储过程是对应的。

<GWConfig>

         
<ContainerMappingSet>

              
<ContainerMapping>

                   
<ContainerMappingId>ERROR_LOG</ContainerMappingId>

                   
<ContainedClass>DbErrorEntry</ContainedClass>

                   
<Insert>

                       
<CommandName>GW_Insert_ERRORLOG_SP</CommandName>

                       
<Parameter>

                            
<ClassMember>ErrorMessage</ClassMember>

                            
<ParameterName>@ErrorMessage</ParameterName>

                            
<DbTypeHint>Varchar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>4000</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>ExtendedInfo</ClassMember>

                            
<ParameterName>@ExtendedInfo</ParameterName>

                            
<DbTypeHint>Varchar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>4000</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>ServerName</ClassMember>

                            
<ParameterName>@ServerName</ParameterName>

                            
<DbTypeHint>Varchar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>50</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>ErrorId</ClassMember>

                            
<ParameterName>@ErrorId</ParameterName>

                            
<DbTypeHint>Int</DbTypeHint>

                            
<ParamDirection>Output</ParamDirection>

                            
<Size>4</Size>

                       
</Parameter>

                   
</Insert>

                   
<Select>

                       
<CommandName>GW_GET_ERRORLOG_SP</CommandName>

                       
<Parameter>

                            
<ClassMember>StartDate</ClassMember>

                            
<ParameterName>@StartDate</ParameterName>

                            
<DbTypeHint>Date</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>8</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>EndDate</ClassMember>

                            
<ParameterName>@EndDate</ParameterName>

                            
<DbTypeHint>Date</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>8</Size>

                       
</Parameter>

                   
</Select>

              
</ContainerMapping>

              
<ContainerMapping>

                   
<ContainerMappingId>GuestBook</ContainerMappingId>

                   
<ContainedClass>GuestBookSet</ContainedClass>

                   
<Insert>

                       
<CommandName>insert_GuestBook</CommandName>

                       
<Parameter>

                            
<ClassMember>UserName</ClassMember>

                            
<ParameterName>@UserName</ParameterName>

                            
<DbTypeHint>NVarChar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>10</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>Mail</ClassMember>

                            
<ParameterName>@Mail</ParameterName>

                            
<DbTypeHint>Varchar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>50</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>Title</ClassMember>

                            
<ParameterName>@Title</ParameterName>

                            
<DbTypeHint>NVarChar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>50</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>Content</ClassMember>

                            
<ParameterName>@Content</ParameterName>

                            
<DbTypeHint>NText</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>10000</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>AddDate</ClassMember>

                            
<ParameterName>@AddDate</ParameterName>

                            
<DbTypeHint>Date</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>50</Size>

                       
</Parameter>

                       
<Parameter>

                            
<ClassMember>IPAddress</ClassMember>

                            
<ParameterName>@IPAddress</ParameterName>

                            
<DbTypeHint>Varchar</DbTypeHint>

                            
<ParamDirection>Input</ParamDirection>

                            
<Size>50</Size>

                       
</Parameter>

                   
</Insert>

              
</ContainerMapping>

         
</ContainerMappingSet>

     
</GWConfig>

4)其它固定配置

这里包括文本类型错误日志保存的目录和数据库链接字符串,这些东西一般保存在appSeettings小节里,值得注意的是为了安全考虑数据库链接字符串可以加密后再保存。Windows提供了一些API来增强安全性,.NET框架也提供了若干个加密相关的类,关于数据库连接字符串的加密可以参考一下PetShop3DataProtector类和ConnectionInfo类,这两个类都在Utility项目下。

<appSettings>

         
<add key="ErrorViewUrlPrefix" value="/GWSetup/ErrorLog/" />

         
<add key="ErrorLogBaseDir" value="E:\me\web.net\GWSetup\Errors\" />

         
<add key="DefaultDataSource" value="user id=sa; password=sa; database=wawabook; server=(local);" />

</appSettings>

当应用程序出错的时候错误处理程序会先试图在数据库做错误日志记录,如果记录成功,会在一个文本文件里做一个标志,这个文本文件的存放位置由ErrorViewUrlPrefix小节配置,如果在往数据库写错误日志也出错的话会以文本的形式记录错误日志,这个文本文件的存放位置由ErrorLogBaseDir小节来配置。

4.       修改Global.asax.cs里面的一些地方

这里要解决的问题是要在应用程序启动的时把CMP元数据加载起来,还有一些全局的变量也要在这里加载,比如说元数据映射的SQL数据类型。我们主要看一下Application_Start方法。

protected void Application_Start(Object sender, EventArgs e)

         
{

              System.Configuration.ConfigurationSettings.GetConfig(
"GWConfig");

 

              SiteProfile.DefaultDataSource 
= System.Configuration.ConfigurationSettings.AppSettings["DefaultDataSource"];

              

              SiteProfile.DbTypeHints[
"Varchar"= System.Data.SqlDbType.VarChar;

              SiteProfile.DbTypeHints[
"Int"= System.Data.SqlDbType.Int;

              SiteProfile.DbTypeHints[
"Date"= System.Data.SqlDbType.DateTime;

              SiteProfile.DbTypeHints[
"Text"= System.Data.SqlDbType.Text;

              SiteProfile.DbTypeHints[
"Bit"= System.Data.SqlDbType.Bit;

              SiteProfile.DbTypeHints[
"Money"= System.Data.SqlDbType.Money;

              SiteProfile.DbTypeHints[
"NVarChar"= System.Data.SqlDbType.NVarChar;

              SiteProfile.DbTypeHints[
"NText"= System.Data.SqlDbType.NText;

     }


如果你的程序里需要加载其它的sql类型,你可以对这个方法做适当的修改。

5.       建立BLL层和数据实体类

为了简单,我把业务逻辑层和数据库实体层放在了一个项目里。建立GuestBookBLL项目,并在里面添加两个类GuestBookGuestBookSet.GuestBookSet类时数据实体类,必须继承自PersistableObject类或者PersistableObjectSet.类,因为这里要用到一些大数据集,所以继承自PersistableObjectSet.

using System;

using System.Data;

using System.Xml;

 

using GW.CMPServices;

 

namespace GuestBookBLL

{

     
/// <summary>

     
/// GuestBookSet 的摘要说明。

     
/// </summary>


     
public class GuestBookSet:PersistableObjectSet

     
{

         
private int _id ;

         
private string _UserName;

         
private string _Mail;

         
private string _Title;

         
private string _Content;

         
private DateTime _AddDate;

         
private string _IPAddress;

         
public GuestBookSet()

         
{

              
//

              
// TODO: 在此处添加构造函数逻辑

              
//

         }


         
public int id

         
{

              
get 

              
{

                   
return this._id;

              }


              
set 

              
{

                   
this._id = value;

              }


         }


         
public string UserName

         
{

              
get 

              
{

                   
return this._UserName;

              }


              
set 

              
{

                   
this._UserName = value;

              }


         }


 

         
public string Mail

         
{

              
get 

              
{

                   
return this._Mail;

              }


              
set 

              
{

                   
this._Mail = value;

              }


         }


         
public string Title

         
{

              
get 

              
{

                   
return this._Title;

              }


              
set 

              
{

                   
this._Title = value;

              }


         }


         
public string Content

         
{

              
get 

              
{

                  
return this._Content;

              }


              
set 

              
{

                   
this._Content = value;

              }


         }


         
public DateTime AddDate

         
{

              
get 

              
{

                   
return this._AddDate;

              }


              
set 

              
{

                   
this._AddDate = value;

              }


         }


         
public string IPAddress

         
{

              
get 

              
{

                   
return this._IPAddress;

              }


              
set 

              
{

                   
this._IPAddress = value;

              }


         }


     }


}


可以看出这个类和刚才我们建立的数据库是对应的。需要注意的是你在定义类的属性时要和数据库的类型相对应,比如说数据库的字段时VarChar类型,对应的C#类型就是string类型,如果字段类型是DateTime类型,对应的C#类型是System.DateTime结构类型。关于更多的类型对应关系在MSDN里可以查到。

有了数据库实体类,我们就可以写业务逻辑了,业务逻辑我们在GuestBook类里实现,这里先看一下怎样利用架构来向数据库里写入数据,也就是填写留言功能。

public static void insert_GuestBook( string UserName,string Mail,string Title,string Content,DateTime AddDate,string IPAddress )

         
{

              GWTrace.EnteringMethod( MethodBase.GetCurrentMethod() );

              SqlPersistenceContainer spc 
= new SqlPersistenceContainer(CMPConfigurationHandler.ContainerMaps["GuestBook"]);

              GuestBookSet gbs 
= new GuestBookSet();

              gbs.UserName
=UserName;

              gbs.Mail
=Mail;

              gbs.Title
=Title;

              gbs.Content
=Content;

              gbs.AddDate
=AddDate;

              gbs.IPAddress
=IPAddress;

              spc.Insert( gbs );

         }


可以看到,先实例化一个SqlPersistenceContainer容器类,然后实例化一个GuestBookSet类,并设置这个类相应的属性,最后运行sql容器类的Insert()方法就可以完成数据插入功能了,因为SqlPersistenceContainer类已经继承了跟踪机制,所以直接在调用这个方法的时候如果.aspx页面启用了Trace就可以看到每个参数详细的赋值过程,为你跟踪应用程序提供了非常有价值的信息,如果出错你会看到程序停止在哪一步,或者能看到是由于哪个参数导致了程序运行错误。这个方法里还用到了GWTrace.EnteringMethod方法,这个方法是跟踪本方法的运行情况的,如果你想跟踪某个方法的运行情况就可以直接用那个方法,前提是你引入了以下名字控件

using GW.CMPServices;

using GW.MonitorServices;

using System.Reflection;

using System.Diagnostics;

using GW.MonitorServices.Logging;

我们来看一下前台页面在调用这个方法是trace里显示的信息。

看到了吗,是不是和我说的一样,根据这里显示的信息你可以知道到底是哪里出了错误,比如说是存储过程还是页面传入的非法参数。

我们再看一下Select方法的实现,先看存储过程。

Create PROCEDURE [select_GuestBook]

         (
@id          [int])

          
AS 

if @id=-1 

         
select * from [wawabook].[dbo].[GuestBook] 

else

         
select * from [wawabook].[dbo].[GuestBook] Where ( [id]     = @id       )

GO

再来看元数据

<Select>

     
<CommandName>select_GuestBook</CommandName>

     
<Parameter>

         
<ClassMember>id</ClassMember>

         
<ParameterName>@id</ParameterName>

         
<DbTypeHint>Int</DbTypeHint>

         
<ParamDirection>Input</ParamDirection>

         
<Size>4</Size>

     
</Parameter>

</Select>

再来看业务逻辑层对应代码

        public static DataSet select_GuestBook(int id)

         
{

              GWTrace.EnteringMethod( MethodBase.GetCurrentMethod() );

              SqlPersistenceContainer spc 
= new SqlPersistenceContainer(CMPConfigurationHandler.ContainerMaps["GuestBook"]);

              GuestBookSet gbs 
= new GuestBookSet();

              gbs.id
=id;

              spc.Select( gbs );

              
return gbs.ResultSet;

     }

最后看一下页面的编码类中相应的代码

private void bind_dgrd1()

     
{

         
//获取所有记录用来作为DataGrid的数据源

         dgrd1.DataSource
=GuestBook.select_GuestBook(1);

         dgrd1.DataBind();

     }


     
private void dgrd1_SelectedIndexChanged(object sender, System.EventArgs e)

     
{

         
//当选择DataGrid上的一列是选择这列数据绑定到Label的Text属性上

         
int id=(int)dgrd1.DataKeys[dgrd1.SelectedIndex];

         System.Text.StringBuilder sb
=new System.Text.StringBuilder();

         DataRow dr
=GuestBook.select_GuestBook(id).Tables[0].Rows[0];

         sb.AppendFormat(
"Title:{0}<br>Content:{1}",dr["Title"].ToString(),dr["Content"].ToString());

         lbl1.Text
=sb.ToString();

}


流程已经很明显了,你每添加一条业务规则,都要建立数据表,写存储过程,配置元数据,写业务逻辑代码,在aspx.cs调用逻辑层代码。你可以试着把留言的修改和删除功能也做出来,也就是试着使用SqlPersistenceContainer类的Update方法和Delete方法,记住操作流程了吗?先实例一个SqlPersistenceContainer类,接着实例化一个业务实体类,设置业务实体类属性,最后执行SqlPersistenceContainer类的某个方法,错误处理已经继承了,可以方便你查看出错的地方,另外你还可以抛出自定义异常GWException来自动记录应用程序正式运行后所发生的错误日志。

       最后建议大家再下载架构源代码后仔细研究一下,这个 架构在你开发的整个周期都在起作用,无论是前期的调试测试还是后期的编码部署和运行,利用好它会给你带来很多的好处,我这里没有引入实现安全的项目,因为 那几个类非常简单,也不具有相当的通用性,你可以利用现有的架构来构建一个适合你自己的安全的类库,另外你也可以继续扩展你的架构中可以重复利用的功能, 比如说建立一个tools类库,把上传文件,发送邮件,过滤危险字符等通用功能集成到里面,以便其它人员随时调用,而不是再从新写一个,一般服务性的类多做一些静态方法以便不用实例化就可以调用。其实你可能已经发现了,存储过程,CMP元数据和数据实体类以及业务逻辑类大多都是和数据库的某个表相对应的,我们每次都需要一边看着数据库的字段一边来构建自己的代码,如果字段非常的多,将会是一个非常繁琐的工作,下次我会和大家一起DIY一个代码生成器,帮助你生成一部分相关代码,免除你一些重复工作。

asp.net电子商务高级编程》源码下载如下

http://support.apress.com/books.asp?bID=1861008031&s=0&Go=Select+Book