ASP.NET MVC和MemoryCache - 我该如何使用它?

7
我在Application_Start中有以下内容:
var crumbsCache = new MemoryCache("breadCrumbsNames");
var crumbsList = new List<CacheItem>
                    {
                        //list of new CacheItem();
                    };
foreach (var cacheItem in crumbsList)
{
    crumbsCache.Add(cacheItem, new CacheItemPolicy());
}

现在,在我的控制器中,我正在这样做:
var cache = new MemoryCache("breadCrumbsNames");
var cacheItem = cache.GetCacheItem("nameOfCacheItem");

但是cacheItem始终为空,我做错了什么?

请查看此链接:http://www.deanhume.com/home/blogpost/object-caching----net-4/37 - Mate
2个回答

12

我认为对你来说更好的选择是使用Ninject或其他依赖注入框架将你的MemoryCache按需注入到控制器中。

首先,您需要将NinjectNinject.Mvc3(以及其他相关部分)添加到您的ASP.NET MVC项目中。如果您正在使用Visual Studio,则可以使用NuGet来完成此操作。这很简单,并且非常自动化。

接下来的步骤是将您的MemoryCache封装到某种接口中,例如:

public interface IMemoryCacheService
{
    MemoryCache MemoryCache
    {
        get;
        set;
    }
}

而且:

public class MemoryCacheService : IMemoryCacheService
{
    public MemoryCacheService()
    {
        MemoryCache = new MemoryCache();
    }

    public MemoryCache MemoryCache
    {
        get;
        set;
    }
}

然后您需要在Ninject中定义一个绑定,以便Ninject知道当您需要一些类型为IMemoryCacheService的东西时,它应该给您MemoryCacheService的实例。

我将在此处粘贴我的自己的Ninject配置类。在您的项目中创建的那个类非常相似,并且将位于名为App_Start(如果您使用NuGet,则会自动创建)的文件夹中。 Ninject默认创建的类名为NinjectWebCommon

public static class NinjectConfig
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));

        bootstrapper.Initialize(CreateKernel);
    }

    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Bind<Func<IKernel>>()
              .ToMethod(context => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>()
              .To<HttpApplicationInitializationHttpModule>();
        kernel.RegisterServices();

        return kernel;
    }

    private static void RegisterServices(this IKernel kernel)
    {
        kernel.Bind<IMemoryCacheService>()
              .To<MemoryCacheService>()
              .InSingletonScope();
              // InSingletonScope() is important so Ninject knows
              // to create only one copy and then reuse it every time
              // it is asked for

        // ignore the stuff below... I have left it in here for illustration
        kernel.Bind<IDbTransactionFactory>()
              .To<DbTransactionFactory>()
              .InRequestScope();
        kernel.Bind<IDbModelContext>()
              .To<DbModelContext>()
              .InRequestScope();
        kernel.Bind<IDbModelChangeContext>()
              .To<DbModelChangeContext>()
              .InRequestScope();
        kernel.Bind<IUserContext>()
              .To<UserContext>()
              .InRequestScope();

        kernel.BindAttributeAndFilter<IgnoreNonAjaxRequestsFilter, IgnoreNonAjaxRequestsAttribute>();
        kernel.BindAttributeAndFilter<ProvideApplicationInfoFilter, ProvideApplicationInfoAttribute>();
        kernel.BindAttributeAndFilter<ProvideSessionInfoFilter, ProvideSessionInfoAttribute>();
        kernel.BindAttributeAndFilter<UseDialogLayoutFilter, UseDialogLayoutAttribute>();
        kernel.BindAttributeAndFilter<CheckResourceAccessFilter, CheckResourceAccessAttribute>();
        kernel.BindAttributeAndFilter<CheckResourceStateFilter, CheckResourceStateAttribute>();
    }

    private static void BindAttributeAndFilter<TFilter, TAttribute>(this IKernel kernel)
    {
        kernel.BindFilter<TFilter>(FilterScope.Action, null)
              .WhenControllerHas<TAttribute>();
        kernel.BindFilter<TFilter>(FilterScope.Action, null)
              .WhenActionMethodHas<TAttribute>();
    }
}

最后,您的控制器将从以下内容更改:

public class HomeController : Controller
{
    public ActionResult Foo()
    {
        ...
    }

    ...
}

致:

public class HomeController : Controller
{
    private IMemoryCacheService memoryCacheService;

    public HomeController(IMemoryCacheService memoryCacheService)
    {
        this.memoryCacheService = memoryCacheService;
    }

    public ActionResult Foo()
    {
        // use this.memoryCacheService in your controller methods...
    }

    ...
}

假设您按照上述策略创建了另一个名为IEmailService的服务,您希望在HomeController中也能使用IEmailService,则:

public class HomeController : Controller
{
    private IMemoryCacheService memoryCacheService;
    private IEmailService emailService;

    public HomeController(IMemoryCacheService memoryCacheService, IEmailService emailService)
    {
        this.memoryCacheService = memoryCacheService;
        this.emailService = emailService;
    }

    public ActionResult Foo()
    {
        // use this.memoryCacheService in your controller methods...
        // and also use this.emailService in your controller methods...
    }

    ...
}

Ninject会更改ASP.NET MVC控制器工厂,以自动向控制器构造函数提供注入的参数。

我认为这种方法从长远来看比保留全局变量等更好。


3
我的看法是,对于大多数情况而言,这样做增加的开销比增加的价值更多。【编辑补充:这可能适用于分布式缓存。】按照Ryan Byrne所描述的方法,只需使用MemoryCache.Default即可。 - Will Rogers
我写了以上所有内容更多是为了说明正确的方法,而不是回答具体的问题,如果说实话。核心问题在于OP试图在控制器中创建一个对象(这将是每个请求),而他需要一个单例。一般来说,我们希望注入依赖项,而不是使用静态类/属性。 - Umar Farooq Khawaja
1
此外,如果您阅读了OP对Ryan Byrne答案的评论,很明显(也许是不知情的情况下),OP正在寻找一个DI机制,而不仅仅是一个正确初始化的全局变量。 - Umar Farooq Khawaja
1
感谢您的详细回答,Umar! - Shane Fulmer

10

您正在每个控制器中创建一个新的MemoryCache实例。由于它是新的,因此其中没有任何内容,这就是您的值始终为空的原因。您需要访问在Application_Start中创建的相同实例。请考虑使用MemoryCache.Default


但是MemoryCache.Default到底是什么?如果我设置了两个不同的memcache实例,MemoryCache.Default怎么知道哪一个是默认的呢? - ojek
将MemoryCache.Default设置为您想要使用的实例。 - Ryan Byrne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接