姜删懔 发表于 2025-5-29 20:36:24

Asp.net MVC 示例项目"Suteki.Shop"分析之---Controller

     在上文中,介绍了如何安装和使用Suteki,今天我们通过源码来看一下Suteki是如何使用
Controller。
     在Suteki中,其使用Abstract的方式来定义一个ControllerBase,以此作为所有Controller
的基类,下面是其Controller的类设计图:
    
    
      在该基类中定义了一些Controller中常用到的方法,比如为当前视图添加MetaDescription,
Title等:   

public abstract class ControllerBase : Controller, IProvidesBaseService
{
        private IBaseControllerService baseControllerService;

        /// 
        /// Supplies services and configuration to all controllers
        /// 
        public IBaseControllerService BaseControllerService
        {
            get { return baseControllerService; }
            set 
            { 
                baseControllerService = value;

                ViewData["Title"] = "{0}{1}".With(
                    baseControllerService.ShopName,
                    GetControllerName());

                ViewData["MetaDescription"] = "\"{0}\"".With(baseControllerService.MetaDescription);
            }
        }

        public ILogger Logger { get; set; }

        public virtual string GetControllerName()
        {
            return " - {0}".With(GetType().Name.Replace("Controller", ""));
        }


        public virtual void AppendTitle(string text)
        {
            ViewData["Title"] = "{0} - {1}".With(ViewData["Title"], text);
        }

        public virtual void AppendMetaDescription(string text)
        {
            ViewData["MetaDescription"] = text;
        }

     public string Message
     {
   get { return TempData["message"] as string; }
   set { TempData["message"] = value; }
     }

  protected override void OnException(ExceptionContext filterContext) {
   Response.Clear();
   base.OnException(filterContext);
  }
}    
      当然,细心的朋友发现了该抽象类中还包括一个IBaseControllerService接口实例。
该接口的主要定义了一些网店系统信息,如店铺名称,版权信息,Email信息等,如下:

public interface IBaseControllerService
{
    IRepository CategoryRepository { get; }
    string GoogleTrackingCode { get; set; }
    string ShopName { get; set; }
    string EmailAddress { get; set; }
    string SiteUrl { get; }
    string MetaDescription { get; set; }
    string Copyright { get; set; }
    string PhoneNumber { get; set; }
    string SiteCss { get; set; }
}   
      而作为唯一一个实现了该接口的子类“BaseControllerService”定义如下:  
public class BaseControllerService : IBaseControllerService
{
        public IRepository CategoryRepository { get; private set; }
        public string GoogleTrackingCode { get; set; }
        public string MetaDescription { get; set; }
        private string shopName;
        private string emailAddress;
        private string copyright;
        private string phoneNumber;
        private string siteCss;

        ..
}    
      而初始化BaseControllerService实例并将配置文件中的信息绑定到该类实例中的操作交给了Windsor,
该组件在Castle中用于实现IOC操作,其配置文件位于项目Suteki.Shop\Configuration\Windsor.config.
下面是其配置结点内容:

  
    Suteki Shop
    info@sutekishop.co.uk
    UA-1643677-4
    Suteki Shop is a new self service eCommerce solution. Search engine optimised and fully customisable
    Site.css
  

    
     这类就完成了把网店的系统信息绑定到Controller中的操作,而Controller就会在其基类中将相关的
信息绑定到ViewData中,如下: 
 ViewData["Title"] = "{0}{1}".With(baseControllerService.ShopName, GetControllerName());
 ViewData["MetaDescription"] = "\"{0}\"".With(baseControllerService.MetaDescription); 
 
     到这里,其实大家应该发现这种对Controller的处理与我们以前所使用的PageBase方式相似,就是将
项目中所有的Page都继承自PageBase,然后在相应的Page中引用PageBase中定义的属性和方法。
     有了ControllerBase,我们看一下在相应的子Controller中是如何使用的,这里有一个例子,
ProductController(位于Suteki.Shop\Controllers\ProductController.cs): 
   


public class ProductController : ControllerBase
{
    
    
 public ActionResult Item(string urlName)
 {
  return RenderItemView(urlName);
 }
    ..
    
 ActionResult RenderItemView(string urlName)
 {
  var product = productRepository.GetAll().WithUrlName(urlName);
  AppendTitle(product.Name);
  AppendMetaDescription(product.Description);
  return View("Item", ShopView.Data.WithProduct(product));
 } 
 
}
 
      该Controller中的Action:"Item"调用了RenderItemView()就是使用了基类中的AppendTitle,
AppendMetaDescription。下面是其运行时的截图:
    
        

        
    
      除了上面所说的这种ControllerBase方式,Suteki.Shop还使用了Controller方式来实现对
一些公用Action的操作,比如列表,编辑,添加记录,调整记录上下位置等。而这块实现代码被放
置在了Suteki.Common\ScaffoldController.cs和OrderableScaffoldController.cs文件中,其中
ScaffoldController为父类,其中包括列表,编辑,添加Action等。
 
Code
public class ScaffoldController : Controller where T : class, new()
{
    public IRepository Repository { get; set; }
    public IRepositoryResolver repositoryResolver { get; set; }
    public IValidatingBinder ValidatingBinder { get; set; }
    public IHttpContextService httpContextService { get; set; }

    public virtual ActionResult Index(int? page)
    {
        return RenderIndexView(page);
    }

    protected virtual ActionResult RenderIndexView(int? page)
    {
        var items = Repository.GetAll().AsPagination(page ?? 1);
        return View("Index", ScaffoldView.Data().With(items));
    }

    public virtual ActionResult New()
    {
        var item = new T();
        return View("Edit", BuildEditViewData().With(item));
    }

 
 
 public ActionResult New( T item)
 {
  if(ModelState.IsValid)
  {
   Repository.InsertOnSubmit(item);
   TempData["message"] = "Item successfully added."; 
   return RedirectToAction("Index");
  }
  return View("Edit", BuildEditViewData().With(item));
 }

    
    public virtual ScaffoldViewData BuildEditViewData()
    {
        var viewData = ScaffoldView.Data();
        AppendLookupLists(viewData);
        return viewData;
    }

    public virtual ActionResult Edit(int id)
    {
        T item = Repository.GetById(id);
        return View("Edit", BuildEditViewData().With(item));
    }

 
 public virtual ActionResult Edit( T item)
 {
  if(ModelState.IsValid)
  {
   TempData["message"] = "Item successfully updated.";
   return RedirectToAction("Index");
  }

  return View("Edit", BuildEditViewData().With(item));
 }

    public virtual ActionResult Delete(int id, int? page)
    {
        T item = Repository.GetById(id);
        Repository.DeleteOnSubmit(item);
        //Repository.SubmitChanges();

        return RedirectToAction("Index", new {page});
    }

    /// 
    /// Appends any lookup lists T might need for editing
    /// 
    /// 
    
    public virtual void AppendLookupLists(ScaffoldViewData viewData)
    {
        // find any properties that are attributed as a linq entity
        foreach (var property in typeof(T).GetProperties())
        {
            if (property.PropertyType.IsLinqEntity())
            {
                AppendLookupList(viewData, property);
            }
        }
    }

    private void AppendLookupList(ScaffoldViewData viewData, PropertyInfo property)
    {
        var repository = repositoryResolver.GetRepository(property.PropertyType);

        // get the items
        object items = repository.GetAll();

        // add the items to the viewData
        viewData.WithLookupList(property.PropertyType, items);
    }
}   
     大家请注意ScaffoldController类中的几个公共属性:   
    public IRepository Repository { get; set; }
    public IRepositoryResolver repositoryResolver { get; set; }
    public IValidatingBinder ValidatingBinder { get; set; }
    public IHttpContextService httpContextService { get; set; }
   
      其中Repository是一些对数据CRUD的操作对象,下面是Repository中的一些接口成员方法:

public interface IRepository where T : class
{
    T GetById(int id);
    IQueryable GetAll();
    void InsertOnSubmit(T entity);
    void DeleteOnSubmit(T entity);
    
    void SubmitChanges();

      这样就可以在ScaffoldController使用统一的接口函数调用相应子类中的实现方法了。   
      而ScaffoldController的子类OrderableScaffoldController则实现了对数据集合中的某行元素
上下移动的操作:
   


public class OrderableScaffoldController : ScaffoldController where T : class, IOrderable, new()
{
    public IOrderableService OrderableService { get; set; }

    protected override ActionResult RenderIndexView(int? page)
    {
        var items = Repository.GetAll().InOrder().AsPagination(page ?? 1);
        return View("Index", ScaffoldView.Data().With(items));
    }

    public override ActionResult New()
    {
        T item = new T
        {
            Position = OrderableService.NextPosition
        };
        return View("Edit", (object)BuildEditViewData().With(item));
    }

   
    public virtual ActionResult MoveUp(int id, int? page)
    {
        OrderableService.MoveItemAtPosition(id).UpOne();
  return RedirectToAction("Index");
    }
    
    public virtual ActionResult MoveDown(int id, int? page)
    {
        OrderableService.MoveItemAtPosition(id).DownOne();
  return RedirectToAction("Index");
    }
}

   
     注:IOrderableService的实现相对复杂一些,具体内容详见Suteki.Common\Services\OrderableService.cs.
     
     按说有了这些功能之后,只要在相应的子类中直接继承使用就可以了,但在Suteki.Shop项目中
作者又对OrderableScaffoldController进行了一个“继承式”扩展,提供了与前面所说的那个“
ControllerBase"相似的方法定义,如下:

public abstract class ShopScaffoldController : OrderableScaffoldController, IProvidesBaseService where T : class, IOrderable, new()
{
    private IBaseControllerService baseControllerService;

    /// 
    /// Supplies services and configuration to all controllers
    /// 
    public IBaseControllerService BaseControllerService
    {
        get { return baseControllerService; }
        set
        {
            baseControllerService = value;
            ViewData["Title"] = "{0}{1}".With(
                baseControllerService.ShopName,
                GetControllerName());
        }
    }

    public virtual string GetControllerName()
    {
        return " - {0}".With(GetType().Name.Replace("Controller", ""));
    }
}   
     而ShopScaffoldController这个抽象类有三个子类,如下图:
  
    

      因为这三个Controller的功能需求相似,而相应的Action实现也在基类“ScaffoldController”
中实现,所以相应的类代码基本上就没有什么了。只不过在这几个子类中都被绑定了UnitOfWork过滤
器(UnitOfWorkFilter),其代码如下Suteki.Common\Filters\UnitOfWorkAttribute.cs:
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();
  }
 }
}    
      其核心功能就是在对用户提交的数据进行有效验证后调用DataContext的SubmitChanges()方法
(注:该逻辑被放在了OnActionExecuted方法中实现)来保存修改,这种做法在以往的MVC示例子没有
看到过,呵呵,不过这种做法还有待研究。
      好了,今天的内容就先到这里了,在下一篇中,将来讨论一下该项目中对MVC框架中Filter的用
法。
      原文链接: http://www.cnblogs.com/daizhj/archive/2009/05/12/1451955.html
      作者: daizhj,代震军,LaoD
      Tags: mvc
      网址: http://daizhj.cnblogs.com/

  
   
   
   
   
   
   

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Asp.net MVC 示例项目"Suteki.Shop"分析之---Controller