[教程]将Flex与Spring框架集成

mikel阅读(800)

随着富Internet应用(RIA)技术的不断成熟,将类似于Adobe Flex这样的RIA应用与健壮的服务器端技术进行集成就变得越来越重要了。Java开发者最喜欢的服务器端框架之一Spring将在这个过程中扮演着重要的角色。

RIAvolutionize the Web的Marco Casario解释到他为何建议BlazeDS成为Spring与Flex结合的企业系统时说道:“Spring是一个开源框架,可以让开发人员开发起来 更加轻松。如果使用标准的JEE方式,你需要编写大量无用或者冗余的代码,还可能花费大量时间去实现J2EE的设计模式,而这些模式仅仅是为了应对技术上 的限制而不是真正的解决方案。Spring可以通过简化这些过程而节省大量时间。”

Christophe Coenraets解释了Flex与Spring集成的基本原理:

Spring的控制反转(Inversion of Control, 即IoC)的主要思想就是让容器实例化组件(并且注入其依赖)。但是在默认情况下,由Flex客户端远程访问的组件是在服务器端由Flex目的文件实例化 的。所以Flex与Spring集成的关键在于配置Flex目的文件以使其能够让Spring容器来管理实例化的Spring beans。Flex数据服务支持工厂的概念以便可以实例化这类客户化组件。工厂的作用仅仅在于向Flex目的文件提供准备好的组件实例,而不是让 Flex目的文件自己去实例化这些组件。

关于Flex与Spring、IBATISCairngorm的集成,Chris Giametta说道:

我相信可以创建一个一致的、模块化的、可重用的架构。这个架构既可以支持小型应用,也可以支持非常健壮 的企业级应用。项目成功的一个关键要素在于创建这样一个架构:新人能迅速投入进去,并且很快就能上手。我觉得将Flex与Spring、iBATIS和 Cairngorm集成可以帮助我迅速构建出一个基于模式的、可重用的架构。 

Sébastien Arbogast在不遗余力地发表一系列博文,以阐述如何构建全栈式Flex、BlazeDS和Spring集成解决方案。

Arbogast的这个全栈式集成,从下到上包括如下内容:JBoss作为应用服务器、MySQL作为数据存储、Hibernate完成数据访问、Spring用来构建业务层、BlazeDS作为远程服务、Flexe-genial用来构建富客户端。该系统使用Mavenflex-compiler-mojo插件进行构建。

Arbogast说:“这个项目的建立当然需要一些工作,但是——除去配置文件复制上的一些小问题外(而且这很快会得到改善)——它真的很干净,而且flex-compiler-mojo也非常棒。”

查看英文原文:Integrate Flex with Spring Framework

[资源]Reflector for .net framework 3.5下载

mikel阅读(737)

Reflector 应该是所有.Net 开发者必备的工具了, 这次重大的更新带来了很多方便和实用的特性:
1. C# 3.0/2.0 语言语法的完美反编译支持, 包括C# 3.0 的扩展方法, LINQ, Lambda, 以及C# 2.0的匿名方法等
2. "Code URL" 支持
3. 程序集浏览器的改进
4. 反编译浏览器的改进, 包括可以动态显示文档
5. 分析器的改进, 添加了“Exposed By”和“Instantiated By” 两种搜索
6. 搜索功能的改进, 包括字符串和常量搜索, 多关键字搜索
7. 资源查看器的改进
8. 更多的自定义选项和Windows Vista 的更好支持.
Reflector 下载地址:
http://www.aisto.com/roeder/dotnet/
Reflector 插件:
http://www.codeplex.com/reflectoraddins
Reflector 5.0 New Feature(PPT格式)
http://www.aisto.com/roeder/paper/reflector5.ppt
Enjoy Your Exploring Experience!~

[教程]理解Asp.Net Pages

mikel阅读(764)

1.动态编译

当一个ASP.NET Page被创建时,实际上创建的是一个.net类, 一个System.Web.UI.Page的实例. 页面的所有内容,包括HTML代码和Script代码,都被编译进这个.net类中.
当 有一个Request请求这个页面时,ASP.NET Framework检查被请求页面的相应的类是否存在,如果不存在则把页面编译成.net类,并且把编译了的类(assembly)存放到 Temporary ASP.NET Files这个目录中.这个文件夹一般位于
                          :\WINDOWS\Microsoft.NET\Framework\[version]\Temporary ASP.NET Files
当有新的请求到来时,编译过程将不在需要,先前编译好的类直接执行,并把结果返回给浏览器.

2.控件树

Asp.Net Page可以理解为是一个.Net类的源代码, 除此之外,也可以把Asp.Net Page理解为是一个控件包( a bag of controls).因为有点控件本身可能含有控件,因此更确切点一点,可以把Asp.Net Page理解为一个控件树.
例如:

 1<%@ Page Language="VB" Trace="true" %>
 2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4<html xmlns="http://www.w3.org/1999/xhtml" >
 5<head id="Head1" runat="server">
 6    <title>Show Control Tree</title>
 7</head>
 8<body>
 9    <form id="form1" runat="server">
10    <div>
11
12    <asp:DropDownList
13        id="DropDownList1"
14        Runat="server">
15        <asp:ListItem Text="Oranges" />
16        <asp:ListItem Text="Apples" />
17    </asp:DropDownList>
18
19    <asp:Button
20        id="Button1"
21        Text="Submit"
22        Runat="server" />
23
24    </div>
25    </form>
26</body>
27</html>
28

这样一个Asp.Net Page, 它的控件树结构如下代码所示:

__Page ASP.showcontroltree_aspx
    ctl02 System.Web.UI.LiteralControl
    ctl00 System.Web.UI.HtmlControls.HtmlHead
        ctl01 System.Web.UI.HtmlControls.HtmlTitle
    ctl03 System.Web.UI.LiteralControl
    form1 System.Web.UI.HtmlControls.HtmlForm
        ctl04 System.Web.UI.LiteralControl
        DropDownList1 System.Web.UI.WebControls.DropDownList
        ctl05 System.Web.UI.LiteralControl
        Button1 System.Web.UI.WebControls.Button
        ctl06 System.Web.UI.LiteralControl
    ctl07

Asp.Net Page中包含的所有内容都被包含进一个.Net类中, 甚至包括HTML和纯文本内容.

3.Code-Behind Pages

Asp.Net Framework允许你创建两种类型的Asp.Net Pages, 单文件Pages和双文件Pages.这两种类型到底怎么样就不再赘述.稍微来看一下code-behind是如何工作的.
在Asp.Net 1.1中,Asp.Net Framework会为表现层页面(.aspx)和code-behind文件(.cs)产生两个单独的类,这两个类之间通过继承相关联,其中表现层页面类继承code-behind类.
由于继承是一个单向的关系,因此这种方法很脆弱.所有在表现层页面中声明的控件也必须同样在code-behind文件中声明,而且属性必须也相同.否则,控件触发的事件就可能不会被code-behind文件所捕获而加以处理.
在Asp.Net 2.0中,引入了一种新的机制:Partial Class.它允许把一个类的定义分散到不同的文件中.因此你就不需要在表现层页面和code-behind文件中同时声明,两者中任何的一个声明都可以在另一个中直接使用.

5.处理页面事件

当你请求一个页面时,页面触发事件的顺序如下:

  1. PreInit

  2. Init

  3. InitComplete

  4. PreLoad

  5. Load

  6. LoadComplete

  7. PreRender

  8. PreRenderComplete

  9. SaveStateComplete

  10. Unload

[教程]ASP.Net MVC入门教程

mikel阅读(774)

Introduction

MVC应该算是一个古老的Design Pattern了,无论是在win form程序还是web程序中,它的应用都是比较广泛的。MVC也是我在学校中学习到的第一个设计模式。终于,可以在ASP.NET中应用了。本文的例子 所用的是ASP.NET MVC Preview 2,可以在这里下载

Create a new MVC project

菜单File->New Project ->Asp.Net Web MVC Application
2.jpg
新创建的项目是一个完整的可以运行的Sample程序。
新创建的MVC Project和传统的Asp.net web application不同,MVC Project包含有如下四个文件夹:

  1. Content Folder : 这个文件夹中放一些支持文件,如CSS等。
  2. Controller Folder :这个文件夹中放所以的Controller文件
  3. Models folder : 这个文件夹存放所有的data model文件,包括:LINQ to SQL DBML文件,Entity文件
  4. Views folder : 存放所有的页面文件,包括master文件。Master等需要被共享访问的需要被放在一个Shared子文件夹中。

Advantages of an MVC-Based Web Application

  1. 把程序分为Model, View和Controller之后,更容易控制程序的复杂性
  2. 没有了传统的Asp.Net中的viewstate和server端的form,使得开发人员可以实现对页面的完全控制。当然也失去了viewstate和server端form带来的各种好处
  3. 支持测试驱动开发

Features of the ASP.NET MVC Framework

  1. 应用程序的业务分离,支持测试驱动开发
  2. 可扩展和支持插件的Framework。开发人员都可以根据自己的需要修改甚至替换ASP.NET MVC Framework的各个component,也可以以插件的形式开发自己的View Engine,URL Routing Policy等各种component。Asp.Net MVC Framework甚至支持依赖注入(Dependency Injection)和控制反转(Inversion of Control)等容器模式。
  3. 强大的URL-Mapping功能。使得URL地址更有意义(REST)。URL中不再包括文件扩展名。
  4. 对很多传统Asp.Net特性的支持。如<%=%>, user control等。

     

The MVC Framework and Postbacks

Asp.Net MVC 不再使用传统的Asp.Net Web Application的postback模式。取而代之的是,所有的客户端发回服务器端的request都会被映射到某一个controller类中, 这使UI logic和business logic得以分离,从而有助于提高程序的可测试性。

Understanding the MVC Project Execution Process

Request被发回服务器端之后,首先都由UrlRoutingModule对象来解析这个Request,并根据URL找到一个匹配的Router对象,之后由这个Router对象来处理这个Request。 MVC Application的处理流程:

  1. Initial Request: routers在Global.ascx中被添加到RouteTable中。
  2. Routing: UrlRoutingModule找到匹配的Router对象,决定使用哪个controller,调用哪个action。
  3. Map to controller: MvcRouteHandler会尝试通过routedata来创建controller的type name.
  4. Create Controller
  5. Execute Controller

[代码]Object/DataSet Relational Mapping(对象/数据集关系映射)补

mikel阅读(717)

  1using System;
  2using System.Data;
  3using System.Configuration;
  4using System.Collections;
  5using System.Web;
  6using System.Web.Security;
  7using System.Web.UI;
  8using System.Web.UI.WebControls;
  9using System.Web.UI.WebControls.WebParts;
 10using System.Web.UI.HtmlControls;
 11
 12using IBatisNet.DataMapper;
 13using System.Reflection;
 14
 15/// <summary>
 16/// ODRM为结合ORM与DataSet,并自动根据O和DataSet生成对象,以便业务层处理
 17/// </summary>

 18public partial class ODRM_test : PageBase
 19{
 20    protected void Page_Load(object sender, EventArgs e)
 21    {
 22        if (!IsPostBack)
 23        {
 24            DataSet set11 = Mapper.Instance().QueryForDataSet("SelectXTM_UserByKey_Test",UIhashtable);
 25            DataTable table1 = ConvertDataTable(set11, "");
 26            //这里为自己定义的序列化类
 27            cXTM_User[] objModel = new cXTM_User[table1.Rows.Count];
 28            //DataTable转化为序列化类数组
 29            for (int y = 0; y < table1.Rows.Count; y++)
 30            {
 31                objModel[y] = new cXTM_User();
 32                DataTableConvertObject(table1.Rows[y], objModel[y]); 
 33            }

 34            //以DataSet模式绑定
 35            ExDataGrid1.DataSource = table1;
 36            //以序列化对象模式绑定
 37            //ExDataGrid1.DataSource = objModel;
 38            ExDataGrid1.DataBind();
 39        }

 40    }

 41
 42    protected void ExDataGrid1_ItemDataBound(object sender, DataGridItemEventArgs e)
 43    {
 44        /*
 45         * 该部分应用范围
 46         * 查询一条数据的修改,可以用objModel.UserName
 47         * 而不必再使用DataTable[0].Rows[0]["UserName"]的模式
 48         * 提高面向对象的程度,并减少业务流程部分编码
 49         */

 50
 51        if (e.Item.ItemIndex != 1)
 52        {
 53            cXTM_User objModel = new cXTM_User();
 54            
 55            //如果为DataSet填充的DataGrid
 56            if (e.Item.DataItem.GetType().FullName == "System.Data.DataRowView")
 57            {
 58                DataTableConvertObject((DataRow)((DataRowView)e.Item.DataItem).Row, objModel);      
 59            }

 60            //否则认为为序列化对象填充
 61            else 
 62            {
 63                objModel = (cXTM_User)e.Item.DataItem; 
 64                   
 65            }

 66        }

 67    }

 68
 69    #region 指定对象函数
 70    /// <summary>
 71    /// 数据集中一行DataRow转换为指定对象,并填充数据
 72    /// </summary>
 73    /// <param name="row">数据集中一行</param>
 74    /// <param name="objModel">指定对象</param>

 75    private void DataTableConvertObject(DataRow row, cXTM_User objModel)
 76    {
 77        Hashtable hTable = new Hashtable();
 78        hTable = DataRowConvertHashtable(row);
 79        Type entitytype = Type.GetType(objModel.GetType().AssemblyQualifiedName);
 80
 81        for (int j = 0; j < objModel.Propertylist.Length; j++)
 82        {
 83            PropertyInfo propertyinfo = entitytype.GetProperty(objModel.Propertylist[j]);
 84            propertyinfo.SetValue(objModel, hTable[objModel.Propertylist[j]], null);
 85        }

 86    }

 87
 88    /// <summary>
 89    /// 对象转换为哈希表
 90    /// </summary>
 91    /// <param name="objModel">有数据的对象</param>
 92    /// <returns>填充数据后的哈希表</returns>

 93    public Hashtable ObjectConvertHashtable(cXTM_User objModel)
 94    {
 95        Hashtable hTable = new Hashtable();
 96        Type entitytype = Type.GetType(objModel.GetType().AssemblyQualifiedName);
 97        for (int j = 0; j < objModel.Propertylist.Length; j++)
 98        {
 99            PropertyInfo propertyinfo = entitytype.GetProperty(objModel.Propertylist[j]);
100            hTable.Add(objModel.Propertylist[j], propertyinfo.GetValue(objModel, null));
101        }

102        return hTable;
103    }

104
105    /// <summary>
106    /// 对象转换为DataTable,并有单行DataRow
107    /// </summary>
108    /// <param name="objModel">有数据的对象</param>
109    /// <returns></returns>

110    public DataTable ObjectConvertDataTableWidthRow(cXTM_User objModel)
111    {
112        return ObjectConvertDataTableWidthRow(objModel, "");
113    }

114
115    /// <summary>
116    /// 对象转换为DataTable,并有单行DataRow
117    /// </summary>
118    /// <param name="objModel">有数据的对象</param>
119    /// <returns></returns>

120    public DataTable ObjectConvertDataTableWidthRow(cXTM_User objModel, string DataMapper)
121    {
122        Type entitytype = Type.GetType(objModel.GetType().AssemblyQualifiedName);
123        DataTable dt = new DataTable();
124        if (DataMapper != "")
125        {
126            dt = new DataTable(DataMapper);
127        }

128        dt.Columns.Clear();
129        for (int j = 0; j < objModel.Propertylist.Length; j++)
130        {
131            PropertyInfo propertyinfo = entitytype.GetProperty(objModel.Propertylist[j]);
132            dt.Columns.Add(new DataColumn(objModel.Propertylist[j], propertyinfo.GetType()));
133        }

134        DataRow row = dt.NewRow();
135        for (int j = 0; j < objModel.Propertylist.Length; j++)
136        {
137            PropertyInfo propertyinfo = entitytype.GetProperty(objModel.Propertylist[j]);
138            row[objModel.Propertylist[j]] = propertyinfo.GetValue(objModel, null);
139        }

140        dt.Rows.Add(row);
141
142        return dt;
143    }

144
145    /// <summary>
146    /// 对象转换为DataTable,并有多行DataRow
147    /// </summary>
148    /// <param name="objModel">有数据的对象</param>
149    /// <returns></returns>

150    public DataTable ObjectConvertDataTableWidthRows(cXTM_User[] objModel)
151    {
152        return ObjectConvertDataTableWidthRows(objModel, "");
153    }

154
155    /// <summary>
156    /// 对象转换为DataTable,并有多行DataRow
157    /// </summary>
158    /// <param name="objModel">有数据的对象</param>
159    /// <returns></returns>

160    public DataTable ObjectConvertDataTableWidthRows(cXTM_User[] objModel, string DataMapper)
161    {
162        Type entitytype = Type.GetType(objModel.GetType().AssemblyQualifiedName);
163        DataTable dt = new DataTable();
164        if (DataMapper != "")
165        {
166            dt = new DataTable(DataMapper);
167        }

168        if (objModel.Length == 0)
169        {
170            return dt;
171        }

172        dt.Columns.Clear();
173        for (int j = 0; j < objModel[0].Propertylist.Length; j++)
174        {
175            PropertyInfo propertyinfo = entitytype.GetProperty(objModel[0].Propertylist[j]);
176            dt.Columns.Add(new DataColumn(objModel[0].Propertylist[j], propertyinfo.GetType()));
177        }

178
179        for (int i = 0; i < objModel.Length; i++)
180        {
181            DataRow row = dt.NewRow();
182            for (int j = 0; j < objModel[i].Propertylist.Length; j++)
183            {
184                PropertyInfo propertyinfo = entitytype.GetProperty(objModel[i].Propertylist[j]);
185                row[objModel[i].Propertylist[j]] = propertyinfo.GetValue(objModel[i], null);
186            }

187            dt.Rows.Add(row);
188        }

189        return dt;
190    }

191    #endregion

192
193    #region 通用函数
194
195    /// <summary>
196    /// 转换为DataTable
197    /// </summary>
198    /// <param name="Source">数据源</param>
199    /// <param name="DataMember">数据表名称</param>

200    public static DataTable ConvertDataTable(object Source, string DataMember)
201    {
202        DataTable baseTable = new DataTable();
203        if (Source is DataTable)
204        {
205            baseTable = (DataTable)Source;
206            return baseTable;
207        }

208        if (Source is DataSet)
209        {
210
211            DataSet set1 = (DataSet)Source;
212            if ((set1.Tables.Count > 1&& ((DataMember == null|| (DataMember == "")))
213            {
214                throw new Exception("If there is more than one table in your dataset, you must define the DataMember property to specify which table to use.");
215            }

216            if (set1.Tables.Count < 1)
217            {
218                throw new Exception("There are no tables in the datasource.");
219            }

220            if ((DataMember != null&& (DataMember != ""))
221            {
222                baseTable = set1.Tables[DataMember];
223                return baseTable;
224            }

225            else
226            {
227                baseTable = set1.Tables[0];
228                return baseTable;
229            }

230
231        }

232        return baseTable;
233    }

234
235    /// <summary>
236    /// 返回DataTable为哈希表键值对
237    /// </summary>
238    /// <param name="SourceTable">数据行对象</param>
239    /// <returns>填充后哈希表</returns>

240    public static Hashtable DataRowConvertHashtable(DataRow SourceRow)
241    {
242        Hashtable hTable = new Hashtable();
243        IList list = SourceRow.ItemArray;
244        object[] tObj = new object[SourceRow.Table.Columns.Count];
245
246        for (int i = 0; i < SourceRow.Table.Columns.Count; i++)
247        {
248            tObj[SourceRow.Table.Columns.IndexOf(SourceRow.Table.Columns[i].ColumnName)] = SourceRow.Table.Columns[i].ColumnName;
249        }

250
251        for (int x = 0; x < list.Count; x++)
252        {
253            hTable.Add(tObj[x].ToString(), list[x]);
254        }

255        return hTable;
256    }

257
258    #endregion

259
260
261
262
263}

264

[问题]插入SQLServer2005中文字符乱码问题

mikel阅读(716)

插入条记录
  发现回显的全部都是 ?????….
  乱码….
  仔细查看了半天
  发现数据库的排序规则是德语…
  修改方法:数据库属性->选项->排序规则
  设置成 CHINESE_PRC_CI_AI 就可以了

[教程]log4net简明手册

mikel阅读(942)

常见面,却不怎么用,究其原因还是觉得太复杂了点。不过,这东西出现次数越来越频繁,也只好写点东西,以备后用。本文仅对 Log4net 的使用做个简要说明,所有涉及到扩展和开发的部分一概忽略。
使用 Log4net,需要熟悉的东东有 Logger、Appender 以及 Layout。Logger 是日志记录器,我们使用其相关方法来完成日志记录;Appender 用于设置日志的存储方式和位置,Logger 的配置中会绑定一个或多个 Appender;Layout 关联具体的 Appender,用于设置日志字符串的格式。
1. Logger
所有的记录器都必须实现 ILog 接口,该接口提供日志记录所需的大量方法。
public interface ILog : ILoggerWrapper
{
void Debug(…);
void Error(…);
void Fatal(…);
void Info(…);
void Warn(…);
bool IsDebugEnabled { get; }
bool IsErrorEnabled { get; }
bool IsFatalEnabled { get; }
bool IsInfoEnabled { get; }
bool IsWarnEnabled { get; }
}
通常情况下,我们通过 LogManager.GetLogger() 来获取一个记录器。LogManager 内部维护一个 hashtable,保存新创建 Logger 引用,下次需要时直接从 hashtable 获取其实例。
ILog log = LogManager.GetLogger(this.GetType());
log.Debug(“aaaaaaaaaaaaaaa”);
所有 Logger 的参数设置都直接或间接继承自 root,其继承关系类似 namespace。比如,名为 “MyLogger.X.Y” 参数设置继承自 “MyLogger.X”。当我们创建 “MyLooger.X.Y” 记录器时,会在配置文件找该名称的记录器设置,如果没找到,则按继承关系向上查找,直到 root。因此,在创建 Logger 时,我们通常使用类型名称做为记录器的名字,缺省情况下,它会使用 root 或某一个父配置,但在需要的时候,我们随时可以为具体的类型添加一个更加 “详细” 的配置。















在创建 Logger 设置时,需要注意 “level” 参数。Log4net 允许我们通过该参数调整日志记录级别,只有高于或等于该级别的日志才会被记录下来。比如在代码调试阶段,我们可能希望记录所有的信息,而在部署阶段,我们只希望记录级别更高的错误信息。这个参数的好处是允许我们在不修改代码的前提下,随时调整记录级别。
(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低)
“appender-ref” 参数用于绑定一个或多个具体的 Appender。












2. Appender / Layout
Log4net 提供了大量的 Appender,最常用的包括 AdoNetAppender、AspNetTraceAppender、ConsoleAppender、FileAppender、 OutputDebugStringAppender。每种 Appender 都有特定一些参数,使用时直接从 《Log4net 手册》的示例中拷贝过去,就OK了。(代码摘自 Log4net 手册)
(1) AspNetTraceAppender





(2) ConsoleAppender





(3) OutputDebugStringAppender





(4) FileAppender







有关 Layout 详细信息,请参考 Log4net 相关文档,本文不做详述。
3. Configuration
Log4net 的配置方式十分灵活,即可以写到应用程序配置文件中,也可以使用独立配置文件。同时它还提供了监测配置文件变化的功能,这样我们随时可以调整配置,而无须重启应用程序。
(1) 使用 app.config / web.config
app.config / web.config















使用代码初始化配置。
log4net.Config.XmlConfigurator.Configure();
(2) 使用自定义配置文件
test.log4net












使用代码初始化配置。
log4net.Config.XmlConfigurator.Configure(new FileInfo(“test.log4net”));
使用 XmlConfigurator.ConfigureAndWatch() 方法除了初始化配置外,还会监测配置文件的变化,一旦发生修改,将自动刷新配置。
(3) XmlConfiguratorAttribute
我们还可以使用 XmlConfiguratorAttribute 代替 XmlConfigurator.Config()/ConfigureAndWatch(),ConfiguratorAttribute 用于定义与 Assembly 相关联的配置文件名。
方式1: 关联到 test.log4net,并监测变化。
[assembly: log4net.Config.XmlConfigurator(ConfigFile=”test.log4net”, Watch=true)]
方式2: 关联到 test.exe.log4net (或 test.dll.log4net,文件名前缀为当前程序集名称),并监测变化。
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension=”log4net”, Watch=true)]

[教程]log4net配置与应用

mikel阅读(848)

log4net 配置与应用

log4net是apache组织开发的日志组件, 同其姐妹log4j一样, 是一个开源项目. 可以以插件的形式应用在你的系统中. 下面仅说明如何应用在web forms项目中. 做为主要的日志输出组件.
1. 首先你应该下载log4net.dll并引入到你的项目References中.
2. 需要修改你的global.asa.cs. 配置application对象启动的时候加载log4net配置. 这一步是不可以缺少的.

protected void Application_Start(Object sender, EventArgs e)
{
   log4net.Config.DOMConfigurator.Configure();
}

3. 可以看到上面的代码没有参数. 可见是载入了缺省配置. 该配置必须设置于web.config中.
在web.cofig根节点 configuration 中加入如下section:

<configSections>
  <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>

4.该 config section 声明了名为 log4net 的另外一个config section. 后者必须位于web.config根节点 configuration 下: 以下是一个sample:

<log4net debug="false">
    <appender name="LogFileAppender" type="log4net.Appender.FileAppender" >
        <param name="File" value="XxxxApplication.log.txt" />
        <param name="datePattern" value="MM-dd HH:mm" />
        <param name="AppendToFile" value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
        </layout>
    </appender>
    <appender name="HttpTraceAppender" type="log4net.Appender.ASPNetTraceAppender" >
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
        </layout>
    </appender>
    <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
        </layout>
    </appender>
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <param name="File" value="_LogData\Log.txt" />
        <param name="AppendToFile" value="true" />
        <param name="MaxSizeRollBackups" value="10" />
        <param name="MaximumFileSize" value="5MB" />
        <param name="RollingStyle" value="Size" />
        <param name="StaticLogFileName" value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
        </layout>
    </appender>
    <root>
        <level value="DEBUG" />
        <appender-ref ref="LogFileAppender" />
    </root>
</log4net>

5. 以上定义了多个appender. 简单来说, 每一个 appender 都是一种输出介质.
6. root节点指定了选用的 appender. 本例选用了LogFileAppender. (文本文件输出). 在Appender定义中定义了输出的格式. 和目标文本文件所在位置. (起始位置是应用程序根目录. (web.config所在目录).
7. 到目前位置就配置好了log4net. 可以在我们的应用中直接使用了.
8. 以下说明应用方法:
要输出日志, 必须首先得到带有一个别名的logger.
使用以下命令
(C#):
log4net.ILog Logger logger = log4net.LogManager.GetLogger(this.GetType());
(可以直接使用GetType得到当前类名)
之后调用
logger.Info(string message);
logger.Error(string message);
logger.Debug(string message);
即可输出日志.
调试后可查找应用程序根目录下是否已经自动创建XxxxxApplication.log.txt文本文件.以及是否正确输出了日志.
log4net是一个非常完善的日志组件. 有着强大的可配置性. 有助于提高开发效率.
关于log4net的配置. 可参考apache组织的官方文档位于
http://logging.apache.org/log4net

[问题]未将对象引用设置到对象的实例

mikel阅读(691)

最近编写程序进行单元测试时发现提示:
“未将对象引用设置到对象的实例”的错误
搜索了一下网上的答案,还是没有好的结果,不过我将提示的string变量进行了赋值,问题解决了,大概是因为没有对变量进行初始化,造成使用时空对象引用的问题。

[原创]翻译:Dropthings.com的Web Portal开发学习笔记

mikel阅读(1167)

最近一直在研究Dropthings的架构以及源码,先暂且写个日志,后续内容不断更新
准备翻译Dropthings项目架构的作者写的这本书,不过翻译书比看书难度大多了,看书只需要领会精神就行了,但是翻译书就需要措词和表达能力同时需要让读者能够看懂就不是简单的英语水平能力了,希望不要误人子弟,呵呵…..:

数据库文件下载:
点击下载此文件
目录:
1.简介:
2.Web Portal和Widgets(部件)架构
整个应用程序有清晰的三层架构组成,分别是UI层、业务逻辑层和数据访问层:

Web表示层:
包含Web页面、Web服务、资源(图片,CSS,JavaScript和resx文件)和配置文件。
业务逻辑层:
提供实体类、业务逻辑和中间层数据缓存
数据访问层:
对数据库和数据源的数据库访问和连接封装成了一个接口。同时实现了对实体类与数据库行的映射的工厂类。
Dropthings使用了.NET3.0和.NET3.5的新功能,Web层使用ASP.NET AJAX的RIA技术、业务逻辑层使用新的WF(WorkFlow)工作流实现复杂的业务逻辑的处理。整体架构使用Linq进行数据持久操作。
C#3.0语言的新特性和Linq使得操作集合、数据库行和XML变得容易。WF使得操作赋值的业务流程变得简单。Linq to SQL 应用在业务逻辑层和数据访问层。尽管简单的增、删、改功能在数据访问层进行了封装,更多的请求要求业务逻辑层的响应速度快。这就是为什么Linq to SQL贯彻整个业务层和数据访问层的原因。
2.Web Portal和Widgets(部件)架构
任何Web Protal的核心是它支持Widgets(部件)并且支持用户自定义起始页面,意思就是用户可以提供自定制的服务,不管是公司的一个部门还是第三方。
书中所说的Defaut.asp就是我们所说的一个ASP.NET实现的用于显示widgets和允许用户添加、删除、移动、自定义并且这些操作不会刷新的主页。应用程序记录下用户自定义的页面的样式。Web Protals允许非注册用户使用添加widgets、编辑、删除、创建页面和设置参数。
一个Dropthings的Widgets就是一个ASP.NET的Web Control。可以是一个用户控件也可以是服务器端控件并且符合ASP.net的生命周期规则。Widgets支持PostBacks、ViewState、Sessions和Caches。唯一不同的地方是所有Widgets都实现了IWidget接口来调用核心框架提供的各种服务。一个自定义的AJAX Control实现了drag-and-drop功能。
每个Widget都包含在框架和容器中,容器(container)提供具有标题、编辑链接、最大、最小化和关闭按钮的标题栏。Widget被加载在标题栏下方的主体部分。事件包括改变标题、编辑链接的click事件、最大、最小和关闭按钮事件调用IWidget接口。
Web Portal实现了异步的请求和异步数据处理,使得用户很少刷新页面。Widgets是一种支持ASP.NET请求机制的控件。因此Dropthings的核心就是Widget,不久你就会感觉到所有Widget都是包含在UpdatePanel中并且全部支持异步请求。虽然你可以不用注册就可以使用自定义Widget,哪怕你在不同的计算机上再次登录也会显示你先前自定义的页面布局。ASP.NET的membership(会员管理)和profile(配置)允许匿名用户有持久状态知道注册用户后进行转换。页面和Widget状态存储在数据库表中。
Object Model 对象模型
ASP.NET的membership包含User和Role。如果用户创建了一个或多个页面,每个页面包含一个或多个Widget实例。Widget实例和Widget不同之处类似于面向对象中的类和对象,例如,Flickr图片Widget是一个用于加载Flickr图片的Widget,当一个用户添加它到一个页面后,也就是实例化了一个Widget成为了一个实例。本书中所说的widget实际上都是widget实例。
如图所示:

Application Components 应用程序组件
Dropthings使用门面模式来提供一个统一的业务逻辑接口。实现对子系统的访问处理用户、页面、widgets和etc。门面命名为DashboardFacade,结构如图:

Web层Default.aspx调用DashboardFacade执行添加Tab或Widget、存储Widget状态。DashboardFacade调用具体工作流的操作进行处理。工作流利用Window Workflow Foundation进行实际的处理。每个工作流都包含多个活动(Activities)。每个活动类似于只实现一个功能的类。活动使用DatabaseHelper和DashboardDataContext类操作数据库。DatabaseHelper用于执行通用的数据库操作。DashboardDataContext是用于Linq to SQL的类到数据库表之间的映射。
Data Model 数据模型

数据模型中应用了ASP.NET的Membership的数据库表aspnet_Users包含所有用户帐户。
具体各表的内容如下:

  • aspnet_Users:是ASP.NET的membership默认的表。不过这个表只包含未注册用户信息,注册用户信息存储在aspnet_membership表。图中没有显示,因为它和其他表没什么关系。
  • Page:通过UserId与aspnet_users建立主外键关系。
  • Widget:存储Widget的详细内容和信息。存储每个widget的标题和是否动态加载。也存储用户第一次访问时创建的默认设置。
  • WidgetInstance:通过WidgetId和PageID分别与Page和Widget表建立关系。
  • UserSetting:同过UserId与aspnet_Users表建立关系。

具体表的描述如下:

Table 索引类型 描述
Page UserID Nondusted 用户页面利用Where UserID=<ID>进行加载
Page ID Clusted 页面的主键,编辑页面用于查询
 Widget ID Clusted 部件的主键,当一个部件实例创建后关联到部件ID
 Widget IsDefault Nondusted 第一次访问,默认部件被创建到页面上。IsDefault设定哪些部件被创建。
 WidgetInstance ID Clusted 部件实例的主键,用于更新删除部件实例标示实例。
 WidgetInstance UserID Nondusted 用户ID,用于按用户读取部件实例。

备注:

  • 簇集索引使用的是自增加的整形字段。因为SQL Server本身的数据库文件管理就是基于簇集索引的。如果不选择自动增加的主键当Insert和Delete时候比较麻烦。
  • 外键值不是子增加的字段是因为他们不需要增加。

文件结构:
Dropthings是基于C#语言的ASP.NET项目。源码可以从http://www.codeplex.com/dropthings处下载。

Default.aspx:
    控制所有Widget的起始页面。
WidgetService.asmx
   声明通过起始页面对Widget所有操作的Web Service。
Proxy.asmx
   允许Widget引用其他第三方的扩展资源和Web service的接口。
WidgetContainer.ascx
   所有Widget的容器,起到核心框架和实际Widget之间的桥梁作用。
所有Widget在Widget文件夹下,每个Widget都是一个Web Control,用到的图片、css、js文件都保存在Widget中的子文件夹下。
Update Panels
UpdatePanel允许整个站点的任何部分都可以进行AJAX异步请求操作。不过,UpdatePanel最重要的是支持拖拽。使用多个UpdatePanel时,多个异步请求同时更新页面会很慢。当你嵌套使用UpdatePanel会更加复杂。所以设计时一定要注意布局。


Dropthings中,所有Widget包含在一个UpdatePanel(ID=UpdatePanelLayout)中,因为当用户切换Tab时需要整体加载页面。并且每个Tab创建和修改后的页面都包含在另一个UpdatePanel(ID=TabUpdatePanel),因为Tab可以动态增加。从Widget列表中添加Widget也是一个UpdatePanel(ID=AddContentUpdatePanel)。如图所示。
代码如下:
       <!–添加WidgetUpdatePanel–>
        <asp:UpdatePanel ID="AddContentUpdatePanel" runat="server" UpdateMode="conditional">
        <ContentTemplate>
                ….略
        </ContentTemplate>
        </asp:UpdatePanel>
        <!–Tab和内容UpdatePanel–>
        <asp:UpdatePanel ID="UpdatePanelTabAndLayout" runat="server" UpdateMode="conditional">
        <ContentTemplate>
             <!–Tab面板 –>
            <asp:UpdatePanel ID="TabUpdatePanel" runat="server" UpdateMode="conditional">
            <ContentTemplate>          
                <div id="tabs">
                    <!– 组件容器UpdatePanel–>
                    <asp:UpdatePanel ID="UpdatePanelLayout" runat="server" UpdateMode="conditional">
                        <ContentTemplate>
                            <!–自定义的三列UpdatePanel –>
                            <uc3:WidgetPanels ID="WidgetPanelsLayout" runat="server" />
                        </ContentTemplate>
                    </asp:UpdatePanel>
                    </li>
                </ul>           
                </div>             
            </ContentTemplate>
        </asp:UpdatePanel>
   
         </ContentTemplate>
    </asp:UpdatePanel>
这样将所有的Widget都放在一个UpdatePanel中的结果是更新或添加Widget时需要更新整体UpdatePanel。当以不更新时要产生大量的异步请求HTML和JavaScript。比较好的解决办法是没列包含一个UpdatePanel,不是整个Widget区域。当你拖拽一个widget跨列时,只是JavaScript进行页面间的处理,不需要更新updatePanel,只需要告诉服务器哪个widget被移动了。服务器端可以像客户端一样计算widget的坐标,这样除了增加和删除修改新的widget需要进行异步请求,其他时候都不要发送请求。

拖拽操作
有两种方法实现拖拽:Free Form 和Columnwise,Protopage 用FreeForm实现的拖拽,这样的应用依赖绝对坐标,widget可以拖拽到任意位置。iGoogleLive.com 是使用Column-wise实现的拖拽,允许你在列间进行widget的拖拽。Column-wise方式清晰布局widget,大部分web protal都使用这种方式。

实现多列间的拖拽操作,整个页面被分成三列,每列包含一个ASP.NET的Panel控件。Widget可以添加到任意panel中。拖拽功能是自定义的功能。拖动存在两种方式:一种是本列中的排列顺序拖动,一种是列间的拖动。
如果我们在ASP.NET的AJAX框架中实现IDropTarget接口实现每列的拖拽区,每个widget作为一个IDrogSource在列间拖动。更有意思的是能够记录下widget的坐标值。例如,拖拽一个widget下移,widget会自动向上填充空出的地方。同样你移动一个widget到另一个上方,widget会自动下移到移动的widget下方。这些功能都是类似Extenders的实现的,所以你可以简单的实现一个panel的extender,它将实现类似IDropTarget的功能,并记录坐标。
当拖拽完成我们怎么实现异步存储坐标到服务器呢?当我们拖拽完毕,UI界面进行了更新,但是服务器不知道。任何请求操作都会触发服务器端响应因为需要刷新页面或列。服务器需要异步获得坐标值并存储,但是客户端拖拽完毕后根本体会不到坐标已经发送到服务器。另外一个问题是整个form实现拖拽只基于一个extender。Extenders需要依附于Column Panel并作为一个拖拽目标,也就是widget的拖拽句柄允许widget被拖拽到任何目的。
下面的内容我们将分析如何处理添加widget和起始页面的容器。
使用Widget框架

Dropthings使用一个weidget框架提供了widget相关的功能:验证、权限、配置、存储。Widget从这个框架获得这些功能。如图所示。
而且,你不要依赖任何对象就能创建widget。在你本地开发的计算机上不需要整个web portal的源代码就能创建widget。你只需要创建一个ASP.NET2.0的普通站点,创建一个用户控件,让它实现一个标准的请求响应处理,实现一个接口,仅次而已。
不必要担心基于Dropthings框架中的AJAX和javascript。这个框架允许你使用任何ASP.NET2.0的AJAX组件和Ajax Control Toolkit控件。服务器端支持.NET2.0,3.0,3.5利用View state存储临时状态信息。ASP.NET缓存用来缓存widget数据。这种方式比用javascript进行缓存要好得多。
Dropthings框架中使用了ASP.net的membership实现了验证和权限管理。允许widgets加载时获得当前用的配置信息。核心持久化了保存了用户的状态也就是用户的操作,例如对widget的展开、关闭装提以及增加、删除、修改操作。Widget和核心通过一个widget容器进行交互。widget container包含一个widget起到中介的作用。widget container对widget实例提供类似持久化服务和事件响应。page 包含多个wiget container,但是每个widget container只包含一个widget实例。如图

Widget很简单,就是一个标准的web Control,你可以在page_load事件中编写代码。也可以从ASP.net用户控件定义事件。Widgets类似与SharePoint Web Parts,但是比Web Part好的地方是你可以使用ASP.net控件到自定义控件。用户控件用Visual Studio编辑,你不用自定义控件。你也可以创建widget到.ascx文件中,不要编辑成dll或者发布dll到服务器,只需要拷贝ascx文件到web文件夹中。
比如你想显示fickr的照片。可以创建一个web control作为widget来实现。下面的代码实现了当页面加载时调用fickr的service功能:
protected void Page_Load(object sender, EventArgs e)
{
if( !base.IsPostBack )
this.ShowPictures(0);
else
this.ShowPictures(PageIndex);
}
在这个widget中加入linkbutton来用于切换图片,编写linkbutton的onclick时间用来浏览图片,代码如下:
protected void LinkButton1_Click(object sender, EventArgs e)
{
if( this.PageIndex > 0 ) this.PageIndex –;
this.ShowPictures(this.PageIndex);
}
protected void LinkButton2_Click(object sender, EventArgs e)
{
this.PageIndex ++;
this.ShowPictures(this.PageIndex);
}
ASP.NET的页面生命周期类似于普通页面处理。在widget中你可以使用ASP.NET的组件和编写这些组件的事件响应代码。
Container提供了widget容器和框架并且定义了header和body区域。实际上widget是运行时被widget container加载到body区域的。页面上的每个widget都是首先创建一个widget container然后widget container在动态加载widget实例到它的body区域。widget container是框架的一部分,你只需要编写一次。widget开发者不需要编写container因为他们只需要编写widget就行了,container已经定义好了。

widget container是web control当加载页面时候用于动态创建每个widget实例。widget也是一个web control在widget container的Page.LoadControl(.,.)事件中动态加载。实际上widget是被加载到UpdatePanel控件中的。因此,不管什么时候都是widget实例都是发送请求的,widget container没有进行任何请求处理操作。
设计Widget Container
设计好Container的关键问题是如何准确获得UpdatePanels。如何布局ASP.NET到UpatePanel中是个问题。将每个widget container放在一个UpdatePanel中足够处理。但是有个问题就是UpdatePanel中需要动态添加HTML代码。当UpdatePanel更新时将删除现有的HTML代码重新读取ASP.NET组件并重新创建。结果,所有extenders的之前生成的HTML内容将被清除,除非extenders一直在UpatePanel。将extenders放在UpdatePanel中就意味着每次刷新UpdatePanel将初始化并创建一个extenders实例。每次请求处理后UI界面将刷新慢,特别是在页面上使用widget时候。
需要将Header和Body分别放在两个Updatepanel中,这样修改Widget时只刷新Body区域不必刷新Header区域,但是所有的Extender需要加载在Header区域,这样就不必每次都加载Extender来减少处理时间提高响应速度。如图所示。

不过,最好是将UpdatePanel包含在HeaderPanel中,只将标题和LinkButton放在UpdatePanel中,这样不用刷新UpdatePanel时(比如单击LinkButton后)重新创建HeaderPanel区域,仅仅需要更新title和LinkButton就可以了.下面的图显示了将HeaderPanel放在UpdatePanel外的设计:

WidgetContainer 实现很简单。Header区只包含标题、最小化、最大化、隐藏按钮和一个body区域用于动
态加载widget。图2.4所示的Dropthings文件结构中所示的WidgetContainer.ascx定义了WidgetContainer
,代码如下:
Example 2-1. The .ascx content for the WidgetContainer
<asp:Panel ID="Widget" CssClass="widget" runat="server">
<asp:Panel id="WidgetHeader" CssClass="widget_header" runat="server">
<asp:UpdatePanel ID="WidgetHeaderUpdatePanel" runat="server"
UpdateMode="Conditional">
<ContentTemplate>
<table class="widget_header_table" cellspacing="0"
cellpadding="0">
<tbody>
<tr>
<td class="widget_title"><asp:LinkButton ID="WidgetTitle"
runat="Server" Text="Widget Title" /></td>
<td class="widget_edit"><asp:LinkButton ID="EditWidget"
runat="Server" Text="edit" OnClick="EditWidget_Click" /></td>
<td class="widget_button"><asp:LinkButton ID="CollapseWidget"
runat="Server" Text="" OnClick="CollapseWidget_Click"
CssClass="widget_min widget_box" />
<asp:LinkButton ID="ExpandWidget" runat="Server" Text=""
CssClass="widget_max widget_box" OnClick="ExpandWidget_Click"/>
</td>
<td class="widget_button"><asp:LinkButton ID="CloseWidget"
runat="Server" Text="" CssClass="widget_close widget_box"
OnClick="CloseWidget_Click" /></td>
</tr>
</tbody>
</table>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
<asp:UpdatePanel ID="WidgetBodyUpdatePanel" runat="server"
UpdateMode="Conditional" >
<ContentTemplate><asp:Panel ID="WidgetBodyPanel" runat="Server">
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
<cdd:CustomFloatingBehaviorExtender ID="WidgetFloatingBehavior"
DragHandleID="WidgetHeader" TargetControlID="Widget" runat="server" />
整个widgetContainer定义在命名为:Widget的Panel中。首先headerPanel包含Header区域和
WidgetHeaderUpdatePanel的UpdatePanel。它里面包含标题和最小化、最大化的LinkButton用来改变编辑
区域。 WidgetBodyUpdatePanel的UpdatePanel包含一个运行时创建的Widget,并且包含在一个Panel中,
加载widget 是在Page.LoadControl(..)事件中处理的,并添加到Body panel中。
CustomFloatingBehaviorExtender 用于添加widget的Header并且是整个widget支持拖拽。
3.使用ASP.NET AJAX 构建表示层
4.使用.Net3.5构建持久层和业务逻辑层
5.构建客户端Widgets(部件)
6.优化ASP.NET AJAX
7.创建异步,事务和WebService缓存
8.调试和优化服务器端性能
9.优化客户端性能
10.一般部署、发布问题解答