Fork me on GitHub
更有效率的使用Visual Studio2

PS色调均化滤镜的快捷实现(C#源代码)。

photoshop色调均化功能通常是在进行修片处理前期比较常用的功能之一,其对扩展图像的对比度,增强视觉效果有一定的作用。在很多课本或者文章中,也称这种处理为灰度均衡化、直方图均衡化等等。算法原理都是对原始图像统计其直方图,然后通过累计分布函数,重新隐射直方图数据,使每个色阶的的分布概率都往一致的方向调整。我看到的网络上的代码,抑或是一些教材中的代码,对这个过程的描述都是相当的冗余,要么是代码累赘,效率低下,要么是萝莉啰唆,很是难受。

   在给出我的代码之前,还需要说明一些问题。对于灰度图像,由于只有一个通道,这个问题不明显,对于常见的24位图像,由于有RGB三个通道,那就存在是每个通道都单独均衡还是三通道联合计算直方图,然后利用相同的映射表在隐射RGB数据了,经过我的测试,在Photoshop中,是取的后者。

复制代码
    for (Y = 0; Y < Height; Y++)
    {
        Pointer = Scan0 + Y * Stride;               // 定位到每个扫描行的第一个像素,以避免溶于数据的影响
        for (X = 0; X < Width; X++)
        {
            HistGram[*Pointer]++;                   // Blue
            HistGram[*(Pointer + 1)]++;             // Green
            HistGram[*(Pointer + 2)]++;             // Red    
            Pointer += 3;                           // 移向下一个像素
        }
    }
    Num = 0;
    for (Y = 0; Y < 256; Y++)
    {
        Num = Num + HistGram[Y];
        Lut[Y] = (byte)((float)Num / (Width * Height * 3) * 255);       // 计算映射表
    }
    for (Y = 0; Y < Height; Y++)
    {
        Pointer = Scan0 + Y * Stride;
        for (X = 0; X < Width * 3; X += 3)          //  也可以这样写
        {
            Pointer[X] = Lut[Pointer[X]];
            Pointer[X + 1] = Lut[Pointer[X + 1]];
            Pointer[X + 2] = Lut[Pointer[X + 2]];
        }
    }
复制代码

  算法部分就是这么简单的二十几行代码,代码清晰,执行效率又特别高,数码相机中常见的4000*3000的照片100ms内就可以处理完。

    一副图像如果执行了一次色调均化,那么再次执行色调均化应该不会有任何像素发生变换了。

    从广义上讲,色调均化可以看成是直方图匹配的一个特例,即匹配的直方图分布为一条水平线。

    一些书中的色调均化是对各通道分别进行校正的,似乎这样处理的效果容易导致图像整体颜色不搭配,比如经典的Lena图:

             

                      原图                               PS的色调均化                               郎锐课本上的效果

   C#版色调均化代码:https://files.cnblogs.com/Imageshop/HistGramEqualize.rar

 

 
 
标签: 色调均化直方图均衡化C#

MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

 
 

〇、目录

一、前言

二、工具准备

三、T4代码生成预热

  (一) 单文件生成:HelloWorld.cs

  (二) 多文件生成

四、生成数据层实体相关相似代码

  (一) 生成准备

  (二) 生成实体相关相似代码

    1. 生成实体映射配置类
    2. 生成实体仓储接口
    3. 生成实体仓储实现

五、源码下载

六、系列导航

一、前言

  经过前面EF的《第一篇》与《第二篇》,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的IEntityRepository与EntityRepository。而且每添加一个实体类型,就要手动去添加一套相应的代码,也是比较累的工作。如果能有一个根据实体类型自动生成这些相似度较高的代码的解决方案,那将会减少大量的无聊的工作。

  VS提供的“文本模板”(俗称T4)功能,就是一个较好的解决方案。要添加一个实体类型,只要把实体类型定义好,然后运行一下定义好的T4模板,就可以自动生成相应的类文件。

二、工具准备

   为了更好的使用 T4模板 功能,我们需要给VS安装如下两个插件:

  • Devart T4 Editor:为VS提供智能提示功能。
  • T4 Toolbox:在生成多文件时很有用。

三、T4代码生成预热

(一) 单文件生成:HelloWorld.cs

   下面,我们先来体验一个最简单的T4代码生成功能,输出一个最简单的类文件。

  首先,在 GMF.Demo.Core.Data中 添加一个名为 T4 的文件夹,用于存放生成本工程内的代码的T4模板文件。并在其中添加一个名为 HelloWorld.tt的“文本模板”的项。

  HelloWorld.tt定义如下:

复制代码
 1 <#@ template debug="false" hostspecific="false" language="C#" #>
 2 <#@ assembly name="System.Core" #>
 3 <#@ import namespace="System.Linq" #>
 4 <#@ import namespace="System.Text" #>
 5 <#@ import namespace="System.Collections.Generic" #>
 6 <#@ output extension=".cs" #>
 7 using System;
 8 
 9 namespace GMF.Demo.Core.Data.T4
10 {
11     public class HelloWorld
12     {
13         private string _word;
14 
15         public HelloWorld(string word)
16         {
17             _word = word;
18         }
19     }
20 }
复制代码

   直接保存文件(T4的生成将会在保存模板,模板失去焦点等情况下自动触发生成。),将会在模板的当前位置生成一个同名的类文件:

  HelloWorld.cs的内容如下:

复制代码
 1 using System;
 2 
 3 namespace GMF.Demo.Core.Data.T4
 4 {
 5     public class HelloWorld
 6     {
 7         private string _word;
 8 
 9         public HelloWorld(string word)
10         {
11             _word = word;
12         }
13     }
14 }
复制代码

   这样,我们的HelloWorld之旅就结束了,非常简单。

(二) 多文件生成

  当前位置方案的方案只能生成如下所示的代码:

  生成的文件会与T4模板在同一目录中,这里就不详述了,可以参考 蒋金楠 一个简易版的T4代码生成"框架" 。

  本项目的多文件需要生成到指定文件夹中,但又想对T4模板进行统一的管理,T4文件夹里放置T4模板文件,但生成的映射文件EntityConfiguration将放置到文件夹Configurations中,仓储操作的文件IEntityRepository与EntityRepository将放置到Repositories文件夹中。且生成的代码文件应能自动的添加到解决方案中,而不是只是在文件夹中存在。

  要实现此需求,一个简单的办法就是通过 T4 Toolbox 来进行生成。想了解 T4 Toolbox 的细节,可以自己使用 ILSpy 来对 T4Toolbox.dll 文件进行反编译来查看源代码,如果是通过 T4 Toolbox 是通过VS的插件来安装的,将在“C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\11.0\Extensions\dca4f0lt.jdx”文件夹中(我的机器上)。下面,我们直接进行多文件生成的演示。

  首先,添加一个名为 HelloWorldTemplate.tt 的 T4 Toolbox 的代码模板。

  此模板将继承于 T4 Toolbox 的 CSharpTemplate 类:

复制代码
 1 <#+
 2 // <copyright file="HelloWorldTemplate.tt" company="郭明锋@中国">
 3 //  Copyright © 郭明锋@中国. All Rights Reserved.
 4 // </copyright>
 5 
 6 public class HelloWorldTemplate : CSharpTemplate
 7 {
 8     private string _className;
 9 
10     public HelloWorldTemplate(string className)
11     {
12         _className = className;
13     }
14 
15     public override string TransformText()
16     {
17 #>
18 using System;
19 
20 namespace GMF.Demo.Core.Data.T4
21 {
22     public class <#=_className #>
23     {
24         private string _word;
25 
26         public <#=_className #>(string word)
27         {
28             _word = word;
29         }
30     }
31 }
32 <#+
33         return this.GenerationEnvironment.ToString();
34     }
35 }
36 #>
复制代码

   模板类中定义了一个 className 参数,用于接收一个表示要生成的类名的值。所有生成类的代码都以字符串的形式写在重写的 TransformText 方法中。

  再定义一个T4模板文件 HelloWorldMulti.tt,用于调用 上面定义的代码模板进行代码文件的生成。

复制代码
 1 <#@ template debug="false" hostspecific="false" language="C#" #>
 2 <#@ assembly name="System.Core" #>
 3 <#@ import namespace="System.IO" #>
 4 <#@ import namespace="System.Linq" #>
 5 <#@ import namespace="System.Text" #>
 6 <#@ import namespace="System.Collections.Generic" #>
 7 <#@ include file="T4Toolbox.tt" #>
 8 <#@ include file="HelloWorldTemplate.tt" #>
 9 <#
10     string curPath = Path.GetDirectoryName(Host.TemplateFile);
11     string destPath = Path.Combine(curPath, "outPath");
12     if(!Directory.Exists(destPath))
13     {
14         Directory.CreateDirectory(destPath);
15     }
16     string[] classNames = new[]{"HelloWorld1", "HelloWorld2", "HelloWorld3"};
17     foreach(string className in classNames)
18     {
19         HelloWorldTemplate template = new HelloWorldTemplate(className);
20         string fileName = string.Format(@"{0}\{1}.cs", destPath, className);
21         template.Output.Encoding = Encoding.UTF8;
22         template.RenderToFile(fileName);
23     }
24  #>
复制代码

  以上是整个T4模板的执行方,在执行方中,要引用所有需要用到的类库文件,命名空间,包含的模板文件等。

  最后,文件的生成是调用 T4 Toolbox 的Template基类中定义的 RenderToFile(string filename)方法来生成各个文件的,输入的参数为生成文件的文件全名。在这里,生成将如下所示:

  outPPath文件夹中生成了 HelloWorld1.cs、HelloWorld2.cs、HelloWorld3.cs 文件,而 HelloWorldMulti.tt 所在文件夹中也会生成一个空的 HelloWorldMulti.cs 类文件。

四、生成数据层实体相关相似代码

(一) 生成准备

  我们的生成代码是完全依赖于业务实体的,所以,需要有一个类来对业务实体的信息进行提取封装。

复制代码
 1 namespace GMF.Component.Tools.T4
 2 {
 3     /// <summary>
 4     /// T4实体模型信息类
 5     /// </summary>
 6     public class T4ModelInfo
 7     {
 8         /// <summary>
 9         /// 获取 模型所在模块名称
10         /// </summary>
11         public string ModuleName { get; private set; }
12 
13         /// <summary>
14         /// 获取 模型名称
15         /// </summary>
16         public string Name { get; private set; }
17 
18         /// <summary>
19         /// 获取 模型描述
20         /// </summary>
21         public string Description { get; private set; }
22 
23         public IEnumerable<PropertyInfo> Properties { get; private set; }
24 
25         public T4ModelInfo(Type modelType)
26         {
27             var @namespace = modelType.Namespace;
28             if (@namespace == null)
29             {
30                 return;
31             }
32             var index = @namespace.LastIndexOf('.') + 1;
33             ModuleName = @namespace.Substring(index, @namespace.Length - index);
34             Name = modelType.Name;
35             var descAttributes = modelType.GetCustomAttributes(typeof(DescriptionAttribute), true);
36             Description = descAttributes.Length == 1 ? ((DescriptionAttribute)descAttributes[0]).Description : Name;
37             Properties = modelType.GetProperties();
38         }
39     }
40 }
复制代码

   另外,通过模板生成的代码,与我们手写的代码有如下几个区别:

  1. 手写代码可以自由定义,生成代码是根据模板生成的,必然遵守模板定义的规范。
  2. 手写代码的修改可以永久保存,生成代码的修改将会在下次生成后丢失,即生成代码不应该进行修改。

  基于以上几个区别,我提出如下解决方案,来解决生成代码的修改问题

  1. 通过模板生成的类应尽量定义为分部类(partial class),在需要进行修改及扩展的时候可以定义另外的同名分部类来进行修改。
  2. 对于需要外部实现的功能,定义分部方法来对外提供扩展。
  3. 生成代码的类文件命名把扩展名由“.cs”更改为“generated.cs”,以区分生成代码与手写代码文件。

(二) 生成实体相关相似代码

  1. 生成实体映射配置类

  实体映射配置类模板 EntityConfigurationTemplate.tt 定义:

复制代码
 1 <#+
 2 // <copyright file="EntityConfigurationTemplate.tt" company="郭明锋@中国">
 3 //  Copyright © 郭明锋@中国. All Rights Reserved.
 4 // </copyright>
 5 
 6 public class EntityConfigurationTemplate : CSharpTemplate
 7 {
 8     private T4ModelInfo _model;
 9         
10     public EntityConfigurationTemplate(T4ModelInfo model)
11     {
12         _model = model;
13     }
14 
15     /// <summary>
16     /// 获取 生成的文件名,根据模型名定义
17     /// </summary>
18     public string FileName
19     {
20         get
21         { 
22             return string.Format("{0}Configuration.generated.cs", _model.Name);
23         }
24     }
25 
26     public override string TransformText()
27     {
28 #>
29 //------------------------------------------------------------------------------
30 // <auto-generated>
31 //     此代码由工具生成。
32 //     对此文件的更改可能会导致不正确的行为,并且如果
33 //     重新生成代码,这些更改将会丢失。
34 //        如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 <#= _model.Name #>ConfigurationAppend 分部方法。
35 // </auto-generated>
36 //
37 // <copyright file="<#= _model.Name #>Configuration.generated.cs">
38 //        Copyright(c)2013 GMFCN.All rights reserved.
39 //        CLR版本:4.0.30319.239
40 //        开发组织:郭明锋@中国
41 //        公司网站:http://www.gmfcn.net
42 //        所属工程:GMF.Demo.Core.Data
43 //        生成时间:<#= DateTime.Now.ToString("yyyy-MM-dd HH:mm") #>
44 // </copyright>
45 //------------------------------------------------------------------------------
46 
47 using System;
48 using System.Data.Entity.ModelConfiguration;
49 using System.Data.Entity.ModelConfiguration.Configuration;
50 
51 using GMF.Component.Data;
52 using GMF.Demo.Core.Models;
53 
54 
55 namespace GMF.Demo.Core.Data.Configurations
56 {
57     /// <summary>
58     /// 实体类-数据表映射——<#= _model.Description #>
59     /// </summary>    
60     internal partial class <#= _model.Name #>Configuration : EntityTypeConfiguration<<#= _model.Name #>>, IEntityMapper
61     {
62         /// <summary>
63         /// 实体类-数据表映射构造函数——<#= _model.Description #>
64         /// </summary>
65         public <#= _model.Name #>Configuration()
66         {
67             <#= _model.Name #>ConfigurationAppend();
68         }
69         
70         /// <summary>
71         /// 额外的数据映射
72         /// </summary>
73         partial void <#= _model.Name #>ConfigurationAppend();
74         
75         /// <summary>
76         /// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
77         /// </summary>
78         /// <param name="configurations">实体映射配置注册器</param>
79         public void RegistTo(ConfigurationRegistrar configurations)
80         {
81             configurations.Add(this);
82         }
83     }
84 }
85 <#+
86         return this.GenerationEnvironment.ToString();
87     }
88 }
89 #>
复制代码

   生成模板调用方 EntityCodeScript.tt 定义

复制代码
 1 <#@ template language="C#" debug="True" #>
 2 <#@ output extension="cs" #>
 3 <#@ Assembly Name="System.Core" #>
 4 <#@ Assembly Name="$(SolutionDir)\GMF.Component.Tools\bin\Debug\GMF.Component.Tools.dll" #>
 5 <#@ import namespace="System.IO" #>
 6 <#@ Import Namespace="System.Linq" #>
 7 <#@ Import Namespace="System.Text" #>
 8 <#@ import namespace="System.Reflection" #>
 9 <#@ Import Namespace="System.Collections.Generic" #>
10 <#@ Import Namespace="GMF.Component.Tools" #>
11 <#@ Import Namespace="GMF.Component.Tools.T4" #>
12 <#@ include file="T4Toolbox.tt" #>
13 <#@ include file="Include\EntityConfigurationTemplate.tt" #>
14 <#
15     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
16     string projectPath =currentPath.Substring(0, currentPath.IndexOf(@"\T4"));
17     string solutionPath = currentPath.Substring(0, currentPath.IndexOf(@"\GMF.Demo.Core.Data"));
18 
19     string modelFile= Path.Combine(solutionPath, @"GMF.Demo.Core.Models\bin\Debug\GMF.Demo.Core.Models.dll");
20     byte[] fileData= File.ReadAllBytes(modelFile);
21     Assembly assembly = Assembly.Load(fileData);
22     IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => typeof(Entity).IsAssignableFrom(m) && !m.IsAbstract);
23     foreach(Type modelType in modelTypes)
24     {
25         T4ModelInfo model = new T4ModelInfo(modelType);
26         //实体映射类
27         EntityConfigurationTemplate config = new EntityConfigurationTemplate(model);
28         string path = string.Format(@"{0}\Configurations", projectPath);
29         config.Output.Encoding = Encoding.UTF8;
30         config.RenderToFile(Path.Combine(path, config.FileName));
31     }
32 #>
复制代码

  调用方通过反射从业务实体程序集 GMF.Demo.Core.Models.dll 中获取所有基类为 Entity 的并且不是抽象类的实体类型信息,再调用模板逐个生成实体配置类文件。

  例如,生成的登录记录信息(LoginLog)的映射文件 LoginLogConfiguration.generated.cs 如下:

复制代码
 1 //------------------------------------------------------------------------------
 2 // <auto-generated>
 3 //     此代码由工具生成。
 4 //     对此文件的更改可能会导致不正确的行为,并且如果
 5 //     重新生成代码,这些更改将会丢失。
 6 //        如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 LoginLogConfigurationAppend 分部方法。
 7 // </auto-generated>
 8 //
 9 // <copyright file="LoginLogConfiguration.generated.cs">
10 //        Copyright(c)2013 GMFCN.All rights reserved.
11 //        CLR版本:4.0.30319.239
12 //        开发组织:郭明锋@中国
13 //        公司网站:http://www.gmfcn.net
14 //        所属工程:GMF.Demo.Core.Data
15 //        生成时间:2013-06-16 17:45
16 // </copyright>
17 //------------------------------------------------------------------------------
18 
19 using System;
20 using System.Data.Entity.ModelConfiguration;
21 using System.Data.Entity.ModelConfiguration.Configuration;
22 
23 using GMF.Component.Data;
24 using GMF.Demo.Core.Models;
25 
26 
27 namespace GMF.Demo.Core.Data.Configurations
28 {
29     /// <summary>
30     /// 实体类-数据表映射——登录记录信息
31     /// </summary>    
32     internal partial class LoginLogConfiguration : EntityTypeConfiguration<LoginLog>, IEntityMapper
33     {
34         /// <summary>
35         /// 实体类-数据表映射构造函数——登录记录信息
36         /// </summary>
37         public LoginLogConfiguration()
38         {
39             LoginLogConfigurationAppend();
40         }
41         
42         /// <summary>
43         /// 额外的数据映射
44         /// </summary>
45         partial void LoginLogConfigurationAppend();
46         
47         /// <summary>
48         /// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
49         /// </summary>
50         /// <param name="configurations">实体映射配置注册器</param>
51         public void RegistTo(ConfigurationRegistrar configurations)
52         {
53             configurations.Add(this);
54         }
55     }
56 }
复制代码

  要配置登录信息与用户信息的 N:1 关系,只需要添加一个分部类 LoginLogConfiguration,并实现分类方法 LoginLogConfigurationAppend 即可。

复制代码
 1 namespace GMF.Demo.Core.Data.Configurations
 2 {
 3     partial class LoginLogConfiguration
 4     {
 5         partial void LoginLogConfigurationAppend()
 6         {
 7             HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
 8         }
 9     }
10 }
复制代码

  2. 生成实体仓储接口

  实体映射配置类模板 EntityConfigurationTemplate.tt 定义:

复制代码
 1 <#+
 2 // <copyright file="IEntityRepositoryTemplate.tt" company="郭明锋@中国">
 3 //  Copyright © 郭明锋@中国. All Rights Reserved.
 4 // </copyright>
 5 
 6 public class IEntityRepositoryTemplate : CSharpTemplate
 7 {
 8     private T4ModelInfo _model;
 9         
10     public IEntityRepositoryTemplate(T4ModelInfo model)
11     {
12         _model = model;
13     }
14 
15     /// <summary>
16     /// 获取 生成的文件名,根据模型名定义
17     /// </summary>
18     public string FileName
19     {
20         get
21         { 
22             return string.Format("I{0}Repository.generated.cs", _model.Name);
23         }
24     }
25 
26     public override string TransformText()
27     {
28 #>
29 //------------------------------------------------------------------------------
30 // <auto-generated>
31 //     此代码由工具生成。
32 //     对此文件的更改可能会导致不正确的行为,并且如果
33 //     重新生成代码,这些更改将会丢失。
34 //       如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类进行实现。
35 // </auto-generated>
36 //
37 // <copyright file="I<#= _model.Name #>Repository.generated.cs">
38 //        Copyright(c)2013 GMFCN.All rights reserved.
39 //        CLR版本:4.0.30319.239
40 //        开发组织:郭明锋@中国
41 //        公司网站:http://www.gmfcn.net
42 //        所属工程:GMF.Demo.Core.Data
43 //        生成时间:<#= DateTime.Now.ToString("yyyy-MM-dd HH:mm") #>
44 // </copyright>
45 //------------------------------------------------------------------------------
46 
47 using System;
48 
49 using GMF.Component.Data;
50 using GMF.Demo.Core.Models;
51 
52 
53 namespace GMF.Demo.Core.Data.Repositories
54 {
55     /// <summary>
56     ///   数据访问层接口——<#= _model.Description #>
57     /// </summary>
58     public partial interface I<#= _model.Name #>Repository : IRepository<<#= _model.Name #>>
59     { }
60 }
61 
62 <#+
63         return this.GenerationEnvironment.ToString();
64     }
65 }
66 #>
复制代码

   相应的,在调用方 EntityCodeScript.tt 中添加模板调用代码(如下 11-15 行所示):

复制代码
 1     foreach(Type modelType in modelTypes)
 2     {
 3         T4ModelInfo model = new T4ModelInfo(modelType);
 4 
 5         //实体映射类
 6         EntityConfigurationTemplate config = new EntityConfigurationTemplate(model);
 7         string path = string.Format(@"{0}\Configurations", projectPath);
 8         config.Output.Encoding = Encoding.UTF8;
 9         config.RenderToFile(Path.Combine(path, config.FileName));
10 
11         //实体仓储操作接口
12         IEntityRepositoryTemplate irep= new IEntityRepositoryTemplate(model);
13         path = string.Format(@"{0}\Repositories", projectPath);
14         irep.Output.Encoding = Encoding.UTF8;
15         irep.RenderToFile(Path.Combine(path, irep.FileName));
16     }
复制代码

 

  生成的登录记录信息仓储操作接口 ILoginLogRepository.generated.cs:

复制代码
 1 //------------------------------------------------------------------------------
 2 // <auto-generated>
 3 //     此代码由工具生成。
 4 //     对此文件的更改可能会导致不正确的行为,并且如果
 5 //     重新生成代码,这些更改将会丢失。
 6 //       如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类进行实现。
 7 // </auto-generated>
 8 //
 9 // <copyright file="ILoginLogRepository.generated.cs">
10 //        Copyright(c)2013 GMFCN.All rights reserved.
11 //        CLR版本:4.0.30319.239
12 //        开发组织:郭明锋@中国
13 //        公司网站:http://www.gmfcn.net
14 //        所属工程:GMF.Demo.Core.Data
15 //        生成时间:2013-06-16 17:56
16 // </copyright>
17 //------------------------------------------------------------------------------
18 
19 using System;
20 
21 using GMF.Component.Data;
22 using GMF.Demo.Core.Models;
23 
24 
25 namespace GMF.Demo.Core.Data.Repositories
26 {
27     /// <summary>
28     ///   数据访问层接口——登录记录信息
29     /// </summary>
30     public partial interface ILoginLogRepository : IRepository<LoginLog>
31     { }
32 }
复制代码

  如果 IRepository<T> 中定义的仓储操作不满足登录记录仓储操作的需求,只需要添加一个相应的分部接口,在其中进行需求的定义即可。这里当前没有额外的需求,就不需要额外定义了。

  3. 生成实体仓储实现

  实体仓储操作的实现与仓储操作接口类似,这里略过。

  完成生成后,代码结构如下所示:

  如果添加了或者删除了一个实体,只需要重新运行一下调用模板 EntityCodeScript.tt 即可重新生成新的代码。

五、源码下载

 GMFrameworkForBlog5.zip

六、系列导航

  1. MVC实用架构设计(〇)——总体设计
  2. MVC实用架构设计(一)——项目结构搭建
  3. MVC实用架构设计(二)——使用MEF实用IOC
  4. MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext
  5. MVC实用架构设计(三)——EF-Code First(2):实体映射、数据迁移,重构
  6. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码
  7. MVC实用架构设计(三)——EF-Code First(4):数据查询
  8. MVC实用架构设计(三)——EF-Code First(5):二级缓存
  9. MVC实体架构设计(四)——日志记录
  10. 未完待续。。。
如果您看完本篇文章感觉不错,请点击一下右下角的“推荐”来支持一下博主,谢谢!

作者:郭明锋
出处http://www.cnblogs.com/guomingfeng

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


 

更有效率的使用Visual Studio(二)

没想到上一篇文章有这么多人喜欢,多谢大家支持。继续~

很多比较通用的快捷键的默认设置其实是有一些缩写在里面的,这个估计也是MS帮助我们记忆。比如说注释代码的快捷键是Ctrl + E + C,我们如果知道它是 Ctrl + Edit + Comment Code 的缩写不是更好记么?我也会尽量YY把快捷键和功能联系起来来帮助我自己记忆。另外很多功能在VS中有多个快捷键可以实现,我猜是为了防止一些快捷键冲突所设计的吧,我一般只会去记好记的,冲突了再说。

三、继续更有效率的编辑器

3.1 更有效的编辑(补充)

a. 注释代码:Ctrl + E(Edit) + C(Comment), Ctrl + K + C(Comment)。打开文件类型不同行为可能不同,在cs文件类型中会将选中行的代码注释,cpp中会将选中的内容进行注释。

   反注释代码:Ctrl + E(Edit) + U(Uncomment), Ctrl + K + U(Uncomment)

b. 调整格式选中代码格式:Ctrl + E(Edit) + F(Format)。

c. 调整整个文档代码格式:Ctrl + E(Edit) + D(Document Format)。

3.2 更有效率的搜索:

a. Incremental Search(增量搜索,不知道翻译得恰不恰当):Ctrl + I(Incremental) (移动到下一个匹配按Ctrl + I, 移动到上一个Shift + Ctrl + I)。按住快捷键然后输入要查询的字符串,VS会马上定位到而不需要想Ctrl + F那种确认的过程,可以通过我截的图中看到效果。我一般如果只是想在当前文档进行简单搜索的话一般会使用这个搜索,遇到是一些比较复杂的搜索条件才去动用弹框搜索。

Incremental Search

b. Ctrl + F(Find):在Visual Studio 2012中其实这个功能已经和Increment Search很相似了,你会发现在VS2012+里使用Ctrl + F和上面的效果是一样的,都是输入即可看到搜索结果。不过与Increment Search不同的是,这个搜索可以指定更多的条件,如是否匹配大小写、是否整词搜索、是否用正则表达式以及搜索的范围。

c. 在文件中查找:Ctrl + Shift + F,这个可以实现的搜索功能与Ctrl + F 一模一样,唯一不同就是这个可以将你搜索的结果输出到查找结果窗口中,而不是一个一个显示出来。这个比较合适搜一些比较多匹配的东西,然后在输出的结果窗口中在肉眼筛选。

四、更有效的导航:

1. 快速打开Solution Explorer:Ctrl + W(Windows)+ S(Solution)、Ctrl + Alt + L。当你在写代码想打开工程中另一个文件时就可以用这个快速打开解决方案窗口选择文件。

2. 打开当前打开文件列表:Ctrl+Alt+Down。这个很好用,但是这个快捷键在很多电脑上都会翻转屏幕,囧。如果实在要用这个功能,可以通过自定义快捷键来实现。

File List

3. 快速将焦点移到类列表(这个名词纯属YY,见图便知我指的是啥):Ctrl +F2

Class list

4. 内部文件切换:Ctrl + Tab。这个不仅在VS中,很多软件中都是这个功能。

5. 全屏:Shift + Alt + Enter。可以让你进入全屏无干扰模式,本人很喜欢这个功能。

五、更有效的智能感知

智能感知本来就很智能,但是很多时候我们想强制的调出一些提示来看一下的时候这些功能就爽。很多功能语言描述可能比较累,而且由于我语文不好很可能你还看不懂,所以我会附图。

1. 列出成员。Ctrl + K + L(List Member), Ctrl + J。我们知道当我们需要访问对象方法的时候按.VS会自动提示出有哪些方法,但是有时候我们需要在.操作符已经存在的情况下再查看。以前我会把点删掉然后再点一次,我承认我当时很傻,后来知道这个快捷键以后就好多了。

List Member

2. 列出选项(表述不明确,具体看后面描述)。Ctrl + . 或者Ctrl + Shift +F10。当我们用到一些类型在我们工程引用的程序集里但是没有在当前当前文件引用的命名空间内时,或者我们写了一个不存在的函数时,那行代码会有错误提示,并且在左下角有一个小符号。如图:UnknowType。我们鼠标移到符号附近会出现一些帮助,可以自动帮助我们添加引用或者生成函数。这个快捷键就是在不移动鼠标的情况下让其出现这个提示。

六、其他

1. 任务列表(Task List),可以通过View->Task List打开这个窗口。很多人可能不知道这个功能,我觉得挺有用。写代码的时候我往往会遇到这种情况,某一些代码我现在不确定需求或者觉得可能会有问题将来需要改善,我会加上注释://TODO:reason。相信很多人会有同样的习惯,这个任务列表的功能就是让我们可以看到我们当前工程中有多少个TODO项。当然不局限于TODO这个词,可以自定义词汇。我一般会在commit之前看一下这个列表看看还有没有需要改的地方。这个还可以直接添加一些任务,具体使用自己用一下就知道了。

 Task List

七、插件

这里在推荐两个插件:C# outline 和Smart Paster。

1. C# Outline

Visual Studio默认的outline是只有在函数级别的,但是很多时候有一些循环条件很长也需要缩起来看比较方便。于是就有了这个插件。效果如下:

Before outline->Before outline

After outline-> After outline

2. Smart Paster

这个插件可以将文字粘帖为注释、和string字符串和StringBuilder。特别是对于粘贴多行的文字的时候很有用。

八、推荐资料

1. Favorite Visual Studio keyboard shortcuts:Stackoverflow 上一群人在讨论自己最喜欢的快捷键,可以去里面看看或许你会看到一些你意想不到的快捷键。

2. Visual Studio 程序员箴言:这本书介绍了很多关于VS方面的知识。

3. 可以多看看VS菜单栏上那些没用过东西,或许你会发现一些对你很有用的东西。

总结

这些只是对我来说最有用的一些技巧,强烈推荐大家可以去看看我推荐的那些资料去探索一些更加适合你的习惯的一些功能。因为当你不知道有这个功能的存在的情况下你根本就想不到要去找这么一个功能。

容我再罗嗦几句。我们绝对没有必要去死记硬背很多很多快捷键然后装逼,因为并非所有的快捷键所有人都需要。很大程度上一些功能的使用是取决于你的工作习惯,同时我也不推荐你去记一些你觉得你都不会用到的快捷键,没意义,浪费时间。这也就是为什么我只列出一些对我工作效率有切身帮助的一些快捷键,而不是把Visual Studio中所有的快捷键都列出来,因为那样子的话就没意义了!

所以我强烈建议你们只去记你觉得有用的那些东西。

 
 
分类: 经验总结
标签: .NETVisual Studio高效工作快捷键

 

 

四:江湖学艺(上)

创业帮派项目遇难,引入技术总监人物,力挽狂澜;本人敬之并从其学得武艺

 

本节开始,语言就不复古了,转成大众话的记述文,不然怕是得拖十年才能写完。 

 

话说当年,我去游乐乐的时候加上我也才4个人,美工2名,加我和技术老大,还有BOSS,凑和5个。

 

就在我闭头苦B Coding 时,短短数日, 游乐乐人员招聘速度飞快;
转眼间各部门已不断成立,美工一部门,编辑一部门,策划一部门,技术一部门。

BOSS想法也日复一日不断演变中,项目已超出原有的规划,上线日期也不断往后推延。

 

最神秘的,当属原活动版块负责人神秘失踪后,才新招一人负责,第一天一起吃过中午饭,第二天发现人又失踪了。

由于技术老大和BOSS有朋友关系,说好帮手数月,大体项目差不多就会离开,所以BOSS引入技术总监人物,负责统管技术部。

同时原来的技术老大,则转到开发Ulolo后台管理系统。

 

技术总监来的第一件事,就是审核项目代码,并找出了项目里“经常黄页”的原因:DataReader关闭问题。

我曾经写过一篇关于DataReader,可以参考:DataReader不奇怪,该出手时就出手!,此文介绍了DataReader本质上的相关内涵信息。

关于DataReader问题,由于项目里多处是Ctrl+C,Ctrl+V,批量代码生成量产,已经散落在各个页面中。

所以技术总监的想法是重写SQLHelper里的SqlDataReader的返回

通过重写DataReader, 将原来DataReader的数据,快速读取到一个新的容器MDataReader,然后关闭链接再返回。

所以整体项目代码基本不用改动,当年的秋天小白还是按原来的方式编码,继续CTRL+C+V。

 

这是当年的MDataReader的实现代码:

 

MDataReader 实现using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
namespace UloloDataFramework.Core.Data
{
    public class MDataReader : System.Data.IDataReader, System.Collections.IEnumerable
    {
        public static implicit operator MDataReader(System.Data.SqlClient.SqlDataReader _SourceDataReader)
        {
            MDataReader mrdr = new MDataReader();
            mrdr.CreateSelf(ref _SourceDataReader);
            _SourceDataReader.Close();
            _SourceDataReader.Dispose();
            return mrdr;
        }

        //public static implicit operator MDataReader(System.Data.IDataReader _SourceDataReader)
        
//{
        
//    MDataReader mrdr = new MDataReader();
        
//    mrdr.CreateSelf(ref (SqlDataReader)_SourceDataReader);
        
//    _SourceDataReader.Close();
        
//    _SourceDataReader.Dispose();
        
//    return mrdr;
        
//}


        private System.Collections.Specialized.OrderedDictionary[] _MeList;
        private int _MeListI = -1;
        private int _BaseListI = 0;

        private bool isRead = false;
        private int _RecordsAffected = -1;
        private int _FieldCount = -1;

        public MDataReader()
        {
        }
        public MDataReader(System.Data.IDataReader _SourceDataReader)
        {
            //MDataReader mrdr = new MDataReader();
            this.CreateSelf(ref  _SourceDataReader);
            _SourceDataReader.Close();
            _SourceDataReader.Dispose();
            //return mrdr;
        }
        public MDataReader(System.Data.SqlClient.SqlDataReader _SourceDataReader)
        {
            //MDataReader mrdr = new MDataReader();
            this.CreateSelf(ref _SourceDataReader);
            _SourceDataReader.Close();
            _SourceDataReader.Dispose();
            //return mrdr;
        }
        public void CreateSelf(ref System.Data.SqlClient.SqlDataReader _SourceDataReader)
        {
            do
            {
                if (_SourceDataReader.HasRows)
                {
                    _MeListI++;
                    System.Array.Resize<System.Collections.Specialized.OrderedDictionary>(ref _MeList, _MeListI + 1);
                    _MeList[_MeListI] = new System.Collections.Specialized.OrderedDictionary();

                    _FieldCount = _SourceDataReader.FieldCount;
                    _RecordsAffected = _SourceDataReader.RecordsAffected;
                    for (int j = 0; _SourceDataReader.Read(); j++)
                    {
                        _MeList[_MeListI].Add(j.ToString(), new ReaderDataItemClass());
                        for (int i = 0; i < _SourceDataReader.FieldCount; i++)
                        {
                            ((ReaderDataItemClass)_MeList[_MeListI][j.ToString()]).Add(_SourceDataReader.GetName(i), _SourceDataReader[_SourceDataReader.GetName(i)]);
                        }
                    }
                }

            } while (_SourceDataReader.NextResult());
        }
        public void CreateSelf(ref System.Data.IDataReader _SourceDataReader)
        {
            System.Data.SqlClient.SqlDataReader srd = (System.Data.SqlClient.SqlDataReader)_SourceDataReader;
            CreateSelf(ref srd);
            srd.Close();
            srd.Dispose();
        }
        public bool NextResult()
        {
            _BaseListI++;
            if (_BaseListI > _MeListI)
            {
                Dispose();
                return false;

            }
            else
            {
                _MeList[_BaseListI - 1].Clear();
                _MeList[_BaseListI - 1] = null;
                isRead = false;
                return true;
            }
        }

        public bool Read()
        {
            try
            {
                if (!isRead)
                {
                    isRead = true;
                    return (_MeList[_BaseListI].Count == 0) ? false : true;
                }


                ((ReaderDataItemClass)_MeList[_BaseListI][0]).Clear();
                _MeList[_BaseListI][0] = null;
                _MeList[_BaseListI].RemoveAt(0);
                return (_MeList[_BaseListI].Count == 0) ? false : true;
            }
            catch { return false; }
        }

        public object this[int index]
        {
            get
            {
                return ((ReaderDataItemClass)_MeList[_BaseListI][0])[index];
            }
        }

        public object this[string key]
        {
            get
            {
                return ((ReaderDataItemClass)_MeList[_BaseListI][0])[key];
            }
        }

        public bool HasRows
        {
            get
            {
                return (_MeList[_BaseListI].Count == 0) ? false : true;
            }
        }



        public void Dispose()
        {
            _MeList = null;
        }

        public void Close()
        {
            _MeList = null;
        }

        #region IDataRecord 成员

        public int FieldCount
        {
            get { return _MeList == null ? 0 : ((ReaderDataItemClass)_MeList[_BaseListI][0]).Count; }
        }

        public bool GetBoolean(int i)
        {
            return bool.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public byte GetByte(int i)
        {
            return byte.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
        {
            return 0;
        }

        public char GetChar(int i)
        {
            return char.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
        {
            return 0;
        }


        public string GetDataTypeName(int i)
        {
            return (((ReaderDataItemClass)_MeList[_BaseListI][0])[i].GetType().Name);
        }

        public DateTime GetDateTime(int i)
        {
            return DateTime.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public decimal GetDecimal(int i)
        {
            return decimal.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public double GetDouble(int i)
        {
            return double.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public Type GetFieldType(int i)
        {
            return (((ReaderDataItemClass)_MeList[_BaseListI][0])[i].GetType());
        }

        public float GetFloat(int i)
        {
            return float.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public Guid GetGuid(int i)
        {
            return new Guid((((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString()));
        }

        public short GetInt16(int i)
        {
            return short.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public int GetInt32(int i)
        {
            return int.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public long GetInt64(int i)
        {
            return long.Parse(((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString());
        }

        public string GetName(int i)
        {
            return ((ReaderDataItemClass)_MeList[_BaseListI][0]).Keys[i].ToString();

        }

        public int GetOrdinal(string name)
        {
            System.Collections.ArrayList tmpList = ((ReaderDataItemClass)_MeList[_BaseListI][0]).Keys;
            return tmpList.IndexOf(name);
        }

        public string GetString(int i)
        {
            return ((ReaderDataItemClass)_MeList[_BaseListI][0])[i].ToString();
        }

        public object GetValue(int i)
        {
            return ((ReaderDataItemClass)_MeList[_BaseListI][0])[i];
        }

        public int GetValues(object[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = ((ReaderDataItemClass)_MeList[_BaseListI][0])[i];
            }
            return values.Length;
        }

        public bool IsDBNull(int i)
        {
            return (TypeCode)((ReaderDataItemClass)_MeList[_BaseListI][0])[i] == System.TypeCode.DBNull;
        }

        #endregion



        #region IDataReader 成员


        public int Depth
        {
            get { return 0; }
        }

        public DataTable GetSchemaTable()
        {
            DataTable dt = new DataTable();
            object[] tmpObj = null;
            for (int i = 0; i < ((ReaderDataItemClass)_MeList[_BaseListI][0]).Count - 1; i++)
            {
                dt.Columns.Add(this.GetName(i));
            }

            while (this.Read())
            {
                ((ReaderDataItemClass)_MeList[_BaseListI][0]).CopyTo(tmpObj, 0);
                dt.Rows.Add(tmpObj);
            }

            return dt;
        }

        public bool IsClosed
        {
            get { return true; }
        }

        public int RecordsAffected
        {
            get { return _RecordsAffected; }
        }

        #endregion

        #region IDataRecord 成员


        public IDataReader GetData(int i)
        {
            throw new Exception("ERR");
        }

        #endregion

        #region IEnumerable 成员

        public IEnumerator GetEnumerator()
        {
            return new System.Data.Common.DbEnumerator(this);
        }

        #endregion
    }

    class ReaderDataItemClass : System.Collections.Specialized.OrderedDictionary, System.Collections.Specialized.IOrderedDictionary
    {
        private System.Collections.ArrayList _KeyList;
        public ReaderDataItemClass()
            : base()
        {
            _KeyList = new System.Collections.ArrayList();
        }



        public new System.Collections.ArrayList Keys
        {
            get { return _KeyList; }
        }



        public new void Add(object key, object value)
        {
            _KeyList.Add(key);
            base.Add(key, value);
        }

        public new void Clear()
        {
            _KeyList.Clear();
            base.Clear();
        }


        public void Remove(int index)
        {
            _KeyList.RemoveAt(index);

            base.RemoveAt(index);
        }

    }

 

 

当年我是小白,连SQLHelper看着都吃力,连存储过程的编写,也是扔给技术老大的,所以对技术总监的实力就很崇拜了。 

后来招了一个“副技术总监”,负责了原来的活动模块和新开发论坛模块,还有论坛的编辑器。

当我原有项目基本落定时,前后又完成了几个小项目,犹记得刚完成了“目地的指南”模块时,事情就发生了。

 

我稍有点闲空时,技术总监突然让我写一个缓存类:

 

我苦思N小时,基于对控件的熟悉程度,我知道页面有这东东可以设置缓存:

然而写成缓存类,那会小白的我,除了会写个实体类,写其它类,咪咪,一时找不着北,蛋疼了近小时。。。

 

稍会,技术总监和BOSS开完会后出来,问我写的怎样了,我摸了摸头,说了句无从下手。

然后他花了点时间把缓存类的大体写了下,然后让我看懂了再补充。

 

我看了看,看到一个List<string>,就看不懂了,问技术总监那个List<T>是什么东东。

总监只回答了两个字“泛型”,我没有再问,在网络搜索泛型,看了相关文章和介绍。

同时当年电脑里存档了这样一份CHM知识库文档,在我闲时,总会抽空看看:

 

 

经过多篇文章的学习之后,加上上下左右前中后的扫了多次代码,基本把缓存这块,除了不懂的,能懂的都懂了。

于是一转身,问总监需要加什么功能?

后来大体是要求“缓存优先级别,和界限设置,及按要求定时检测并清空缓存功能”。

基本上前后按总监要求,来回改了N十次,还特别写了个winform界面来测试缓存的稳定性。

2天左右。。一句差不多也算交差了。

 

那会的源码,好在我的硬盘里有存档,这里顺路提供大伙下载,见识下我当年的潜力:点击下载 

 

那段快速时间,是求知欲最强的时候,和技术总监的交流中,总会遇到一些不明白的词,像“网关”,“VPN”,"DNS“,”Hosts“,"Hook"。

聊的时候我不懂,只能呵呵,总监以为我知道,聊完后回电脑桌边第一件事就是查相关词,了解下是什么东东。

经过不断的搜索,看文章,那时候的智商,看完文章,还是有理解不了词,像”VPN“,”流“,”Hook“。

 

下篇预告:

四:江湖学艺(下):缓存过后,技术总监让我写MDataTable,不明觉厉。 

 

posted on 2013-06-16 20:13  HackerVirus  阅读(713)  评论(0编辑  收藏  举报