[JQuery]jQuery事件总结

mikel阅读(870)

转载:http://www.cnblogs.com/Clingingboy/archive/2008/02/14/1068908.html
    很少写这些,看了1.2.3版本的改进,确实佩服,很方便.
1.绑定事件
(1)

$("p").bind("click", function(e){});

(2)

$("p").click(function() {})

2.删除事件
(1)删除特定事件

$("div").unbind("click");

(2)删除所有事件

$("div").unbind();

3.触发事件
(1)trigger方法 触发特定元素事件

$("div").trigger('click');

(2)triggerHandler方法 与trigger方法相似,但不触发浏览器默认事件,如focus事件,使用此方法,将会阻止焦点到元素上

$("input").triggerHandler("focus");

4.特殊事件
(1)one(string event,function data)
此事件只执行一次则被删除

$("p").one("click", function(){
  alert(
"test");
}
);

(2)hover(over, out)
切换mouSEOver与mouSEOut事件

$("td").hover(
  function () 
{
    $(
this).addClass("hover");
  }
,
  function () 
{
    $(
this).removeClass("hover");
  }

);

可用unbind mouSEOver与mouseout方法来删除此事件
(3)toggle(oldclick,newclick)
切换执行click事件

    $("li").toggle(
      function () 
{
        $(
this).css("list-style-type""disc")
               .css(
"color""blue");
      }
,
      function () 
{
        $(
this).css({"list-style-type":"""color":""});
      }

    );

可用unbind click方法来删除此事件
5.     1.2.3版本新增功能
(1)事件命名空间(便于管理)
实际使用方面:
1.当不需要全部事件,删除特定2个以上的事件.
示例:

  $("div").bind("click.plugin",function() {} );
  $(
"div").bind("mouseover.plugin", function(){});
  $(
"div").bind("dblclick", function(){});
  $(
"button").click(function() {$("div").unbind(".plugin");  })

在事件名称后面加命名空间,在删除事件时,只需要指定命名空间即可.以上代码执行以后,dbclick仍然存在.
(2)相同事件名称,不同命名的事件执行方法
示例:

$("div").bind("click", function(){ alert("hello"); });
  $(
"div").bind("click.plugin", function(){ alert("goodbye"); });
  $(
"div").trigger("click!"); // alert("hello") only

以上trigger方法则根据事件名称来执行事件.
简单的写几句.以上的几个方法还是非常实用方便的

[MVC]ASP.NET MVC实践系列8-对查询后分页处理的解决方案

mikel阅读(793)

转载:http://www.cnblogs.com/nuaalfm/archive/2009/11/18/1605364.html

一、前言:

GridView中的分页是用post做的,所以将查询表单中的内容可以存到ViewState中,翻页的时候可以利用,实现起来就比较容易些,而 在mvc中这些就要我们自己来做了,Contrib中的分页只能应付简单应用,对于查询后结果的分页没做处理,下面我们来改造一下这个分页程序。

二、准备工作

首先准备一个数据源


 1 public class News
 2 {
 3     public int ID { getset; }
 4     public string Author { getset; }
 5     public string Title { getset; }
 6     public DateTime CreateTime { getset; }
 7 }
 8 public class ListNews
 9 {
10     public static List<News> GetList()
11     {
12         List<News> list = new List<News>();
13         list.Add(new News { ID = 1, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
14         list.Add(new News { ID = 2, Author = "lfm2", Title = "中华人民共和国61周年", CreateTime = DateTime.Now.AddHours(1) });
15         list.Add(new News { ID = 3, Author = "lfm3", Title = "中华人民共和国62周年", CreateTime = DateTime.Now.AddHours(2) });
16         list.Add(new News { ID = 4, Author = "lfm3", Title = "中华人民共和国63周年", CreateTime = DateTime.Now.AddHours(3) });
17         list.Add(new News { ID = 5, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
18         list.Add(new News { ID = 6, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
19         list.Add(new News { ID = 7, Author = "lfm2", Title = "中华人民共和国61周年", CreateTime = DateTime.Now.AddHours(1) });
20         list.Add(new News { ID = 8, Author = "lfm3", Title = "中华人民共和国62周年", CreateTime = DateTime.Now.AddHours(2) });
21         list.Add(new News { ID = 9, Author = "lfm3", Title = "中华人民共和国63周年", CreateTime = DateTime.Now.AddHours(3) });
22         list.Add(new News { ID = 10, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
23         list.Add(new News { ID = 11, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
24         list.Add(new News { ID = 12, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
25         list.Add(new News { ID = 13, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
26         list.Add(new News { ID = 14, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
27         list.Add(new News { ID = 15, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
28         list.Add(new News { ID = 16, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
29 
30         return list;
31     }
32 }

然后添加一个View:


 1  <%Html.BeginForm();%>
 2     Title:<%=Html.TextBox("title",Request.Form["title"]??Request.QueryString["title"]) %>
 3     Author:<%=Html.TextBox("author",Request.Form["author"]??Request.QueryString["author"]) %>
 4     <input type="submit" value="查询" />
 5     <%Html.EndForm(); %>
 6     <%=Html.Grid(Model).Columns(column=>{
 7     column.For(x=>x.ID).Named("News ID");
 8     column.For(x => Html.ActionLink(x.Title, "NewsDetils"new { newsId = x.ID })).DoNotEncode();
 9     column.For(x => x.Author).Header("<th>"+Html.ActionLink("作者","CustomPager",new{desc = Convert.ToBoolean(ViewData["desc"]),sortName="Author"})+"</th>");
10     column.For(x=>x.CreateTime);
11 })
12     %>
13 
14     <%= Html.Pager(Model,ViewData["search"])%>

这里的分页代码和Contrib中略有不同,一会儿我们来讲解这个不同的原因。

添加一个Controller:


 1  public ActionResult CustomPager(int? page)
 2         {
 3             int pageSize = 3;
 4             int pageNumber = page ?? 1;
 5             var list = ListNews.GetList()
 6                 .Where(p => p.Title.Contains(Request.QueryString["title"?? ""))
 7                 .Where(p => p.Author.Contains(Request.QueryString["author"?? ""));
 8                 
 9             var pageList=list.Skip((pageNumber  1* pageSize).Take(pageSize);
10             int total = list.Count();
11             CustomPagination<News> customes = new CustomPagination<News>(pageList, pageNumber, pageSize, total);
12 
13             return View(customes);
14         }
15         [AcceptVerbs(HttpVerbs.Post)]
16         public ActionResult CustomPager(FormCollection formCollection)
17         {
18             int pageSize = 3;
19             int pageNumber =  1;
20             var list = ListNews.GetList().Where(p => p.Title.Contains(Request.Form["title"]))
21                 .Where(p => p.Author.Contains(Request.Form["author"]));
22             int total = list.Count();
23             var pageList = list.Skip((pageNumber  1* pageSize).Take(pageSize);
24             CustomPagination<News> customes = new CustomPagination<News>(pageList, pageNumber, pageSize, total);
25             Dictionary<stringstring> d = new Dictionary<stringstring>();
26             d.Add("title", Request.Form["title"]);
27             d.Add("author", Request.Form["author"]);
28             ViewData["Search"= d;
29             return View(customes);
30         }

注:这部分内容的详细讲解可以参见:ASP.NET MVC实践系列7-Grid实现(下-利用Contrib实现)

三、Contrib的分页源码分析

我们先把Pagination和Pager两个文件夹中的源码copy出来,经过分析我们知道CustomPagination是实现了 IPagination接口的集合,我们把数据整合到CustomPagination中就可以使用Pager进行分页 了,PaginationExtensions是辅助HtmlHelper使用扩展方法的静态类。

四、Pager类改造

经过分析我们发现Pager是通过ToString()方法将分页字符串输出到前台的,所以我们这里需要先改造Pager的ToString()方法。我们常常希望分页是这样显示的:

 上一页  1  2 3 4 5 6 下一页,所以先将ToString()方法改造如下:


 1 public override string ToString()
 2         {
 3             if (_pagination.TotalItems == 0)
 4             {
 5                 return null;
 6             }
 7 
 8             var builder = new StringBuilder();
 9 
10             builder.Append("<div class=" + _pageStyle + ">");
11 
12             if (_pagination.PageNumber > 1)
13                 builder.Append(CreatePageLink(_pagination.PageNumber  1, _pagePrev));
14             else
15                 builder.Append(CreatePageText(_pagePrev));
16             for (int i = 0; i < _pagination.TotalPages; i++)
17             {
18                 var current = i + 1;
19                 if (current == _pagination.PageNumber)
20                 {
21                     builder.Append(CreatePageText(current.ToString()));
22                 }
23                 else
24                 {
25                     builder.Append(CreatePageLink(current, current.ToString()));
26                 }
27                 builder.Append("  ");
28             }
29 
30             if (_pagination.PageNumber < _pagination.TotalPages)
31                 builder.Append(CreatePageLink(_pagination.PageNumber + 1, _pageNext));
32             else
33                 builder.Append(CreatePageText(_pageNext));
34             builder.Append(@"</div>");
35 
36             return builder.ToString();
37         }

这里需要交代一下将要实现查询分页的原理,这个方案中我们将会把查询的信息附加到分页的Url上,首先我们会把需要附加的条件添加到一个Dictionary<string, string>类中,然后传给Pager类进行处理。

下面我们来改造一下CreateQueryString方法:

 


 1  private string CreateQueryString(NameValueCollection values)
 2         {
 3             var builder = new StringBuilder();
 4             if (_searchValues != null)
 5             {
 6                 builder = GetSearchPage(values);
 7             }
 8             else
 9             {
10                 foreach (string key in values.Keys)
11                 {
12                     if (key == _pageQueryName)
13                     //Don't re-add any existing 'page' variable to the querystring – this will be handled in CreatePageLink.
14                     {
15                         continue;
16                     }
17 
18                     foreach (var value in values.GetValues(key))
19                     {
20                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(value));
21                     }
22                 }
23             }
24             
25 
26             return builder.ToString();
27         }
28         /// <summary>
29         /// 根据传入的_searchValues来组织分页字符串
30         /// </summary>
31         /// <param name="values"></param>
32         /// <returns></returns>
33         private StringBuilder GetSearchPage(NameValueCollection values)
34         {
35             var builder = new StringBuilder();
36             Dictionary<stringstring> dictionary = new Dictionary<stringstring>();
37             foreach (var item in _searchValues)
38             {
39                 dictionary.Add(item.Key, item.Value);
40             }
41             foreach (string key in values.Keys)
42             {
43                 if (key == _pageQueryName)
44                 //Don't re-add any existing 'page' variable to the querystring – this will be handled in CreatePageLink.
45                 {
46                     continue;
47                 }
48 
49                 foreach (var value in values.GetValues(key))
50                 {
51                     if (_searchValues.Keys.Contains(key))
52                     {
53                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(dictionary[key]));
54                         dictionary.Remove(key);
55                     }
56                     else
57                     {
58                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(value));
59                     }
60 
61                 }
62 
63             }
64             foreach (var item in dictionary)
65             {
66                 builder.AppendFormat("&amp;{0}={1}", item.Key, HttpUtility.UrlEncode(item.Value));
67 
68             }
69             return builder;
70         }

 

这里边主要添加的就是这个GetSearchPage方法,这个方法就是根据客户端传入的查询条件来组织显示分页字符串的。

五、缺点:

因为时使用附加url的方式实现的,所以对于查询条件过多的时候可能有问题,有空我会再实现一个post方案供大家参考。另外这里大家还需要注意的就是url中的查询字符串的名字不能重复,如果重复会直接被替换,详见源码。

六、源码下载

 

我的ASP.NET MVC实践系列

ASP.NET MVC实践系列1-UrlRouting

ASP.NET MVC实践系列2-简单应用

ASP.NET MVC实践系列3-服务器端数据验证

ASP.NET MVC实践系列4-Ajax应用

ASP.NET MVC实践系列5-结合JQuery

ASP.NET MVC实践系列6-Grid实现(上)

ASP.NET MVC实践系列7-Grid实现(下-利用Contrib实现)

其他:

在ASP.NET MVC中对表进行通用的增删改

[C#]清晰的图片缩略方案

mikel阅读(1285)

购吧网 :独步全球的BAC电子商务模式基于一批有着传统营销领域丰富经验的专家及对电子商务运作深刻了解的网络专家整合,由广州丽联网络科技有限公司架构购吧网实现。http://www.365goba.com/

希望这篇文章能给对于需要经常生成缩略图的朋友提供帮助!

购吧网目前拥有4000余种商品,在售商品超过2000万,其中图片量截至目前已有8G。
目前我们的方案是用单独的文件服务器存放商品的图片,通过file.365goba.com访问。
文件服务器上架一个用于上传图片的WCF服务对上传的图片进行缩略并保存。

购吧网前期的缩略算法用的是网略上广泛流传的三线性插值算法(效果并不是很好),代码如下:

using System;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Ants.Tools
{
public class Image
{
public int Width { get; set; }
public int Height { get; set; }
private Image() { }
public Image(int width, int height)
{
this.Width = width;
this.Height = height;
}
public MemoryStream getHightThumb(Stream imgData, string Mode_HW_W_H_Cut)
{
MemoryStream result = new MemoryStream();
System.Drawing.Image.GetThumbnailImageAbort myCallBack = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallBack);
try
{
System.Drawing.Image originalImage = System.Drawing.Image.FromStream(imgData);
int X, Y;
X = Width;
Y = Height;
int towidth = X;
int toheight = Y;
int x = 0;
int y = 0;
int ow = originalImage.Width;
int oh = originalImage.Height;
switch (Mode_HW_W_H_Cut)
{
case "HW": //指定高宽缩放(可能变形)
  break;
case "W"://指定宽,高按比例
toheight = originalImage.Height * X / originalImage.Width;
break;
case "H//指定高,宽按比例
                        towidth = originalImage.Width * Y / originalImage.Height;
break;
case "Cut":
if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
{
oh = originalImage.Height;
ow = originalImage.Height * towidth / toheight;
y = 0;
x = (originalImage.Width - ow) / 2;
}
else
{
ow = originalImage.Width;
oh = originalImage.Width * Y / towidth;
x = 0;
y = (originalImage.Height - oh) / 2;
}
break;
default:
break;
}
System.Drawing.Image bitmap = new System.Drawing.Bitmap(towidth, toheight);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.Clear(System.Drawing.Color.Transparent);
g.DrawImage(originalImage, new System.Drawing.Rectangle(0, 0, towidth, toheight),
new System.Drawing.Rectangle(x, y, ow, oh),
System.Drawing.GraphicsUnit.Pixel);
bitmap.Save(result, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch
{
//do something
}
return result;
} 
    }
}
此算法可以满足一般的网站的需求,但是作为一个电子商务网站,商品的图片的清晰度直接影响到消费都对此商品的购买欲望。
为了找到更好的方案,终于让我们找到了一个好的组件:MagickNet 这个组件是用C++写的,不过网络上已经有可用的C#调用版,文章的
后我也会给出这个DLL文件,值得一提的是这个组件是开源的。大家可以去研究下。
MagickNet 的功能很多,我这里就贴出一下他的缩略方法的用法(MagickNet 的资料在网上很难早)
using System;
using System.Collections.Generic;
using System.Text;
namespace KeChenDLL
{
public class UploadFile
{
private string _path;//要处理的图片(原图)地址
public UploadFile(string path)
{
this._path = path;
}
private UploadFile()
{
}
public void ReSize(int width, int height,string SaveToPath)
{
MagickNet.Image img = new MagickNet.Image(_path);
img.Quality = 100;
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(_path);
int x = bitmap.Width;
int y = bitmap.Height;
float rank = ((float) x)/y;
if (x > y)
{
height =Convert.ToInt32(height / rank);
}
else
{
width =Convert.ToInt32(width * rank);
}
img.Resize(new System.Drawing.Size(width, height));
img.Write(SaveToPath);
img.Dispose();
}
}
}
希望这篇文章能帮到需要的人。
附MagickNet组件地址

原创文字只代表本人某一时间内的观点或结论,本人不对涉及到的任何代码担保。转载请标明出处!

[OSGi]插件平台的类加载机制

mikel阅读(915)

作为一个插件平台,除了要解决在运行时插件的交互问题外,还需要解决一个非常重要的问题——类加载。原因在于:(1)类加载机制可以绕过默认类型加 载器按需自动加载每一个插件的所有类型;(2)插件具备独立性,即一个插件的运行不能对其它插件和插件内核平台产生影响,这使得类加载机制必须维护每一个 插件的类型;(3)每一个插件可能会引用同一个Class的不同版本,这要求类加载机制必须能够加载多个版本的同一个类并为每一个插件维护其特定版本的类 型。本文将讨论一下基于Java和.NET的插件平台的类加载机制。

 

在Java平台,每一个类型是由ClassLoader和类型全称唯一标识。ClassLoader具有父子关系的层次结构。来自于不同 ClassLoader的相同全名的类被视为不同类型。ClassLoader提供了简单、灵活且优雅的类型加载机制,在默认的ClassLoader 中,利用其进行类型加载时,它会先尝试使用父ClassLoader进行加载,如果在父ClassLoader没有加载成功,才考虑用子 ClassLoader中加载。如果请求子ClassLoader加载的类型,若在父ClassLoader中加载成功,则子ClassLoader不会 再加载,会直接从父ClassLoader中获取。这意味着,一个在父ClassLoader中加载的类型可以和所有的子ClassLoader共享,不 过,子ClassLoader间是不能够共享类型的(这种父ClassLoader向子ClassLoader的共享行为可以通过自定义 ClassLoader来改变)。考虑到Java的默认ClassLoader机制,在我看来理想状态下的插件平台的类加载机制的设计应该如下图(虽然我 不是专业的Java开发人员,但是对其ClassLoader非常感兴趣,于是好好研究了一下,个人觉得该图的ClassLoader层次设计要比 JavaPluginFramework优雅)。这个类加载机制由3层ClassLoader组成,类加载管理器必须严格控制线程执行过程中的 ClassLoader,从而实现插件独立行、多版本类型支持、插件与平台无关性。(附:Flex的类型加载类似于Java的ClassLoader,只 不过它被命名为AppDomain,估计是吸收了.NET和Java的优点。不过,在开发插件化的Flex平台时,如果采用的是分层的AppDomain 的话,很容易碰到TypeA转换成TypeA异常的问题,我把它称为Singleton Hell Issue,因为Flex底层类库使用了不少的单件类,如果一个AppDomain注册一个实例,另一个AppDomain获取并使用则出现异常,这类异 常在Flex SDK 3.0中大量存在。因此,我们在使用Java的ClassLoader和Flex的AppDomain的时候,需要谨慎并精细。)

 

 

(鉴于本文有点抽象,于是举个例子说明一下 )假设在当前线程运行到Plugin B的代码,插件管理器在执行Plugin B的代码之前已经将其类加载器设置为PluginClassLoader了,因此,线程在执行代码的过程中,如果碰到新的类型,则将从 PluginClassLoader B中加载所需的类型;同理,当执行Plugin A的代码时,其加载器使用的是PluginClassLoader A。这为插件类型空间提供了独立性,从而使得在A和B插件可以使用同一个类型的不同版本。 

 

在.NET平台中,每一个类型是由AppDomain + 程序集全名 + 类全名来唯一标识的。微软在程序集中直接声明了版本号。因此,CLR Loader允许在一个AppDomain存在多个版本的相同程序集,这意味着在同一AppDomain可能存在不同版本的类型。.NET的程序集相对于 Java的Package而言,它天生就对版本进行了标识,也就直接解决了插件平台类型加载的多版本问题。不过,遗憾的是,加载到一个AppDomain 的程序集是无法被卸载的,除非卸载整个AppDomain。为了实现插件的动态卸载,这就要求为每一个插件创建一个独立的程序集,但这又引入了 Remoting Call,极大降低了性能。我在这里提到的类加载机制,为了性能和系统简单性考虑,忽略了插件的动态卸载,仅为一个插件平台使用一个AppDomain, 并引入了一套新的类型加载机制,该类型加载机制能够按需加载程序集,其设计,部分借鉴了Java ClassLoader的思路,如下图。在此机制的TypeLoader由其加载的Assembly组成,也具备有一个层次结构,每一个插件的类型加载都 使用自身的BundleTypeLoader进行加载。BundleTypeLoader会从CoreTypeLoader、 SharedLibTypeLoader和依赖的BundleTypeLoader加载所需的类型。

 

 

本文基于.NET的TypeLoader将基于Assembly.LoadFile实现多版本程序集的加载,因此,我们需要为每一个插件管理好其相 应的程序集,在Bundle B执行过程中,它将使用BundleTypeLoader B来加载所需的类型,而这个Loader会根据需要将对应的程序集加载到当前应用域。从而实现,在插件A和B,其类型加载仅限于从插件本身依赖的程序集加 载,虽然所有的程序集都在同一个应用域,但已经被分层管理起来了。(其实原理就只有一句话“确保在插件平台上线程执行过程中能够加载到正确的类型”,比如 执行插件A的代码时,插件A依赖的类型必须在其类加载器存在。)

 

Creative Commons License

本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名道法自然(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

[下载]很不错的后台界面收集

mikel阅读(715)

刚刚在百度上看到了,很多都蛮好看的。呵呵… …
最近打算重整下后台界面,给用户操一个舒适,易作的空间,收集到以下模板,挺不错,供大家下载,转载请注明文章的原出处,谢谢
很漂亮实用的后台模板,深蓝色风格,带登陆页面及后台操作页面,功能页面以选项卡形式打开,左边菜单效果也挺动感的,确实是个不错的后台模板。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 419 次)

//PHP开源代码
办公自动化系统OA简单后台模板,深蓝色风格,带登录页面及后台操作页面,操作页以选项卡形式打开,操作还是挺简单的,是不久前开发的一款OA系统所改编而来。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 266 次)

 

绿色清新简单的后台模板,绿色风格,含登陆页面及后台操作页面,界面简单,操作方便,且后台还是DIV+CSS实现

//OSPHP.com.CN

 

请下载附件中的源码,不要直接复制 (已下载 97 次)

点击在新窗口中浏览此图片
仿126邮箱系统后台界面,绿色风格,看上去挺简单的,功能页面都在新选项卡下打开,IE6、IE7下显示正常,其他浏览器未测试
点击在新窗口中浏览此图片

//OSPHP.COm.CN

 

请下载附件中的源码,不要直接复制 (已下载 102 次)

Ext 做的清爽简单后台模板,清爽淡蓝色风格,功能较简单,以选项卡方式打开功能,前台中要做用户后台的时候可以考虑下
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 113 次)

//PHP开源代码

绿色经典后台模板,适合开发CMS、HR、OA、CRM等,绿色风格,是一套结构稍微复杂的模板,适合开发各类中小型系统,并包含配套登陆页面模板,
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 144 次)

仿outlook邮件系统管理后台模板,蓝色风格,分为左中右(左边菜单、中间内容、右边辅助功能)三栏,对于需要做邮件系统的朋友可以下啦,不过左边有两张图片如果需要的话还要自己手工切割一下。

//开源OSPhP.COM.CN

点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 91 次)

管理信息系统后台模板,简单的蓝黑风格,包含登陆页面,适合简单、快速的后台功能开发
 

请下载附件中的源码,不要直接复制 (已下载 93 次)

//OSPHP.com.CN
点击在新窗口中浏览此图片
学校管理系统后台模板,浅蓝色风格,登陆页面虽然跟里面的功能页有点不搭,但挺时尚哦,后台功能菜单也是半透明效果
 

请下载附件中的源码,不要直接复制 (已下载 103 次)

点击在新窗口中浏览此图片

//OSPHP.COm.CN

学校管理系统后台模板,浅蓝色风格,登陆页面虽然跟里面的功能页有点不搭,但挺时尚哦,后台功能菜单也是半透明效果
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 92 次)

企业邮件处理系统后台模板,深蓝色风格,包含登陆页面,顶部菜单已经实现,整个后台看上去挺动感的,只是左边的菜单还需要自己找一段脚本去完善
点击在新窗口中浏览此图片

//开源OSPhP.COM.CN

 

请下载附件中的源码,不要直接复制 (已下载 96 次)

网站后台管理系统模板,蓝色风格,菜单较复杂,带横向菜单和纵向菜单,功能交错处理,权限不好控制,有需要的下吧。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 92 次)
//开源代码OSPHP.COM.Cn

仿163简约风格邮件系统后台模板,蓝色风格
 

请下载附件中的源码,不要直接复制 (已下载 79 次)

点击在新窗口中浏览此图片
一款超炫的后台,Ext模拟Windows桌面,Ext经典浅蓝风格,功能非常强大,包括最大化、最小化、状态栏、桌面图标等,不过需要非常懂Ext脚本的才可驾驭它。
点击在新窗口中浏览此图片 //OsPHP.COM.CN
 

请下载附件中的源码,不要直接复制 (已下载 99 次)

国外简单后台模板[1],白色风格,相对于上面那套模板来说,不提供风格切换功能,但是在用户操作方面设计的超简单的,所谓后台越简单越好嘛,所以前台有会员功能要做的时候可以借鉴一下哦。含PSD文件,懂设计的朋友可以自己修改。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 107 次)
//开源代码OSPhP.COm.CN

国外简单后台模板[1],白色风格,相对于上面那套模板来说,不提供风格切换功能,但是在用户操作方面设计的超简单的,所谓后台越简单越好嘛,所以前台有会员功能要做的时候可以借鉴一下哦。含PSD文件,懂设计的朋友可以自己修改。
 

请下载附件中的源码,不要直接复制 (已下载 83 次)

点击在新窗口中浏览此图片
蓝色动网风格后台模板,蓝色风格,经典的动网蓝色风格后台,好像又回到几年前了,结构简单,开发起来速度快。 //开源代码OSPhP.COm.CN
 

请下载附件中的源码,不要直接复制 (已下载 71 次)

 

请下载附件中的源码,不要直接复制 (已下载 54 次)

国外简单后台模板[2],多种风格,蓝色、橙色、黑色等,可相互切换,如果开发网站的时候有会员机制的话用这套就再简单不过了。含PSD文件,懂设计的朋友可以自己修改。
 

请下载附件中的源码,不要直接复制 (已下载 74 次)

//oSPHP.COM.CN
点击在新窗口中浏览此图片
乡镇农户评级系统后台模板,淡绿色风格,带登陆页面及后台操作页面html文件,菜单有点复杂,横向纵向结合,不过本人觉得还行,适合开发各类系统后台,
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 81 次)

//OSPHP.COm.CN
一款简单黑色后台管理模板,黑色风格,有登陆页面及操作页面,含PSD文件,会设计的朋友自己切割会好很多,因为此模板的HTML文件切割的不是很好
 

请下载附件中的源码,不要直接复制 (已下载 61 次)

点击在新窗口中浏览此图片
人力资源管理系统(HR)HTML模板,蓝色风格,带登陆页面及后台操作页面html文件
点击在新窗口中浏览此图片 //开源代码OSPHP.COM.Cn
 

请下载附件中的源码,不要直接复制 (已下载 81 次)

TaskMenu3.0仿WindowsXP后台模板,WindowsXP蓝色、银色、经典三种风格,因为全是js控制,所以自带了风格切换功能以及菜单控制功能,这款后台出来应该有几年了,经典值得收藏
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 73 次)
//OSPHP.COM.Cn开源

淡蓝色网站后台管理模板,淡蓝色风格,带登陆页及后台操作页面,全部HTML文件,后台采用框架集页面完成,适合各种网站开发后台管理或CMS后台也可以
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 97 次)

操作简单的蓝色设计后台模板,蓝色风格,含登陆页面及后台操作页面,设计上体现了对用户操作的考虑,是您开发一般后台的首选。 //OSPHP.COM.Cn开源
 

请下载附件中的源码,不要直接复制 (已下载 61 次)

点击在新窗口中浏览此图片
一款非常漂亮的后台管理模板,深蓝色风格,带登陆页面及后台操作页面,含PSD文件,懂设计的可以自己动手。
 

请下载附件中的源码,不要直接复制 (已下载 84 次)

//开源代码OSPHP.COM.Cn
点击在新窗口中浏览此图片
一款不错的物业管理系统全套html模板,绿色风格,带登陆页面及后台操作页面,看上去跟前几款的设计模式差不多,不过风格比较清新,个人觉得挺喜欢的
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 84 次)
//OsPHP.COM.CN

国外简单后台模板[3],蓝白风格,国外的模板没的说,简单,后台就更不用说了,设计超简单的,但功能又介于其内,真是不多得的好模板。
 

请下载附件中的源码,不要直接复制 (已下载 80 次)

点击在新窗口中浏览此图片
另附:http://www.uimaker.com/,经常去的设计网站.

 

原出处:http://www.osphp.com.cn/index.php

[Debug]监控 SQL Server (2005/2008) 的运行状况

mikel阅读(821)

原文地址:
http://technet.microsoft.com/zh-cn/library/bb838723.aspx

Microsoft SQL Server 2005 提供了一些工具来监控数据库。方法之一是动态管理视图。动态管理视图 (DMV) 和动态管理函数 (DMF) 返回的服务器状态信息可用于监控服务器实例的运行状况、诊断问题和优化性能。

常规服务器动态管理对象包括:

  • dm_db_*:数据库和数据库对象

  • dm_exec_*:执行用户代码和关联的连接

  • dm_os_*:内存、锁定和时间安排

  • dm_tran_*:事务和隔离

  • dm_io_*:网络和磁盘的输入/输出

此部分介绍为监控 SQL Server 运行状况而针对这些动态管理视图和函数运行的一些常用查询。

摘录部分精彩SQL如下:

下面的查询显示 CPU 平均占用率最高的前 50 个 SQL 语句。

Select TOP 50
total_worker_time
/execution_count AS [Avg CPU Time],
(
Select SUBSTRING(text,statement_start_offset/2,(CASE WHEN statement_end_offset = 1 then LEN(CONVERT(nvarchar(max), text)) * 2 ELSE statement_end_offset end statement_start_offset)/2FROM sys.dm_exec_sql_text(sql_handle)) AS query_text, *
FROM sys.dm_exec_query_stats 
ORDER BY [Avg CPU Time] DESC

下面的查询显示一些可能占用大量 CPU 使用率的运算符(例如 ‘%Hash Match%’、‘%Sort%’)以找出可疑对象。

select *
from 
      sys.dm_exec_cached_plans
      
cross apply sys.dm_exec_query_plan(plan_handle)
where 
      
cast(query_plan as nvarchar(max)) like '%Sort%'
      
or cast(query_plan as nvarchar(max)) like '%Hash Match%'

运行下面的 DMV 查询以查看 CPU、计划程序内存和缓冲池信息。

select 
cpu_count,
hyperthread_ratio,
scheduler_count,
physical_memory_in_bytes 
/ 1024 / 1024 as physical_memory_mb,
virtual_memory_in_bytes 
/ 1024 / 1024 as virtual_memory_mb,
bpool_committed 
* 8 / 1024 as bpool_committed_mb,
bpool_commit_target 
* 8 / 1024 as bpool_target_mb,
bpool_visible 
* 8 / 1024 as bpool_visible_mb
from sys.dm_os_sys_info

下面的示例查询显示已重新编译的前 25 个存储过程。plan_generation_num 指示该查询已重新编译的次数。

select top 25
sql_text.
text,
sql_handle,
plan_generation_num,
execution_count,
dbid,
objectid 
from sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(sql_handle) as sql_text
where plan_generation_num > 1
order by plan_generation_num desc

下面的 DMV 查询可用于查找哪些批处理/请求生成的 I/O 最多。如下所示的 DMV 查询可用于查找可生成最多 I/O 的前五个请求。调整这些查询将提高系统性能。

select top 5 
    (total_logical_reads
/execution_count) as avg_logical_reads,
    (total_logical_writes
/execution_count) as avg_logical_writes,
    (total_physical_reads
/execution_count) as avg_phys_reads,
     Execution_count, 
    statement_start_offset 
as stmt_start_offset, 
    sql_handle, 
    plan_handle
from sys.dm_exec_query_stats  
order by  (total_logical_reads + total_logical_writes) Desc

[Debug]一个强大的LogParser的UI工具--logparserlizard简介

mikel阅读(1066)

转载:http://www.cnblogs.com/downmoon/archive/2009/09/02/1558409.html
日志分析,特别是IIS日志,一般人都会想到LogParser工具,的确很强。但是命令行的操作界面令很多非专业的管理人员望而生畏,现在好了,有一个可视化的LogParser的UI工具可以使用了!
Log Parser Lizard 1.1, 这是一款用Vc++.net写的logParser增强工具。主要有以下特点:
1、封装了logParser命令,带图形界面,大大降低了LogParser的使用难度。
2、集成了几个开源工具,如log4net等。可以对IIS logs\EventLogs\active directory\log4net\File Systems\T-SQL进行方便的查询。
3、集成了Infragistics.UltraChart.Core.v4.3、Infragistics.Excel.v4.3.dll等,使查询结果可以方便的以图表或EXCEL格式展示。
4、集成了常见的查询命令,范围包含六大模块:IIS
5、可以将查询过的命令保存下来,方便再次使用。
最重要的是,它是完全免费的。
下载地址为:
http://www.brothersoft.com/log-parser-lizard-download-238815.html
需要先安装LogParser 2.2,下载地址:
http://www.microsoft.com/DownLoads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en
下载后4.9M的一个MSI文件,直接安装即可。注意安装位置尽量不要带空格。
注意:如果LogParser没有安装,会运行出错!

初始界面如图:


下面以一个检查IISLog的例子来比较LogParser与LogParserLizard的区别。
某日早上,发现网站阻塞,
第一种速查方案:
迅速启动LogParser:
进入命令行模式:
输入:

LOGPARSER -i:IISW3C file:D:\Log\log_SQL\Slowest20FilesInIIS_MySite.SQL -o:DataGrid -q:off

其中,Slowest20FilesInIIS_MySite.sql的内容如下:

Code

执行结果如图:

从图中可以看出,访问最慢而且最频繁的页面是/Company/List.aspx, 而且集中在一个IP: 116.7.16.249  ,基本可以肯定主·这是有人恶意爬数据
,再输入:

LOGPARSER -i:IISW3C file:D:\Log\log_SQL\Slowest10IPInIIS_MySite.sql -o:DataGrid -q:off

 其中,Slowest10IPInIIS_MySite.sql的内容如下:

Code


启动管理工具,禁IP!!!
第二种速查方案:
启动logParserLizard,在界面中选择IIS Log–"New Query"–格式选"IIS W3C Logs"
在查询窗口输入同样的SQL,点击"Generate"

Code

,得到如下结果:

再在查询窗口输入

Code

得到下图:

且慢,右上角,Chart,打开一看,好多的饼图哟,

处理方法同上
为了下次方便使用,将该query保存即可。
此外 用户除了前面的六大类型查询外,还可以自增加新的Group,把自己常用的查询整理一下。
本文至此结束。有任何问题请联系邀月3w@live.cn。助人等于自助!   3w@live.cn

[MVC]ASP.NET MVC 开源博客

mikel阅读(866)

转载:http://www.dsjian.com/10002/00191

前段时间学习ASP.NET MVC的一个小小成果。能支持搭建个人博客,具体细节如下:

1、多层架构
以面向接口的开发模式,将前端页面、服务层、数据库层完全解耦。为什么要这样做,我想既然用MVC模式来搭建系统,目的就是构建低耦合的应用程序,就因充分发挥其系统结构清晰、高可测试性、高效率开发的特点。

2、ORM使用MySoft.Data
选择一个好的ORM工具,可以大大的提高开发效率。在这个项目中,我选择了MySoft.Data, 为什么选择这个ORM框架,有兴趣的可以看看之前我写的文章。对于数据库层,使用了经典的Repository模式,这样可以使你可以方便的替换ORM框 架,你可以很方便的重新构建一个数据访问层。只要你遵守我的IRepository接口规范,就可以通过WebConfig配置的方式替换数据访问层。

3、DsJian.OC — 简易的IOC容器
根据我对IOC的理解,实现了一个自认为简易的IOC容器(但不知道真正意义上的IOC具体是什么样子,也没有深入的研究)。实现IOC容器的目的也就是为了将数据访问层、服务层、Web层中的对象管理起来,实现依赖注入,最终目的也是构建低耦合的应用程序。

4、脚本框架用JQeury
这也是ASP.NET MVC推荐的脚本框架,个人认为JQuery也是不错的,轻量级但功能强大的脚本框架。

有兴趣的朋友,可以在这里下载源码:
http://dsjian.codeplex.com

特别提醒,UI我套用了wordpress的 iNove 主题。
演示:http://demo.dsjian.com/

代码配置说明,看这里

[EXT]Ext、以及Coolite下实现表格锁定列和多行表头

mikel阅读(1062)

转载:http://www.cnblogs.com/zhujiechang/archive/2009/11/10/1599930.html

      这段时间我们的项目中采用了Coolite来做富客户端,Coolite目前的版本是0.8.1,它所采用的Ext版本是2.2.1,因此下面主要是说明 在这个版本下所实现的锁定列和多行表头,需要说明的是Ext在2.0版本以后就不自带锁定列功能了,只能自己实现,中国人的习惯和国外的不太一样,喜欢表 格化得数据,而且形式又多种,表格最基本的功能之一,就是要锁定列和多行表头,而这两项又是Coolite和Ext所提供的组件中所没有直接包含的,虽然 他们这两个框架没有提供,但依然要说的是Ext的js功能很强大, Coolite为Ext在ASP.NET上的应用封装了绝大多数功能,还扩展了一部分,庞大的功能模块,非常不错,可以和官方开源封装的java版 GWT2.0媲美。
     看了些废话,讲入主题了,看下效果图:

    

     实现锁定列目前较为常见的是继承Grid,然后重写模块,实现双表格,下面的示例是采用MeDavid所实现的锁定列方式,版本为[Update 5],Ext的代码很长就贴点主要和修正的,例子在后面有完整提供。Ext为这些控件都提供了一个模板的方式以供重写,就是重写了主模板.

 


 1 initTemplates : function(){
 2         if(!this.templates){
 3             this.templates = {};
 4         }
 5         if(!this.templates.master){
 6             this.templates.master = new Ext.Template(
 7                 '<div class="x-grid3" hidefocus="true">',
 8                     '<div class="x-grid3-locked">',
 9                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{lockedHeader}</div></div><div class="x-clear"></div></div>',
10                         '<div class="x-grid3-scroller"><div class="x-grid3-body">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
11                     '</div>',
12                     '<div class="x-grid3-viewport">',
13                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{header}</div></div><div class="x-clear"></div></div>',
14                         '<div class="x-grid3-scroller"><div class="x-grid3-body">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
15                     '</div>',
16                     '<div class="x-grid3-resize-marker"> </div>',
17                     '<div class="x-grid3-resize-proxy"> </div>',
18                 '</div>'
19             );
20         }
21         Ext.grid.LockingGridView.superclass.initTemplates.call(this);
22     },
23 

     同时为ColumnModel增加getTotalLockedWidth方法,原有[Update 5]中的脚本中Ext.grid.LockingColumnModel要去掉。


1 Ext.grid.ColumnModel.prototype.getTotalLockedWidth=function(){
2         var totalWidth = 0;
3         for(var i = 0; i < this.config.length; i++){
4             if(this.isLocked(i) && !this.isHidden(i)){
5                 totalWidth += this.getColumnWidth(i);
6             }
7         }
8         return totalWidth;
9     };

      另外,版本为[Update 5]目前支持行选择模式有点BUG,就是在获得焦点时行会移动,这是因为没有重新定位造成的,在Ext.grid.LockingGridView中加入以下方法即可修正。

 


1 getResolvedXY : function(resolved){
2         if(!resolved){
3             return null;
4         }
5         var c = resolved.cell, r = resolved.row;
6         return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
7     },
8 

     多行表头分别对应有两个文件,一个是针对有锁定列的表格,一个是针对没有锁定列的表格,两个表格输出的对象是不一样的,要经过判断来输出不一样的值,至于实现的方式大家看下代码就知道了。

     下面把它融入进Coolite中。新建一个ExtGridPanel继承自GridPanel,然后重写OnPreRender如下:

 


 1 protected override void OnPreRender(EventArgs e)
 2         {
 3             //判断是否有锁定列
 4             bool haveLock=false;
 5             ExtColumn extcolumn;
 6             int i;
 7             for (i = 0; i < ColumnModel.Columns.Count; i++)
 8             {
 9                 extcolumn = ColumnModel.Columns[i] as ExtColumn;
10                 if (extcolumn == null)
11                     break;
12                 else if (extcolumn.Locked)
13                 {
14                     haveLock = true;
15                     break;
16                 }
17             }            
18             //判断是否有多行表头            
19             foreach (Plugin plugin in Plugins)
20             {
21                 if (plugin is ExtGroupHeaderGrid)
22                 {
23                     //如果表格是由锁定的,那一样通知插件也是要求锁定的
24                     //这里用一个属性进行区分
25                     ((ExtGroupHeaderGrid)plugin).Locked = haveLock;                    
26                     break;
27                 }
28             }
29 
30             System.ComponentModel.AttributeCollection attrscoll = TypeDescriptor.GetAttributes(thistrue);
31 
32             //注册js
33             if (haveLock)
34             {
35                 //锁定列之前的列也必须锁定
36                 for (int j = 0; j < i; j++)
37                 {
38                     extcolumn = ColumnModel.Columns[j] as ExtColumn;
39                     if (extcolumn != null)
40                         extcolumn.Locked = haveLock;                    
41                 }
42                 ((InstanceOfAttribute)attrscoll[typeof(InstanceOfAttribute)]).ClassName = "Ext.grid.LockingEditorGridPanel";
43                 ScriptManager.RegisterClientScriptInclude(base.GetType(), "CDPFLibrary.Build.extjs.Grid.LockingGridPanel.js");
44             }
45             else
46             {
47                 ((InstanceOfAttribute)attrscoll[typeof(InstanceOfAttribute)]).ClassName = "Coolite.Ext.GridPanel";
48             }
49              
50             base.OnPreRender(e);
51         }

     同时新增了几个类,新增类如下:ExColumnModel、ExtColumn、ExtGridPanel、ExtGroupHeaderGrid、 ExtRow、ExtRowCollection、ExtRows、ExtRowsCollection这些,具体代码都在源码中可以自己分析。
因为同一个表格同时要支持锁定列和非锁定列,因为功能不同输出的js也不一样,那么InstanceOfAttribute的ClassName类名要进行变动,所以要采用TypeDescriptor.GetAttributes来取得属性,这样可以保存当前实例所进行修改过后的自定义属性值,而用Type. GetCustomAttributes只能取得实例定义时的值,不能获得当前实例改动后的值,这一点要在Coolite.Ext.Web程序集的WebControl_Helpers类的GetClientConstructor方法和InstanceOf属性中进行修改,个人认为它是一个明显的BUG。

     其实涉及的变动的地方还挺多,上面仅仅是关键点,源码随文章发布,Coolite的源码可以去官方下来,下面也有链接。

     下载:锁定列和多表头源码    Coolite源码0.8.1
     项目需要用Vs2008打开,需要ASP.NET3.5支持,欢迎发表意见。

[C#]解除具体依赖的技术

mikel阅读(1072)

转载:http://www.cnblogs.com/wayfarer/archive/2009/11/10/1600467.html

一 个外部具体对象的引入,必然会给一个模块带来与外部模块之间的依赖。而具体对象的创建始终是我们无法规避的。即使我们可以利用设计模式的工厂方法模式或抽 象工厂封装具体对象创建的逻辑,但却又再次引入了具体工厂对象的创建依赖。虽然在设计上有所改进,但没有彻底解除具体依赖,仍让我心有戚戚焉。

以 一个电子商务网站的设计为例。在该项目中要求对客户的订单进行管理,例如插入订单。考虑到访问量的关系,系统为订单管理提供了同步和异步的方式。显然,在 实际应用中,我们需要根据具体的应用环境,决定使用这两种方式的其中一种。由于变化非常频繁,因而我们采取了“封装变化”的设计思想。譬如,考虑应用Strategy模式,因为插入订单的行为,实则就是一种插入订单的策略。我们可以为此策略建立抽象对象,如IOrderStrategy接口。

public interface IOrderStrategy

{

   void Insert(OrderInfo order);

}

然后分别定义两个类OrderSynchronousOrderAsynchronous实现IOrderStrategy接口。类结构如图1所示。

image 1  订单策略的设计

当领域对象Order类需要插入订单时,将根据IOrderStrategy接口的运行期类型,执行相关的订单插入策略,如下代码所示。

public class order

{

    private IOrderStrategy m_orderStrategy;

    public order(IOrderStrategy orderStrategy)

   {

          m_orderStrategy = orderStrategy;

   }

     public void Insert(OrderInfo order)

   {

          m_orderStrategy.Insert(order);

   }

}

由于用户随时都可能会改变插入订单的策略,因此对于业务层的订单领域对象而言,绝不能与具体的订单策略对象产生耦合关系。也就是说,在领域对象Order类中,不能new一个具体的订单策略对象,如下面的代码:

IOrderStrategy orderStrategy = new orderSynchronous();

虽然在前面的实现中,我们通过领域对象的构造函数传递了IOrderStrategy接 口对象。但这样的实现仅仅是将具体订单策略对象的创建推迟到了领域对象的调用者那里而已,调用者无法避免具体订单策略对象的创建。显然,这是一种“治标不 治本”的做法。我们当然也期望能够有一种理想的状态,就是具体对象的创建永远都不要在代码中出现。事实上,模块与模块间之所以产生依赖关系,正是因为有具 体对象的存在。一旦在一个模块中创建了另一个模块中的具体对象,依赖就产生了。现在,我们的目的就是要将这些依赖消除。

1、配置文件与反射技术

 

 

 

使用硬编码方式创建一个对象,必然会带来对象之间的具体依赖。一种最简单的方式是将反射技术与配置文件相结合,在具体对象拥有共同抽象的前提下,通过配置文件获得具体对象的类型信息,然后利用反射创建相应的对象。例如,在领域对象Order类中,可以如此实现:

public class order

{

   private static readonly IOrderStrategy orderInsertStrategy =

        LoadInsertStrategy();

   private static IOrderStrategy LoadInsertStrategy()

   {

           //通过配置文件找到具体的订单策略对象

               string path = ConfigurationManager.AppSettings["OrderStrategyAssembly"];

 

 

 

               string className = ConfigurationManager.AppSettings["OrderStrategyClass"];

 

 

 

 

 

            //通过反射创建对象实例

            return (IOrderStrategy)Assembly.Load(path).CreateInstance(className);

 

 

 

   }

}

在配置文件web.config中,配置如下的节:

<add key="OrderStrategyAssembly" value="AgileDon.BLL"/>

<add key="OrderStrategyClass"     value="BLL.OrderSynchronous"/>

通过引入泛型,我们可以对前面的逻辑进行有效的封装,例如定义如下的工厂辅助类。

public static class FactoryHelper<T>

    where T:class

{

    private static T instance = null;

 

 

    public static T Create(string typeNameKey,

                                 string nameSpace,

                                 string assemblyPath)

    {

        if (instance == null)

        {

            string typeName = ConfigurationManager.AppSettings[typeNameKey];

            string className = nameSpace + "." + typeName;

 

 

            instance = (T)Assembly.Load(assemblyPath).

                                CreateInstance(className);

        }

 

 

        return instance;

    }

}

注意, Create()辅助方法中的typeNameKey,是指向具体对象类型的键值。通常建议将其键值赋值为具体对象类型的抽象接口类型名,而对应的值则是目标创建对象的类型名。例如:

<add key="IOrderStrategy"    value="OrderSynchronous"/>

然后,我们可以为属于相同命名空间的类统一定义工厂类,并在其中调用工厂辅助类FactoryHelperCreate()辅助方法。例如,为业务逻辑层的对象定义工厂类BLLFactory

    public static class BLLFactory<T>

        where T:class

    {

        public static T Create(string typeNameKey)

        {          

            string nameSpace = ConfigurationManager.AppSettings["BLLAssembly"]

            string assemblyPath = ConfigurationManager.AppSettings["BLLPath"];

            return BaseFactory<T>.CreateT(

                          typeNameKey, nameSpace, assemblyPath);

        }

}

针对订单策略对象,对应的配置文件为:

<add key="BLLAssembly"      value="AgileDon.BLL"/>

<add key="BLLPath"          value="AgileDon.BLL"/>

<add key="IOrderStrategy"  value="OrderSynchronous"/>

现在,我们就可以调用BLLFactory类的Create ()方法,传入类型名以获得具体的对象。例如:

IOrderStrategy orderInsertStrategy = BLLFactory<IOrderStrategy>.Create(

    "IOrderStrategy");

如果需要将订单插入策略从同步修改为异步方式,只需将配置文件中IOrderStrategy键对应的值修改为"OrderAsynchronous"即可。

2、表驱动法

 

 

 

借鉴表驱动法【注:参见Steve McConnell著作《代码大全》第18章】的思想,我们可以利用一个Dictionary集合来维护目标对象与键值之间的映射关系。当我们需要获得对象时,可以利用键值对表进行查询,这样就可以有效地消除if语句。例如,可以在Strategy模式中使用表驱动法,将其作为模式的上下文对象,而不必执行对策略对象类型的逻辑判断。利用表驱动法,我们也可以解除对象之间的具体依赖。

仍然以订单的管理为例。我为订单的管理专门定义了一个OrderManager类,它负责初始化并维持对象表。

public static class orderManager

{

    private static IDictionary<string,IOrderStrategy> m_strategyTable;

    static orderManager()

    {

        Init();

    }

    private static void Init()

    {

        m_strategyTable = new Dictionary<string,IOrderStrategy>();

        m_strategyTable.Add("sync",new orderSynchronous());

        m_strategyTable.Add("async",new orderAsynchronous());

    }

 

 

    public static IOrderStrategy GetOrderStrategy(string strategyKey)

    {

        IOrderStrategy strategy;

 

 

        if (m_strategyTable.TryGetValue(strategyKey, out strategy))

        {

            return strategy;

        }

        else

        {

            throw new Exception("无法找到正确的订单策略对象");

        }

    }

}

在调用OrderManagerGetOrderStrategy()方法时,为提供更好的灵活性,寻找策略对象的键值应该放在配置文件中,以避免修改源代码。

string strategyKey = ConfigurationManager.AppSettings["StrategyKey"]

IOrderStrategy strategy = orderManager.GetOrderStrategy(strategyKey);

我们甚至可以提供一个注册方法RegisterStrategy(),用以应对未来可能的扩展。

public static class orderManager

{

    //其余实现略

 

 

    public static void RegisterStrategy(

          string strategyKey,

          IOrderStrategy strategy)

    {

        if (String.IsNullOrEmpty(strategyKey))

        {

            throw new ArgumentNullException(strategyKey);

        }

        if (strategy == null)

        {

            throw new ArgumentNullException("策略对象不能为null");

        }

        if (m_strategyTable.ContainsKey(strategyKey))

        {

            throw new ArgumentException("已经存在键值" + strategyKey);

        }

        m_strategyTable.Add(strategyKey,strategy);

    }

}

3、依赖注入

 

 

 

依赖注入(Dependency Injection) 是一个漂亮的隐喻。依赖关系就像是被注入的液体,我们可以在任何时候将依赖关系注入到模块中,而不只限于在编译时绑定。既然这种依赖关系是通过注入的方式 完成,就意味着我们可以随时更新,因为注入的液体与模块并无直接关联。实现依赖注入的前提是面向接口编程,而辅助的技术则是利用反射技术。

依赖注入是目前大多数轻量级IoC(控制反转,Inversion of Control)容器用于解除外部服务与容器服务之间依赖关系的一把利刃。首先,容器服务包含了外部服务接口的定义。然后,依赖注入通过使用一个专门的装配器对象,提供外部服务的具体实现,并其赋值给对应的容器服务对象。Martin Fowler将依赖注入的形式分为三种:构造函数注入(Constructor Injection)、设置方法注入(Setter Injection)和接口注入(Interface Injection)。 其中,接口注入通过定义接口约束的方式实现依赖注入,会给容器带来设计的限制。而构造函数注入与设置方式注入则表现了产生依赖的两个连接点:构造函数与属 性。如果构造函数参数或属性对象的类型为抽象的接口类型,则产生具体依赖的源头在于具体对象的创建。将创建具体对象的职责转移到IoC容器,就可以在运行时为构造函数参数或属性对象传递依赖。

目前,实现了依赖注入的轻量级容器已经应用在许多框架产品中,如Java平台下的SpringPicoContainer等。在.NET平台下,常见的依赖注入框架包括AutoFacNinjectSpring.NETStructureMapWindsor等。

Ninject框架为例,我们可以定义这样的Order类:

public class order

{

    private IOrderStrategy m_strategy;

 

 

    public order(IOrderStrategy strategy)

    {

        m_strategy = strategy;

    }

    public void Insert(OrderInfo order)

    {

        m_strategy.Insert(order);

    }

}

然后,我们需要自定义一个OrderModule类,它派生自Ninject.Core.StandardModule类。这是Ninject实现依赖注入的一个特色,它抛弃了传统的xml映射文件,而是利用类型绑定的方式,并根据要创建的具体对象分组建立对应的Module类。注意,它不同于之前的解耦方法,因为它对业务逻辑代码没有造成任何侵略与污染。如上定义的Order类,保留了领域对象的本来面貌。使得领域层的开发人员可以专心致志着力于业务逻辑的实现。OrderModule类的定义如下所示:

using Ninject.Core;

 

 

public class orderModule:StandardModule

{

    public override void Load()

    {

        Bind<IOrderStrategy>().To<OrderSynchronous>();

    }

}

客户端调用的代码可以通过Ninject提供的IKernel对象获得Order对象:

OrderModule module = new orderModule();

IKernel kernal = new StandardKernel(module);

 

 

Order order = kernal.Get<Order>();

order.Insert(new orderInfo());

4、惯例优于配置

 

 

 

使 用配置文件固然可以解除与具体对象之间的依赖,然而,它带来的良好可扩展性,却是以牺牲系统的可维护性乃至于可靠性为代价的。配置文件很难管理,尤其是在 配置信息相对较多的情况下。不管是集中管理还是分散管理,都存在一些与生俱来的缺陷。如果采用集中管理,则配置文件过大,既影响性能,也不能很好地展现配 置信息的分类与层次。在.NET中,虽然可以利用<section></section>对 配置文件进行分节,但终究不够直观。采用分散管理,则不同大小的配置文件千头万绪,既会给维护者带来管理的障碍,也不利于部署与使用。使用配置文件尤其不 便于调试。开发环境提供的编译期检查,对于配置文件只能是“望洋兴叹”。所谓“差之毫厘,谬以千里”,小小的一个配置项错误,可能会造成难以弥补的巨大损 失。为了弥补这些缺陷,许多产品或框架都提供了专门的配置或管理工具,使用直观的UI界面对配置文件进行操作,但繁杂的配置项仍然有可能让使用者望而却步。

惯例优于配置(Convention over Configuration)来源于Ruby On Rails框架的设计理念,也被认为是Rails大获成功的关键因素之一。这里所谓的惯例,可以理解为框架对编程的一些约束,我们可以根据实现制订的默认规则,通过反射技术完成对象的创建,对象的协作,甚至是应用程序的组装。例如在Rails中对MVC模式的实现中,就事先确立了ModelViewController的目录结构与命名规范。在这种情况下,我们不需要对元数据进行任何配置。ASP.NET MVC框架同样采纳了惯例优于配置的思想。采用惯例,虽然在一定程度上损失了系统的灵活性,带来的却是良好的可维护性。同时,它仍然可以解除系统与具体对象之间的强耦合关系。

惯例优于配置的技术并不是非常适合于本文中的订单策略示例。不过,在.NET框架中,有关WebRequest对象的创建,却可以改用惯例优于配置的思想来实现。图2WebRequest对象的继承体系:

image

2 WebRequest的类结构

.NET框架中,创建一个WebRequest实例的方法是调用WebRequest的静态方法Create()

WebRequest myRequest = WebRequest.Create("http://www.agiledon.com");

由于,传入的Uri地址其前缀为"http",因此创建的myRequest对象应该为HttpWebRequest具体对象。如果需要根据不同的Request协议,扩展不同的WebRequest对象,就需要引入一些设计技巧,来解除与具体对象创建的依赖。.NET框架的实现能够达到这样的目的,但非常复杂,这里不提。我想要介绍的是如何利用惯例优于配置来实现WebRequest对象的扩展。利用“惯例优于配置”的思想有一个前提,就是我们要对WebRequest对象的命名规范进行惯例约束。例如,我们规定所有的WebRequest子类对象均由协议名加上“WebRequest”后缀构成。通过解析传入的Uri,可以获得传输协议的名称,之后将它与“WebRequest”连接起来,获得WebRequest子类对象的类名,再利用反射技术创建该对象。在WebRequest类中定义如下的Create()静态方法:

public static WebRequest Create(Uri requestUri)

{

   if (requestUri == null)

   {

                 throw new ArgumentNullException("requestUri");

   }

   string prefix = requestUri.Scheme.ToLower();

              if (prefix == null)

   {

                 throw new ArgumentNullException("requestUri");

   }

   if (prefix.Contains(""))

   {

          prefix = prefix.Replace(".","");

   }

   StringBuilder typeName = new StringBuilder();

   typeName.Append("System.Net.");

   typeName.Append(prefix.Substring(0,1).ToUpper());

   typeName.Append(prefix.ToLower().Substring(1,prefix.Length – 1));

   typeName.Append("WebRequest");

 

 

   return (WebRequest)Activitor.CreateInstance(

          System.Type.GetType(typeName));

}

只要WebRequest的子类对象能够遵循我们的惯例,即该类的类型名符合事先制订的规范,改进后的Create()方法就能够运行良好。以新增Tcp协议的WebRequest对象为例。该协议的Schema为“net.tcp”,因此其类名必须为“NettcpWebRequest”,并放在“System.Net”命名空间下。如果客户端调用WebRequest.Create()方法,并传入“net.tcp://www.agiledon.com”值,则Create()方法就会对该Uri地址进行解析,获得完整的类型名为“System.Net.NettcpWebRequest”,然后,利用反射技术创建该对象。采用“惯例优于配置”的方式,可以极大地简化工厂方法的实现代码,抛弃了繁琐的设计理念,具有非常灵活的扩展性以及良好的代码可读性。或许,唯一的遗憾是由于反射技术带来的性能损耗。

利用抽象的方式封装变化,固然是应对需求变化的王道,但它也仅仅能解除调用者与被调用者之间的耦合关系。只要还涉及具体对象的创建,即使引入了创建型模式,例如Factory Method模 式,具体工厂对象的创建依然是必不可少的。不要小看这一点点麻烦,需知“千里之堤,溃于蚁穴”,牵一发而动全身,小麻烦可能会酿成大灾难。对于那些业已被 封装变化的对象,我们还应该学会利用诸如“依赖注入”、“表驱动法”等技术,彻底解除两者之间的耦合;至于选择何种技术,则需要根据具体的应用场景做出判 断。当然,模块或对象解耦的重要前提,则源于封装变化,要求我们针对接口编程,而不是实现。这也是GOF提出的面向对象设计原则。