[转载]SQL Server2012 T-SQL对分页的增强尝试

mikel阅读(976)

[转载]SQL Server2012 T-SQL对分页的增强尝试 – CareySon – 博客园.

简介

SQL Server 2012中在Order By子句之后新增了OFFSET和FETCH子句来限制输出的行数从而达到了分页效果。相比较SQL Server 2005/2008的ROW_Number函数而言,使用OFFSET和FETCH不仅仅是从语法角度更加简单,并且拥有了更优的性能(看到很多人下过这 个结论,但我测试有所偏差,暂且保留意见)。

MSDN上对于OFFSET和FETCH的详细描述可以在(http://msdn.microsoft.com/en-us/library/ms188385%28v=SQL.110%29.aspx)找到。

 

OFFSET和FETCH

这两个关键字在MSDN原型使用方式如代码1所示。

ORDER BY order_by_expression
[ COLLATE collation_name ]
[ ASC | DESC ]
[ ,...n ]
[ ]

::=
{
OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS }
[
FETCH { FIRST | NEXT } {integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY
]
}

代码1.OFFSET和FETCH在MSDN的原型

 

可以看到,OFFSET使用起来很简单,首先在OFFSET之后指定从哪条记录开始取。其中,取值的数可以是常量也可以是变量或者表达式。而Row和ROWS在这里是一个意思。

然后通过FETCH关键字指定取多少条记录。其中,FIRST和NEXT是同义词,和前面的ROW和ROWS一样,它们可以互相替换。同样,这里取的记录条数也可以是常量或者变量表达式。

 

下面通过一个例子来看OFFSET和FETCH的简单用法。首先创建测试数据,这里我就偷懒了,使用我上篇文章的测试数据,创建表后插入100万条测试数据,这个表非常简单,一个自增的id字段和一个int类型的data字段,创建表的语句我就不贴了,插入测试数据的代码如图1所示。

4

图1.插入测试数据

 

下面,我要取第500000到500100的数据,如图2所示。

1

图2.取50万到500100之间的数据

 

可以看到,使用OFFSET和FETCH关键字使分页变得如此简单。

 

OFFSET…FETCH分页对性能的提升

OFFSET和FETCH语句不仅仅是语法糖,还能带来分页效率上的提升。下面我们通过一个例子进行比较SQL Server 2012和SQL Server 2005/2008不同分页方式的分页效率。我们同样取50万到500100之间的数据,性能对比如图3所示。

2

图3.SQL Server 2012分页和SQL Server 05/08之间分页效率对比

 

但是,查询计划中我看到SQL Server2012中FETCH..NEXT却十分损耗性能。这和前面的测试结果严重不符,如图4所示。

3

图4.两种方式的执行计划

通过对比扫描聚集索引这步,我发现对于估计执行行数存在严重偏差,如图5所示。

45

图5.存在偏差的执行计划

 

上图中,第一张图片是使用OFFSET…FETCH进行分页的。估计行数居然占到了500100,严重不符。这令我十分费解,暂时还没有找出原因,求各路大神指导….

总结

SQL Server 2012带来的分页效果十分强大,使得大大简化在SQL Server下的分页。对于性能的影响,由于出现了上述执行计划的偏差,暂且不下结论。待日后研究有了进展再来补上。

[转载]如何利用C#代码获取SQLite数据库的元数据

mikel阅读(1166)

[转载]如何利用C#代码获取SQLite数据库的元数据 – 伍华聪 – 博客园.

SQLite数据库,在很多场合已经用得比较多,由于我的代码生成工具的需要,需要把SQLite的表、字段、视图等信息获取出来,以便实现各种数据库快速生成项目工程的操作。这里就需要利用C#获取SQLite数据库的元数据了,和其他数据库一样。

为了获取Sqlite的数据库对象数据,我做了一个测试的例子来获取他的相关信息,其实它的元数据还是和Access的操作方式很接近。首先我们先通过Sqlite的数据库管理工具或者Visual Studio来打开创建一些表,如下所示。

首先我们先来看看通过C#代码获取到数据库对象的操作界面,如下所示。

获取表的元数据界面效果如下所示,视图和这个也查不多,很有意思的一点,就是它把创建的脚本的显示出来了,呵呵。

获取的表字段信息效果如下所示。

有了这些数据,我就很方便在我的代码生成工具Database2Sharp里面实现代码生成操作了。

现在我们来看看以上实现的后台代码是如何的,来了解Sqlite的数据库获取元数据的操作。

string connectionString = "";

public Form1()
{
InitializeComponent();

connectionString = string.Format(@"Data Source={0}\OrderWater.db;Version=3;", Application.StartupPath);
}

private void btnGetSchema_Click(object sender, EventArgs e)
{
using (SQLiteConnection conn = new SQLiteConnection(connectionString))
{
conn.Open();
DataTable schemaTable = conn.GetSchema("TABLES");
this.dataGridView1.DataSource = schemaTable;
}
}

获取表字段的操作代码如下所示。

private void btnGetColumns_Click(object sender, EventArgs e)
{
using (SQLiteConnection conn = new SQLiteConnection(connectionString))
{
conn.Open();
DataTable table = conn.GetSchema("TABLES");
if (table != null && table.Rows.Count > 0)
{
string tableName = table.Rows[0]["TABLE_NAME"].ToString();

DataTable schemaTable = GetReaderSchema(tableName, conn);
this.dataGridView1.DataSource = schemaTable;
}
}
}

private DataTable GetReaderSchema(string tableName, SQLiteConnection connection)
{
DataTable schemaTable = null;
IDbCommand cmd = new SQLiteCommand();
cmd.CommandText = string.Format("select * from [{0}]", tableName);
cmd.Connection = connection;

using (IDataReader reader = cmd.ExecuteReader(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly))
{
schemaTable = reader.GetSchemaTable();
}
return schemaTable;
}

为了实现和我代码生成工具中的数据库字段信息绑定,需要通过获取设置Sqlite的属性为对应的ColumnInfo对象,如下所示。

using (SQLiteConnection conn = new SQLiteConnection(ConnectString))
{
conn.Open();
DataTable schemaTable = GetReaderSchema(tableName, conn);

foreach (DataRow dr in schemaTable.Rows)
{
ColumnInfo info = new ColumnInfo();
info.Name = new NameElement(dr["ColumnName"].ToString());
info.Ordinal = Convert.ToInt32(dr["ColumnOrdinal"].ToString());
info.AllowDBNull = (bool)dr["AllowDBNull"];
info.MaxLength = Convert.ToInt32(dr["ColumnSize"].ToString());
info.DataTypeId = Convert.ToInt32(dr["ProviderType"].ToString());
info.DataType = dr["DataTypeName"].ToString().Trim();
info.AutoIncrement = (bool)dr["IsAutoIncrement"];
info.IsPrimaryKey = (bool)dr["IsKey"];
info.Unique = (bool)dr["IsUnique"];
info.IsReadOnly = (bool)dr["IsReadOnly"];
string netType = dr["DataType"].ToString();

list.Add(info.Name.Name.ToString(), info);
}

conn.Close();
}

代码生成工具中,这些数据库的元数据实体类信息是可以提供访问的,方便我们定制代码生成工具的模板,代码生成工具关于这些数据库对象的帮助如下所示。

 

这样,在代码生成工具中,就可以利用Sqlite的数据库对象数据,来生成和Oracle数据库、SQLServer数据库、Access数据库、MySql等数据库一样的项目工程代码了,获取甚至可以在自定义的模板代码模块中,添加自己的处理逻辑。

快速生成的代码如下所示。

本文通过介绍获取Sqlite的数据库元数据库,并在代码生成工具中的应用为例,给大家提供一个使用元数据进行程序开发的一个思路,希望大家可以同这种方式实现更多自定义模板或者逻辑的引用。

天下虽大同,一直在努力,孜孜不倦,终有所获。

[转载]MVC、MVP以及Model2[下篇]

mikel阅读(1205)

[转载]MVC、MVP以及Model2[下篇] – Artech – 博客园.

 5: }

正确的接口和实现该接口的View(一个Web页面)应该采用如下的定义方式。Presenter通过属性Departments和 Employees进行赋值进而实现对DropDownList和GridView进行绑定,通过属性SelectedDepartment得到用户选择 的筛选部门。为了尽可能让接口只暴露必须的信息,我们特意将对属性的读写作了控制。

 1: public interface IEmployeeSearchView
 2: {
 3:     IEnumerable<string> Departments { set; }
 4:     string SelectedDepartment { get; }
 5:     IEnumerable<Employee> Employees { set; }
 6: }
 7:
 8: public partial class EmployeeSearchView: Page, IEmployeeSearchView
 9: {
 10:     //其他成员
 11:     public IEnumerable<string> Departments
 12:     {
 13:         set
 14:         {
 15:             this.DropDownListDepartments.DataSource = value;
 16:             this.DropDownListDepartments.DataBind();
 17:         }
 18:     }
 19:     public string SelectedDepartment
 20:     {
 21:         get { return this.DropDownListDepartments.SelectedValue;}
 22:     }
 23:     public IEnumerable<Employee> Employees
 24:     {
 25:         set
 26:         {
 27:             this.GridViewEmployees.DataSource = value;
 28:             this.GridViewEmployees.DataBind();
 29:         }
 30:     }
 31: }

虽然从可测试性的角度来说PV模式是一种不错的选择,因为所有的UI处理逻辑全部定义在Presenter上,意味着所有的UI处理逻辑都可以被测 试。但是我们需要将View可供操作的UI元素定义在对应的接口中,对于一些复杂的富客户端(Rich Client)View来说,接口成员将会变得很多,这无疑会提升编程所需的代码量。从另一方讲,由于Presenter需要在控件级别对View进行细 粒度的控制,这无疑会提供Presenter本身的复杂度,往往会使原本简单的逻辑复杂化,在这种情况下我们往往采用SoC模式。

在SoC(Supervising Controller)模式下,为了降低Presenter的复杂度,我们将诸如数据绑定和格式化这样简单的UI处理逻辑逻辑转移到View中,这些处理 逻辑会体现在View实现的接口中。尽管View从Presenter中接管了部分UI处理逻辑,但是Presenter依然是整个三角关系的驱动 者,View被动的地位依然没有改变。对于用户作用在View上的交互操作,View本身并不进行响应,而是直接将交互请求转发给Presenter,后 者在独立完成相应的处理流程(可能涉及到针对Model的调用)之后会驱动View或者创建新的View作为对用户交互操作的响应。

View和Presenter交互的规则(针对SoC模式)

View和Presenter之间的交互是整个MVP的核心,能够正确地应用MVP模式来架构我们的应用极大地取决于能够正确地处理View和 Presenter两者之间的关系。在由Model、View和Presenter组成的三角关系的核心不是View而是 Presenter,Presenter不是View调用Model的中介,而是最终决定如何响应用户交互行为的决策者。

打个比方,View是Presenter委派到前端的客户代理,而作为客户的自然就是最终的用户。对于以鼠标/键盘操作体现的交互请求应该如何处 理,作为代理的View并没有决策权,所以它会将请求汇报给委托人Presenter。View向Presenter发送用户交互请求应该采用这样的口 吻:“我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”,而不应该是这样:“我现在处理用户交互请求了,我知道该怎么办,但是我需要你 的支持,因为实现业务逻辑的Model只信任你”。

对于Presenter处理用户交互请求的流程,如果中间环节需要涉及到Model,它会直接发起对Model的调用。如果需要View的参与(比如需要将Model最新的状态反应在View上),Presenter会驱动View完成相应的工作。

对于绑定到View上的数据,不应该是View从Presenter上“拉”回来的,应该是Presenter主动“推”给View的。从消息流 (或者消息交换模式)的角度来讲,不论是View向Presenter完成针对用户交互请求的同志,还是Presenter在进行交互请求处理过程中驱动 View完成相应的UI操作,都是单向(One-Way)的。反应在 应用编程接口的定义上就意味着不论是定义在Presenter中被View调用的方法,还是定义在IView接口中被Presenter调用的方法最好都 是没有返回值得。如果不采用方法调用的形式,我们也可以通过事件注册的方式实现View和Presenter的交互,事件机制体现的消息流无疑是单向的。

View本身仅仅实现单纯的、独立的UI处理逻辑,它处理的数据应该是Presenter实时推送给它的,所以View尽可能不维护数据状态。定义 在IView的接口最好只包含方法,而避免属性的定义,Presenter所需的关于View的状态应该在接收到View发送的用户交互请求的时候一次得 到,而不需要通过View的属性去获取。

实例演示:SoC模式的应用

为了让读者对MVP模式,尤其是该模式下的View和Presenter之间的交互方式具有一个深刻的认识,我们现在来进行一个简单的实例演示。本 实例采用上面提及的关于员工查询的场景,并且采用ASP.NET Web Form来建立这个简单的应用,最终呈现出来的效果如上图所示。前面我们已经演示了采用PV模式下的IView应该如何定义,现在我们来看看SoC模式下 的IView有何不同。先来看看表示员工信息的数据类型如何定义,我们通过具有如下定义的数据类型Employee来表示一个员工。简单起见,我们仅仅定 义了表示员工基本信息(ID、姓名、性别、出生日期和部门)的5个属性。

 1: public class Employee
 2: {
 3:     public string     Id { get; private set; }
 4:     public string     Name { get; private set; }
 5:     public string     Gender { get; private set; }
 6:     public DateTime   BirthDate { get; private set; }
 7:     public string     Department { get; private set; }
 8:
 9: public Employee(string id, string name, string gender, DateTime birthDate, string department)
 10:     {
 11:         this.Id         = id;
 12:         this.Name       = name;
 13:         this.Gender     = gender;
 14:         this.BirthDate  = birthDate;
 15:         this.Department = department;
 16:     }
 17: }

作为包含应用状态和状态操作行为的Model通过如下一个简单的EmployeeRepository类型还体现。如代码所示,表示所有员工列表的 数据通过一个静态字段来维护,而GetEmployees返回指定部门的员工列表。如果没有指定筛选部门或者指定的部门字符为空,则直接返回所有的员工列 表。

 1: public class EmployeeRepository
 2: {
 3:     private static IList<Employee> employees;
 4:     static EmployeeRepository()
 5:     {
 6:         employees = new List<Employee>();
 7:         employees.Add(new Employee("001", "张三", "男", new DateTime(1981, 8, 24), "销售部"));
 8:         employees.Add(new Employee("002", "李四", "女", new DateTime(1982, 7, 10), "人事部"));
 9:         employees.Add(new Employee("003", "王五", "男", new DateTime(1981, 9, 21), "人事部"));
 10:     }
 11:     public IEnumerable<Employee> GetEmployees(string department = "")
 12:     {
 13:         if (string.IsNullOrEmpty(department))
 14:         {
 15:             return employees;
 16:         }
 17:         return employees.Where(e => e.Department == department).ToArray();
 18:     }
 19: }

接下来我们来看作为View接口的IEmployeeSearchView的定义。如下面的代码片断所示,该接口定义了BindEmployees 和BindDepartments两个方法,分别用于绑定基于部门列表的DropDownList和基于员工列表的DataView。除此之 外,IEmployeeSearchView接口还定义了一个事件DepartmentSelected,该事件会在用户选择了筛选部门后点击“查询”按 钮时触发。DepartmentSelected事件参数类型为自定义的DepartmentSelectedEventArgs,属性 Department表示用户选择部门。

 1: public interface IEmployeeSearchView
 2: {
 3:     void BindEmployees(IEnumerable<Employee> employees);
 4:     void BindDepartments(IEnumerable<string> departments);
 5:     event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
 6: }
 7:
 8: public class DepartmentSelectedEventArgs : EventArgs
 9: {
 10:     public string Department { get; private set; }
 11:     public DepartmentSelectedEventArgs(string department)
 12:     {
 13:         Guard.ArgumentNotNullOrEmpty(department, "department");
 14:         this.Department = department;
 15:     }
 16: }

作为MVP三角关系核心的Presenter通过具有如下定义的EmployeeSearchPresenter表示。如下面的代码片断所示,表示 View的只读属性类型为IEmployeeSearchView接口,而另一个只读属性Repository则表示作为Model的 EmployeeRepository对象,两个属性均在构造函数中初始化。

 1: public class EmployeeSearchPresenter
 2: {
 3:     public IEmployeeSearchView View { get; private set; }
 4:     public EmployeeRepository Repository { get; private set; }
 5:
 6:     public EmployeeSearchPresenter(IEmployeeSearchView view)
 7:     {
 8:         this.View = view;
 9:         this.Repository = new EmployeeRepository();
 10:         this.View.DepartmentSelected += OnDepartmentSelected;
 11:     }
 12:     public void Initialize()
 13:     {
 14:         IEnumerable<Employee> employees = this.Repository.GetEmployees();
 15:         this.View.BindEmployees(employees);
 16:         string[] departments = new string[] { "销售部", "采购部", "人事部", "IT部" };
 17:         this.View.BindDepartments(departments);
 18:     }
 19:     protected void OnDepartmentSelected(object sender, DepartmentSelectedEventArgs args)
 20:     {
 21:         string department = args.Department;
 22:         var employees = this.Repository.GetEmployees(department);
 23:         this.View.BindEmployees(employees);
 24:     }
 25: }

在构造函数中我们注册了View的DepartmentSelected事件,作为事件处理器的OnDepartmentSelected方法通过 调用Repository(即Model)实现了针对所选部门的筛选,而返回的员工列表通过调用View的BindEmployees方法实现了在 View上的数据绑定。在Initialize方法中,我们通过调用Repository获取了表示所有员工的列表,并通过View的 BindEmployees方法显示在界面上;通过调用View的BindDepartments方法将作为筛选条件的部门列表绑定在View上。

最后我们来看看作为View的Web页面如何定义,如下所示的是作为页面主体部分的HTML,核心部分之包括一个用于绑定筛选部门列表的DropDownList和一个绑定员工列表的GridView。

 1: <html xmlns="http://www.w3.org/1999/xhtml">
 2:     <head runat="server">
 3:         ...
 4:     </head>
 5:     <body>
 6:         <form id="form1" runat="server">
 7:             <div id="page">
 8:                 <div class="top">
 9:                     选择查询部门:
 10:                     <asp:DropDownList ID="DropDownListDepartments" runat="server" />
 11:                     <asp:Button ID="ButtonSearch" runat="server" Text="查询" OnClick="ButtonSearch_Click" />
 12:                 </div>
 13:                 <asp:GridView ID="GridViewEmployees" runat="server" AutoGenerateColumns="false" Width="100%">
 14:                     <Columns>
 15:                         <asp:BoundField DataField="Name" HeaderText="姓名" />
 16:                         <asp:BoundField DataField="Gender" HeaderText="性别" />
 17:                         <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
 18:                         <asp:BoundField DataField="Department" HeaderText="部门"/>
 19:                     </Columns>
 20:                 </asp:GridView>
 21:             </div>
 22:         </form>
 23:     </body>
 24: </html>

如下所示的是该Web页面的后台代码的定义。它实现了定义在IEmployeeSearchView接口的两个方法(BindEmployees和 BindDepartments)和一个事件(DepartmentSelected)。表示Presenter的同名属性在构造函数中被初始化。在页面 加载的时候(Page_Load方法)Presenter的Initialize方法被调用,而在“查询”按钮被点击的时候 (ButtonSearch_Click)事件DepartmentSelected被触发。

 1: public partial class Default : Page, IEmployeeSearchView
 2: {
 3:     public EmployeeSearchPresenter Presenter { get; private set; }
 4:     public event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
 5:     public Default()
 6:     {
 7:         this.Presenter = new EmployeeSearchPresenter(this);
 8:     }
 9:     protected void Page_Load(object sender, EventArgs e)
 10:     {
 11:         if (!this.IsPostBack)
 12:         {
 13:             this.Presenter.Initialize();
 14:         }
 15:     }
 16:     public void BindEmployees(IEnumerable<Employee> employees)
 17:     {
 18:         this.GridViewEmployees.DataSource = employees;
 19:         this.GridViewEmployees.DataBind();
 20:     }
 21:     public void BindDepartments(IEnumerable<string> departments)
 22:     {
 23:         this.DropDownListDepartments.DataSource = departments;
 24:         this.DropDownListDepartments.DataBind();
 25:     }
 26:     protected void ButtonSearch_Click(object sender, EventArgs e)
 27:     {
 28:         string department = this.DropDownListDepartments.SelectedValue;
 29:         DepartmentSelectedEventArgs eventArgs = new DepartmentSelectedEventArgs(department);
 30:         if (null != DepartmentSelected)
 31:         {
 32:             DepartmentSelected(this, eventArgs);
 33:         }
 34:     }
 35: }

 

二、Model2

Trygve M. H. Reenskau当初提出的MVC是作为基于GUI的桌面应用的架构模式,并不太适合Web本身的特性。虽然MVC/MVP也可以直接用于ASP.NET Web Form应用,但这是因为微软基于桌面应用的编程模式 来设计基于Web Form的ASP.NET应用框架的。Web应用不同于GUI桌面应用在于用户是通过浏览器与应用进行交互,交互请求和相应是通过HTTP请求和回复来完 成的。

为了让MVC能够Web应用提供原生的支持,另一个被称为Model2 的MVC变体被提出来,Model2来源于基于Java的Web应用架构模式。Java Web应用具有两种基本的架构模式,分别被称为Model1和Model2。Model1类似于我们前面提及的自治试图模式,它将数据的可视化呈现和用户 交互操作的处理逻辑合并在一起。Model1使用于那些比较简单的Web应用,对于相对复杂的应用应该采用Model 2。

为了让开发者采用相应的编程模式进行GUI桌面应用和Web应用的开发,微软通过ViewState和Postback对背后的HTTP请求和回复 机制进行了封装,使我们能够像编写Windows Forms应用一样采用事件驱动的方式进行ASP.NET Web Forms应用的编程。而Model 2采用完全不同的设计,它让开发者直接面向Web,让他们关注HTTP的请求和回复流程,所以Model 2提供对Web应用原生的支持。

对于Web应用来说,和用户直接交互的UI界面由浏览器来提供,接下来我们详细讨论作为MVC的三要素是如何相互协作对从浏览器发出的用户交互请求的响应的,下图所示的序列图体现了整个流程的全过程。

image

Model 2种一个HTTP请求的目标是Controller中的某个Action,后者体现为定义在Controller类型中的某个方法,所以对请求的处理最终 体现在对Controller对象的激活和对Action方法的执行。一般来说,Controller、Action以及作为Action方法的部分参数 (针对HTTP-GET)可以直接通过请求的URL解析出来。

如上图所示,我们通过一个拦截器(Interceptor)对抵达Web服务器的HTTP请求进行拦截。一般的Web应用框架都提供了针对这样一种 拦截机制,对于ASP.NET来说,我们可以以HttpModule的形式来定义这么一个拦截器。拦截器根据请求解析出目标Controller和对应的 Action,Controller被激活之后Action方法被执行。对于需要传入Action方法的输入参数,则来源于请求地址或/和Post的数 据。

在Controller的Action方法被执行过程中,它可以调用Model获取或者改变其状态。在Action方法执行的最后阶段会选择相应的 View,绑定在View上的数据来源Model或者基于显示要求进行得简单逻辑计算,我们有时候它们成为VM(View Model),即基于View的Model(MVC中的Model是与UI无关的)。生成的View最终写入HTTP回复并最终呈现在用户的浏览器中。

和MVP一样,Model 2完全隔断了View和Model之间的联系。Controller作为支配者地位在Model 2体现尤为明显,用户交互请求不再由View报告给Controller(Presenter),而是由拦截器直接转发给Controller。 Controller不仅仅决定着Model的调用,还决定了View的选择和生成。ASP.NET MVC就是基于Model 2模式设计的。

参考资料:
1、Dino Esposito,Andrea Saltarello《Micorsoft .NET Architecting Applications for Enterprise》
2、Adam Freeman, Steven Sanderson《Pro ASP.NET MVC 3 Framework》
3、Martin Fowler 《GUI Architectures》: http://martinfowler.com/eaaDev/uiArchs.html
4、Martin Fowler 《Passive View》:http://martinfowler.com/eaaDev/PassiveScreen.html
5、Martin Fowler 《Supervising Controller》:http://martinfowler.com/eaaDev/SupervisingPresenter.html
6、Mike Potel,VP & CTO,Taligent, Inc. 《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》
7、Model View Controller- Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
8、Model View Presenter- Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Model_View_Presenter
9、Steve Burbeck, Ph.D. 《Applications Programming in Smalltalk-80(TM):How to use Model-View-Controller (MVC)》:http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html

MVC、MVP以及Model2[上篇]
MVC、MVP以及Model2[下篇]

[转载]MVC、MVP以及Model2[上篇]

mikel阅读(933)

[转载]MVC、MVP以及Model2[上篇] – Artech – 博客园.

对于大部分面向最终用户的应用来说,它们都需要具有一个可视化的UI与用户进行交互,我 们将这个UI称为视图(View)。在早期,我们倾向于将所有与视图相关的逻辑糅合在一起,这些逻辑包括数据的呈现、用户操作的捕捉与相应以及和针对数据 存储(比如数据库)的操作。我们将这种设计模式称为自治视图(AV,Autonomous View)。

目录
一、自治视图
二、MVC模式
三、多层架构中的MVC

一、自治视图

说 到自治视图,可能很多人会感到模式,但是我想很多人(尤其是.NET开发人员)可能经常在采用这种模式来设计我们的应用。Windows Forms和ASP.NET Web Forms虽然分别属于GUI和Web开发框架,但是它们都采用了事件驱动的开发方式。所有与UI相关的逻辑都可以定义在针对视图(Windows Form或者Web Form)的后台代码(Code Behind)中,并最终注册到视图本身或者视图元素(控件)的相应事件上。

一个典型的人机交互应用具有三个主要的关注点,即数据在可视化界面上的呈现、UI处理逻辑(用于处理用户交互式操作的逻辑)和业务逻辑。对于自治视图模式来说,它实际上这三种混合在一起,势必会带来如下一些问题:

首先,业务逻辑是与UI无关的,应该最大限度地被重用。由于业务逻辑定义在自治视图中,相当于完全与视图本身绑定在一起。如果我们能够将UI的行为抽象出来,基于抽象化UI的处理逻辑也是可以被共享的,定义在自治视图的UI处理逻辑完全丧失了重用的可能。

其次,业务逻辑具有最强的稳定性,UI处理逻辑次之,而可视化界面上的呈现最差,比如我们经常会为了更好的呈现效果来调整HTML。将具有不同稳定性的元素融为一体,具有最差稳定性的元素决定了整体的稳定性,这是“短板理论”在软件设计中的体现。

再次,任何涉及到UI的组件都不易测试。UI是呈现给人看的,并且用于与人进行交互,用机器来模拟活生生的人来对组件实施自动化测试不是一件容易的事,自治视图严重损害了组件的可测试性。

为 了解决自治视图导致的这些问题,我们需要采用采用关注点分离(SoC, Seperation of Concerns)的方针将可视化界面呈现、UI处理逻辑和业务逻辑三者分离出来,并且采用采用合理的交互方式将它们之间的影响降到最低。由于将三者“分 而治之”,自然也使UI逻辑和业务逻辑编程的容易被测试的组件,使测试驱动设计与开发变成了可能。这里用于进行关注点分离的模式就是MVC。

二、MVC模式

MVC 的创建者是Trygve M. H. Reenskau,他是挪威的计算机专家,同时也是奥斯陆大学的名誉教授。MVC是他在1979年访问施乐帕克研究中心(Xerox PARC,Xerox Palo Alto Research Center)期间是提出一种主要针对GUI应用的软件架构模式。MVC最初用于SmallTalk,Trygve最初对MVC的描述记录在 《Applications Programming in Smalltalk-80(TM):
How to use Model-View-Controller (MVC)》这篇论文中,有兴趣的读者可以通过地址http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html阅读这篇论文。MVC体现了关注点分离这一基本的设计方针,它将构成一个人机交互应用涉及到的功能分为Model、Controller和View三部分,三者各自具有的基本职责或者功能范围如下:

  • Model:是对应用状态和业务功能的封装,可以看成是同时包含数据和行为的领域模型(Domain Model)。Model接受Controller的请求执行相应的业务功能,并在状态改变的时候通知View。
  • View:实现可视化界面的呈现,捕捉最终用户的交互操作(比如鼠标和键盘操作)。
  • Controller:View 捕获到用户交互操作后会直接转发给Controller,后者完成相应的UI逻辑。如果需要涉及业务功能的调用,Controller会直接调用 Model。在完成UI处理之后,Controller会根据需要控制原View或者创建新的View对用户交互操作予以响应。

下 图揭示了MVC模式下Model、View和Controller之间的交互。对于传统的MVC模式,很多人认为Controller仅仅是View和 Model之间的中介,实则不然,View和Model存在直接的联系。View可以直接调用Model查询其状态信息。当Model状态发生改变的时 候,它也可以直接通知View。比如在一个提供股票实时价位的应用,维护股价信息的Model在股价变化的情况下可以直接通知相关的View改变其显示信 息。

image

从 消息交换模式的角度来讲,Model针对View的状态通知和View针对Controller的用户交互通知都是单向的,我们推荐采用事件机制来实现这 两种类型的通知。从设计模式的角度来讲就是采用观察者(Observer)模式通过注册/订阅的方式来实现它们,即View作为Model的观察者通过注 册相应的事件来检测状态的改变,而Controller作为View的观察者通过注册相应的事件来处理用户的交互操作。

三、多层架构中的MVC

我看到很多人将MVC和所谓的“三层架构”进行比较,其实两者并没有什么可比性,MVC更不是分别对应着UI、业务逻辑和数据存取三个层次。不过两者也不能说完全没有关系,我们现在就来讨论这个问题。

Trygve M. H. Reenskau当时提出MVC的时候实际上将其作为构建整个GUI应用的架构模式,而Model维护着整个应用的状态并实现了所有的业务逻辑,它更多地 体现为一个领域模型。而对于多层架构来说(比如我们经常提及的三层架构),MVC是被当成是UI呈现层(Presentation Layer)的设计模式,而Model则更多地体现为访问业务层的入口(Gateway)。如果采用面向服务的设计,将业务功能定义成相应服务并通过接口 (契约)的形式暴露出来,这里的Model甚至还可以表示成进行服务调用的代理。

MVC、MVP以及Model2[上篇]
MVC、MVP以及Model2[下篇]

[转载]SQLite学习手册(目录)

mikel阅读(999)

[转载]SQLite学习手册(目录) – Stephen_Liu – 博客园.

      在实际的应用中,SQLite作为目前最为流行的开源嵌入式关系型数据库,在系统的架构设计中正在扮演着越来越为重要的角色。和很多其它嵌入式NoSQL 数据库不同的是,SQLite支持很多关系型数据库的基本特征,这在数据移植、程序演示等应用中有着不可替代的优势。从官方文档中我们可以获悉 到,SQLite支持的数据量和运行效率都是非常骄人的,因此在海量数据的解决方案中,SQLite可以作为数据预计算的桥头堡,从而显著减少存储在关系 型数据库服务器中的数据数量,最终提高系统的查询效率和运行期效率,同时也可以显著的降低数据备份的磁盘开销。这里提供了该系列博文的目录,以方便网友和 我个人的学习与参阅。
Finally, if you are interseting in my series blogs, please pay more attention on my following ones, such as Redis, MongoDB, Lua and PostgreSQL. Thank you for your reading and comments, that will give me more effective encouragement and stimulate me to move ahead with stable and continuous.

 

SQLite学习手册(开篇)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/09/2317603.html
一、简介
二、SQLite的主要优点
三、和RDBMS相比SQLite的一些劣势
四、个性化特征

SQLite学习手册(C/C++接口简介)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/13/2321386.html
一、概述
二、核心对象和接口
三、参数绑定

SQLite学习手册(数据表和视图)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/13/2321668.html
一、创建数据表
二、表的修改
三、表的删除
四、创建视图
五、删除视图

SQLite学习手册(内置函数)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/13/2322027.html
一、聚合函数
二、核心函数
三、日期和时间函数

SQLite学习手册(索引和数据分析/清理)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/14/2322335.html
一、创建索引
二、删除索引
三、重建索引
四、数据分析
五、数据清理

SQLite学习手册(数据库和事物)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/14/2322575.html
一、Attach数据库
二、Detach数据库
三、事物

SQLite学习手册(表达式)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/16/2323907.html
一、常用表达式
二、条件表达式
三、转换表达式

SQLite学习手册(数据类型)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/18/2325258.html
一、存储种类和数据类型
二、类型亲缘性
三、比较表达式
四、操作符

SQLite学习手册(命令行工具)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/18/2325981.html

SQLite学习手册(在线备份)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/19/2326309.html
一、常用备份
二、在线备份APIs简介
三、高级应用技巧

SQLite学习手册(内存数据库)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/20/2328348.html
一、内存数据库
二、临时数据库

SQLite学习手册(临时文件)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/21/2328483.html
一、简介
二、具体说明
三、相关的编译时参数和指令
四、其它优化策略

SQLite学习手册(锁和并发控制)
http://www.cnblogs.com/stephen-liu74/archive/2012/01/22/2328753.html
一、概述
二、文件锁
三、回滚日志
四、数据写入
五、SQL级别的事物控制

SQLite学习手册(实例代码<一>)
http://www.cnblogs.com/stephen-liu74/archive/2012/02/07/2340780.html
一、获取表的Schema信息
二、常规数据插入

SQLite学习手册(实例代码<二>)
http://www.cnblogs.com/stephen-liu74/archive/2012/02/07/2341480.html
三、高效的批量数据插入
四、数据查询

如果您觉得这个系列的博客可以让您有所收获,请保持持续的关注。
如果您发现博客中有明显的纰漏,欢迎指正。
如果您有意进行技术上的交流,可以通过邮件共同探讨(stephenland74@hotmail.com)。
如果您有更好的建议或更好的实现方式,敬请赐教。
如果您已经是我的关注者,希望随后发布的Redis系列不会让您失望。
I am Stephen Liu.

[转载]2011 程序员薪资调查报告全文发布_IT新闻

mikel阅读(914)

[转载]2011 程序员薪资调查报告全文发布_IT新闻_博客园.

自 2011 年初,CSDN 在网上发起“2011年软件行业技术人员薪资大调查”(网址:http://vip.csdn.net/2011/xinzi/main.html) 以来,引起了广大开发者们的热烈反响,短短两月时间内,近万名开发者提交了调查数据。尽管这只是中国百万开发者大军的一小部分,但他们所在的行业几乎涵盖 了整个中国软件的产业链,他们的职位几乎代表了一个软件团队体系的每一个层面,而且“一叶知秋”,所以透过这些调查数据和变化,我们或许可以一瞰中国软件 开发者的普遍生存状态,甚至可帮助开发者,更准确地定位自己在产业内的坐标。

2011:程序员的日子不算差

相信每个开发者在回忆当初高校毕业,加入滚滚求职大军的情形时,都能记得那番对美好生活的憧憬和闯荡世界的豪情。而在经济社会,判断成功的可量 化方式无疑是薪资了,尽管有点世俗,但暂时也找不到更好的标准。那么现在,中国的程序员们,总体收入水平如何呢?首先我们看程序员们对薪资水平的满意度。图一

我们发现绝大多数程序员(近 73%)对自己的薪资并不满意,这种普遍不满意的情绪有多少是主观预期过高,多少是客观生存环境造成的呢?我们需要做进一步的考察。

我们按月薪大小把收入水平划分为四类:低收入(小于 2000 元)、中等收入(2000~5000元)、中高收入(5000~10000元)、高收入 (大于 10000 元)。从调查数据看,来自中国 17 座重要 IT 城市的开发者们,占据绝大多数的是月薪 2000~5000元,它在 13 个城市占据最高的比例,其中排前 3 名的是沈阳(67.5%)、济南(65.8%)、珠海(62.5%)。而北京、上海、深圳的开发者收入水平相对高些,这三座城市占据绝大多数的是月薪 5000~10000元的群体。图二

如果仅依据国家统计局公布的数据显示,2010全年城镇居民家庭人均总收入 21033 元(月平均 1753 元),其中北京城镇居民人均可支配收入 2.9 万元(月平均 2417 元)。这两年国家经济相对比较稳定,估计 2011 年的情况也差不多。所以总的来说,2011年的中国程序员群体,在所处的城市里,和其他居民比,算相对收入不错的了。

当然也有生存环境堪忧的, 我们发现月薪少于 2 0 0 0 元的群体, 主要分布在济南(15.8%) 、西安(13.8%) 、青岛(12.7%)、武汉(12.6%)。

而如果以月薪 10000 元(及以上)算高薪,排名前列的则是上海(26.9%)、北京(20.6%)、深圳(14.7%)、杭州(11.3%),杭州程序员群体的崛起令人关注, 说明杭州近年来的信息化建设成就卓著。再回过头来看程序员的薪资满意度,我们通过交叉分析发现,程序员的满意度确实和薪资大小相关,收入越高,不满意的比 例越小。但值得注意的是,不管哪个收入群体,都超过 50% 以上都表达了对当前薪资的不满,说明尽管日子过得不算差,但中国程序员们的幸福感普遍不高。图三

最佳跳槽次数,最好不超过 3 次

跳槽,一直是程序员们在职场生涯里所面临的热点话题。它是一把双刃剑,一方面会带给你更多的视野和经历;另一方面,会降低你的企业忠诚度和所在企业平台的积累。所以很多开发者往往会面临是否跳槽的煎熬和苦恼。那么本次调查的数据显示,资薪和跳槽此数存在潜在的规律吗?表一

从上面的“薪资/跳槽次数交叉分析表”,我们发现在四个收入群体中,“少于 2000 元”和“2000~5000元”群体中的绝大多数人都未换过工作,而从收入高于 5000 元的群体开始,有跳槽经历的人数显著加大。从工资高于 10000 元的高收入群体看,我们发现有 3 次跳槽经历的人占据最多的比重, 达到 24.6%,但从第 4 次开始又急剧下跌到 10.3%。所以从这样的数据结果可以看出,凡是有一定收入水准的开发者,基本上是有跳槽经历的,但跳槽的次数越多,并不绝对保证薪资高。数据显示跳槽次 数存在一个“天花板”——3次。看来适度的跳槽有利于经验和技能的提升,但如果跳槽过于频繁,则不利专业的积累,自然在薪资上提升的空间也不大了。

技术菜鸟到牛人的距离,5年是分水岭

  再来看工龄和薪资的具体量化关系,我们发现工作1~2年的开发者,工资在 2000~5000元之间占据绝大多数,而工龄超过 2 年的,大多数人的收入达到 5000 元以上。  同时我们发现薪资在 5000~10000元群体在 10 年以内都基本处于一个稳定状态,没有明显增幅。而 10000 元以上的高收入群体,一个非常显著的变化是,前 5 年的人数增幅明显加快,但之后几年一直均处于稳定状态。

图四表二

  所以,“3年(月薪 5000 元)”、“5年(月薪 10000 元)”是两个关键的分水岭。凡是月薪 5000 元以内的,随着工作年数的增加,人数递减; 但随着年数达到 3 年后,月薪 5000 元以上的群体,人数开始显著递增。这不难理解,因为工龄的增加,开发者的工作熟练程度也越高,所以自然薪水也就高了。 而工龄超过“5年”达到月薪 10000 元以上后的高收入群体,随后也基本开始保持稳定了。这说明,岁月对于技术开发者的薪资,同样存在一个瓶颈,并不是无限制正比例上升的。由此我们似乎可以推 断,在中国软件行业,一个程序员菜鸟发展到业界认可的“熟练工”大概是“3年”,而“技术牛人”所需要的成长时间, 大概是“5年”。

什么工作最赚钱?——不上班

“男怕入错行,女怕嫁错郎”。随着信息化在全社会范围内的渗透,所从事的细分行业的信息化发展水平和市场前景,已经成为决定开发者收入水平重要 因素。那么作为开发者,选择什么样的工作,选择哪个行业的软件公司最有发展前景呢?调查结果令人诧异——自由职业者(SOHO)收入水准最高,超过 30% 的 SOHO 月收入超过 10000 元,月收入 5000 元以上的比例更是超过 84%。但细想也在情理之中,有勇气做自由职业的开发者,往往具备超高的技术水准和丰富的行业积累。图五

再看具体的细分行业,高收入开发者比例最高的领域是欧美外包(21.4%),看来中国软件本质上离“中国创造”的目标,还有很长一段距离。其次 是原厂商(17.1%),这里的原厂商指的是诸如微软、甲骨文、IBM 等软件巨头,其员工收入高并不意外。排名第三的是移动和手机应用(16.9%),这现象令人欣慰,毕竟未来就是移动互联网的时代。

从调查数据看,最不合适介入的是教育行业,小于 2000 元的低收入者比例接近 15%,月收入小于 5000 元的接近 65%。教育产业在国家属于公共资源,被严格管理,介入门槛比较高,再加上以“高考”为指挥棒的单一教学导向,不容易衍生丰富多彩的信息化应用。此外,餐 厅零售行业也是开发者需要谨慎选择的,低收入者 10.87%,小于 5000 元的接近 71%。不过餐厅零售业不像教育那样受到政府的严格管理,所以从乐观的角度,说明这个行业的信息需求没有充分挖掘。

开发语言,选谁都一样

工欲善其事,必先利其器。开发语言、平台对于开发者来说,如同披荆斩棘的利剑。尽管对于顶尖高手来说,达到了编程思想、方法论层面的炉火纯青, 可鸟瞰一切平台和工具,但对大多数初涉软件行业的程序员来说,熟悉哪种语言、开发工具往往直接决定了当下的收入水平和生活水准。从调查数据看,绝大多数开 发者都使用 JAVA,达到 45.3% 之高,其次是C#、C++、C、.NET、JavaScript,它们相对比较均衡,基本在 25% 左右(注:很多开发者往往实际会使用一种以上语言)。我们发现,C#、.NET 开发者中,小于 5000 元的比例最高,基本在 55%。但不能因此说C#、.NET 没有前途,因为另一数据发现,所有的语言,在 5000~10000元的群体里,比例竟然惊人趋近,基本都在 30%~40% 之间,这说明不管选择哪个平台,只要达到“熟练工”水准,收入不会差太远。至于一些语言的低收入群体比例偏高,这和它容易学习,适合编程菜鸟上手有关,因 为我们同时通过交叉分析,注意到工龄 2 年内的 C#、.NET 程序员小于月薪 5000 元收入水平的比例竟然高达 80% 左右,而工龄超过 3 年后,这个比例开始明显下降了。图六   再看高收入群体,我们发现在使用 Erlang、Perl、Scala 技术的人中,高收入人群的比例较高,分别为 41.2%、36.7%、 36.4%。但我不建议大家一窝蜂地去学习这些语言,因为同时发现它们的样本量极低,分别是 17、98、11,远小于近万份的总样本量,看来主要是物以稀为贵,会的人少,自然收入就上去了。图七

结束语

在本次调查中,我们还结合英语技能、学历高低对开发者的薪资水平进行了分析,发现结果和常识是一致的,即英语、学历越高,获得高薪资的比重越 大。总的来说,开发者要想过更好的物质生活,重要的是在专业性上下工夫,因为尽管随着城市(考虑消费水平在内)、技术平台、行业等外在条件的不同,低收 入、中等收入群体的比例千差万别,但高收入群体的比例是基本接近的。

[转载]SQL Server 2012正式版发布

mikel阅读(911)

[转载]SQl Server 2012正式版发布 – zeus2 – 博客园.

from cnbeta

软昨日宣布,SQL Server 2012已经完成了RTM版,并且提供给了制造商。继RC0候选版之后,微软也公开了SQL Server 2012 RTM版的下载,不过是评估版本(Evaluation)。微软将在4月1日正式发布SQL Server 2012,当前面向所有消费者和合作伙伴公开了其评估试用版本的下载

现在就可以到微软官网上下载SQL Server 2012 RTM的ISO镜像文件,首批微软提供了简体中文、繁体中文、俄语、德语、意大利语、日语、法语、英语、葡萄牙语、西班牙语、韩语11种语言版本。

简体中文版官方下载页面:
http://www.microsoft.com/downloads/zh-cn/details.aspx?FamilyID=a74d1b60-6566-4551-b581-03337853b82b

SQL Server 2012提供了一个云计算信息平台,该平台可帮助企业对整个组织有突破性的深入了解,并且能够快速在内部和公共云端重部署方案和扩展数据。

主要服务:

— 通过 AlwaysOn 提供所需运行时间和数据保护;

— 通过列存储索引获得突破性和可预测的性能;

— 通过用于组的新用户定义角色和默认架构,帮助实现安全性和遵从性;

— 通过列存储索引实现快速数据恢复,以便更深入地了解组织;

— 通过SSIS改进、用于Excel的 Master Data Services外接程序和新Data Quality Services,确保更加可靠、一致的数据;

— 通过使用 SQL Azure 和 SQL Server 数据工具的数据层应用程序组件 (DAC) 奇偶校验,优化服务器和云间的IT和开发人员工作效率,从而在数据库、BI和云功能间实现统一的开发体验。

系统要求:

— 支持的操作系统:Windows 7、Windows Server 2008 R2、Windows Server 2008 SP2、Windows Vista SP2

— 32位系统:具有Intel 1GHz(或同等性能的兼容处理器)或速度更快的处理器(建议使用2GHz或速度更快的处理器)的计算机

— 64位系统:1.4 GHz或速度更快的处理器

— 最低1GB RAM(建议使用2GB或更大的RAM)

— 2.2 GB可用硬盘空间

[转载]SQL Server数据库帐号密码安全设计

mikel阅读(1006)

[转载]SQL Server数据库帐号密码安全设计 – 听风吹雨 – 博客园.

一、背景

作为一个DBA,或许你有很多的系统需要管理,而且不同的系统使用了不同的数据库,通常的情况下,我们都是通过sa进行设置密码的,而且在config文件里面明文的写上我们的帐号和密码,这样的设计我们是否太大方了呢?

或许你会说,我们的数据库是安全的,因为我们的数据库是只有内网可以访问的,但是那台机器的安全性呢?因为能进入操作系统就能看到我们的帐号密码了。再加上有些时候我们不希望客户能看到数据库密码的。那么应该如何保证我们的数据库账号密码的安全性呢?

 

二、架构设计

针对上面的安全性问题,我设计了下面的方案,主要是运用加密算法进行加密,再通过分离一些职责,设置几道关卡,加强数据的安全性。

下面是这个方案的总体架构图:


(架构图)

 

三、架构解析说明

1.      程序1,程序2表示我们的应用程序,在程序的config中再也没有明文的数据库帐号和密码的了,它调用一个公用的webservice,只需要传入一个唯一的key值(有DBA发布这个key值),获取数据库IP、帐号、密码;

2.      网页接口调用程序是我们提供的一个webservice,它使用只读帐号视图,查询key值所对应加密后的数据库IP、帐号、密码;如下图所示:

[LinkName] 表示key值;

[En_LinkIP]表示程序需要链接的数据库IP地址;

[En_LinkSa]表示数据库使用的帐号;

[En_LinkPassword]表示数据库帐号所对应的密码;


(只读账号视图)

3.      把上图获取到的数据返回给网页接口调用程序,把这些值以|符号进行串联成一个字符串,再进行二次加密(使用不同的加密算法);再把加密后的字符串返回给程序1、程序2;

4.      当程序拿到这些加密后的字符串后,使用我们发布的DLL进行解密,这里的解密包括两个解密算法,把解密后的值返回给程序;

5.      那这些记录是怎么录入的呢?你猜对了,那就是DBA,DBA收集这些帐号和密码进行管理,只要开发人员告知数据库的相关信息(或许根本就不需要,因为部署是DBA的事情,或许开发人员根本就不需要知道这个数据库在哪里),我就使用单机机密程序,加密我们的数据库IP、帐号、密码,并生成唯一的key值,把这个key告诉开发人员。

把加密后的数据插入到数据库;超级管理员视图如下图所示:


(超级管理员视图)

 

四、特别说明

1.      就安全性考虑,LinkIP的值尽量能使用端口进行设置,修改默认的1433端口;

2.      虽然在程序中可以获取到明文的帐号密码,但是这比明文写在config上要安全很多倍了;

3.      对字符串进行二次加密是为了防止数据的拦截的;

4.      如果网页接口调用程序给爆掉了,拿到的数据还是加密后的数据;而且这个帐号是只读数据的,是没有insert、update、delete的权限的;

5.      有这个超级管理员帐号,这个帐号可以只有为数几个人知道,与网页接口调用程序的数据库帐号是完全分离的;

6.      程序中可能需要在使用帐号密码的时候需要捕获一下异常,就是当我们DBA修改了帐号密码后引起的链接异常;

7.      这个帐号表中,我们可以通过State字段进行控制这个帐号是否可用(甚至你可以直接删掉这条记录),这样当我们的业务系统泄漏了数据帐号的时候,我们可以很快速的修改帐号密码,不需要通知开发人员去修改config文件;

8.      只读账号视图现在只是简单的MD5加密,你完全可以设计自己算法;

9.      LinkName是需要唯一的,所以在表设计的时候是使用了这个作为主键的;

10.   其实上面的设计我是为了体现出只读帐号视图和超级管理员视图的区别的;可以参考:SQL Server 2005控制用户权限访问表,其实你完全可以让网页接口调用程序调用数据库的config的帐号密码也进行一次加密,让程序去解密,这样只需要做一次就够了。

 

五、SQL代码

创建表

CREATE TABLE [dbo].[LinkConfigTest](

    [Id] [int] IDENTITY(1,1) NOT NULL,

    [LinkName] [nvarchar](50) NOT NULL,

    [LinkIP] [varchar](50) NULL,

    [LinkSa] [varchar](50) NULL,

    [LinkPassword] [varchar](50) NULL,

    [Description] [nvarchar](100) NULL,

    [State] [int] NULL CONSTRAINT [DF_LinkConfigT_State]  DEFAULT ((0)),

    [StateDescription] [nvarchar](50) NULL,

    [HostName] [nvarchar](50) NULL,

    [CreateTime] [datetime] NULL,

    [En_LinkIP] [varchar](50) NULL,

    [En_LinkSa] [varchar](50) NULL,

    [En_LinkPassword] [varchar](50) NULL,

 CONSTRAINT [PK_LinkConfigT] PRIMARY KEY NONCLUSTERED

(

    [LinkName] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]

插入测试数据

INSERT [LinkConfigTest] ([LinkName],[LinkIP],[LinkSa],[LinkPassword],[Description],[State],[HostName],[CreateTime],[En_LinkIP],[En_LinkSa],[En_LinkPassword]) VALUES ( ‘sd9_DB1’,‘192.168.1.147,14339’,‘sd158admin’,‘sd158admin+-987’,内网_DB1′,1,‘SERVR9’,‘2012-3-7 14:08:52’,‘0xddf4a1c5a5dadc7374a9ca3ed3aa9f6b’,‘0xe319cb7bedd3b0d400c3da3484c6a6e3’,‘0xdc24223e94b4c4d754cfc40230680156’)

INSERT [LinkConfigTest] ([LinkName],[LinkIP],[LinkSa],[LinkPassword],[Description],[State],[HostName],[CreateTime],[En_LinkIP],[En_LinkSa],[En_LinkPassword]) VALUES ( ‘sc13’,‘192.168.1.25,13433’,‘sc13admin’,‘sc13adminasdfg’,内网_DB1′,1,‘BAREOT-4’,‘2012-3-7 14:08:52’,‘0x0f3e6234263b544bbb7861fc3be9d4a9’,‘0xe969c53b6c44990ac38a92f1c6ad2d97’,‘0xac7663644f8de76a3da122257a6593ea’)

INSERT [LinkConfigTest] ([LinkName],[LinkIP],[LinkSa],[LinkPassword],[Description],[State],[StateDescription],[HostName],[CreateTime],[En_LinkIP],[En_LinkSa],[En_LinkPassword]) VALUES ( ‘sd9_DB2’,‘192.168.1.147,14339’,‘gd14admin’,‘sc13admin#$’,内网_DB2′,0,停用,‘SERVR9’,‘2012-3-7 14:08:52’,‘0xddf4a1c5a5dadc7374a9ca3ed3aa9f6b’,‘0x8cfaec93141b6fb3fc5de4cef40c8648’,‘0x99f2fb6b0f14d47fa725a79c62ae60c3’)

INSERT [LinkConfigTest] ([LinkName],[LinkIP],[LinkSa],[LinkPassword],[Description],[State],[HostName],[CreateTime],[En_LinkIP],[En_LinkSa],[En_LinkPassword]) VALUES ( ‘jh16’,‘192.168.1.30,16433’,‘jh16admin’,‘sc13admin$%as’,内网_DB1′,1,‘SERVR16’,‘2012-3-7 14:08:52’,‘0x012f0e163b9f1d9624e56154e0159452’,‘0x13c540aff4fe6d61f48de0e097fd1c66’,‘0xb59032aaffddc07b72d9958359a5da97’)

INSERT [LinkConfigTest] ([LinkName],[LinkIP],[LinkSa],[LinkPassword],[Description],[State],[HostName],[CreateTime],[En_LinkIP],[En_LinkSa],[En_LinkPassword]) VALUES ( ‘hb17’,‘192.168.1.35,17433’,‘hb17admin’,‘sc13admin$%asdf’,内网_DB5′,1,‘BAEF-1’,‘2012-3-7 14:08:52’,‘0x8723f377c50a33f3cb6794978be09895’,‘0x4bce38f66dfba3f8779f070636a6a70d’,‘0x8cab2ef5b6a6c5c48d499c5ef84c3436’)

[转载]从高耦合到低耦合到底有多远?

mikel阅读(971)

[转载]从高耦合到低耦合到底有多远? – 两会 – 博客园.

随笔- 9  文章- 13  评论- 64

从高耦合到低耦合到底有多远?

  无论书还是博客, 耦合这个词已被无数人说烂,任何一位程序员都会告诉你设计软件要注意低耦合,可究竟什么是低耦合?每次去查这个问题,就会牵扯出各种术语和理论,让人头晕。最近看了一些英文资料,发现低耦合其实没那么复杂。

什么是耦合?怎样的代码叫高耦合?

“耦合”翻译自英文(coupling),英文描述是:”when a component has a dependency on something else”. 这句话简单易懂–当一个组件对其他东西有依赖就叫耦合,为方便描述,先给段代码:

public class EmailService {
    public void SendMessage() { }
}

public class NotificationSystem {
    private EmailService svc;

    public NotificationSystem()
    {
        svc = new EmailService();
    }

    public void InterestingEventHappend()
    {
        svc.SendMessage();
    }
}

 

代码的逻辑很简单:NotificationSystem通过内置的EmailService类发送邮件,即NotificationSystem的功能依赖EmailService类。我相信应有不少人对代码感觉亲切,我参与过的项目基本都这种风格,可惜这就是高耦合的设计。

  高耦合翻译自“tightly coupled”,描述是这样的:”A class that knows a lot about the other classes it interacts with is said to be tightly coupled”.翻译过来就是—它知道的太多了。^_^

最快的解耦方式

为了让它知道的不那么多,现在贴一份改良后的代码:

public interface IMessageService {
        void SendMessage();
    }

    public class EmailService : IMessageService {
        public void SendMessage() { }
    }

    public class NotificationSystem {
        private IMessageService svc;

        public NotificationSystem()
        {
            svc = new EmailService();
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

 

  与之前比较,svc变量类型变成了接口IMessageService ,从而使NotificationSystem依赖IMessageService接口,而不是EmailService类。 但svc通过new 方式赋值,这让两个类藕断丝连,一旦EmailService变化,NotificationSystem也跟着变,违背了开闭原则。

通过控制反转彻底解耦

  想彻底解耦,就要换一种方式对svc赋值,于是想到控制反转模式,控制反转翻译自“inversion of control”简称Ioc,一句话描述:“Moving the creation of dependencies outside of the class that consumes those dependencies”,简单翻译过来就是:在外面创建这个类。

现在我们先抽象一个接口用于“外部创建”。

 

public interface IMessageService {
        void SendMessage();
    }

    public class EmailService : IMessageService {
        public void SendMessage() { }
    }

    public interface IServiceLocator {
        IMessageService GetMessageService();
    }

    public class NotificationSystem {
        private IMessageService svc;

        public NotificationSystem(IServiceLocator locator)
        {
            svc = locator.GetMessageService();
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

 

  从代码看出,现在svc是通过IServiceLocator接口创建,从而让原类之间解耦。IServiceLocator的实现类就像工厂模式,通过参数或配置文件等决定生成哪个类。然而这种做法让IServiceLocatorIMessageService 的实现类之间增加了耦合,每添加一个IMessageService 的实现类,就要修改IServiceLocator的代码,可能是switch或连续的if,这样看似不错的模式仍然违反开闭原则:

public class ServiceLocator:IServiceLocator {
        public IMessageService GetMessageService()
        {
            string type = "type1";
            switch (type)
            {
                case "type1":return new EmailService1();
                case "type2": return new EmailService2();
                case "type3": return new EmailService3();
                   …………
            }
        }
    }

 

用Ioc容器完成高耦合到低耦合的蜕变

  完全蜕变,就要求助于依赖注入了,这个词和控制反转是好基友,一般都同时出现。实际开发中,我们往往使用IoC容器来实现依赖注入的需求。通过Ioc容器(Autofac)改善代码如下:

public class NotificationSystem {
        private IMessageService svc;

        public NotificationSystem(IMessageService messageService)
        {
            svc = messageService;
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

  可以看到NotificationSystem的构造函数直接传入IMessageService接口变量做参数。在全局类的代码如下:

var builder = new ContainerBuilder();
   builder.RegisterType<EmailService>().As<IMessageService>();

通过依赖注入来实例化NotificationSystem类:

IMessageService messageService= container.Resolve<IMessageService>();
 NotificationSystem system=new NotificationSystem(messageService);

借助Ioc的帮助,当IMessageService添加新的实现类时,也不用修改其他类的内部代码,在配置代码中,仅修改类名便可实现功能的切换。

结语

  现在很多的开源 代码都是这种模式,类内部依赖接口,通过Ioc容器灵活配置,熟悉了这种模式,有助于理解别人的设计意图,我也从中收益良多。但也有让我不爽的地方就是看 别人的代码时,每次用F12跟踪源码,都会跳转到描述接口的代码文件中,想看具体实现总要害我找半天。

参考资料:<Professional Asp.Net MVC 3>

[转载]easyui datagrid标题列宽度自适应

mikel阅读(1364)

[转载]easyui datagrid标题列宽度自适应.

最近项目中使用easyui做前端界面,相信大部分使用过easyui datagrid的朋友有这么一个疑问:如果在columns中不设置width属性能不能写个方法让datagrid的头部标题和数据主体自适应呢? 如: columns: [[{ field: ‘testName’, title: ‘测试名’, align: ‘center’ },{ field: ‘testValue’, title: ‘测试值’, align: ‘center’ }]],如果按照上面这样设置列而不做其他处理的话。绑定出来的数据将会变成:

          

如上图这样列标题和数据主体对不上号。或许有的朋友会想,直接设个固定值不就完了,但是对于一些不能确定长度的数据设固定值显然不能达到我们的要 求。带着这个问题我百度谷歌了一番 发现网络上并没有我太满意的相关资料。毛主席曾经曰过:自己动手丰衣足食。我只好听从毛主席的教导自己解决问题。By 梨洛

要设置列宽度,我们必须知道easyui datagrid在html中是怎么样的。于是乎动用chrome的开发人员工具,查看一番如图:

头部列标题为:

即我们可以用JQuery选择器 $(“.datagrid-header-inner table tr:first-child”)来取到标题列  (数据主体列也差不多我就不贴出来了)。

既然能取得到这些,只要我们判断数据主体列的宽度大还是 标题列的宽度大。相应的设置回去 那标题和数据不就对其了。来上代码:

<script type="text/javascript" language="javascript">// <![CDATA[
    $(document).ready(function () {
        $("#test").datagrid({
            url: "/Test/Test1Data",
            type: "post",
            datatype: "json",
            width: 465,
            height: 280,
            loadMsg: "数据加载中,请稍后...",
            fitCloumns: true,
            nowrap: true,
            rownumbers: false,
            pagination: true,
            singleSelect: true,
            showFooter: true,
            columns: [[
                    { field: 'testName', title: '测试名', align: 'center' },
                    { field: 'testValue', title: '测试值', align: 'center' }
                    ]],
            //bind数据成功设置列宽度
            onLoadSuccess: function (data) {
                //datagrid头部 table 的第一个tr 的td们,即columns的集合
                var headerTds = $(".datagrid-header-inner table tr:first-child").children();
                //datagrid主体 table 的第一个tr 的td们,即第一个数据行
                var bodyTds = $(".datagrid-body table tr:first-child").children();
                var totalWidth = 0; //合计宽度,用来为datagrid头部和主体设置宽度
                //循环设置宽度
                bodyTds.each(function (i, obj) {
                    var headerTd = $(headerTds.get(i));
                    var bodyTd = $(bodyTds.get(i));
                    $("div:first-child", headerTds.get(i)).css("text-align", "center");
                    var headerTdWidth = headerTd.width(); //获取第i个头部td的宽度
                    //这里加5个像素 是因为数据主体我们取的是第一行数据,不能确保第一行数据宽度最宽,预留5个像素。有兴趣的朋友可以先判断最大的td宽度都在进行设置
                    var bodyTdWidth = bodyTd.width() + 5;
                    var width = 0;
                    //如果头部列名宽度比主体数据宽度宽,则它们的宽度都设为头部的宽度。反之亦然
                    if (headerTdWidth > bodyTdWidth) {
                        width = headerTdWidth;
                        bodyTd.width(width);
                        headerTd.width(width);
                        totalWidth += width;
                    } else {
                        width = bodyTdWidth;
                        headerTd.width(width);
                        bodyTd.width(width);
                        totalWidth += width;
                    }
                });
                var headerTable = $(".datagrid-header-inner table:first-child");
                var bodyTable = $(".datagrid-body table:first-child");
                //循环完毕即能得到总得宽度设置到头部table和数据主体table中
                headerTable.width(totalWidth);
                bodyTable.width(totalWidth);
                bodyTds.each(function (i, obj) {
                    var headerTd = $(headerTds.get(i));
                    var bodyTd = $(bodyTds.get(i));
                    var headerTdWidth = headerTd.width();
                    bodyTd.width(headerTdWidth);
                });
            }
        });
        $("#test").datagrid('getPager').pagination({
            showPageList: false,
            showRefresh: false,
            beforePageText: "第",
            afterPageText: "页 <a href='javascript:void(0)' onclick='GoEnterPage()'><img src='http://www.cnblogs.com/Content/themes/icons/Go_.gif'></a>,共{pages}页",
            displayMsg: '当前{from}到{to}条,总共{total}条'
        });
    });
    function GoEnterPage() {
        var e = jQuery.Event("keydown");
        e.keyCode = 13;
        $("input.pagination-num").trigger(e);
    }
// ]]></script>

设置宽度的相关代码都已经打上注释了。测试了下 可行。无图无真相 附上效果图: