[MVC]Asp.net MVC生命周期

mikel阅读(930)


     public class MvcApplication : System.Web.HttpApplication
        {
            
public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute(
"{resource}.axd/{*pathInfo}");
               
                
//The controller route value is a special value that the System.Web.Mvc.MvcHandler class uses to call into the IControllerFactory interface.
                
//The basic route handler is an instance of IRouteHandler named MvcRouteHandler.
                
//We have complete control and could provide our own implementation of IRouteHandler if we wished.
                routes.MapRoute(
                    
"Default",                                              // Route name
                    "{controller}/{action}/{id}",                           // URL with parameters
                    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
                );
             
    
            }
    
            
protected void Application_Start()
            {
                RegisterRoutes(RouteTable.Routes);
            }
    

 

UrlRoutingMoudulePostResolveRequestCache阶段从RouteCollection中获取当前请求的RouteData.RouteData包含了一个请求处理对应的ControllerAction,RouteData这个作用贯穿请求的处理过程.RouteData中提取RouteHandler,这里默认是MvcRouteHandler,MvcRouteHandler获取HttpHandler,这里默认的是MvcHandler.

 


    public virtual void PostResolveRequestCache(HttpContextBase context)
{
    RouteData routeData 
= this.RouteCollection.GetRouteData(context);
    
if (routeData != null)
    
{
        IRouteHandler routeHandler 
= routeData.RouteHandler;
        
if (routeHandler == null)
        
{
            
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
        }

        
if (!(routeHandler is StopRoutingHandler))
        
{
            RequestContext requestContext 
= new RequestContext(context, routeData);
            IHttpHandler httpHandler 
= routeHandler.GetHttpHandler(requestContext);
            
if (httpHandler == null)
            
{
                
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
            }

            RequestData data2 
= new RequestData();
            data2.OriginalPath 
= context.Request.Path;
            data2.HttpHandler 
= httpHandler;
            context.Items[_requestDataKey] 
= data2;
            context.RewritePath(
"~/UrlRouting.axd");
        }

    }

}

rh 

  

MvcHandler.ProcessRequest()中首先使用HttpContextWrapperHttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
ControllerBuilder.GetControllerFactory –> ControllerFactory.CreateController –> IController.Execute

ControllerBase实现了IController接口,Initialize时将RequestContext封装成为ControllerContext,Controller继承自ControllerBase并实现抽象方法ExecuteCore()

 

mvchttp 

 

ExecuteCore,Controller首先从RouteData中获得ActionName,然后执行ActionInvoker.InvokeAction.

ActionInvoker中我们可以看到各种Filter,这是一种AOP实践:Action方法执行的前后执行若干方法.这里有四种Filter:ActionFilters,ResultFilters,AuthorizationFilters,ExceptionFilters.这四种Filter并不是封闭的,都有对应的接口,这四个只是默认实现.Filter的执行顺序是:AuthorizationFilter—>Action Filter.OnActionExecuting—>Action Method—>ActionFilter.OnActionExecuted.InvokeActionMethodWithFilters返回的结果是ActionExecutedContext,接下来将Controller执行OnResultExecuting 方法.ActionResult执行的结果可以是ViewResult,JsonResult,RedirectResult,ContentResult,或者是自定义的Result类型.

       如果返回的类型是ViewResult,我们先看一下ViewReuslt的继承关系:ViewResult–>ViewResultBase–>ActionResult,ViewResult包含两个属性ViewViewEngineCollection,实际上是包含了两个接口的实现:IViewEngine定义了怎么定位View/Partial View.IView定义了如何RenderView.默认的实现时WebFormViewWebFormViewEngine.

Filter OnResultExecuted 最后一步了,可以这里捕获异常.上面我们说过还有ExceptionFilters,如果前面过程中的异常没有被捕获那么最终都会到冒泡到ExceptionFilters.

  • RouteData中获得ActionName
  • ActionInvoker.InvokeAction
  • 通过ControllerContext获取ControllerDescriptor
  • FindAction-获取ActionDescriptor
  • GetFilters
  • ModelBinderRequest中的数据转换成Action方法需要的参数
  • AuthorizationFilter
  • Action Filter.OnActionExecuting
  • Action
  • ActionFilter.OnActionExecuted
  • ResultFilter.OnResultExecuting
  • ActionResult Execution
  • ResultFilter.OnResultExecuted
  • WebFormViewEngine.CreateView
  • WebFormView.Render
  • ResultFilter.OnExecuted

result 

 

控制权归还到HttpApplication完成后续的生命周期.

 

嗯哼,全文完.

[Flex]Flash Builder 4 beta、Flex SDK(Gumbo)

mikel阅读(791)

详细请看:http://www.k-zone.cn/zblog/post/adobe-flash-builder-gumbo-flash-catalyst-list.html

昨天是2009年的国际儿童节,Adobe也没忘记作为不是儿童的我们,如期的发布了Adobe Flash Builder 4 beta 和 Adobe Flash Catalyst beta 这两个我认为09年上半年最为重磅级的产品:)

而昨天也只是匆忙的介绍了一下Adobe Flash Builder 4 beta 和 Adobe Flash Catalyst beta的下载地址,作为Flex程序员来说,不详细介绍一下这些工具下都对不起大家。:)

其实昨天对外发布的除了Adobe Flash Builder 4 beta 和 Adobe Flash Catalyst beta以外,还应该包括一个重量级的选手 – Flex SDK 4 ( Gumbo )

只不过Gumbo应该是旧消息了,因此才没有大书特书:)
早在MAX 2008的时候,就已经放出了Flex SDK 4 ( Gumbo )的内容,同时在Adobe驱动的open source site上面就可以下载Nightly Builds的Flex SDK 4 ( Gumbo )

Flex SDK 4 ( Gumbo )的一些知识的汇总:
Flex SDK 4 ( Gumbo )主页:
包含了Flex SDK 4 (&
nbsp;Gumbo )的功能、文档、规格等内容:
http://opensource.adobe.com/wiki/display/flexsdk/Gumbo

Flex SDK 4 ( Gumbo )的SDK下载地址:
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4

Adobe Flex SDK 4 ( Gumbo ) Language Reference:
http://livedocs.adobe.com/flex/gumbo/langref/index.html

Adobe Flex SDK 4 ( Gumbo )官方介绍:
http://www.adobe.com/devnet/flex/articles/flex4sdk_whatsnew.html

Flash Builder 4.0 beta的一些知识的汇总:
Flash Builder 4.0 beta是专门配合FFlex SDK 4 ( Gumbo ) ,虽然我们可以在其他版本中导入Flex SDK 4 ( Gumbo ) ,但是却无法体现Flex SDK 4 ( Gumbo ) 的一些特点。
Flash Builder 4.0 beta包含的功能:
1、支持Theme切换。
2、FXP的导入和导出。
3、FlexUnit集成。
4、ASDoc视图。
5、Data Services支持。
等等。

这里有一篇关于Flash Builder 4.0 beta最新功能的介绍:
http://www.adobe.com/devnet/flex/articles/flex4builder_whatsnew.html

这是一个关于Flash Builder 4.0 beta的video介绍。(MAX 2008的介绍,已经很

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

mikel阅读(857)

   在Suteki.Shop中使用了NVeloctiy模版引擎,用于提供可订制的邮件模版。而邮件的功能就是当定单状
态发生变化时,系统会向买家发送邮件通知。其中的邮件信息内容就是采用NVeloctiy的模版(.vm扩展名)
进行订制的。
     因为在Sutekie.Shop的最新源码包中只是部分实现了其功能,而全部的功能还在完善中,所以要运行本
文中所说的功能,需要在下面的链接地址中下载其最新程序文件(包括单元测试文件):

    http://code.google.com/p/sutekishop/source/detail?r=282
   
     要下载的文件包括:
    

/branches/JtG_Enhancements/Suteki.Shop/Suteki.Shop/Views/EmailTemplates/OrderConfirmation.vm 
/branches/JtG_Enhancements/Suteki.Shop/Suteki.Shop/Views/EmailTemplates/OrderDispatch.vm  
/branches/JtG_Enhancements/Suteki.Shop/Suteki.Shop/Views/EmailTemplates/_orderDetails.vm
/branches/JtG_Enhancements/Suteki.Shop/Suteki.Shop/Controllers/OrderStatusController.cs  
/branches/JtG_Enhancements/Suteki.Shop/Suteki.Shop/Services/EmailService.cs 

    
     等等。
   
     当下载并覆盖(或添加)到本地项目中后,我们还需要在Castle Windsor中注册相应的EmailBuilder
组件。我们只要打开ContainerBuilder类并找到其Build方法(Suteki.Shop\ContainerBuilder.cs),并
添加如下代码:

    Component.For<IEmailService>().ImplementedBy<EmailService>().LifeStyle.Singleton
   
    注:有关Castle Windsor 的 IOC的内容我已在这篇文章中做了介绍.  

  
    最终的代码如下所示:   
 

   
container.Register(
  Component.For
<IUnitOfWorkManager>().ImplementedBy<LinqToSQLUnitOfWorkManager>().LifeStyle.Transient,
  Component.For
<IFormsAuthentication>().ImplementedBy<FormsAuthenticationWrapper>(),
  Component.For
<IServiceLocator>().Instance(new WindsorServiceLocator(container)),
  Component.For
<AuthenticateFilter>().LifeStyle.Transient,
  Component.For
<UnitOfWorkFilter>().LifeStyle.Transient,
  Component.For
<DataBinder>().LifeStyle.Transient,
  Component.For
<LoadUsingFilter>().LifeStyle.Transient,
  Component.For
<CurrentBasketBinder>().LifeStyle.Transient,
  Component.For
<ProductBinder>().LifeStyle.Transient,
  Component.For
<OrderBinder>().LifeStyle.Transient,
  Component.For
<IOrderSearchService>().ImplementedBy<OrderSearchService>().LifeStyle.Transient,
  Component.For
<IEmailBuilder>().ImplementedBy<EmailBuilder>().LifeStyle.Singleton,
        Component.For
<IEmailService>().ImplementedBy<EmailService>().LifeStyle.Singleton //新加的代码
 ); 
 

 

     完成了这些工作后,我们就可以编译运行该项目了。
   
     下面我们来看一下今天的主角 EMailBuilder,其实现了使用NVelocityEngine加载模版信息并将
ViewData中的数据与模版中的指定变量进行绑定的工作。下面是其类图:
    
        
 

     下面对其中相关类和接口做一下介绍:
   
     首先是IEmailBuilder接口,该接口中只有一个方法GetEmailContent,用于将指定的NVelocity
版与ViewData的数据进行绑定,其中参数templateName就是指定的NV模版名称,而 viewdata就是服务
(EmailService)所传递过来的定单数据。

 /// <summary>
 
/// Provide the base method and property to build email
 
/// </summary>
 public interface IEmailBuilder
 {
      
/// <summary>
      
/// Get the email content
      
/// </summary>
      
/// <returns>Return the email content.</returns>
      string GetEmailContent(string templateName, IDictionary<stringobject> viewdata);
 }

 
     而做为IEmailBuilder接口的实现类,EMailBuilder 中相应的GetEamilContent方法实现代码如下:

public string GetEmailContent(string templateName, IDictionary<stringobject> viewdata)
{
    
return BuildEmail(templateName, viewdata);
}
string BuildEmail(string templateName, IDictionary<stringobject> viewdata)
{
    
if (viewdata == null)
    {
      
throw new ArgumentNullException("viewData");
    }
    
if (string.IsNullOrEmpty(templateName))
    {
      
throw new ArgumentException("TemplateName");
    }
    var template 
= ResolveTemplate(templateName);
    var context 
= new VelocityContext();
    
foreach (var key in viewdata.Keys)
    {
     context.Put(key, viewdata[key]);
    }
    
using (var writer = new StringWriter())
    {
       template.Merge(context, writer);
       
return writer.ToString();
    }
}

   

     可以看出,最终获取Email内容的工作交给了BuildEmail这个方法,其实现的逻辑还是很清晰的。
首要是判断传入参数是否为空(包括viewdata,templateName),然后调用ResolveTemplate
方法来获取指定NV模版的信息内容,其方法(ResolveTemplate)内容如下:

Template ResolveTemplate(string name)
{
    name 
= Path.Combine(templatePath, name);
    
if (!Path.HasExtension(name))
    {
      name 
+= ".vm";
    }
    
if (!velocityEngine.TemplateExists(name))
    {
      
throw new InvalidOperationException(string.Format("Could not find a template named '{0}'", name));
    }
    
return velocityEngine.GetTemplate(name);
}

   

      ResolveTemplate的工作流程即:先判断指定的模版路径中是否包括扩展名,如不包括则添加"vm"
结尾的扩展名(该扩展名是NV模版的指定扩展名)。然后继续判断指定路径下的模版文件是否存在“
TemplateExists”。并最终使用velocityEngine来完成获取模版文件内容信息的功能。

     注:velocityEngine的初始化在构造方法:
    
     EmailBuilder(IDictionary<string, object> nvelocityProperties) 中实现。

      分析完ResolveTemplate方法,再回到上面的BuildEmail方法中看一下其余的代码。运行完获取模
版信息的方法之后, 接着就是使用viewdata中的数据构造一个 VelocityContext对象并使用它来完成
与指定模版信息的绑定了。即下面的这几行代码:

   var context = new VelocityContext();
   
foreach (var key in viewdata.Keys)
   {
     context.Put(key, viewdata[key]);
   }
   
using (var writer = new StringWriter())
   {
     template.Merge(context, writer);
     
return writer.ToString();
   }

  

     到这里其本上就完成了对NV模版的数据绑定并返回其最终结果了。下面看一下Suteki.Shop是如何
使用它的。

     首先我们要看一下整个EMail通知发送体系的类图:

 
   

     注:图中右下角就是上面我们所说的那个EmailBuilder 
     
     我们要先清楚了解一下上图中的类关系:
   
     图中右上角OrderStatusController这个Controller,顾名思义其用于定单状态发生变化时的控制器操作,
它定义了几个Action方法(图中的Method),其中Dispatch方法就包括对EmailService类中“发送Dispatch通
知(SendDispatchNotification)”的方法调用,而就是该方法会最终完成对EMailBuilder调用,下面就以调用
的先后顺序依次介绍一下其实代码:

    首先是OrderStatusController中的Dispatch方法,其实现代码如下:  

  [AdministratorsOnly]
    
public class OrderStatusController : ControllerBase
    {
        
readonly IRepository<Order> orderRepository;
        
readonly IUserService userService;
        
readonly IEmailService emailService;
        
public OrderStatusController(IRepository<Order> orderRepository, IUserService userService, IEmailService emailService)
        {
            
this.orderRepository = orderRepository;
            
this.emailService = emailService;
            
this.userService = userService;
        }
        [UnitOfWork]
        
public ActionResult Dispatch(int id)
        {
            var order 
= orderRepository.GetById(id);
            
if (order.IsCreated)
            {
                order.OrderStatusId 
= OrderStatus.DispatchedId;
                order.DispatchedDate 
= DateTime.Now;
                order.UserId 
= userService.CurrentUser.UserId;
                emailService.SendDispatchNotification(order);
            }
            
return this.RedirectToAction<OrderController>(c => c.Item(order.OrderId));
        }
        
    }

    
     首先就是把Http请求过来的定单ID作为参数,并调用orderRepository.GetById方法获取该定单ID的相关
信息,然后判断其是否已被创建(IsCreated为“true”), 如果已创建就可以将当前的定单信息中的状态设
为“DispatchedId”,同时将 DispatchedDate时间设置为系统当前时间,然后是用户ID的绑定。当一切完
成后,就可以将该定单数据作为参数传递给IEmailService的SendDispatchNotification方法,以启动“发送
Email通知买家”的流程了。

     下面是接口IEmailService的实现类“EmailService”(Suteki.Shop\Services\EmailService.cs)中相应
方法的实现代码:
  

public void SendDispatchNotification(Order order)
{
    var viewdata 
= new Dictionary<stringobject>
    {
       { 
"order", order },
       { 
"shopName", baseService.ShopName }
    };
    var email 
= builder.GetEmailContent(OrderDispatchTemplate, viewdata);
    var subject 
= "{0}: Your Order has Shipped".With(baseService.ShopName);
    sender.Send(order.Email, subject, email, 
true);
}

 

     在这里就完成了对EmailBuilder类中的GetEmailContent方法的调用,并最终使用EmailSender(发送
邮件的功能类)来发送邮件(“Send方法”)给指定的买家。

     下面就完看一下其最终的运行效果,首先我们要先创建一个定单(注:创建定单的流程在本系列文章的第
一篇中已做过说明,这里就暂且略过了)。然后我们以管理员的身份登录系统,并单击顶部导航的“Online-
Shop”链接,然后点击左侧的“Orders”链接即可看到一个订单列表页面,如下图所示:

      
    

     然后点击相应的订单“Number”,就会进入到相应订单明细页面,如下:
   
      

     点击页面中的“Dispatch”链接,之后就会修改当前定单的状态同时发送相应的Email给买家了,这里为
了清楚起见,我在EmailBuilder中设置了一个断点,并截了一张图,来让大家看一下其最终返回的邮件信息内
容:

       
   
     因为我本地的机器上未安装发送Email的插件,所以无法真正将该Email发送到我指定的邮箱,以便能看到
最终效果,但这并不影响大家对本文的了解,呵呵。
   
    
    今天就先到这里了。

    
    原文链接:http://www.cnblogs.com/daizhj/archive/2009/06/03/1457521.html

    作者: daizhj,代震军,LaoD

    Tags: mvc,Suteki.Shop,NVelocity

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

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

mikel阅读(800)

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

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阅读(886)

     在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阅读(998)

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阅读(766)

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阅读(899)

问题的提出:在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阅读(765)

转载: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/