获取另一个控制器动作的ActionResult?

时间:2012-12-16 05:56:27

标签: asp.net-mvc caching view controller httpcontext

我想将项目存储在应用程序缓存中,以便从主布局视图中延迟加载。

我还希望能够使缓存无效,所以如果它无效,下次请求items-collection时,它会重新加载到缓存位置。

这是我实施的内容:

在控制器中:

protected IEnumerable<Slide> CachedSlides
{
  get { return HttpContext.Application[SlidesCacheKey] as IEnumerable<Slide>; }
  set { HttpContext.Application[SlidesCacheKey] = value; }
}

private void ClearSlides()
{
  CachedSlides = null;
}

[AllowAnonymous]
public IEnumerable<Slider> GetSlides()
{
  if (CachedSlides == null)
    CachedSlides = Context.Slides.OrderBy(p => p.SortOrder).ToArray();
  return CachedSlides;
}

我是视图(更好地在'a'视图中说,我希望能够从每个视图加载它):

@{
  var sliderController = new LevEl.Controllers.Admin.SliderController().  
  var sliderModel = sliderController.GetSlides();
}

它抛出异常,因为当我在视图中初始化控制器时,HttpContext属性返回null(导致NullReferenceException)。

欢迎任何其他实现方法。

2 个答案:

答案 0 :(得分:4)

首先,您可能需要考虑使用Cache而不是Application字典,特别是因为您的缓存数据将在某个时候到期。看看this question

另外,请考虑控制器方法是否仅由视图使用,因为MVC将公开控制器中的所有公共方法。如果您不希望使用网址自由访问此方法,请设置[NonAction] attribute

关于您的错误,修复它的一种快速方法是在控制器的CachedSlides属性的实现中通过System.Web.HttpContext.Current访问Application对象。

您还可以在视图中创建SliderController的新实例时设置ControllerContext。这样,当访问CachedSlides属性时,控制器中的HttpContext将不返回null:

@{
    var sliderController = new LevEl.Controllers.Admin.SliderController();
    sliderController.ControllerContext = new ControllerContext(ViewContext.RequestContext, sliderController);
    var sliderModel = sliderController.GetSlides();
}

如果你有一个能够处理所有这些视图的基本控制器类是有意义的,那么获得控制器会更清晰。您只需要将ViewContext.Controller实例强制转换为基本控制器类:

var sliderController = ViewContext.Controller as BaseSlideController;
var sliderModel = sliderController.GetSlides();

但是,所有这些方法都需要您将这段代码添加到每个视图中。您可以考虑为所有需要访问Slide集合的视图设置基类:

public abstract class SlidesEnabledView<T> : WebViewPage<T>
{
    private IEnumerable<Slide> _slides;
    protected IEnumerable<Slide> Slides
    {
        get
        {
            if(_slides == null)
            {
                var sliderController = ViewContext.Controller as BaseSlideController;
                _slides = sliderController.GetSlides();
            }
            return _slides;
        }
    }
}

然后您将@inherits标记添加到您的视图中,因此它们从我们刚创建的基类继承(当使用@inherits时,您也不能使用@model,所以在@inherits中您将绑定通用基本视图类型ti具体模型类型)。这将允许您使用基本SlidesEnabledView视图类中定义的属性。假设SlidesEnabledView的命名空间是Level.ViewClasses.Admin,这看起来像:

@inherits Level.ViewClasses.Admin.SlidesEnabledView<SomeViewModelClass> 

Number of Slides: @Slides.Count() 

最后,如果您通过网站使用DI并且已经配置了DependencyResolver,那么您可以考虑将幻灯片移动到其自己的类和界面(如ISlidesProviderCachedSlideProvider)。然后,您可以在抽象视图类中使用属​​性注入来获取将在Slides属性中使用的ISlidesProvider实例:

public interface ISlidesProvider
{ 
    IEnumerable<Slide> GetSlides();
}

public class CachedSlideProvider : ISlidesProvider
{
    //you will need a constructor that takes the "Context" object, which will be injected into this class

    public IEnumerable<Slide> GetSlides()
    {
        if (CachedSlides == null)
            CachedSlides = Context.Slides.OrderBy(p => p.SortOrder).ToArray();
        return CachedSlides;
    }

    private IEnumerable<Slide> CachedSlides
    {
        get { return System.Web.HttpRuntime.Cache[SlidesCacheKey] as IEnumerable<Slide>; }
        set { System.Web.HttpRuntime.Cache[SlidesCacheKey] = value; }
    }
}

public abstract class SlidesEnabledView<T> : WebViewPage<T>
{
    private IEnumerable<Slide> _slides;
    protected IEnumerable<Slide> Slides
    {
        get
        {
            if(_slides == null)
            {
                _slides = this.SlidesProvider.GetSlides();
            }
            return _slides;
        }
    }

    //This property will be set by your DI container
    //You have configured ISlidesProvider to be resolved as CachedSlidesProvider in the DI container
    //You have also implemented and registered an MVC DependencyResolver that uses your DI container
    //For example, using Microsoft Unity you could set this attribute
    [Dependency]
    public ISlidesProvider SlidesProvider { get; set; }        
}

当您使用无参数构造函数创建控制器实例时,可能您没有在您的站点上使用DI。所以这个DI选项对于解决这个特殊问题可能有点过分。

答案 1 :(得分:1)

您可以在GetSlides

中定义BaseController方法
public class BaseController : Controller
{
    protected IEnumerable<Slide> CachedSlides
    {
        get { return HttpContext.Application[SlidesCacheKey] as IEnumerable<Slide>; }
        set { HttpContext.Application[SlidesCacheKey] = value; }
    }

    private void ClearSlides()
    {
        CachedSlides = null;
    }

    public IEnumerable<Slide> GetSlides()
    {
        if (CachedSlides == null)
            CachedSlides = Context.Slides.OrderBy(p => p.SortOrder).ToArray();
        return CachedSlides;
    }
}

因此,所有控制器都必须从BaseController继承。

在视图中,您可以这样做:

@{
    var baseController = (BaseController)ViewContext.Controller;
    var slides = baseController.GetSlides();
}