[PHP]二十五个顶级PHP模板引擎整理

mikel阅读(788)

为了找到一个好的模板引擎,我在互联网上进行搜索,目前已经整理出了以下名单:

Smarty
Smarty的特点是将模板编译成PHP脚本,然后执行这些脚本。很快,非常灵活。

Heyes Template Class
一个非常容易使用,但功能强大并且快速的模板引擎,它帮助你把页面布局和设计从代码中分离。

FastTemplate
一个简单的变量插值模板类,它分析你的模板,把变量的值从HTML代码中分离处理。

ShellPage
一个简单易用的类,可以让你的整个网站布局基于模板文件,修改模板就能改变整个站点。

STP Simple Template Parser
一个简单、轻量级并且易于使用的模板分析类。它可以从多个模板中组装一个页面,把结果页面输出到浏览器或者文件系统。

OO Template Class
一个你可以用在自己程序中的面向兑现的模板类。

SimpleTemplate
一个可以创建和结构化网站的模板引擎。它可以解析和编译模板。

bTemplate
短小但是快速的模板类,允许你把PHP逻辑代码从HTML修饰代码中分离。

Savant
一个强大且轻量级的PEAR兼容模板系统。它是非编译型的,使用PHP语言本身做为它的模板语言。

ETS – easy template system
可以使用完全相同数据重组模板的模板系统。

EasyTemplatePHP
适用于你的站点的一个简单但是强大的模板系统。

vlibTemplate
一个快速、全能的模板系统,它包含一个缓存和调试类。

AvanTemplate
多字节安全的模板引擎,占用很少系统资源。它支持变量替换,内容块可以设置显示或隐藏。

Grafx Software’s Fast Template
一个修改版本的Fast Template系统,它包括缓存功能,调试控制台以及沉默去除为赋值块。

TemplatePower
一个快速、简单、功能强大的模板类。主要功能有嵌套的动态块支持,块/文件包含支持以及显示/隐藏未赋值的变量。

TagTemplate
这个库的功能被设计来使用模板文件,同时允许你从HTML文件检索信息。

htmltmpl: templating engine
一个适用于Python和PHP的模板引擎。它面向希望在项目中分离代码和设计的web应用开发人员。

PHP Class for Parsing Dreamweaver templates
一个分析Dreamweaver模板的简单类,被用于Gallery 2 和WordPress的自定义模块中。

MiniTemplator (Template Engine)
针对HTML文件的一个紧凑型模板引擎。对于模板变量和块定义它具有简单的语法。其中块可以嵌套。

Layout Solution
简化网站开发和维护。它拥有常用的变量和页面元素使你不需要重复做页面布局工作。

Cached Fast Template
它已经纳入FastTemplate,允许你缓存模板文件,甚至可以在分离的块内容上缓存不同的规格。

TinyButStrong
一个支持MySQL, Odbc, SQL-Server和ADODB的模板引擎。它包含7个方法和两个属性。

Brian Lozier’s php based template engine
只有2K大小,非常快并且是面向对象设计。

WACT
一个从设计中分离代码的模板引擎。

PHPTAL
一个PHP下面的XML/XHTML模板库。

[MVC]Asp.net MVC 示例项目"Suteki.Shop"分析之---ModelBinde

mikel阅读(877)

     在Suteki.Shop中,作者构造了一个ModelBinder基类“DataBinder”,其本身继承自IModelBinder
接口,并以此其类派生出其它一些子类类如ProductBinder等等。可以说除了极个别的地方之外,Data
Binder被用于了Suteki.Shop大多数的ModelBinder绑定场景之路。

      首先看一下其类图结构:
    
         
      作为基类,DataBinder(图中左下方)实现了将HTTP请求过来的数据转换成为模型中相应的类型。
其核心方法就是BindModel,下面做一下解释说明:

public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
            
object
 entity;
            
if(declaringAttribute == null ||
 declaringAttribute.Fetch)
            {
                entity 
=
 FetchEntity(bindingContext, controllerContext);
            }
            
else
 
            {
                entity 
=
 Activator.CreateInstance(bindingContext.ModelType);
            }        
            
try

            {
                validatingBinder.UpdateFrom(entity, controllerContext.HttpContext.Request.Form, bindingContext.ModelState, bindingContext.ModelName);
            }
            
catch(ValidationException ex) 
            {
                
//
Ignore validation exceptions – they are stored in ModelState. 
                
//The controller can access the errors by inspecting the ModelState dictionary.

            }
            
return
 entity;
    }

     首先要说明的就是declaringAttribute,这个变量其实是DataBindAttribute类型实例,其作用
是判断当前所请求过来的数据是用于创建新的Model信息还是获取并绑定已有的数据记录。该实例的
实始化是交给Accept方法来实现的(注意Accept方法的声明是在IAcceptsAttribute接口中定义的):

public void Accept(Attribute attribute)
{
     declaringAttribute 
=
 (DataBindAttribute) attribute;   
}

 
      而对该方法的调用是放在了BindUsingAttribute的GetBinder()方法中,所以这里要先看一下
BindUsingAttribute的具体实现: 

public class BindUsingAttribute : CustomModelBinderAttribute
{
        
private readonly
 Type binderType;
        
public
 BindUsingAttribute(Type binderType)
        {
            
if(!typeof
(IModelBinder).IsAssignableFrom(binderType))
            {
                
throw new InvalidOperationException("Type '{0}' does not implement IModelBinder."
.With(binderType.Name));
            }
            
this.binderType =
 binderType;
        }
        
public override
 IModelBinder GetBinder()
        {
            var binder 
=
 (IModelBinder) ServiceLocator.Current.GetInstance(binderType);
            
if(binder is
 IAcceptsAttribute)
            {
                ((IAcceptsAttribute)binder).Accept(
this
);
            }
            
return
 binder;
        }
}

 
      这个属性类也就是在Action中进行ModelBinder绑定时用到的,其类构造方法中通过一个类型参
数初始化并在GetBinder()方法中使用ServiceLocator来进行最终的ModelBinder实例化构建,注意
在该方法中有对当前binder的逻辑判断(IAcceptsAttribute),并以此来区别当前Action绑定的Model
Binder是否实现了IAcceptsAttribute接口。如果没实现或declaringAttribute.Fetch为true时,就
会在之前的DataBinder类方法“BindModel”中调用“FetchEntity()”方法,FetchEntity会从提交
的数据中的主键(PrimaryKey)中检索数据并返回该对象实例,代码如下:
 

private object FetchEntity(ModelBindingContext bindingContext, ControllerContext controllerContext)
{
        
object
 entity;
        var primaryKey 
= bindingContext.ModelType.GetPrimaryKey();//从Shop.dbml中查找相应的主键信息

        string name = bindingContext.ModelName + "." + primaryKey.Name;//拼接出要获取的主键名称

        
string rawKeyValue = controllerContext.HttpContext.Request.Form[name];
        
if (string
.IsNullOrEmpty(rawKeyValue))
        {
            
throw new InvalidOperationException("Could not find a value named '{0}'"
.With(name));
        }
        
int key =
 Convert.ToInt32(rawKeyValue);
        var repository 
=
 resolver.GetRepository(bindingContext.ModelType);
        entity 
=
 repository.GetById(key);
        
return
 entity;
}

 

     注意上面代码中的这两行:   

    var repository = resolver.GetRepository(bindingContext.ModelType);
    entity 
= repository.GetById(key);

     其通过bindingContext.ModelType方法获取指定的Model类型的Repository实例(包括该Model类型
的CRUD方法),并使用接口方法GetById()来获取最终的Model对象。

    下面就是IRepository的接口定义(当然其也定义了泛型接口,后面会提到)。 

public interface IRepository
 {
     
object GetById(int
 id);
     IQueryable GetAll();
     
void InsertOnSubmit(object
 entity);
     
void DeleteOnSubmit(object
 entity);
     [Obsolete(
"Units of Work should be managed externally to the Repository."
)]
     
void
 SubmitChanges();
 } 

 
     看到这里,我感觉用这种方式设计还是很讨巧的,因为只要每个Model都实现了相应的IRepository
接口操作类,就可以通过上面两行代码进行调用,这也同时让 FetchEntity获取了最大的稳定性,基本
上可以做到与具体的Model类解藕,呵呵。

     正如前面所说DataBinder本身就是个基类,其实现了一些默认设置和功能,并以此来简化子类的实
现代码。

     下面接着看一下其子类的实现,这里以ProductBinder(Suteki.Shop\Binders\ProductBinder.cs)
为例进行说明,ProductBinder主要实现商的信息更新,包括商品的Size, 图片等等,其实现代码如下:
    

public class ProductBinder : DataBinder
{
        
readonly IRepository<Product>
 repository;
        
readonly
 IHttpFileService httpFileService;
        
readonly IOrderableService<ProductImage>
 orderableService;
        
readonly
 ISizeService sizeService;

        public ProductBinder(IValidatingBinder validatingBinder, IRepositoryResolver resolver, IRepository<Product> repository, IHttpFileService httpFileService, IOrderableService<ProductImage> orderableService, ISizeService sizeService)

 

 

            : base(validatingBinder, resolver)
        {
            
this.repository =
 repository;
            
this.httpFileService =
 httpFileService;
            
this.orderableService =
 orderableService;
            
this.sizeService =
 sizeService;
        }
        
public override object
 BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var product 
= base.BindModel(controllerContext, bindingContext) as
 Product;
            
if(product != null
)
            {
                UpdateImages(product, controllerContext.HttpContext.Request);
                CheckForDuplicateNames(product, bindingContext);
                UpdateSizes(controllerContext.HttpContext.Request.Form, product);
            }
            
return
 product;
        }
        
void
 UpdateSizes(NameValueCollection form, Product product)
        {
            sizeService.WithValues(form).Update(product);
        }
        
void
 UpdateImages(Product product, HttpRequestBase request)
        {
            var images 
=
 httpFileService.GetUploadedImages(request);
            var position 
=
 orderableService.NextPosition;
            
foreach (var image in
 images)
            {
                product.ProductImages.Add(
new
 ProductImage
                {
                    Image 
=
 image,
                    Position 
=
 position
                });
                position
++
;
            }
        }
        
void
 CheckForDuplicateNames(Product product, ModelBindingContext bindingContext)
        {
            
if (!string
.IsNullOrEmpty(product.Name))
            {
                
bool productWithNameAlreadyExists =

                    repository.GetAll().Any(x 
=> x.ProductId != product.ProductId && x.Name == product.Name);
                
if
 (productWithNameAlreadyExists)
                {
                    
string key = bindingContext.ModelName + ".ProductId"
;

                    bindingContext.ModelState.AddModelError(key, "Product names must be unique and there is 

                                already a product called '{0}'".With(product.Name));

                }

            }
        }
}

 

      ProductBinder的构造方法主要是用于进行单元测试,所以就不多做说明了。值得注意的是其
重写了BindModel方法,这也是我们使用继承方法的精妙所在,可以根据自己的需求“覆盖”已有
的基类方法,以此实现自己的业务逻辑。当然如果将来ProductBinder下面有子类继承的话,同时
又要定制自己的BindModel内容时,也可以如法泡制。

     另外就是在上面的CheckForDuplicateNames方法中对于成员 repository的使用,该成员的
如下:
    

IRepository<Product>  repository;

    
     其IRepository<>泛型接口的实现目的与IRepository相同,也是为了隔离“变化的需求”,同
时提高了可扩展性。

    
    下面就是IRepository<>泛型接口的声明:

public interface IRepository<T> where T : class
{
    T GetById(
int id);
    IQueryable
<T>
 GetAll();
    
void
 InsertOnSubmit(T entity);
    
void
 DeleteOnSubmit(T entity);
    [Obsolete(
"Units of Work should be managed externally to the Repository."
)]
    
void
 SubmitChanges();
}

     除了从DataBinder上继承之外,Suteki.Shop中还有两个与其继承层次相同的ModelBinder,
们是CurrentBasketBinder(购物车),OrderBinder(定单),而这两个Binder与之前所说的
DataBinder”的一个区别就是其均未实现IAcceptsAttribute接口。换句话说当使用BindUsing-
Attribute绑定到Action时,其均不会运行BindUsingAttribute类中GetBinder()方法的“IAcce-
ptsAttribute.Accept(this)语句”。

    下面是其类图:

     今天的内容看着有点复杂,但其实与前几天所说的Controller,Filter的实现方式都有些相似,
就是对于MVC所提供的类都是先做一个其类,然后在基类的基础上写新入自己想要的功能代码,这
种方式应该说是一种常识或是基本功了,好处大家也都能分析的出来,呵呵。

     好了,今天的内容就先到这里了。   
   
     原文链接:
http://www.cnblogs.com/daizhj/archive/2009/05/11/1454300.html

     作者: daizhj,代震军,LaoD

     Tags: mvc

     网址: http://daizhj.cnblogs.com/

[C#]Microsoft .NET PetShop 4.0 架构与技术分析(一)

mikel阅读(981)

1.框架的概要介绍

微软刚推出了基于ASP.NET 2.0下的Pet Shop 4, 该版本有了一个全新的用户界面。是研究ASP.NET 2.0的好范例啊,大家都知道,一直以来,在.NET和Java之间争论不休,到底使用哪个平台开发的企业级应用性能最好、结构最优、生产力最高。为了用事实说话,通过对项目各方面的性能评估进而在比较.NET和Java的高下。用户做比较的这个项目就是Petshop。正因为Petshop肩负着上面所说的重任,各方面必须是最优的架构设计应该是经过慎重考虑的。所以其一经推出,便成为了开发者、架构师等人学习、研究的典范。

日前微软推出了基于.NET Framework 2.0开发的Petshop 4。新的Petshop4实现了与Petshop 3相同甚至更多的特性,由于采用了Master Pages,Membership,以及Profile,SQLCacheDependency,但是代码量却减少了四分之一。同时,在事务、数据缓存、安全方面使用了.NET 2.0附带的特性,构建了一个灵活的最佳实践的应用程序。

 

他们利用了Project Conversion Wizard把项目从ASP.NET 1.1移植到了ASP.NET 2.0,然后做了以下改动:

1.用System.Transactions代替了原来的Serviced Components提供的事务功能

代码实现:PetShop.BLL.OrderSynchronous 的 public void Insert(PetShop.Model.OrderInfo order)

2.用强类型的范型集合代替了原来的弱类型集

        public IList<ProductInfo> GetProductsByCategory(string category)

        {

            // Return new if the string is empty

            if (string.IsNullOrEmpty(category))

                return new List<ProductInfo>();

 

            // Run a search against the data store

            return dal.GetProductsByCategory(category);

        }

3.采用ASP.NET 2.0 Membership来做认证和授权

4.创建了针对Oracle 10g的Custom ASP.NET 2.0 Membership Provider

5.利用ASP.NET 2.0的Custom oracle 和 SQL Server Profile Providers 做用户状态管理,包括购物车等

6.采用了Master Pages,取代了原来的用户控件,来实现统一的界面效果

7.使用了ASP.NET 2.0 Wizard控件实现check-out

8.使用了SqlCacheDependency来实现数据库层次的缓存更新(cache invalidation)功能

9.使用了消息队列来实现异时订单处理。

 

2.整体架构:


数据库:(暂略)

项目列表:从整体可以看出,Pet Shop 4的项目体系已经很庞大,考虑的方面也较3.0更全面复杂。

 

序号

项目名称

描述

1

BLL

业务逻辑层

2

CacheDependencyFactory

缓存依赖类的工厂类

3

WEB

表示层

4

DALFactory

数据层的抽象工厂

5

DBUtility

数据访问类组件

6

IBLLStrategy

同步/异步策略接口

7

ICacheDependency

缓存依赖类接口

8

IDAL

数据访问层接口定义

9

IMessaging

异时处理消息队列接口定义

10

IProfileDAL

Profile的数据访问层接口定义

11

Membership

Membership认证和授权管理

12

MessagingFactory

异时处理消息队列的抽象工厂

13

Model

业务实体

14

MSMQMessaging

异时处理消息队列的实

15

OracleDAL

Oracle数据访问层

16

OracleProfileDAL

Oracle的Profile Providers

做用户状态管理,包括购物车等

17

OrderProcessor

后台处理进程,处理订单队列

18

Profile

Profile的数据访问层

19

ProfileDALFactory

ProfileDAL的工厂类(反射创建ProfileDAL)

20

SQLProfileDAL

SQL Server 的Profile Providers

做用户状态管理,包括购物车等

21

SQLServerDAL

SQLServer数据访问层

22

TableCacheDependency

缓存依赖实现类

 

项目分解

由于整体已经有22个项目,所以,对于初学者一看就晕了,所以,我做了分解,可以大体上分几块去理解。

序号

项目名称

描述

1

WEB

表示层

2

Model

业务实体

3

BLL

业务逻辑层

4

DALFactory

数据层的抽象工厂

5

IDAL

数据访问层接口定义

6

SQLServerDAL

SQLServer数据访问层

7

OracleDAL

Oracle数据访问层

8

DBUtility

数据库访问组件基础类

9

CacheDependencyFactory

缓存依赖类的工厂类

10

ICacheDependency

缓存依赖类接口

11

TableCacheDependency

缓存依赖实现类

12

IBLLStrategy

同步/异步处理策略接口(实现在bll根据配置反射选择)

13

MessagingFactory

异时处理消息队列的抽象工厂

14

IMessaging

异时处理消息队列接口定义

15

MSMQMessaging

异时处理消息队列的实

16

Profile

Profile的数据访问层

17

ProfileDALFactory

ProfileDAL的工厂类(反射创建ProfileDAL)

18

IProfileDAL

Profile的数据访问层接口定义

19

OracleProfileDAL

Oracle的Profile Providers

做用户状态管理

20

SQLProfileDAL

SQL Server 的Profile Providers

做用户状态管理

21

Membership

Membership认证和授权管理

22

OrderProcessor

后台处理进程,处理订单队列

 

 

 

3.Petshop 4中的设计模式

工厂模式:

首当其冲的就是工厂模式,很容易就可以看出来,也是应用最多的。

DALFactory:数据访问层的抽象工厂(决定创建哪种数据库类型的数据访问层。可以选择:SQLServer,Oracle)

CacheDependencyFactory缓存依赖类的工厂类。(创建具体表的缓存依赖)

MessagingFactory 异时处理消息队列的抽象工厂(反射创建具体的异时处理类

ProfileDALFactoryProfileDAL的工厂类(反射选择创建Oracle 和SQL Server的 ProfileDAL)

 

策略模式: IorderStrategy

 

 

 

中介模式

CategoryDataProxy ItemDataProxy  ProductDataProxy

 

 

 

 

具体介绍可以参看MSDN

.NET Pet Shop 4: Migrating an ASP.NET 1.1 Application to 2.0

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdasamppet4.asp

 

下载

http://download.microsoft.com/download/8/0/1/801ff297-aea6-46b9-8e11-810df5df1032/Microsoft%20.NET%20Pet%20Shop%204.0.msi

[MVC]Asp.net MVC 示例项目"Suteki.Shop"分析之---数据验证

mikel阅读(761)

Asp.net MVC 示例项目"Suteki.Shop"分析之—数据验证

     在Suteki.Shop,实现了自己的数据校验机制,可以说其设计思路还是很有借鉴价值的。而使用
这种机制也很容易在Model中对相应的实体对象(属性)添加校验操作方法。下面就来介绍一下其实
现方式。

     首先,看一下这样类图:

     在Suteki.Shop定义一个“IValidatingBinder”接口,其派生自IModelBinder:  

public interface IValidatingBinder : IModelBinder 
{
    
void UpdateFrom(object target, NameValueCollection values);
    
void UpdateFrom(object target, NameValueCollection values, string objectPrefix);
    
void UpdateFrom(object target, NameValueCollection values, ModelStateDictionary modelStateDictionary);
    
void UpdateFrom(object target, NameValueCollection values, ModelStateDictionary modelStateDictionary, string objectPrefix);
}

 

     其接口中定义了一个重载方法UpdateFrom,其要实现的功能与MVC中UpdateFrom一样,就是自动读取
我们在form中定义的有些元素及其中所包含的内容。  

     实现IValidatingBinder接口的类叫做:ValidatingBinder,下面是其核心代码说明。
   
     首先是BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
该方法是在IModelBinder接口中定义的,是其核心功能,用于将客户端数据转成我们希望Model类型。
       

/// <summary>
/// IModelBinder.BindModel
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="bindingContext"></param>
/// <returns></returns>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    
if (bindingContext == null)
    {
        
throw new ArgumentNullException("bindingContext");
    }
    
if (IsBasicType(bindingContext.ModelType))
    {
        
return new DefaultModelBinder().BindModel(controllerContext, bindingContext);
    }
    var instance 
= Activator.CreateInstance(bindingContext.ModelType);
    var request 
= controllerContext.HttpContext.Request;
    var form 
= request.RequestType == "POST" ? request.Form : request.QueryString;
    UpdateFrom(instance, form);
    
return instance;
}

   

      上面代码第二个if 用于判断bindingContext的Model类型是否是系统类型,比如decimal,string等。
如果是则使用MVC自带的DefaultModelBinder来进行处理。否则就使用该类自己的UpdateFrom方法,从而
实现对当前form中的数据与Model中相应类型的信息绑定,并返相应的 Model 实例(instance)。下面
是其核心代码:

 

public virtual void UpdateFrom(BindingContext bindingContext)
{
    
foreach (var property in bindingContext.Target.GetType().GetProperties())
    {
        
try
        {
            
foreach (var binder in propertyBinders)
            {
                binder.Bind(property, bindingContext);
            }
        }
        
catch (Exception exception)
        {
            
if (exception.InnerException is FormatException ||
                exception.InnerException 
is IndexOutOfRangeException)
            {
    
string key = BuildKeyForModelState(property, bindingContext.ObjectPrefix);
                bindingContext.AddModelError(key, bindingContext.AttemptedValue, 
"Invalid value for {0}".With(property.Name));
    bindingContext.ModelStateDictionary.SetModelValue(key, 
new ValueProviderResult(bindingContext.AttemptedValue, bindingContext.AttemptedValue, CultureInfo.CurrentCulture));
            }
            
else if (exception is ValidationException)
            {
    
string key = BuildKeyForModelState(property, bindingContext.ObjectPrefix);
                bindingContext.AddModelError(key, bindingContext.AttemptedValue, exception.Message);
    bindingContext.ModelStateDictionary.SetModelValue(key, 
new ValueProviderResult(bindingContext.AttemptedValue, bindingContext.AttemptedValue, CultureInfo.CurrentCulture));
            }
            
else if (exception.InnerException is ValidationException)
            {
    
string key = BuildKeyForModelState(property, bindingContext.ObjectPrefix);
                bindingContext.AddModelError(key, bindingContext.AttemptedValue, exception.InnerException.Message);
    bindingContext.ModelStateDictionary.SetModelValue(key, 
new ValueProviderResult(bindingContext.AttemptedValue, bindingContext.AttemptedValue, CultureInfo.CurrentCulture));
            }
            
else
            {
                
throw;
            }
        }
    }
    
if (!bindingContext.ModelStateDictionary.IsValid)
    {
        
throw new ValidationException("Bind Failed. See ModelStateDictionary for errors");
    }
}

     上面代码中的TRY部分就是其数据绑定的代码,而其Catch部分实现了在数据绑定过程中出现的
错误异常(主要是数据验证等,会在后面提到)收集到ModelState(ModelStateDictionary)中
便于后续处理。而这里Suteki.Shop还定义了自己的验证异常类“ValidationException”(位于:
Suteki.Common\Validation\ValidationException.cs,因为代码很简单,就不多做解释了。

    有了ValidatingBinder之后,下面就来看一下Suteki.Shop是如何使用它的。这里以一个业务
流程—“编辑用户”来进行说明。
 
    下面就是UserController(Suteki.Shop\Controllers\UserController.cs) 中的Edit操作:

[AcceptVerbs(HttpVerbs.Post), UnitOfWork]
public ActionResult Edit([DataBind] User user, string password)
{
 
if(! string.IsNullOrEmpty(password))
 {
  user.Password 
= userService.HashPassword(password);
 }
    ..    
}

     在该Action中,我们看到其定义并使用了DataBind这个ModelBinder进行绑定处理,所以我们要
先看一下DataBinder(注:它是Suteki.Shop中关于数据绑定的“ModelBinder的基类)中倒底做了
些什么,下面是其实现代码:
   

public class DataBinder : IModelBinder, IAcceptsAttribute
{
public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
 {
  
object entity;
  
if(declaringAttribute == null || declaringAttribute.Fetch)
  {
   entity 
= FetchEntity(bindingContext, controllerContext);
  }
  
else 
  {
   entity 
= Activator.CreateInstance(bindingContext.ModelType);
  }
  
  
try
  {
   validatingBinder.UpdateFrom(entity, controllerContext.HttpContext.Request.Form, bindingContext.ModelState, bindingContext.ModelName);
  }
  
catch(ValidationException ex) 
  {}
  
return entity;
 } 
 
}

 

      其BindModel方法中“获取当前要编辑的用户数据操作”就是通过下面这一行完成的:      

FetchEntity(bindingContext, controllerContext)

      
     而try中的代码validatingBinder.UpdateFrom()就是对上面所说的“ValidatingBinder”中的
“UpdateFrom”调用。通过UpdateFrom之后就会将绑定时出现的错误异常进行收集。

     有了这种绑定,可以说设置上完成了,而如何将验证规则绑定到相应的Model对象上呢?
   
     为了实现这个功能,Suteki.Shop提供了一个叫做ValidationProperty的泛型类,它提供了对于
数字,是否为空, IsDecimal,最大值,最小值,IsEmail等验证功能。并以扩展方法的行式提供出
来,相应代码如下:     

Code

    
     使用它就可以很方便的对Model中的相关属性添加验证规则了。以User为例,其验证规则添加
内容如下(Suteki.Shop\Models\User.cs):

public void Validate()
{
   Validator validator 
= new Validator
   {
       () 
=> Email.Label("Email").IsRequired().IsEmail(),
       () 
=> Password.Label("Password").IsRequired(),
   };
    validator.Validate();
}

    
    在规则添加完成后,就把对获取到的信息进行验证了,下面是验证的实现方法:

public class Validator : List<Action>
{
    
public void Validate()
    {
        var errors 
= new List<ValidationException>();
        
foreach (Action validation in this)
        {
            
try
            {
                validation();
            }
            
catch (ValidationException validationException)
            {
                 errors.Add(validationException);
            }
        }
        
if (errors.Count > 0)
        {
            
//backwards compatibility
            string error = string.Join("", errors.Select(x => x.Message + "<br />").ToArray());
            
throw new ValidationException(error, errors);
        }
    }
}

 

      代码比较简单,大家看一下就可以了。
   
      到这里,主要的代码就介绍完了,下面再后到UserController中看看Action是如何调用验证方法
并发验证错误信息复制到ModelState中的,接着看一下编辑用户信息这个Action:

[AcceptVerbs(HttpVerbs.Post), UnitOfWork]
public ActionResult Edit([DataBind] User user, string password)
{
    
if(! string.IsNullOrEmpty(password))
    {
       user.Password 
= userService.HashPassword(password);
    }
    
try
    {
        user.Validate();
    }
    
catch (ValidationException validationException)
    {
        validationException.CopyToModelState(ModelState, 
"user");
        
return View("Edit", EditViewData.WithUser(user));
    }
    
return View("Edit", EditViewData.WithUser(user).WithMessage("Changes have been saved")); 
}

     

      大家看到了吧,Try中的user.Validate()就是启动验证的功能,而在Catch中使用CopyToModelState
方法将错误信息Copy到当前Controller中的ModelState中,如下:

public void CopyToModelState(ModelStateDictionary dictionary, string prefix)
{
    
foreach(var error in errors)
    {
        
string key = string.IsNullOrEmpty(prefix) ? error.propertyKey : prefix + "." + error.propertyKey;
       dictionary.AddModelError(key, error.Message);
    }

 

     这样在前台View中,通过Html.ValidationSummary()方法来显示验证结果,现在我们看一下最终的
运行效果:

    以“输入错误的Email地址”为例:
    
   
    
    好了,今天的内容就先到这里了。
   
    原文链接:http://www.cnblogs.com/daizhj/archive/2009/05/08/1452735.html

    作者: daizhj,代震军,LaoD

    Tags: mvc,Suteki.Shop

    网址: http://daizhj.cnblogs.com/   

   

[MVC]ASP.NET MVC下读取XML生成动态表单的例子

mikel阅读(892)

问题的提出:在MVC下动态表单怎么生成?

好的,我们来看下面的例子:

接着上次的MVCMembership来讲

我们首先添加一个目录XML。然后添加View:

1.checkXml.aspx 用来检查我们输入的XML格式(利用XSD检查)

2.New.aspx 用来新增XML表单的

3.Show.aspx 用来显示XML表单的

4.ShowResult.aspx 用来显示XML表单提交的结果的

一、数据库结构

要用到动态的表单,这里我们利用SQLServer2005的XML类型来保存,建表的SQL如下:

use Test
/*==============================================================*/
/* DBMS name:      Microsoft SQL Server 2005                    */
/* Created on:     2009/5/8 7:56:50                             */
/*==============================================================*/
if exists (select 1
from  sysindexes
where  id    = object_id('XMLFORM')
and   name  = 'IDX_XML'
and   indid > 0
and   indid < 255)
drop index XMLFORM.IDX_XML
go
if exists (select 1
from  sysindexes
where  id    = object_id('XMLFORM')
and   name  = 'IDX_ID'
and   indid > 0
and   indid < 255)
drop index XMLFORM.IDX_ID
go
if exists (select 1
from  sysobjects
where  id = object_id('XMLFORM')
and   type = 'U')
drop table XMLFORM
go
/*==============================================================*/
/* Table: XMLFORM                                               */
/*==============================================================*/
create table XMLFORM (
ID                   int                  identity,
FORMXML              xml                  not null,
constraint PK_XMLFORM primary key (ID)
)
go
declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'XMLFORM',
'user', @CurrentUser, 'table', 'XMLFORM'
go
/*==============================================================*/
/* Index: IDX_ID                                                */
/*==============================================================*/
create unique index IDX_ID on XMLFORM (
ID ASC
)
go
/*==============================================================*/
/* Index: IDX_XML                                               */
/*==============================================================*/
create PRIMARY XML INDEX IDX_XML on XMLFORM (
FORMXML
)
go

好了我们建了一个名为XMLForm的表,其中ID自增,FORMXML为我们需要的XML表单的内容

二、编写XML的Controller

XMLController.cs

主要的action

1.New

image

 image image image

 [AcceptVerbs(HttpVerbs.Post), ValidateInput(false)]
public ActionResult New(string xmlContent)
{
System.Threading.Thread.Sleep(2000);    //模拟提交等待
xmlContent = Server.UrlDecode(xmlContent);
if (!string.IsNullOrEmpty(xmlContent))
{
//if (!CheckXML(xmlContent, out strError)) //服务器端检测,如果用了ajax检测,就没必要了
//{
//    ModelState.AddModelError("_FORM",strError);  
//    return View();
//}
XMLFormDataContext db = new XMLFormDataContext();
TransactionOptions opt = new TransactionOptions();
ViewData["xmlContent"] = xmlContent;
opt.IsolationLevel = IsolationLevel.Serializable;
using (TransactionScope tran = new TransactionScope(TransactionScopeOption.RequiresNew, opt))
{
XMLFORM f = new XMLFORM();
try
{
f.FORMXML = XElement.Parse(xmlContent);
db.XMLFORMs.InsertOnSubmit(f);
db.SubmitChanges();
var id = db.XMLFORMs.Max(p => p.ID);
ViewData["result"] = "success";
ViewData["id"] = (int)id;
tran.Complete();
return View();
}
catch
{
ViewData["result"] = "failure";
ModelState.AddModelError("_FORM", "envalid xml format");
return View();
}
}
}
else
return View();
}
XML:
<Form Title="testForm">
<Input Text="ID" Name="txtID" Type="TextBox" Required="true"></Input>
<Input Text="Name" Name="txtName" Type="TextBox" Required="true"></Input>
<Input Text="Job" Name="txtJob" Type="TextBox" Required="false"></Input>
<Input Text="Validate" Name="btnValidate" Type="Button"></Input>
<Input Text="Commit" Name="btnComit" Type="Button"></Input>
</Form>
注意:我们使用了事务,所以不要忘了最后要提交。我就是忘了,找了3天错误   

2.Show

image

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Show(int? ID)
{
int nId = 0;
XElement doc = null;
if (int.TryParse(Request["id"], out nId))
{
try
{
XMLFormDataContext db = new XMLFormDataContext();
var q = from f in db.XMLFORMs
where f.ID == nId
select f.FORMXML
;
if (q.Count() > 0)
{
foreach (var qq in q)
{
doc = qq;
}
ViewData["xml"] = doc;
}
else
{
ModelState.AddModelError("_FORM", "Not Exists");
}
ViewData["id"] = nId;
}
catch (Exception e)
{
ModelState.AddModelError("_FORM", e.Message);
}
}
else
{
ModelState.AddModelError("_FORM", "Miss ID");
}
return View();
}

注意这里Show.asp的写法.不能用<input/>直接输出控件字符串,而要

   Response.Write(Html.TextBox(i.Attribute("Name").Value, "", new { @class = "InputNormal" }));
否则提交后得不到form的值
代码如下:
  <%using (Html.BeginForm())
{ %>
<%=Html.ValidationSummary()%>
<h2>
Show XML Form</h2>
<div id="divResult"></div>
<fieldset>
<legend>XML Form Information</legend>
<table cellpadding="0" cellspacing="0" width="90%">
<%
XElement xml=(XElement)ViewData["xml"];
StringBuilder sbScript = new StringBuilder();
sbScript.AppendLine("<Script language='javascript'>");
sbScript.AppendLine("function checkControls() {");
var input = from p in xml.Elements()
where p.Name == "Input" && p.Attribute("Type").Value != "Button"
select p;
foreach (var i in input)
{
Response.Write("<tr>");
if (bool.Parse(i.Attribute("Required").Value))
{
sbScript.AppendLine(string.Format(
@"$('#{0}').formValidator({{onshow:'please input {1}',onfocus:'{1} required',oncorrect: 'OK' }})",
i.Attribute("Name").Value,
i.Attribute("Text").Value)
);
sbScript.AppendLine(
string.Format(".inputValidator({{ min: 1, empty: {{ leftempty: true, rightempty: true}}, onerror: '{1} required' }});",
i.Attribute("Name").Value,
i.Attribute("Text").Value
));
}
Response.Write(string.Format("<td>{0}</td>", i.Attribute("Text").Value));
Response.Write("<td>");
Response.Write(Html.TextBox(i.Attribute("Name").Value, "", new { @class = "InputNormal" }));
Response.Write("</td>");
Response.Write(string.Format("<td><div id='{0}Tip'></div></td>",i.Attribute("Name").Value));
Response.Write("</tr>");
}
sbScript.AppendLine("}</script>");
Response.Write(sbScript);
input = from p in xml.Elements()
where p.Name == "Input" && p.Attribute("Type").Value == "Button"
select p;
foreach (var b in input)
{
Response.Write("<tr>");
switch (b.Attribute("Text").Value.ToLower())
{
case "commit":
Response.Write(string.Format("<td colspan=3><input id='{0}' type='submit' value='{1}' /></td>", b.Attribute("Name").Value, b.Attribute("Text").Value));
break;
case "validate":
Response.Write(string.Format("<td colspan=3><input id='{0}' type='button' value='{1}' onclick='return jQuery.formValidator.pageIsValid();' /></td>", b.Attribute("Name").Value, b.Attribute("Text").Value));
break;
}
Response.Write("</tr>");
}
%>
</table>
</fieldset>
<%} %>

XML内容的检测

我们可以利用XSD来检查。

代码如下

  #region 检查xml
[AcceptVerbs(HttpVerbs.Post), ValidateInput(false)]
public ActionResult checkXML(string xmlContent)
{
string strOut = string.Empty;
int nResult = (CheckXML(xmlContent, out strOut) == true) ? 1 : 0;
Dictionary<string, string> dicResult = new Dictionary<string, string>();
dicResult.Add("State", nResult.ToString());
dicResult.Add("Message", strOut);
return Json(dicResult);
}
private bool CheckXML(string xmlContent)
{
bool bResult = true;
if (!string.IsNullOrEmpty(xmlContent))
{
//利用XmlSchemaSet(XSD)检查XML
XmlSchemaSet validator = new XmlSchemaSet();
validator.Add("", XmlReader.Create(Server.MapPath(Url.Content("~/Models/xmlTest.xsd")))); //Url.Content必须,否则找不到文件
XDocument doc = null;
try
{
doc = XDocument.Parse(xmlContent);
doc.Validate(validator, (sender, e) =>
{
bResult = false;
});
}
catch
{
bResult = false;
}
}
else
bResult = false;
return bResult;
}
private bool CheckXML(string xmlContent, out string outString)
{
bool bResult = true;
string strError = string.Empty;
if (!string.IsNullOrEmpty(xmlContent))
{
//利用XmlSchemaSet(XSD)检查XML
XmlSchemaSet validator = new XmlSchemaSet();
validator.Add("", XmlReader.Create(Server.MapPath(Url.Content("~/Models/xmlTest.xsd")))); //Url.Content必须,否则找不到文件
XDocument doc = null;
try
{
doc = XDocument.Parse(xmlContent);
doc.Validate(validator, (sender, e) =>
{
strError = e.Message;
bResult = false;
});
}
catch (XmlException e)
{
strError = e.Message;
bResult = false;
}
}
else
bResult = false;
if (!bResult)
outString = strError;
else
outString = "OK";
return bResult;
}
#endregion

这里我们返回json数据,来ajax检查

        $(document).ready(function() {
$("form").submit(function() {
if (jQuery.formValidator.pageIsValid())
$.blockUI({ message: '<h1 class="loading">Please Wait...</h1>' });
});
if ($("#xmlContent").val() != "")
$("#xmlContent").val(decodeURI($("#xmlContent").val()));
$.formValidator.initConfig();
$("#xmlContent").formValidator({ onshow: "please input xml", onfocus: "xml required", oncorrect: "OK" })
.inputValidator({ min: 1, empty: { leftempty: true, rightempty: true }, onerror: "xml required" })
.ajaxValidator({
type: "post",
url: "checkXML",
datatype: "json",
success: function(responseData) {
if (responseData.State == "1") {
return true;
}
else {
return false;
}
},
buttons: $("#submit"),
error: function() { alert("server busy,try later..."); },
onerror: "XML Format error or same content",
onwait: "XML checking,please wait..."
});
});
function CheckForm() {
result = jQuery.formValidator.pageIsValid();
if (result) {
$("#xmlContent").val(encodeURI($("#xmlContent").val()));
}
return result;
}

注意我们利用了encodeURI,decodeURI进行编码,来避免A potentially dangerous Request.QueryString value was detected from the client问题

3.最后编写ShowResult

        [AcceptVerbs(HttpVerbs.Post)]
public ActionResult ShowResult()
{
StringBuilder sbResult = new StringBuilder();
foreach(string key in Request.Form.Keys)
{
sbResult.AppendLine(string.Format("{0}={1}", key, Request.Form[key]));
}
return Json(sbResult.ToString());
}
很简单的直接输出form的内容,你也可以进行你自己的处理,用ajax来避免再次刷新
最后结果
image image 
代码演示和下载 我的个人网站

[Flash]通过Flash实现ASP.NET多文件上传

mikel阅读(753)

转载:http://www.cnblogs.com/beniao/archive/2009/05/15/1457679.html
通过Flash实现ASP.NET多文件上传

      关 于多文件上传,以前我一直使用JQuery去动态创建文件选择组件,然后POST到服务器去。最近一段时间一直在Flash身边打滚,Flash对于多文 件上传有很好的支持,于是我写了一个Flash的多文件上传组件,然后将其封装为ASP.NET控件,当我们在开发ASP.NET程序的时候可以像使用普 通控件一样,从工具箱里拉到界面上就可以使用。

      Flash我采用Flex开发的,实现了选择文件和统计文件大小的功能,选择了多个文件时会动态的创建Flex组件显示到界面上,并且可以移除所选择的文件。以下是Flex项目结构图:                        

                              

      Flex是完全基于事件驱动的开发模型,通过自定义一个Flex的Application来实现文件的多选和文件数量以及上传文件大小 的统计等功能。如上图的FileUpload就是自定义的一个组件,扩展自VBox实现了动态创建组件和处理事件。详细参考下面代码:

自定义Application代码

 

自定义组件

 

自定义组件事件

 

      在Flex的App程序里就使用上面自定义的Application组件来进行设计,详细如下代码块:

<?xml version="1.0" encoding="utf-8"?>
<custom:CustomApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" 
    xmlns:custom
="components.*" layout="horizontal"
         backgroundAlpha
="0"  fontSize="12">
    
<mx:Style>
        .button {fontWeight:normal;}
    
</mx:Style>
    
<mx:HBox>
        
<mx:VBox width="400" id="fileContainer">
            
<mx:VBox id="fileUploadBox" maxHeight="250" width="100%" label="Files to Upload" >
            
</mx:VBox>
            
<mx:HBox id="uploadStats" width="100%" visible="false">
                
<mx:Text text="文件总数:" /><mx:Text id="totalFiles" />
                
<mx:Text text="文件总大小:" /><mx:Text id="totalSize" />
            
</mx:HBox>
            
<mx:ProgressBar id="totalProgressBar" width="275" mode="manual" visible="false" />
        
</mx:VBox>
        
        
<mx:VBox>
            
<mx:Button id="browseButton" label="添加文件" width="100" styleName="button"/>
            
<mx:Button id="clearButton" label="移除文件" visible="false" width="100" styleName="button"/>
            
<mx:Button id="uploadButton" label="上传文件" visible="false" width="100" styleName="button"/>
            
<mx:Button id="cancelButton" label="取消上传" visible="false" width="100" styleName="button"/>
            
<mx:Text id="mytext" />
        
</mx:VBox>
    
</mx:HBox>
</custom:CustomApplication>

       完成了多文件选择功能的Flash开发,现在就是想法将其生成的.swf封装到dll成为ASP.NET的服务器端控件,实现这一步很非常 简单,将生成的.swf作为资源嵌入到ASP.NET的dll文件里就OK。然后自己定义一个类继承于Control,进行相应方法的重写就达到了目的, 这里我们就需要重写Render()方法,在方法里通过调用资源的方法从.dll里读取出嵌入里面的.swf文件资源,然后向客户端输出嵌入flash文 件(.swf)的编码就完成了。

自定义ASP.NET控件代码

 

                                           

 

     控件中定义了多个属性,方便可视化设置控件的属性值,其中最为主要的属性为:UploadPage,该属性的作用就是指定通过那一个处理 程序来处理上传文件的请求,实现请求中上传文件的保存功能。在示例程序里,我自定义了一个HttpHandler来处理上传文件的请求,实现 IHttpHandler接口。程序代码如下:

namespace FlashUpLoadWeb
{
    
/// <summary>
    
/// 上传文件处理程序
    
/// </summary>
    public class UploadHandler : IHttpHandler, IRequiresSessionState
    {
        
#region IHttpHandler 成员
        
public bool IsReusable
        {
            
get { return true; }
        }
        
public void ProcessRequest(HttpContext context)
        {
            
//文件上传存储路径
            string uploadPath = context.Server.MapPath(context.Request.ApplicationPath + "/Upload");
            
for (int i = 0; i < context.Request.Files.Count; i++)
            {
                HttpPostedFile uploadFile 
= context.Request.Files[i];
                
if (uploadFile.ContentLength > 0)
                {
                    uploadFile.SaveAs(Path.Combine(uploadPath, uploadFile.FileName));
                }
            }
            HttpContext.Current.Response.Write(
" ");
        }
        
#endregion
    }
}

 

      定义好了请求处理程序,这时只需要通过配置文件简单的配置就可以设置控件的处理程序页地址了,如下:

<httpHandlers>
    
<remove verb="POST,GET" path="Upload.axd"/>
    
<add verb="POST,GET" path="Upload.axd" type="FlashUpLoadWeb.UploadHandler"/>
</httpHandlers>

                              

 

      另外一个属性就是OnUploadComplete,通过它指定当文件上传完毕后调用客户端的那一个JavaScript提交更新页面状态,详细请查看示例代码。最终效果图下图:

                        

                        

                      

 

      控件还有许多需要完善的地方,比如请求处理程序那一块,上传文件的时候对文件的选择进行判断,不允许重复选择,判断选择队列中的文件在服务器上是否已经存在等等。有兴趣的朋友可以下去改善这些功能!源代码下载

 

版权说明

  本文属原创文章,欢迎转载,其版权归作者和博客园共有。  

  作      者:Beniao

 文章出处:http://beniao.cnblogs.com/  或  http://www.cnblogs.com/

[MVC]Asp.net MVC 示例项目"Suteki.Shop"分析之---Filter

mikel阅读(696)

    在Suteki.Shop中对于Filter的使用上提供了两种方式,一种是从FilterAttribute
(抽象类属性)以及接口 IActionFilterIResultFilter中继承并实现。另一种是我们经
常提到的从ActionFilterAttribute 上继承方式来实现自己的ActionFilter。首先看一下
第一种,同时它也是该项目中被Action广泛使用的方式, 下面是类图:

     

     当然图中最核心的当属FilterUsingAttribute,它同时继承了 FilterAttribute类和
IAuthorizationFilter, IActionFilter, IResultFilter这三个接口,所以其所实现的功能
与MVC中所定义的ActionFilterAttribute如出一辙。同时,下面是其核心代码:
    

public class FilterUsingAttribute : FilterAttribute, IAuthorizationFilter, IActionFilter, IResultFilter
{
        
private readonly Type filterType;
        
private object instantiatedFilter;
        
public FilterUsingAttribute(Type filterType)
        {
            
if(!IsFilterType(filterType))
            {
                
throw new InvalidOperationException("Type '{0}' is not valid within the FilterUsing 
                   attribute as it is not a filter type.".With(filterType.Name));
            }
            
this.filterType = filterType;
        }
        
private bool IsFilterType(Type type)
        {
            
return typeof(IAuthorizationFilter).IsAssignableFrom(type) 
                   
|| typeof(IActionFilter).IsAssignableFrom(type) 
                   
|| typeof(IResultFilter).IsAssignableFrom(type);
        }
        
public Type FilterType
        {
            
get { return filterType; }
        }
        
private T GetFilter<T>() where T : class
        {
            
if(instantiatedFilter == null)
            {
                instantiatedFilter 
= ServiceLocator.Current.GetInstance(filterType); 
            }
            
return instantiatedFilter as T;
        }
        
private void ExecuteFilterWhenItIs<TFilter>(Action<TFilter> action) where TFilter :class 
        {
            var filter 
= GetFilter<TFilter>();
            
if(filter != null)
            {
                action(filter);
            }
        }
        
public void OnAuthorization(AuthorizationContext filterContext)
        {
            ExecuteFilterWhenItIs
<IAuthorizationFilter>(f => f.OnAuthorization(filterContext));
        }
        
public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            ExecuteFilterWhenItIs
<IActionFilter>(f => f.OnActionExecuting(filterContext));
        }
     
        
public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            ExecuteFilterWhenItIs
<IActionFilter>(f => f.OnActionExecuted(filterContext));
        }
        
public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            ExecuteFilterWhenItIs
<IResultFilter>(f => f.OnResultExecuting(filterContext));
        }
        
public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            ExecuteFilterWhenItIs
<IResultFilter>(f => f.OnResultExecuted(filterContext));
        }
}

 
     在上面的OnAction..和OnResult..事件中,都调用了ExecuteFilterWhenItIs这个泛型方法,
而该方法的作用是对泛型约束中使用到的相应IActionFilter进行操作,而获取相应的Filter实例
的工作就交给了GetFilter<T>()方法,因为该方法使用IOC方式将filterType以服务组件的方式进
行创建,所以我们会看到在ContainerBuilder(Suteki.Shop\ContainerBuilder.cs)中有如下代
码,注意最后一行:  

container.Register(
    Component.For
<IUnitOfWorkManager>().ImplementedBy<LinqToSQLUnitOfWorkManager>().LifeStyle.Transient,
    Component.For
<IFormsAuthentication>().ImplementedBy<FormsAuthenticationWrapper>(),
    Component.For
<IServiceLocator>().Instance(new WindsorServiceLocator(container)),

    
     看来其最终会使用Castle框架所提供的IOC功能。
   
     其实理解上面代码并不难,就是Suteki.Shop以自己实现的FilterUsingAttribute代替了MVC
自己的 ActionFilterAttribute, (当然它做的并不彻底,大家会在接下来的内容中看到)。当然有
了FilterUsingAttribute之后,Suteki.Shop并没有直接就去在Action中直接使用它,而是以它派
生出了几个Filter属性:
     UnitOfWorkAttribute:项目中大部分Action使用
     AuthenticateAttribute:用户信息认证
     LoadUsingAttribute
    
     其中UnitOfWorkAttribute和AuthenticateAttribute被用的最多,下面就分别加以介绍说明。
   
     首先是UnitOfWorkAttribute,其构造方法声明中将UnitOfWorkFilter作为其基类方法的构
造类型,如下:

public class UnitOfWorkAttribute : FilterUsingAttribute
{
        
public UnitOfWorkAttribute() : base(typeof (UnitOfWorkFilter))
        {
        }
}
public class UnitOfWorkFilter : IActionFilter
{
        
private readonly IDataContextProvider provider;
        
public UnitOfWorkFilter(IDataContextProvider provider)
        {
            
this.provider = provider;
        }
        
public void OnActionExecuting(ActionExecutingContext filterContext)
        {
        }
        
public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            var context 
= provider.DataContext;
            
if (filterContext.Controller.ViewData.ModelState.IsValid)
            {
                context.SubmitChanges();
            }
        }
}

 

     其要实现的功能主要是对用户所做的数据操作进行判断,如果没有发生异常:
ModelState.IsValid为True时,则提交所做的修改到数据库中。

     接下来再看一个AuthenticateAttribute,前面说过,其所实现的功能就是对当前
用户身份进行验证,其核心代码如下,因为内容比较简单,大家一看便知。
   

public class AuthenticateAttribute : FilterUsingAttribute
{
        
public AuthenticateAttribute() : base(typeof(AuthenticateFilter))
        {
            Order 
= 0;
        }
}
public class AuthenticateFilter : IAuthorizationFilter
{
        
private IRepository<User> userRepository;
        
private IFormsAuthentication formsAuth;
        
public AuthenticateFilter(IRepository<User> userRepository, IFormsAuthentication formsAuth)
        {
            
this.userRepository = userRepository;
            
this.formsAuth = formsAuth;
        }
        
public void OnAuthorization(AuthorizationContext filterContext)
        {
            var context 
= filterContext.HttpContext;
            
if(context.User != null && context.User.Identity.IsAuthenticated)
            {
                var email 
= context.User.Identity.Name;
                var user 
= userRepository.GetAll().WhereEmailIs(email);
                
if (user == null
                {
                    formsAuth.SignOut();
                }
                
else 
                {
                    AuthenticateAs(context, user);
                    
return;
                }
            }
            AuthenticateAs(context, User.Guest);
        }
        
private void AuthenticateAs(HttpContextBase context, User user)
        {
            Thread.CurrentPrincipal 
= context.User = user;                
        }

     当然在本文开篇说过,Suteki.Shop也使用了我们经常用到的方式,即从ActionFilterAttribute
继承实现自己的ActionFilter,比如说CopyMessageFromTempDataToViewData(Suteki.Shop
\Filters\CopyMessageFromTempDataToViewData.cs),从字面可以看出,其要实现的功能就
是把临时数据复制到ViewData中,以便于前台视图显示,下面是其类图:

      

     其实现代码如下:

public class CopyMessageFromTempDataToViewData : ActionFilterAttribute
{
        
public override void OnActionExecuted(ActionExecutedContext filterContext) 
        {
                var result 
= filterContext.Result as ViewResult;
                
if(result != null && filterContext.Controller.TempData.ContainsKey("message"))
                {
                    var model 
= result.ViewData.Model as ShopViewData;
                    
if(model != null && string.IsNullOrEmpty(model.Message))
                    {
                        model.Message 
= filterContext.Controller.TempData["message"as string;
                    }
                }
        }
}

 

     其实看到这里,我感觉Suteki.Shop对于ActionFilter的使用还有待商榷,必定 MVC中的 Filter
是一种耗时的操作,对于程序的运行速度和执行效率来说都是一个考验。这其实也能部分解释为什么我
在本地运行Suteki.Shop时速度会比较慢。

     这里不妨开句玩笑,Suteki.Shop开发者似乎得到ActionFilter强迫症,因为我感觉一个项目中
个Action绑定的Filter最好别超过2个,否则必然会影响程序运行效率,尽管Suteki.Shop基本上控
在了2个左右,但其还是运行速度偏慢。当然这里我并没有做到具体的测试,只是部分猜测,不过有兴
趣的朋友不妨测试一下,看看结果如何,相信会见分晓。

     好了,今天的内容就先到这里了。
       
     原文链接:http://www.cnblogs.com/daizhj/archive/2009/05/14/1453885.html

     作者: daizhj,代震军,LaoD

     Tags: mvc,Suteki.Shop

     网址: http://daizhj.cnblogs.com/

[C#]浅拷贝和深拷贝(shallow copy VS deep copy )

mikel阅读(817)

转载:http://www.cnblogs.com/Roping/archive/2009/05/13/1455880.html

引言

       C#中有两种类型变量,一种 是值类型变量,一种是引用类型变量,对于值类型变量,深拷贝和前拷贝都是通过赋值操作符号(=)实现,其效果一致,将对象中的值类型的字段拷贝到新的对象中.这个很容易理解。 本文重点讨论引用类型变量的拷贝机制和实现。

       C#中引用类型对象的copy操作有两种:
  • 浅拷贝(影子克隆/shallow copy):只复制对象的值类型字段,对象的引用类型,仍属于原来的引用.
  • 深拷贝(深度克隆):不仅复制对象的值类型字段,同时也复制原对象中的对象.就是说完全是新对象产生的.

    浅拷贝和深拷贝之间的区别:浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。
    注意:string类型有点特殊,对于浅拷贝,类值类型对象进行处理。

浅拷贝的实现

  1. 使用Object类MemberwiseClone实现

    MemberwiseClone:创建当前 Object 的浅表副本。
    MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

    代码实现如下:

     public class Person
        {
            
    public int Age { getset; }
            
    public string Address { getset; }
            
    public Name Name { getset; }
            
    public object Clone()
            {
               
    return   this.MemberwiseClone();    
            }
            
        }
        
    public class Name
        {
            
    public Name(string frisName,string lastName)
            {
                FristName 
    = frisName;
                LastName 
    = lastName;
            }
            
    public string FristName { getset; }
            
    public string LastName { getset; }
        }

     

  2. 赋值操作(=)VS使用Object类MemberwiseClone实现
    对于引用类型的变量,我们有种误解,认为赋值操作就是浅拷贝一种,其实不然,两者有区别。

    1. 浅拷贝(shallow copy)对于引用类型对象中的值类型字段进行了逐位复制。赋值运算符只是把源对象的引用赋值给目的对象,两者引用同一个对象。
    2. 浅拷贝后的对象的值类型字段更改不会反映到源对象,而赋值运算后的对象的值类型字段更改会反映到源对象

    代码实现如下:

        public class Person
        {
            
    public int Age { getset; }
            
    public string Address { getset; }
            
    public Name Name { getset; }
        }
        
    public class Name
        {
            
    public Name(string frisName,string lastName)
            {
                FristName 
    = frisName;
                LastName 
    = lastName;
            }
            
    public string FristName { getset; }
            
    public string LastName { getset; }
        }

     

深拷贝实现

      相对于浅拷贝,是指依照源对象为原型,创建一个新对象,将当前对象的所有字段进行执行逐位复制并支持递归,不管是是值类型还是引用类型,不管是静态字段还是非静态字段。
      在C#中,我们们有三种方法实现深拷贝
  1. 实现ICloneable接口,自定义拷贝功能。

    ICloneable 接口,支持克隆,即用与现有实例相同的值创建类的新实例。
    ICloneable 接口包含一个成员 Clone,它用于支持除 MemberwiseClone 所提供的克隆之外的克隆。Clone 既可作为深层副本实现,也可作为浅表副本实现。在深层副本中,所有的对象都是重复的;而在浅表副本中,只有顶级对象是重复的,并且顶级以下的对象包含引 用。 结果克隆必须与原始实例具有相同的类型或是原始实例的兼容类型。

    代码实现如下:


     public class Person:ICloneable
        {
            
    public int Age { getset; }
            
    public string Address { getset; }
            
    public Name Name { getset; }
            
    public object Clone()
            {
                Person tem 
    = new Person();
                tem.Address 
    = this.Address;
                tem.Age 
    = this.Age;
                tem.Name 
    = new Name(this.Name.FristName, this.Name.LastName);
                
    return tem;
            }
        }
        
    public class Name
        {
            
    public Name(string frisName, string lastName)
            {
                FristName 
    = frisName;
                LastName 
    = lastName;
            }
            
    public string FristName { getset; }
            
    public string LastName { getset; }
        }

     

    大家可以看到,Person类继承了接口ICloneable并手动实现了其Clone方法,这是个简单的类,试想一下,如果你的类有成千上万个引用类型成员(当然太夸张,几十个还是有的),这是不是份很恐怖的劳力活?

  2. 序列化/反序列化类实现
    不知道你有没有注意到DataSet对象,对于他提供的两个方法:

    DataSet.Clone 方法,复制 DataSet 的结构,包括所有 DataTable 架构、关系和约束。不要复制任何数据。
    新 DataSet,其架构与当前 DataSet 的架构相同,但是不包含任何数据。注意 如果已创建这些类的子类,则复本也将属于相同的子类。

    DataSet.Copy 方法复制该 DataSet 的结构和数据.
    新的 DataSet,具有与该 DataSet 相同的结构(表架构、关系和约束)和数据。注意如果已创建这些类的子类,则副本也将属于相同的子类。

    好像既不是浅拷贝,又不是深拷贝,是不是很失望?但是两个结合起来不是我们要的深拷贝吗?看看DataSet的实现,注意序列化接口:ISerializable

    序列化是将对象或对象图形转换为线性字节序列,以存储或传输到另一个位置的过程。反序列化是接受存储的信息并利用它重新创建对象的过程。
    通过 ISerializable 接口,类可以执行其自己的序列化行为。

    转换为线性字节序列后并利用其重新创建对象的过程是不是和我们的深拷贝的语意“逐位复制”很相像?

    代码实现如下:

      [Serializable]
        
    public class Person : ICloneable
        {
            
    public int Age { getset; }
            
    public string Address { getset; }
            
    public Name Name { getset; }
            
    public object Clone()
            {
                
    using (MemoryStream ms = new MemoryStream(1000))
                {
                    
    object CloneObject;
                    BinaryFormatter bf 
    = new BinaryFormatter(nullnew StreamingContext(StreamingContextStates.Clone));
                    bf.Serialize(ms, 
    this);
                    ms.Seek(
    0, SeekOrigin.Begin);
                    
    // 反序列化至另一个对象(即创建了一个原对象的深表副本)
                    CloneObject = bf.Deserialize(ms);
                    
    // 关闭流 
                    ms.Close();
                    
    return CloneObject;
                }
            }
        }
        [Serializable]
        
    public class Name
        {
            
    public Name(string frisName, string lastName)
            {
                FristName 
    = frisName;
                LastName 
    = lastName;
            }
            
    public string FristName { getset; }
            
    public string LastName { getset; }
        }
    }

     

    注意:通过序列化和反序列化实现深拷贝,其和其字段类型必须标记为可序列化类型,既添加特性(Attribute)[Serializable]。

  3. 通过反射实现
    通 过序列化/反序列化方式我们能比较流畅的实现深拷贝,但是涉及到IO操作,托管的的环境中,IO操作比较消耗资源。 能不能有更优雅的解决方案。CreateInstance,对,利用反射特性。这个方法大家可以参考这篇博客:http://rubenhak.com /?p=70 文章反射类的Attribute,利用Activator.CreateInstance New一个类出来(有点像DataSet.Clone先获得架构),然后利用PropertyInfo的SetValue和GetValue方法,遍历的 方式进行值填充。

    代码实现如下:

    public class Person
    {
        
    private List<Person> _friends = new List<Person>();
        
    public string Firstname { getset; }
        
    public string Lastname { getset; }
        [Cloneable(CloneableState.Exclude)]
        [Cloneable(CloneableState.Include, 
    "Friends")]
        
    public List<Person> Friends { get { return _friends; } }
        [Cloneable(CloneableState.Exclude)]
        
    public PersonManager Manager { getset; }
    }

     

C#为什么要设计深拷贝和浅拷贝?

这个我也一直也找不到一个合适的答案,希望有人来讨论下!

代码下载

声明: 本文作者:宗哥,宗子城
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明。 …

[工具]Java项目转化为C#项目

mikel阅读(866)

把java 项目转化为C#项目。

狂搞java 墙角,

前提:

1,下载 Eclipse  或 MyEclipse .其实两个都一样。(我用的版本是 Myeclpse 6.5)

2,安装 Eclipse SVN插件。

好了,我们开始了。

1,先从https://source.db4o.com/db4o/trunk/sharpen 作用SVN检出里边的代码,记住,要把下面的项目作为4个项目分别检出。

2,Export 这4个项目为 “Deployable plug-ins and fragments”,导出的位置为 Eclipse 的根目录,如果是Myeclipse 也是导出到 Eclpse 根目录 。

3,对了, 这4个项目要跟你的项目放在同一个工作区里,后面会用到的。(放在其它的位置我没有试过)。

4,在你将要进行转化的项目里写上加上下面的两个文件

文件1:

sharpen.properties

# eclipse workspace
    dir.workspace=C\:/Documents and Settings/Administrator/\u684C\u9762/paoding
    # java executable
    file.jvm.jdk1.5=D\:/Java/jdk1.5.0/bin/java.exe
    # Eclipse home directory
    eclipse.home=D\:/Java/MyEclipse 6.5/eclipse
    # Eclipse startup jar
    eclipse.startup.jar=${eclipse.home}/plugins/org.eclipse.equinox.launcher_1.0.0.v20070606.jar
    # Sandcastle can be used to convert javadoc to .NET xml comments
    # dir.lib.sandcastle=e:/sandcastle/
    # sharpen compile directory
    dir.dist.classes.sharp=dist/sharpen
    # Eclipse plugins home
    plugins.home=${eclipse.home}/plugins

#上面的路径要改成你的系统中的相应的位置

文件2:

Sharpen-Common.Xml

<project name="sharpen common">
 <property file="sharpen.properties" />

 <macrodef name="reset-dir">
  <attribute name="dir" />
  <sequential>
   <delete dir="@{dir}" />
   <mkdir dir="@{dir}" />
  </sequential>
 </macrodef>

 <macrodef name="sharpen">
  <attribute name="workspace" />
  <attribute name="resource" />

  <element name="args" optional="yes" />

  <sequential>
   <exec executable="${file.jvm.jdk1.5}" failonerror="true"
    timeout="1800000">
    <arg value="-Xms256m" />
    <arg value="-Xmx512m" />
    <arg value="-cp" />
    <arg value="${eclipse.startup.jar}" />
    <arg value="org.eclipse.core.launcher.Main" />
    <arg value="-data" />
    <arg file="@{workspace}" />
    <arg value="-application" />
    <arg value="sharpen.core.application" />
    <arg value="-header" />
    <arg file="config/copyright_comment.txt" />
    <arg value="@{resource}" />

    <args />

   </exec>
  </sequential>
 </macrodef>

 <target name="install-sharpen-plugin">

  <property name="sharpen.core.dir" location="../sharpen.core" />
  <reset-dir dir="${dir.dist.classes.sharp}" />

  <echo>${eclipse.home}/plugins</echo>
  <javac fork="true" Debug="true" target="1.5" source="1.5"
   destdir="${dir.dist.classes.sharp}" srcdir="${sharpen.core.dir}/src"
   encoding="UTF-8">
   <classpath>
    <fileset dir="${eclipse.home}/plugins">
     <include name="org.eclipse.osgi_*/osgi.jar" />
     <include
      name="org.eclipse.core.resources_*/resources.jar" />
     <include
      name="org.eclipse.core.runtime_*/runtime.jar" />
     <include name="org.eclipse.jdt.core_*/jdtcore.jar" />
     <!– redundant entries: in newer eclipse installs those reside in jars –>
     <include name="org.eclipse.osgi_*.jar" />
     <include name="org.eclipse.core.resources_*.jar" />
     <include name="org.eclipse.core.runtime_*.jar" />
     <include name="org.eclipse.jdt.core_*.jar" />
     <include name="org.eclipse.jdt.launching_*.jar" />
     <include name="org.eclipse.equinox.*.jar" />
     <include name="org.eclipse.core.jobs_*.jar" />
    </fileset>
   </classpath>
  </javac>

  <property name="plugin.dir"
   value="${plugins.home}/sharpen.core_1.0.0" />
  <reset-dir dir="${plugin.dir}" />
  <jar destfile="${plugin.dir}/sharpen.jar"
   basedir="${dir.dist.classes.sharp}" />
  <copy todir="${plugin.dir}"
   file="${sharpen.core.dir}/plugin.xml" />

 </target>

</project>

 

下面加两个图说明一下问题吧。

项目结构:

 

生成时选项:

 

 

其它的也没什么说的了。

 

参考: http://developer.db4o.com/Resources/view.aspx/Reference/Sharpen/How_To_Setup_Sharpen#