当通过控制器注入时,范围内的生命周期服务变成单例

Scoped lifetime services become singletons when injected through controllers

我在 .NET Core 应用程序中有一个控制器:

public FriendsController(IFriendRepository friendRepository)
        {
            this.friendRepository= friendRepository;
        }

IFriendRepository 是一个通过 class:

实现的接口
public class FriendRepository : IFriendRepository {
...
}

在 Startup 中,我在 ConfigureServices() 中使用以下行进行设置:

services.AddScoped<IFriendRepository , FriendRepository >();

但是,当使用控制器时,FriendRepository 的生命周期设置为单例而不是作用域。我能够找到的原因是在这个页面上:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

在服务生命周期下,Scoped。它显示:

我不明白如何使用 Invoke 而不是构造函数。他们使用的示例是针对自定义中间件的,我至少不知道如何为构造函数解释它。

public class FriendRepository : IFriendRepository
    {
        private readonly ManagementDbContext dbContext;

        public FriendRepository(ManagementDbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        public void Add(Friend friend)
        {
            this.dbContext.Friends.Add(friend);
        }
        public void Remove(Friend friend)
        {
            this.dbContext.Remove(friend);
        }


        public void Update(Friend friend)
        {
            this.dbContext.Update(friend);
        }
    }

下面是FriendRepository里面的“GetFriends”:

public async Task<QueryResult<Friend>> GetFriendsAsync(FriendQuery queryObj)
        {
            var result = new QueryResult<Friend>();

            var query = dbContext.Friends
                        .Include(c => c.Type)
                        .AsQueryable();

            if(queryObj.TypeId.HasValue)
            {
                query = query.Where(c => c.Type.Id == queryObj.TypeId);
            }

            if(queryObj.Name != null && queryObj.Name.Length > 0)
            {
                query = query.Where(c => c.Name.Contains(queryObj.Name));
            }

            // todo add total price here
            var columnsMap = new Dictionary<string, Expression<Func<Calculation, object>>>()
            {
                ["id"] = c => c.Id,
                ["name"] = c => c.Name,
                ["type"] = c => c.Type,
                ["totalFriends"] = c => c.TotalFriends,
                ["createdTime"] = c => c.CreatedTime
            };
            query = query.ApplyOrdering(queryObj, columnsMap);

            result.TotalItems = await query.CountAsync();

            query = query.ApplyPaging(queryObj);

            result.Items = await query.ToListAsync();

            return result;
        }

我解决了,我会先解释我的假设,因为修复可能非常局限于我的场景。

我在 3 个存储库中使用了所有 DBContext。它们都使用异步函数,但是它们都包含等待内部使用的任何异步函数的内部。 这个问题似乎只在我开始使用这些存储库后才会出现,就像我在控制器中直接访问 dbContext 之前一样。这让我想到了link中的问题,我也在问题中贴了一张图片:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

尽管它只指定了中间件,但我认为它值得一试,因为我想不出任何其他问题。

现在说说实际问题。我在 UserRepository 中的一个函数 GetUser() 是一个异步方法,即使错误似乎出现在 FriendRepository 方法中,但由于它们总是崩溃,结果证明 GetUser() 函数被使用了一次无需等待即可在 AddJwtBearer 下启动。

我原以为因为它内部有一个 await,所以不会产生问题。我也没有注意到这是一个问题,因为我非常专注于另一个存储库。我希望也许我错过了一些简单的东西,比如在中间件切换生命周期中通过构造函数进行依赖注入,而不管生命周期已经设置成什么。

对于以后的其他人,我最终做了两件事,这让我能够清楚地逐步调试我的应用程序。

  1. 我创建了一个 Logger static class,它允许我轻松地将文本保存到文件中。我用它来记录正在使用的函数、构造函数等。这让我可以确保我可以跟踪构造函数和函数被调用的次数、调用顺序以及哪些不会被调用。这是其他人的记录器:

    public static class Logger
    {
     public static void Log(string text, string fileName)
     {
         string path = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "/" + fileName;
    
         bool done = false;
    
         while (!done)
         {
             done = true;
    
             try
             {
                 FileStream fileStream = null;
                 fileStream = System.IO.File.Open(path, System.IO.File.Exists(path) ? FileMode.Append : FileMode.OpenOrCreate);
    
                 using (StreamWriter fs = new StreamWriter(fileStream))
                 {
                     fs.WriteLine(text);
                 };
                 fileStream.Close();
             }
             catch (IOException)
             {
                 done = false;
    
             }
    
         }
     }
     public static void Log(string text)
     {
         Log(text, "logger.txt");
     }
    }
    
  2. 我向 DBContext 添加了一个字符串,每当我在任何函数中使用它时,我都会在使用它的 class 的名称之后添加函数的名称。所以如果我的 FriendsRepository 将在函数 GetTypes 中使用它,它看起来像:

    myDbContext.methodUsing = "FriendsRepository>GetTypes()";
    

感谢@GuruStron 的耐心,并就如何一步一步地给我建议,向我解释中间件错误的想法没有立足之地,并向我建议如何进行调试。