[转载]C#利用Emit反射实现AOP,以及平台化框架封装思路 - 以利亚 - 博客园

mikel阅读(1320)

[转载]C#利用Emit反射实现AOP,以及平台化框架封装思路 – 以利亚 – 博客园.

这是前两天扒的一段动态代理AOP代码,用的Emit反射生成子类来实现代理模式,在这里做个小笔记,然后讨论一下AOP框架的实现思路。

首先是主函数:

复制代码
        static void Main(string[] args)
        {
            RealClass proxy = (RealClass)DynamicProxyBuilder.Wrap(typeof(RealClass));
            proxy.Test();

            Console.ReadKey();
        }
复制代码

用一个动态代理Builder包装了真实的被代理类,这是被代理类:

复制代码
    public class RealClass
    {
        public RealClass() { }

        //必须是虚方法
        public virtual bool Test()
        {
            return false;
        }
    }
复制代码

我们需要在Test执行前后做一些事情,也就是拦截器,这里以一个布尔值为例子,随便一写:

复制代码
    public class Interceptor
    {
        public Object Call(String methodName, MulticastDelegate methodDelegate, params Object[] args)
        {
            Object obj = null;
            try
            {
                Console.WriteLine("进入拦截器,执行之前方法");

                obj = methodDelegate.Method.Invoke(methodDelegate.Target, args);
                if ((bool)obj)
                {
                    Console.WriteLine("返回真");
                }
                else
                {
                    Console.WriteLine("返回假");
                }

                Console.WriteLine("执行之后方法,离开拦截器");
            }
            catch (ApplicationException ex)
            {
                Console.WriteLine("出现异常");
            }

            return obj;
        }
    }
复制代码

即,在主函数里通过一个“框架API”调用这个类的代理子类来执行拦截器里的方法,DynamicProxyBuilder类代码如下:

View Code

这个程序在运行时会在bin下创建一个DynamicProxy.dll,里面是用Emit反射生成的代理子类,复写了父类的方法。

上面的代码里有一个Interceptor类,封装AOP框架的一个思路就是把这个类提出一个接口,里面有之前、之后、异常等方法,然后让一个抽象类实现这个接口,提供空实现骨架(模板方法模式),把这个抽象类注入到子类构造器当中来构造子类。

如果要结合项目造轮子的话,则允许以这个接口为标准二次开发具体的拦截器,并且可以根据需求封装配置界面,来配置针对系统当中哪一个具体操作命令来进行拦截。

DynamicProxyBuilder.Wrap这个方法可以封装为一个人性化的框架接口(作为AOP框架的API)来创建代理子类。

有必要将拦截器类放入IoC容器当中以防每次反射。

最后,动态代理性能很差,至少第一次生成dll很慢,而且看不懂!

真不想用框架、要自己写AOP的话,还是直接让最终执行核心方法的类实现拦截器接口,然后直接在自己的框架里调接口吧,这样还看得懂,别搞什么Emit!

最后引用马老师的一句经典语录——搞毛飞机啊!

[转载]利用Aspose.Cells完成easyUI中DataGrid数据的Excel导出功能 - finesite - 博客园

mikel阅读(1080)

[转载]利用Aspose.Cells完成easyUI中DataGrid数据的Excel导出功能 – finesite – 博客园.

我准备在项目中实现该功能之前,google发现大部分代码都是利用一般处理程序 HttpHandler实现的服务器端数据的Excel导出,但是这样存在的问题是ashx读取的数据一般都是数据库中视图的数据,难免会含有方便操作的 主键ID这列的记录。现在项目需要在easyUI的DataGrid中显示的数据能全部导出Excel,包括DataGrid中的中文标题,其他的统统不 要。

完成该功能所需的工具和环境:Newtonsoft.Json序列化和反序列化类库、easyUI前端UI框架、HttpHandler一般处理程序、Aspose.Cells电子表格生成组件;.Net Framework4.0。

前端完成DataGrid中的题头和数据行信息遍历访问,并以JS中二维数组的形式返回后台供ashx程序处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function ExportExcel() {
    // 返回grid的所有可见行给后端供导出Excel用
    var rows = $('#userlist').datagrid("getRows");
    if (rows.length == 0) {
        msgShow("没有数据可供导出");
        return;
    }
    //返回grid的所有列的选项title、列宽等
    // var columns = $('#userlist').datagrid("options").columns;
    //定制DataGrid的columns信息,只返回{field:,title:}
    var columns = new Array();
    var fields = $('#userlist').datagrid('getColumnFields');
    for (var i = 0; i < fields.length; i++) {
        var opts = $('#userlist').datagrid('getColumnOption', fields[i]);
        var column = new Object();
        column.field = opts.field;
        column.title = opts.title;
        columns.push(column);
    }
    var excelWorkSheet = new Object();
    excelWorkSheet.rows = rows;
    excelWorkSheet.columns = columns;
    excelWorkSheet.sheetName = "设置导出的Excel工作表名";
    var filename = "/HttpHandlers/<span style="color: #ff0000;">FileHandler.ashx?action=exportexcel&excelWorkSheet</span>=" + JSON.stringify(excelWorkSheet);
 
    location.href = filename;
}

其中FileHandler.ashx是公共的文件处理程序,根据action=Exportexcel来实现Excel导出功能.

1
2
3
4
5
6
7
8
9
10
11
12
public void ProcessRequest(HttpContext context)
       {
           var action = context.Request["action"];
           switch (action.ToLower())
           {
               case "exportexcel":
                   <span style="color: #ff0000;">EasyUIGrid2Excel</span>(context);
                   break;
           }
       }

EasyUIGrid2Excel完成前台传递的关于Excel报表导出的参数,比如工作表名sheetName,题头标题和行记录。同时它完美解决了在各浏览器下文件名中文乱码问题,最后以弹窗的形式让用户选择直接打开还是下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void EasyUIGrid2Excel(HttpContext context)
       {
          
           var jsonString = context.Request["excelWorkSheet"];
       //使用Newtonsoft.Json.Linq.JObject将json字符串转化成结构不固定的Class类
           dynamic jsonObject =<span style="color: #ff0000;"> JObject.Parse</span>(jsonString);
         
           string fileName = String.Concat(jsonObject.sheetName, DateTime.Now.ToString("yyyyMMdd-HHmmss"), ".xls") ;
           
           //解决中文文件名乱码只在IE中有效
           //   filename = HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8);
           if (context.Request.UserAgent.ToLower().IndexOf("msie") > -1)
           {
               //当客户端使用IE时,对其进行编码;
               //使用 ToHexString 代替传统的 UrlEncode();
               fileName = <span style="color: #ff0000;">CommonHelper.ToHexString</span>(fileName);
           }
           if (context.Request.UserAgent.ToLower().IndexOf("firefox") > -1)
           {
               //为了向客户端输出空格,需要在当客户端使用 Firefox 时特殊处理
               
               context.Response.AddHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
           }
           else
               context.Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
           string extension = Path.GetExtension(fileName);
           context.Response.ContentType = <span style="color: #ff0000;">CommonHelper.GetMimeType</span>(extension);
           context.Response.ContentEncoding = Encoding.UTF8;
           Workbook workbook = <span style="color: #ff0000;">Object2Workbook</span>(jsonObject, context);
           context.Response.Clear();
           context.Response.Buffer = true;
           context.Response.BinaryWrite(workbook.SaveToStream().ToArray());
           
           context.Response.End();
       }

上面代码中的红色标注部分详见下载中的公共类库,其中Object2Workbook主要实现Aspose.cells中的Excel格式化输出定制,没有太多技巧,Aspose.cells还带有授权文件的哦,你懂的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
private Workbook Object2Workbook(dynamic jsonObject, HttpContext context)
       {
           #region Aspose.Cell引用
           Aspose.Cells.License licExcel = new License();  //Aspose.Cells申明
           if (File.Exists(context.Server.MapPath("~/Bin/cellLic.lic")))
               licExcel.SetLicense(context.Server.MapPath("~/Bin/cellLic.lic"));
           #endregion
           Workbook workbook = new Workbook();
           Worksheet sheet = workbook.Worksheets[0];
           Styles styles = workbook.Styles;
           int styleIndex = styles.Add();
           Aspose.Cells.Style borderStyle = styles[styleIndex];
           borderStyle.Borders.DiagonalStyle = CellBorderType.None;
           borderStyle.HorizontalAlignment = TextAlignmentType.Center;//文字居中
           Cells cells = sheet.Cells;
            sheet.FreezePanes(1, 1, 1, 0);//冻结第一行
           sheet.Name = jsonObject.sheetName;//接受前台的Excel工作表名
          
           //为标题设置样式    
           Style styleTitle = workbook.Styles[workbook.Styles.Add()];//新增样式
           styleTitle.HorizontalAlignment = TextAlignmentType.Center;//文字居中
           styleTitle.Font.Name = "宋体";//文字字体
           styleTitle.Font.Size = 18;//文字大小
           styleTitle.Font.IsBold = true;//粗体
           //题头样式
           Style styleHeader = workbook.Styles[workbook.Styles.Add()];//新增样式
           styleHeader.HorizontalAlignment = TextAlignmentType.Center;//文字居中
           styleHeader.Font.Name = "宋体";//文字字体
           styleHeader.Font.Size = 14;//文字大小
           styleHeader.Font.IsBold = true;//粗体
           styleHeader.IsTextWrapped = true;//单元格内容自动换行
           styleHeader.Borders[BorderType.LeftBorder].LineStyle = CellBorderType.Thin;
           styleHeader.Borders[BorderType.RightBorder].LineStyle = CellBorderType.Thin;
           styleHeader.Borders[BorderType.TopBorder].LineStyle = CellBorderType.Thin;
           styleHeader.Borders[BorderType.BottomBorder].LineStyle = CellBorderType.Thin;
           //内容样式
           Style styleContent = workbook.Styles[workbook.Styles.Add()];//新增样式
           styleContent.HorizontalAlignment = TextAlignmentType.Center;//文字居中
           styleContent.Font.Name = "宋体";//文字字体
           styleContent.Font.Size = 12;//文字大小
           styleContent.Borders[BorderType.LeftBorder].LineStyle = CellBorderType.Thin;
           styleContent.Borders[BorderType.RightBorder].LineStyle = CellBorderType.Thin;
           styleContent.Borders[BorderType.TopBorder].LineStyle = CellBorderType.Thin;
           styleContent.Borders[BorderType.BottomBorder].LineStyle = CellBorderType.Thin;
           var rowCount = jsonObject.rows.Count;//表格行数
           var columnCount = jsonObject.columns.Count;//表格列数
           //生成行1 标题行   
           cells.Merge(0, 0, 1, columnCount);//合并单元格
           cells[0, 0].PutValue(jsonObject.sheetName);//填写内容
           cells[0, 0].Style = styleTitle;
           cells.SetRowHeight(0, 25);
           //生成题头列行
           for (int i = 0; i < columnCount; i++)
           {
               cells[1, i].PutValue(jsonObject.columns[i]["title"]);
               cells[1, i].Style = styleHeader;
               cells.SetRowHeight(1, 23);
           }
           //生成内容行,第三行起始
           //生成数据行
           for (int i = 0; i < rowCount; i++)
           {
               for (int k = 0; k < columnCount; k++)
               {
                   var currentColumnName = jsonObject.columns[k]["field"];
                   cells[2 + i, k].PutValue(jsonObject.rows[i][currentColumnName.Value]);
                   cells[2 + i, k].Style = styleContent;
               }
                cells.SetRowHeight(2 + i, 22);
           }
           
           //添加制表日期
           cells[2 + rowCount, columnCount-1].PutValue("制表日期:" + DateTime.Now.ToShortDateString());
           sheet.AutoFitColumns();//让各列自适应宽度
           sheet.AutoFitRows();//让各行自适应宽度
           return workbook;
       }

该code snippet也能很好的移植,在需要实现Excel导出的js文件中调用ExportExcel函数,传递sheetName值即可

国际惯例,无图无真相

文件下载

btw:另外请教两个问题,知道的童鞋回答一下,谢谢

1,通过客户端导出Excel的方式,当打开文件的时候,提示:文件错误,数据可能丢失, 初步认为可能数字以文本形式存到Excel中出现了问题?如果直接服务器端导出却没有这个错误

2,一对多表中数据更新问题,比如三张表User,Role和对应的关联表UserRole,其中主键都是自动递增ID。如果我更改了用户角色,当前的做法是在UserRole中删掉该用户的所有旧角色,再新增。感觉这样麻烦也不合理,请教大家有好的做饭没?

[转载]提高eclipse使用效率(二) 提高Android开发效率的小技巧 - sw926 - 博客园

mikel阅读(1093)

[转载]提高eclipse使用效率(二) 提高Android开发效率的小技巧 – sw926 – 博客园.

XML文件的代码提示

adt中也有xml文件的代码提示,为了让提示来的更加猛烈,我们还要设置一下

打开eclipse – Window – Preferences,在右边的目录树中切换到XML – XML Files – Editor – Content Assist,是不是很熟悉,没错,就是Content Assist

接下来就简单了,延迟设为50ms,提示字母把能填的都填上去,设置完之后可以试一下。输入id,代码直接提示 Android:id,回车就可以输入,而且光标定位到双引号内。

强大的 Ctrl + 1

在代码上按Ctrl +1,你可能会收到意想不到的效果

例如以下错误,有一个错误,没有id,还有一个警告,test没有写到string.xml里面

在错误处按Ctrl+1,弹出提示框,选择Create resource @id/tv_test,id就会自动添加。

同样,在警告处按Ctrl+1,在弹出框中选择第一项,输入你想要起的名字,就可以在string.xml中创建一个以“test”为内容的选项

color也可以使用此方法添加

在没有错误的情况下,对一个xml节点使用Ctrl+1会有以下提示

Wrap in Container 把当前节点包裹到另一个节点中,例如在TextView外传加一个Layout。

Remove Container 把当前节点的父节点去除,例如去掉TextView外面的Layout。

Change Widget Type 修改当前节点的类型,目标是widget,比如TextView之类的。

Change Layout Type 修改当前节点的类型,目标是Layout。

Extract as Include 把当前节点变为一个include,会把当前节点的内容提取到另一个新建的xml文件中。

Extract Style 提取当期节点的属性,用一个style来替代,这个非常有用,缺憾就是不能把属性完全提取,但也省去了新建style的烦恼。

Rename 就不解释了,重命名,等同于ctrl+shif+r

Surround with new element 将当前节点用一个新的节点包围。

自动生成代码

对应继承字父类的函数,以Activity为例,要生成onStart,onPause这些函数,点击菜单source – override/Implement Methods…

选中对应的函数,可以选择在那个函数或者变量后生成代码,点击OK就可以生成代码。

同样也可以通过Generate Getters and Setters生成成员变量的set和get函数,或者在直接输入get或set,等待代码提示。

 为support-V4添加源码

直接引入Android-support-v4.jar在是不能查看源码的,也允许定位源码的位置,解决方法是在在libs目录下新建一个

Android-support-v4.jar.properties

文件,输入src=C:\\Android\\sdk\\extras\\android\\support\\v4\\src

C:\\Android\\sdk\\extras\\android\\support\\v4\\src为源码的文件夹,如果想把源码随身携带,可以打成包放到工程目录,

jar cvf android-support-v4-src.jar -C src .

android-support-v4-src.jar为要生成的报名,src为源码目录

打包完成后将android-support-v4-src.jar复制到libs目录下,android-support-v4.jar.properties文件内容修改为

src=./android-support-v4-src.jar,

重启下eclipse就可以看到源码了。

 其他

一直使用F3的同学可能还 悲剧的没有发现,在代码中是可以直接定位到xml的。在代码中对资源id使用F3,会定位到R.java,基本没什么用,但是使用Ctrl+鼠标左键点 击,就会直接进入xml文件,实在太方便了。如果一个id在多个xml文件中使用,按住ctrl,鼠标移动到id位置的时候,会弹出一个列表,单击列表会 跳转到对应的xml

在eclipse中可以同时预览不同分辨率的显示效果,对适配很用帮助

eclipse工具栏中有几个很有用的按钮

依次新建包,新建类,新建android xml文件,代码扫描(可以扫出string.xml中哪些定义的字符串没有使用到等等,具体功能还不太熟悉),

这些快捷按钮比使用右键要方便的多。

[转载]将 Eclipse 的配色改为黑底白字 - Carve_Time - 博客园

mikel阅读(1109)

[转载]将 Eclipse 的配色改为黑底白字 – Carve_Time – 博客园.

1.先到 eclipsecolorthemes下载一个主题。

2.Eclipse File–>Import

3.Import视窗内选择 General–>Preferences

4.选择下载的主题即可

 

 

————————————————————————————————————-

太多程序员没有太多心思去关注他们每天都在面对的编程字体,然后编码工作需要长时间盯着屏幕并阅读一些非常复杂的文本。一个好的字体可以很大程度上提升阅读代码的愉悦感,提高生产力。

 

本文介绍 10 个适合在编程时使用的等宽字体,它们都是免费的,效果很好。

1. Source Code Pro

Source Code Pro 是 Adobe 的作品,可从 Github 上免费下载.

SourceCodeSplash

2. Anonymous Pro

作品来自 Mark Simonson studio.

anonymous-pro

3. DejaVu Sans Mono

DejaVu 的等宽版本,一个广受欢迎的开源字体

DejaVu_specimen

4. Envy Code R

一个 固定宽带的字体 ,外观和感觉都很有凝聚力,也是我最喜欢的

EnvyCodeR-PR7-Humane

5. Droid Sans Mono

一个好看的和清晰的编码的字体,有两个不同版本 —— slashed 和 dotted

Droid9VibrantInk

6. Pragmata Pro

一个 很狭窄的编程字体 用于减低间距减少滚动

pr+coding2

7. Terminus

Terminus Font 是一个简介、固定宽度的 bitmap 字体

8x16b

8. Proggy Small

一个 编程字体 主要用于很小字显示

screenshot_proggy_small

9. Liberation Mono

不是最漂亮的 等宽字体, 但仍不失为一个好的选择

liberation13

10. Ubuntu Mono

Linux 上很受欢迎的 Linux distro’s font family.

ubuntu-mono-regular

via typography-daily

参考:http://www.oschina.net/search?scope=project&q=%E7%A8%8B%E5%BA%8F%E5%91%98%E5%AD%97%E4%BD%93

[转载]Asp.net MVC使用Model Binding解除Session, Cookie等依赖 - JustRun - 博客园

mikel阅读(944)

[转载]Asp.net MVC使用Model Binding解除Session, Cookie等依赖 – JustRun – 博客园.

上篇文章”Asp.net MVC使用Filter解除Session, Cookie等依赖“介绍了如何使用Filter来解除对于Session, Cookie的依赖。

其实这个也可以通过Model Binding来达到同样的效果。

 

什么是Model Binding?

Model Binding的作用就是将Request请求中包含的散乱参数,根据接受请求的Action方法的参数列表,自动智能地构建这些参数的过程。

 

modelbinding

 

问题分析

常见的对于Session依赖的代码:

复制代码
public ActionResult Index()
{
     var user = Session[“UserAccuont”];//从Session中获取当前登录用户的信息
     //send email
     var email = user.Email;
     …………
}
复制代码

 

我们期望将函数改造成:

public ActionResult Index(UserAccount sessionUser)
{
     //send email
     var email = sessionUser.Email;
     …………
}

让Index action方法不用在方法内访问Session, 从而解除对于Session的依赖。

如果MVC能够智能的构造参数sessionUser, 从session取的数据,就能够解决我们的问题了。这就需要我们的Model Binding出场了。

 

自定义Model Binding

继承接口IModelBinder, 实现BindModel方法。

这个UserAccountModelBinder的作用就是从Session中取得UserAccount.

 

复制代码
public class UserAccountModelBinder : IModelBinder
   {
       public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
       {
           if(controllerContext.HttpContext.Session["UserAccuont"] != null)
           {
               return controllerContext.HttpContext.Session["UserAccuont"];
           }
           return null;
       }
   }
复制代码

 

接下来,我们要为这个ModelBinder,添加到MVC的Model Binding设置中,使得它能够在MVC的生命周期中起作用。

在Global.asax.cs文件的Application_Start()方法中,添加UserAccountModelBinder

复制代码
protected void Application_Start()
{

    ………

    //凡是UserAccount类型的参数,都会使用UserAccountModelBinder来处理,也就是会从Session中取值

    ModelBinders.Binders.Add(typeof(UserAccount), new UserAccountModelBinder ());

}
复制代码

 

上面就是全部的Model Binding解决问题的过程,希望能够帮助大家更好地理解MVC中的Model Binding.

[转载]简单的mvc之二:蜿蜒的管线 - wangjieas - 博客园

mikel阅读(1138)

[转载]简单的mvc之二:蜿蜒的管线 – wangjieas – 博客园.

关于系列的第二篇,在管线与路由之间犹豫了很久,最终选择了管线—为免于盲人摸象的困惑。

管线的位置在哪里呢?webform,mvc以及web api都架构于ASP.NET平台上,管线则是ASP.NET的中枢。

获取管线,其实就是获取HttpApplication的事件(.net版本不同,管线的组成也不一样)

复制代码
1 public void LinePile()
2 {
3     foreach (var ev in typeof(HttpApplication).GetEvents()) {
4         Response.Write(ev.Name);
5         Response.Write("<br />");
6     }
7 }
复制代码

得到的结果如下

图中可以很明显的看到从BeginRequest到EndRequest的管线过程,各个管道过程的名字也很清楚的示意出了它的作用。 asp.net中关键对象之一的HttpModule就是通过订阅这些管线阶段达到功能注入的目的。在使用管线的时候一定要注意阶段的选择。下面有一段从 URLRoutingModule中抽出的代码:

复制代码
1 protected virtual void Init(HttpApplication application)
2 {
3     if (application.Context.Items[_contextKey] == null)
4     {
5         application.Context.Items[_contextKey] = _contextKey;
6         application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
7     }
8 }
复制代码

代码很清晰的表明,路由导航模块注册了PostResolvRequestCache管线事件,具体的事件过程等到讲路由的时候再做具体的分析,只 要知道正是在这个事件中路由系统查找路由表并试图得到最终执行的IHttpHandler。既然在PostResolvRequestCache事件中 IHttphandler可能被设定,那么执行阶段又在那里呢,注意看管线的PreRequestHandlerExcute与 PostRequestHandlerExcute事件,Excute事件就在二者之间执行。所以如果你想使用Application_xx事件或者模块 去指定IHttpHandler请一定要在PreRequestHandlerExcute事件之前完成。

对管线的干预有两种方式,即Application_事件名称和模块,二者的区别在模块化。下面的代码显示了注册的模块

复制代码
1 public void Modules() {
2     foreach (var md in HttpContext.ApplicationInstance.Modules) {
3         Response.Write(md);
4         Response.Write("<br />");
5     }
6 }
复制代码

结果如下:

熟悉的名称是不是很多?

管线由HttpApplication驱动,通过对不同管线事件的订阅,可以将不同的功能注入进去,上面的代码已经证明我们所熟知的 Cache、Session等对象都是通过管线注入实现。对每一个进入的请求,管线的执行是必须的,但管线本身却可以跳跃—通过调用 Request.End(),它在内部调用HttpApplication的CompleteRequest方法,这将直接抵达管线的EndRequest阶段。

[转载]丰富Easyui 的插件 - lookup - 疯狂秀才 - 博客园

mikel阅读(1707)

[转载]丰富Easyui 的插件 – lookup – 疯狂秀才 – 博客园.

插件用途:

主要用于表单中,某字段的内容是用其他表里的记录ID。当然你可以使用combobox、combotree、combogrid等,但有时这 些表现方式并不是很好,希望弹出个层,然后在去做一些查询并选取的操作。所以就封装了一下,方便以后使用,不用在写重复的代码。

效果图:

 

使用方法:

插件使用Easyui 的相关样式,所以使用时需要引入Easyui 的相关脚本及样式。

var lu = $('#txt_ParentId').lookup({
        title: '选择上级区域',
        dialog: {
            content: '<ul id=ptree></ul>',
            height: 400,width:300,
            onOpen:function() {
                $('#ptree').tree({
                    url: actionUrl,
                    data: $('#areatree').tree('getData'),
                    onClick: function(node) {
                        lu.lookup('setValue', node.id).lookup('setText', node.text);
                        lu.lookup('close'); // 关闭弹窗
                    }
                });
            }
        }
    }).lookup('setText', '请选择区域').lookup('setValue', -1);       

dialog:{} 这里就是Easyui 中的dialog ,参数神马的都一样

 

插件源码:

css

.lookup .combo-arrow{
    background:url(../images/icon-select.gif) no-repeat 50% 50%;
}

图片:

js

/*
疯狂秀才(1055818239)
version: 0.01
*/
(function ($) {
    // 创建控件DOM
    function create(target) {
        var id = $(target).attr('id');
        if (!id) {
            id = 'lookup_' + new Date().getTime();
            $(target).attr('id', id);
        }
        $(target).addClass('combo-f').hide();
        var lookup = $('<span class="combo"></span>').insertAfter(target);
        var textbox = $('<input type="text" class="combo-text" />').appendTo(lookup);
        var arrow = $("<span><span class=\"combo-arrow \"></span></span>").appendTo(lookup);
        var valbox = $("<input type=\"hidden\" class=\"combo-value\">").appendTo(lookup);
        lookup.addClass('lookup');
        var state = $.data(target, 'lookup');
        // 添加图标
        if (state.options.iconCls) {
            arrow.find('.combo-arrow').addClass(state.options.iconCls);
        }
        else {
            arrow.find('.combo-arrow').addClass('icon-search');
        }
        // 创建 dialog
        var _dialogOpts = {
            closed: true,
            title: state.options.title,
            onClose:function() {
                state.dialog.dialog('destroy');
                state.dialog = null;
            }
        }
        
        var name = $(target).attr("name");
        if (name) {
            lookup.find("input.combo-value").attr("name", name);
            $(target).removeAttr("name").attr("comboName", name);
        }
        textbox.attr("autocomplete", "off");
        arrow.on('click', function () {
            
            if (!state.dialog) {
                var did = 'dialog_' + id + '-' + new Date().getTime();
                _dialogOpts = $.extend({}, state.options.dialog, _dialogOpts);
                var _dialog = $('<div/>').attr('id', did).dialog(_dialogOpts);
                state.dialog = _dialog;
            }
            state.dialog.dialog('open');
        });
        state.textbox = textbox;
        state.valbox = valbox;
        
    }
    $.fn.lookup = function (options, param) {
        if (typeof options == 'string') {
            var method = $.fn.lookup.methods[options];
            if (method) {
                return method(this, param);
            } else {
                return this.lookup(options, param);
            }
        }
        options = options || {};
        return this.each(function () {
            var state = $.data(this, 'lookup');
            if (state) {
                $.extend(state.options, options);
                create(this);
            }
            else {
                $.data(this, 'lookup', { options: $.extend({}, $.fn.lookup.defaults, options) });
                create(this);
            }
        })
    }
    $.fn.lookup.methods = {
        open: function () { },
        close: function (jq) {
            return jq.each(function() {
                var state = $.data(this, 'lookup');
                state.dialog.dialog('close');
            });
        },
        setValue: function (jq, val) {
            return jq.each(function () {
                var state = $.data(this, 'lookup');
                state.valbox.val(val);
            });
            
        },
        getValue: function (jq) {
            var state = $.data(jq[0], 'lookup');
            return state.valbox.val();
        },
        setText: function (jq, text) {
            return jq.each(function () {
                var state = $.data(this, 'lookup');
                state.textbox.val(text);
            });
        },
        getText: function (jq) {
            var state = $.data(jq[0], 'lookup');
            return state.textbox.val();
        }
    }
    $.fn.lookup.defaults = {
        iconCls: '',
        width: 120, height: 'auto',
        dialog: {
            title: '选择',
            width: 400, height: 300
        },
        onOpened: function() {
        }
    }
})(JQuery);

[转载]扩展 easyui 控件系列:为datagrid 增加过滤行 - 疯狂秀才 - 博客园

mikel阅读(1251)

[转载]扩展 easyui 控件系列:为datagrid 增加过滤行 – 疯狂秀才 – 博客园.

此功能还为真正完成,起到抛砖引玉的效果,发动大家的力量把这个功能完善起来,效果图如下:

 

基本上就是扩展了 datagrid.view 中的onAfterRender 这个事件,具体代码如下:

复制代码
$.extend($.fn.datagrid.defaults.view,{
            onAfterRender:function(target){
                var dc = $.data(target,'datagrid').dc;
                if(dc.header2.find('[filter="true"]').length == 0){
                    var header = dc.header1; //固定列表头
                    var header2 = dc.header2; // 常规列表头
                    var filterRow = $('<tr></tr>');
                    var opts = $.data(target,'datagrid').options;
                    var columns = opts.columns;
                    var frozenColumns = opts.frozenColumns;

                    $.each(frozenColumns[0],function(){
                        if(!this.checkbox){
                            var w = header.find('[field="'+this.field+'"] > div').width();

                            filterRow.append('<td><input style="width:'+w+'px"/></td>');
                        }
                        else{
                            header.find('.datagrid-header-check').parent().attr('rowspan',2)
                        }
                    });
                    header.find('tbody').append(filterRow);
                    filterRow = $('<tr filter="true"></tr>');

                    $.each(columns[0],function(){
                        var w = header2.find('[field="'+this.field+'"] > div').width();
                        if(this.hfilter){
                            var a = $('<input field="'+this.field+'" class="easyui-combobox" style="width:'+w+'px" />');
                            filterRow.append($('<td></td>').append(a));
                            a.data('options',this.hfilter);
                        }else{
                            filterRow.append('<td><input style="width:'+w+'px"/></td>');
                        }

                    });

                    header2.find('tbody').append(filterRow);

                    var dgData = $(target).datagrid('getData').rows;

                    header2.find('input[field]').each(function(){
                        var opts = $(this).data('options');
                        var field = $(this).attr('field');
                        $.extend(opts.options,{
                            onSelect:function(item){
                                var d = _.filter(dgData,function(row){
                                    return row[field].indexOf(item[opts.options.textField]) > -1;
                                });

                                $(target).datagrid('loadData',d);
                            }
                        });

                        $(this)[opts.type](opts.options);
                    })
                }
            }
        });
复制代码

在定义列时,我们加上个自定义的属性

{ title: '部门名称', field: 'depname', width: 120,<span style="color: #ff0000;">hfilter:{type:'combobox',options:{data:roleData,valueField:'KeyId',textField:'RoleName'}}</span> }

红色字体就是自定义的属性,用来定义过滤行中的控件的

在使用的时候,扩展的代码要放到Datagrid 初始化的前边!

 

示例DEMO 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <link rel="stylesheet" type="text/css" href="../../JQuery-easyui-1.3.3/themes/2013/easyui.css">
    <link rel="stylesheet" type="text/css" href="../../JQuery-easyui-1.3.3/themes/icon.css">
    <link rel="stylesheet" type="text/css" href="../../jQuery-easyui-1.3.3/themes/default/lookup.css">
    <script type="text/JavaScript" src="../../jQuery-easyui-1.3.3/jquery-1.10.2.min.js"></script>
    <script type="text/JavaScript" src="../../jquery-easyui-1.3.3/jquery-migrate-1.2.1.min.js"></script>
    <script type="text/JavaScript" src="../../jquery-easyui-1.3.3/jquery.easyui.min.js"></script>
    <script src="../../jquery-easyui-1.3.3/locale/easyui-lang-zh_CN.js"></script>
    <script src="../../jquery-easyui-1.3.3/underscore-min.js"></script>
</head>
<body>
    <table id=userlist></table>
    <script>
       
        var userData ={"total":6,"rows":[{"KeyId":1,"UserName":"admin2","Password":"93BCC5699183BED3AD5E3C050ADEF183","PassSalt":"K6FqbXXK","Email":"hxl_apple@163.com","IsAdmin":false,"IsDisabled":false,"TrueName":"疯狂秀才","DepartmentId":4,"Mobile":"","QQ":"","Remark":"","AddTime":null,"MenusJson":null,"ConfigJson":"{\"theme\":{\"title\":\"默认皮肤\",\"name\":\"default\"},\"showType\":\"menubutton\",\"gridRows\":\"40\"}","depname":"生产部","Departments":"4,15"},{"KeyId":9,"UserName":"admin","Password":"7D34608F3723F586DC28FC5D88BCECC6","PassSalt":"ZWvu4jSn","Email":"hxl_apple@163.com","IsAdmin":true,"IsDisabled":false,"TrueName":"疯狂秀才","DepartmentId":3,"Mobile":"18668088525","QQ":"1055818239","Remark":"系统默认帐号,不可删除","AddTime":null,"MenusJson":null,"ConfigJson":"{\"theme\":{\"title\":\"流行灰\",\"name\":\"gray\"},\"showType\":\"Accordion2\",\"gridRows\":\"20\",\"showValidateCode\":false}","depname":"综合办公室","Departments":"3,15"},{"KeyId":10,"UserName":"test","Password":"5C7E95836A99FB74723F54C43511154A","PassSalt":"RGVJejaR","Email":"","IsAdmin":false,"IsDisabled":false,"TrueName":"Test1","DepartmentId":1,"Mobile":"","QQ":"","Remark":"","AddTime":null,"MenusJson":null,"ConfigJson":null,"depname":"幸福集团","Departments":"3,4,15,5"},{"KeyId":12,"UserName":"ddd","Password":"7A3C56FA5A59953C058E7525FA0F754F","PassSalt":"O8RnCnaH","Email":"","IsAdmin":false,"IsDisabled":false,"TrueName":"ddd","DepartmentId":0,"Mobile":"","QQ":"","Remark":"","AddTime":null,"MenusJson":null,"ConfigJson":null,"depname":"","Departments":""},{"KeyId":13,"UserName":"eee","Password":"2FE5DFAE3F8AC41F907FCF8B1C1579D7","PassSalt":"vKbkyBib","Email":"","IsAdmin":false,"IsDisabled":false,"TrueName":"eeee","DepartmentId":3,"Mobile":"","QQ":"","Remark":"","AddTime":null,"MenusJson":null,"ConfigJson":null,"depname":"综合办公室","Departments":""},{"KeyId":15,"UserName":"007","Password":"9E704A8699D72ADA11A7EB7BF07739FA","PassSalt":"bcLJiqHB","Email":"","IsAdmin":false,"IsDisabled":false,"TrueName":"007","DepartmentId":1,"Mobile":"","QQ":"","Remark":"","AddTime":null,"MenusJson":null,"ConfigJson":null,"depname":"幸福集团","Departments":""}]}
        var roleData = [{"KeyId":2,"RoleName":"生产部","Sortnum":2,"Remark":"工程师","IsDefault":0,"Navigations":null,"Users":null,"Departments":""},
            {"KeyId":3,"RoleName":"幸福集团","Sortnum":1,"Remark":"6666","IsDefault":0,"Navigations":null,"Users":null,
                "Departments":"1,3,4,15,5,9,10,11,12,13,14,8"},
            {"KeyId":17,"RoleName":"综合办公室","Sortnum":1,"Remark":"","IsDefault":1,"Navigations":null,"Users":null,"Departments":"1,4,15"},{"KeyId":20,"RoleName":"普通用户","Sortnum":3,"Remark":"","IsDefault":0,"Navigations":null,"Users":null,"Departments":"1,3,4,15,5,9,10,11,12,13,14,8"}]
        $.extend($.fn.datagrid.defaults.view,{
            onAfterRender:function(target){
                var dc = $.data(target,'datagrid').dc;
                if(dc.header2.find('[filter="true"]').length == 0){
                    var header = dc.header1; //固定列表头
                    var header2 = dc.header2; // 常规列表头
                    var filterRow = $('<tr></tr>');
                    var opts = $.data(target,'datagrid').options;
                    var columns = opts.columns;
                    var frozenColumns = opts.frozenColumns;
                    $.each(frozenColumns[0],function(){
                        if(!this.checkbox){
                            var w = header.find('[field="'+this.field+'"] > div').width();
                            filterRow.append('<td><input style="width:'+w+'px"/></td>');
                        }
                        else{
                            header.find('.datagrid-header-check').parent().attr('rowspan',2)
                        }
                    });
                    header.find('tbody').append(filterRow);
                    filterRow = $('<tr filter="true"></tr>');
                    $.each(columns[0],function(){
                        var w = header2.find('[field="'+this.field+'"] > div').width();
                        if(this.hfilter){
                            var a = $('<input field="'+this.field+'" class="easyui-combobox" style="width:'+w+'px" />');
                            filterRow.append($('<td></td>').append(a));
                            a.data('options',this.hfilter);
                        }else{
                            filterRow.append('<td><input style="width:'+w+'px"/></td>');
                        }
                    });
                    header2.find('tbody').append(filterRow);
                    var dgData = $(target).datagrid('getData').rows;
                    header2.find('input[field]').each(function(){
                        var opts = $(this).data('options');
                        var field = $(this).attr('field');
                        $.extend(opts.options,{
                            onSelect:function(item){
                                var d = _.filter(dgData,function(row){
                                    return row[field].indexOf(item[opts.options.textField]) > -1;
                                });
                                $(target).datagrid('loadData',d);
                            }
                        });
                        $(this)[opts.type](opts.options);
                    })
                }
            }
        });
        $(function(){
            $('#userlist').datagrid({
                toolbar:'#toolbar',
                data:userData,
                fit:true,
                title:'用户列表',
                selectOnCheck:false,
                checkOnSelect:true,
                singleSelect:true,
                //tools:[{iconCls:'icon-add'}],
                frozenColumns:[[
                    {checkbox:true},
                    { title: 'ID', field: 'KeyId', width: 40, sortable: true },
                    { title: '用户名', field: 'UserName', width: 100, sortable: true }
                ]],
                columns:[[
                    { title: '真实姓名', field: 'TrueName', width: 100, sortable: true },
                    { title: '部门名称', field: 'depname', width: 120,hfilter:{type:'combobox',options:{data:roleData,valueField:'KeyId',textField:'RoleName'}} },
                    { title: '邮箱', field: 'Email', width: 100, sortable: true },
                    {
                        title: '超管',
                        field: 'IsAdmin',
                        width: 60,
                        align: 'center',
                        formatter: function (v, d, i) {
                            if (d.UserName == "admin")
                                return '';
                            return v ? '√':'x'
                        }
                    },
                    {
                        title: '状态',
                        field: 'IsDisabled',
                        width: 60,
                        align: 'center',
                        formatter: function (v, d, i) {
                            if (d.UserName == "admin")
                                return '';
                            return v ? '√':'x'
                        }
                    },{title:'描述',field:'Remark',width:160}
                ]],
                pagination: true,
                pageSize:20,
                rowStyler: function (index, row, css) {
                    if (row.UserName=="admin") {
                        return 'font-weight:bold;';
                    }
                }
            });
        });
    </script>
</body>
</html>

 

 

[转载].Net Memcached缓存用法 - merrick - 博客园

mikel阅读(994)

[转载].Net Memcached缓存用法 – merrick – 博客园.

memcached是一个以key-value的形式缓存数据的缓存系统。通过将数据缓存到内存中,从而提高数据的获取速度。
memcached以key-value的形式来保存数据,你可以为你每一段数据关联一个key,然后以后可以通过这个key获取
这段数据。

memcached是一个库还是什么?memcached其实是一个单独的网络服务器程序。它的网络底层基于libevent,你可以
将其运行在网络中的一台服务器上,通过网络,在遵循memcached的协议的基础上与memcached服务器进行通信。

一、安装Memcached服务
1. copy to c:\
2. start -> run -> cmd
3.
C:\memcached -d install -m 500
C:\memcached -d start

二、引用Enyim.Caching.dll

三、配置Config文件

复制代码
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <configSections>
 4     <sectionGroup name="enyim.com">
 5       <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"/>
 6     </sectionGroup>
 7     <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"/>
 8   </configSections>
 9 
10   <enyim.com>
11     <memcached configSource="ConfigFiles\enyim.com.config"/>
12   </enyim.com>
13   <memcached configSource="ConfigFiles\memcached.config"/>
14   
15     <startup> 
16         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
17     </startup>
18 </configuration>
复制代码

enyim.com.config

复制代码
 1 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 2     <!-- 
 3         Please read the documentation first:
 4             http://github.com/enyim/EnyimMemcached/wiki/MemcachedClient-Configuration
 5 
 6         Use this section as a template if you're connecting to regular memcached servers.
 7         Note: you must have the enyim.com/memcached section if you're using the parameterless constructor of EnyimMemcachedClient.
 8       -->
 9     <memcached protocol="Text">
10         <servers>
11             <add address="127.0.0.1" port="11211" />
12         </servers>
13         <socketPool minPoolSize="50" 
14                     maxPoolSize="1000" 
15                     connectionTimeout="00:01:10" 
16                     deadTimeout="00:02:00" 
17                     />
18     </memcached>
复制代码

memcached.config

复制代码
1 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
2 <memcached keyTransformer="Enyim.Caching.TigerHashTransformer, Enyim.Caching">
3     <servers>
4         <add address="127.0.0.1" port="11211" />
5     </servers>
6     <socketPool minPoolSize="50" maxPoolSize="1000" connectionTimeout="00:01:10" deadTimeout="00:02:00" />
7 </memcached>
复制代码

四、实例代码

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using Enyim.Caching.Configuration;
 7 using Enyim.Caching;
 8 using Enyim.Caching.Memcached;
 9 
10 
11 namespace MemcachedConsole
12 {
13     class Program
14     {
15         static void Main(string[] args)
16         {
17             MemcachedClient mc = new MemcachedClient();
18 
19             // store a string in the cache
20             mc.Store(StoreMode.Set, "MyKey", "Hello World");
21             // retrieve the item from the cache
22             Console.WriteLine(mc.Get("MyKey"));
23             // store some other items
24             mc.Store(StoreMode.Set, "D1", 1234L);
25             mc.Store(StoreMode.Set, "D2", DateTime.Now);
26             mc.Store(StoreMode.Set, "D3", true);
27             mc.Store(StoreMode.Set, "D4", new Product());
28             mc.Store(StoreMode.Set, "D5", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
29             Console.WriteLine("D1: {0}", mc.Get("D1"));
30             Console.WriteLine("D2: {0}", mc.Get("D2"));
31             Console.WriteLine("D3: {0}", mc.Get("D3"));
32             Console.WriteLine("D4: {0}", mc.Get("D4"));
33 
34             byte[] tmp = mc.Get<byte[]>("D5");
35             // delete them from the cache
36             mc.Remove("D1");
37             mc.Remove("D2");
38             mc.Remove("D3");
39             mc.Remove("D4");
40             // add an item which is valid for 10 mins
41             mc.Store(StoreMode.Set, "D4", new Product(), new TimeSpan(0, 10, 0));
42             Console.ReadLine();
43         }
44     }
45 
46     [Serializable]
47     class Product
48     {
49         public double Price = 1.24;
50         public string Name = "Mineral Water";
51         public override string ToString()
52         {
53             return String.Format("Product {{{0}: {1}}}", this.Name, this.Price);
54         }
55     }
56 }
复制代码

参考:http://kb.cnblogs.com/page/42777/

参考:http://www.cnblogs.com/czh-liyu/archive/2010/04/27/1722084.html

代码地址:http://pan.baidu.com/share/link?shareid=796487540&uk=3658066951

[转载]ASP.NET MVC 小牛之路05 - 在ASP.NET MVC中使用Ninject - Liam Wang - 博客园

mikel阅读(1069)

[转载][ASP.NET MVC 小牛之路]05 – 在ASP.NET MVC中使用Ninject – Liam Wang – 博客园.

在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来演示Ninject在ASP.NET MVC中的应用。

为了更好的理解和撑握本文内容,强烈建议初学者阅读本文前先阅读依赖注入(DI)和Ninject

本文目录:

1.准备工作

2.创建Controller Factory

3.添加Domain Model

4.添加Repository

5.添加绑定

6.显示列表

准备工作

新建一个名为BookShop的空白解决方案。在该解决方案中分别添加一个名为BookShop.WebUI的MVC空应用程序,和一个名为BookShop.Domain的类库工程。目录结构如下:

两个工程添加完后,在BookShop.WebUI工程下添加BookShop.Domain工程的引用。

使用NuGet分别为BookShop.WebUI工程和BookShop.Domain工程安装Ninject包(NuGet的介绍请阅读依赖注入(DI)和Ninject)。可以通过可视化窗口安装,也可以打开Package Manager Console(视图->其他窗口->Package Manager Console)执行下面命令安装:
Install-Package Ninject -Project BookShop.WebUI
Install-Package Ninject -Project BookShop.Domain
下图说明安装成功:

创建Controller Factory

我们知道,在ASP.NET MVC中,一个客户端请求是在特定Controller的Action中进行处理的。 默认情况下,ASP.NET MVC使用内置的Controller工厂类 DefaultControllerFactory来创建某个请求对应的Controller实例。有时候默认的Controller工厂不能满足我们实 际的需求,我们就需要对这种默认行为进行扩展,即创建一个继承自DefaultControllerFactory类的自定义Controller工厂类 并重写其中的一些方法。为此,我们在BookShop.WebUI工程下创建一个名为Infrastructure的文件夹,在该文件夹中添加一个名为 NinjectControllerFactory的工厂类,代码如下:

public class NinjectControllerFactory : DefaultControllerFactory {
    private IKernel ninjectKernel;

    public NinjectControllerFactory() {
        ninjectKernel = new StandardKernel();
        AddBindings();
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
        return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
    }

    private void AddBindings() {
        // todo:后面再来添加绑定
    }
}

上面代码中 的 ninjectKernel.Get(controllerType) 可获取到一个Controller实例。在这里如果手动实例化Controller类是一个非常复杂的过程,我们不知道Controller类有没有带参 数的构造函数,也不知道构造函数的参数是什么类型。而使用Ninject只需要使用上面的一个Get方法就可以,Ninject内部会自动处理所有的依赖 关系,智能地创建我们需要的对象。

Controller工厂类创建好后,我们就需要告诉MVC用我们的NinjectControllerFactory类来创建Controller对象,为此,需在Global.asax文件的Application_Start方法中添加下面代码:

protected void Application_Start() {
    ......

    //设置Controller工厂
    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

这里我们暂且不去关心上面这段代码是什么原理,知道设置自定义的Controller工厂必须要在这注册就行了,有空的话我会在后续博文对这部分内容进行更深入的讲解。

添加Domain Model

在MVC应用程序中,一切都是围绕Domain Model(领域模型)来的。 所以我们在BookShop.Domain工程中专门建一个名为Entities的文件夹,用来存放领域实体模型。作为一个电子商务类的网上书店,当然最 重要的一个领域实体就是Book了。由于只是为了演示,我们简单定义一个Book类,并在Entities文件夹中添加该类,代码如下:

public class Book {
    public int ID { get; set; }
    public string Title { get; set; }
    public string Isbn { get; set; }
    public string Summary { get; set; }
    public string Author { get; set; }
    public string Thumbnail { get; set; }
    public double Price { get; set; }
    public DateTime Published { get; set; }
}

添加Repository

我们知道,我们肯定需要一种方式来从数据库中读取Book数据。在这我们不防为数据的使用者(这里指Controller)提供一个 IBookRepository接口,在这个接口中声明一个IQueryable<Book>类型的属性Books。这样,通过该接口使用依 赖注入,使用者就可以拿到Books数据集合,而不用关心数据是如何得到的。为此,我们在BookShop.Domain工程中添加一个名为 Abstract的文件夹,在该文件夹中添加我们的IBookRepository接口文件,代码如下:

public interface IBookRepository {
    IQueryable<Book> Books { get; }
}

在MVC中我们一般会用仓储模式(Repository Pattern)把数据相关的逻辑和领域实体模型分离,这样对于使用者来说,通过调用仓储对象,使用者可以直接拿到自己想要的数据,而完全不必关心数据具 体是如何来的。我们可以把仓储比喻成一个超市,超市已经为消费者供备好了商品,消费者只管去超市选购自己需要的商品,而完全不必关心这些商品是从哪些供应 商怎么样运输到超市的。但对于仓储本身,必须要实现读取数据的“渠道”。

在BookShop.Domain工程中添加一个名为Concrete文件夹用于存放具体的类。我们在Concrete文件夹中添加一个实现了 IBookRepository接口的BookRepository类来作为我们的Book数据仓储。BookRepository类代码如下:

public class BookRepository : IBookRepository {

    public IQueryable<Book> Books {
        get { return GetBooks().AsQueryable(); }
    }

    private static List<Book> GetBooks() {  
        //为了演示,这里手工造一些数据,后面会介绍使用EF从数据库中读取。
        List<Book> books = new List<Book>{
            new Book { ID = 1, Title = "ASP.NET MVC 4 编程", Price = 52},
            new Book { ID = 2, Title = "CLR Via C#", Price = 46},
            new Book { ID = 3, Title = "平凡的世界", Price = 37}
        };
        return books;
    }
}

为了演示,上面是手工造的一些数据,后面的文章我将介绍使用Entity Framwork从数据库中读取数据。对于刚接触ORM框架的朋友可能对这里IQueryable感到奇怪,为什么用IQueryable作为返回类型, 而不用IEnumerable呢?后面有机会讲Entity Framwork的时候再讲。

添加绑定

打开之前我们在BookShop.WebUI工程创建的NinjectControllerFactory类,在AddBindings方法中添加如下代码:

private void AddBindings() {
    ninjectKernel.Bind<IBookRepository>().To<BookRepository>();
}

这句代码,通过Ninject把IBookRepository接口绑定到BookRepository,当IBookRepository接口的实现被请求时,Ninject将自动创建BookRepository类的实例。

到这里,Ninject的使用步骤就结束了,接下来我们把本示例剩余的步骤完成。

显示列表

右击BookShop.WebUI工程的Controllers文件夹,添加一个名为Book的Controller,按下面代码对其进行编辑:

public class BookController : Controller {
    private IBookRepository repository;

    public BookController(IBookRepository bookRepository) {
        repository = bookRepository;
    }
}

在这,BookController的构造函数接受了一个IBookRepository参数,当BookController被实例化的时 候,Ninject就为其注入了BookRepository的依赖。接下来我们为这个Controller添加一个名为List的Action,用来呈 现Book列表。代码如下:

public class BookController : Controller {
    ...

    public ViewResult List() {
        return View(repository.Books);
    }
}

当然我们需要添加一个View。右击上面的List方法,选择添加视图,在弹出的窗口进行如下配置:

然后我们在List.cshtml中用foreach循环来列举书本信息,代码如下:

@model IEnumerable<BookShop.Domain.Entities.Book>

@{
    ViewBag.Title = "Books";
}

@foreach (var p in Model) {  
    <div class="item" style="border-bottom:1px dashed silver;"> 
        <h3>@p.Title</h3> 
        <p>价格:@p.Price.ToString("c") </p>   
    </div>     
}

最后我们还需要修改一下默认路由,让系统运行后直接导向到我们的{controller = “Book”, action = “List”},打开Global.asax文件,找到RegisterRoutes方法,进行如下修改:

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default", // Route name 
        "{controller}/{action}/{id}", // URL with parameters 
        new { controller = "Book", action = "List", id = UrlParameter.Optional }
    );
}

到这,我们的程序可以运行了,效果如下:

 

结束语:

本文是Ninject在ASP.NET MVC中使用的一个简单示例,目的是让大家了解Ninject在MVC中的使用方法。当然,Ninject的强大之处不仅限于本文所演示的,相信当你熟悉 了Niject之后,在搭建MVC应用程序时,你一定会喜欢上它的。至于本文的示例BookShop,我会在后续博文中慢慢完善,让它看起来像个像样的网 上书店。

 

参考:

《Pro ASP.NET MVC 4》