【问题标题】:IMemoryCache not instantiated in Class libraryIMemoryCache 未在类库中实例化
【发布时间】:2018-12-26 11:47:49
【问题描述】:

我对 ASP.Net Core、C#、OOP、Javascript ……基本上是我目前使用的所有东西都很陌生。过去几个月我一直在阅读和学习,以便开始一个新的开发项目。 总而言之,我正在稳步前进,但我遇到了 IMemoryCache 的问题,我无法真正理解(或者是 DI?)。

我正在使用 ASP.Net Core 2.0 和 VS2017。我有一个包含 2 个项目的解决方案,我的主要 ASP.Net Core MVC Web 应用程序和一个 .Net Core 2.0 类库。

在类库中,我添加了一个要在其中使用缓存的类,因此我将 IMemoryCache 添加到构造函数中。 为了实例化这个类,我有另一个构造函数调用函数GetModelGuidAsync

public class MdsEntityCRUD
    {
        #region ClassConstruct

        private readonly IMemoryCache _cache;
        public MdsEntityCRUD(IMemoryCache cache)
        {
            _cache = cache;
        }

        public MdsEntityCRUD(ServiceClient client, string modelName, string entityName, string versionFlag)
        {
            this.client = client;
            this.ModelId = Task.Run(async () => await GetModelGuidAsync(modelName)).Result;
            this.EntityName = entityName;
            mdsWS.Version ver = Task.Run(async () => await GetVersionByPolicyAsync(client, VersionPolicy.Flag, versionFlag, this.ModelId)).Result;
            this.VersionId = ver.Identifier.Id;
        }

        public async Task<Guid> GetModelGuidAsync(string modelName)
        {
            Dictionary<string, Guid> modelNames;

            if (!_cache.TryGetValue("ModelNames", out modelNames) || !modelNames.ContainsKey(modelName))
            {
                modelNames = await GetModelNamesAsync();
                _cache.Set("ModelNames", modelNames, new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60) });  // CFG
            }

            return modelNames[modelName];
        }

我已将 services.AddMemoryCache(); 添加到我的 MVC 项目启动中的 ConfigureServices 中。 我正在使用第二个构造函数从我的 MVC 项目中的控制器调用该类。 在运行时,if (!_cache.TryGetValue-statement 出现错误。这些是 Exception Helper 和异常窗口中显示的详细信息:

System.AggregateException HResult=0x80131500 消息=一个或多个 发生错误。 (对象引用未设置为 对象。)来源=System.Private.CoreLib StackTrace:在 System.Threading.Tasks.Task.ThrowIfExceptional(布尔 includeTaskCanceledExceptions) 在 System.Threading.Tasks.Task`1.GetResultCore(布尔 waitCompletionNotification)在 mdXmdsWS.MdsEntityCRUD..ctor(ServiceClient 客户端,字符串模型名称, String entityName) 在 C:\Users..\MdsEntityCRUD.cs:line 59 at MDS_MVC_Proto2.Controllers.DataExplorerController.EntityDataSource(DataManagerRequest dmr) 在 C:\Users..\Controllers\DataExplorerController.cs:165 行
在 Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(对象 目标,对象 [] 参数)在 Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()

内部异常 1:NullReferenceException:对象引用未设置为 一个对象的实例。

和来自 ($exception).InnerException

  • InnerException {System.NullReferenceException:对象引用未设置为对象的实例。在 Microsoft.Extensions.Caching.Memory.CacheExtensions.TryGetValue[TItem](IMemoryCache 缓存、对象键、TItem& 值)在 mdXmdsWS.MdsEntityCRUD.d__18.MoveNext() 在 C:\Users..\MdsEntityCRUD.cs:第 84 行 --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 在 mdXmdsWS.MdsEntityCRUD.c__DisplayClass16_0.b__0>d.MoveNext() 在 C:\Users..\MdsEntityCRUD.cs:line 59} System.Exception {System.NullReferenceException}

我不知道为什么会出现错误: 是因为缓存在类库中而不是在MVC项目中吗? 是因为我试图在从构造函数执行的函数中使用缓存吗? 我是否缺少一些配置?

渴望了解我哪里出错了......

【问题讨论】:

  • 你的第二个构造函数在你的构造函数中没有IMemoryCache 参数,所以它显然是空的。此外,您的第二个构造函数建议您在某处 new-ing 您的类,因为它具有字符串参数(无法通过 .NET Cores 依赖注入解决 - 除非您使用的是第 3 方 DI/IoC 框架,例如 Autofac 等.)
  • 刚刚根据您的评论尝试了一些事情,但这是我迷路的原因有两个:1)从我读到的关于 DI 的内容中,我了解到只有 1 个包含“注入”的构造函数类,你不应该与其他参数混合? 2)当我将 IMemoryCache 作为参数添加到第二个构造函数时,我需要在实例化类时传递一个参数(你是对的,我是从我的控制器中新建类)但我似乎无法传递一个有效的参数,因为我无法创建一个 IMemoryCache 的 new 实例作为参数传递?
  • 依赖注入无法使用魔法。 DI 所做的一切与您在 new-ing 上课时所做的相同。除了一些图书馆为你做这件事并将它传递给你的班级。当您 new-ing 您的班级时,IoC/DI 框架将不会参与。它没有编译器魔法(如 async/await)。作为一个经验法则:永远不要在你想要注入的类型上调用new。当然也有一些例外(工厂)和非服务(模型、pocos 等)
  • 换句话说:当您调用public MdsEntityCRUD(ServiceClient client, string modelName, string entityName, string versionFlag) 构造函数时,MdsEntityCRUD(IMemoryCache cache) 不会被调用。当您使用 DI/IoC 时,您必须在代码中一直使用它,即从控制器到服务再到另一个服务,再到提供者等。
  • 至于评论的第二部分:当你必须new你的班级,并传递IMemoryCache,你需要在你的控制器中注入IMemoryCache,然后将它传递给你的类作为参数

标签: c# caching asp.net-core dependency-injection


【解决方案1】:

正如 cmets 中所指出的,依赖注入 (DI)/控制反转 (IoC) 并不神奇。从根本上说,依赖注入只是意味着:“将对象 A 的实例传递给构造函数/方法,而不是 new-ing 它在构造函数/方法中”。

var service = new MyService(new MyDependency());

上面已经是依赖注入,你注入一个依赖到MyService,如果它接受一个基类/接口,它的实现可以在不改变MyService的情况下改变。这种方法通常被称为“穷人 DI”,没有任何外部工具或库。

然后是 DI/IoC 框架,它使这更容易,因此您不必自己 new 实例并将其注入服务以及为您管理对象的生存时间(如果每个类都获得同一个实例?(Singleton Lifetime)还是每次都有一个新实例?(Transient Lifetime)特定的类组是否应该获得一个实例而其他组获得另一个实例?(Scoped Lifetime))。

当您使用带或不带框架的 DI/IoC 时,您必须一直使用它。没有魔法。 IoC 框架与上述相同,new-ing 一个类及其依赖项并传递给它们的构造函数。

在 ASP.NET Core 的情况下,控制器是通过内置的 DI/IoC 框架解析的,因此您可以注入任何已注册的类(在 Startup 的 ConfigureServices 方法中)。

在您的情况下,您显然是 new-ing 使用第二个构造函数的类,因此永远不会调用第一个构造函数,也永远不会设置 _cache 成员变量。当您尝试访问它时,您会得到可怕的NullReferenceException 异常。您必须将依赖项添加到主构造函数。

如果你喜欢 new 一个类,你必须自己传递依赖项。在您的情况下,您需要将 IMemoryCache 注入您的控制器,然后将其传递给您的 new-ed 类。

IMemoryCache 缓存

public class MyController : Controller 
{
    private readonly MdsEntityCRUD mdsCrud;

    public MyController(IMemoryCache memoryCache) 
    {
        ServiceClient client = ...;
        mdsCrud = new MdsEntityCRUD(memoryCache, client, "modelname", "entityName", "v1");
    }
}

通过这种方式,您可以从控制器中获得正确的 IMemoryCache 实例,您可以将其传递给服务。

还有其他更优雅的方法来抽象它(使用工厂类或ConfigureServices 中的工厂方法)来执行它,但它超出了问题的范围。

【讨论】:

  • 再次感谢。您不仅解决了我的问题,还给出了迄今为止我遇到的对 DI 的最佳解释。
猜你喜欢
  • 2017-07-22
  • 2015-06-11
  • 2020-01-31
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-15
  • 2018-07-25
相关资源
最近更新 更多