简体   繁体   English

C# ASP.NET Core 6 & EF Core数据库服务已经在static方法中配置

[英]C# ASP.NET Core 6 & EF Core database service is already disposed in static method

I am extending the [Authorize] attribute to run custom authorization logic.我正在扩展[Authorize]属性以运行自定义授权逻辑。 Everything looks to be working - that is, it compiles - except when I try to get the related information from the database.一切看起来都在工作——也就是说,它可以编译——除非我尝试从数据库中获取相关信息。

I created a static class, with a static method, I can call from within the attribute OnAuthorization method.我使用 static 方法创建了 static class,我可以从属性OnAuthorization方法中调用。 The static method checks the cache for the information, or gets it directly from the database if it's not in cache yet. static 方法检查缓存中的信息,如果尚未在缓存中,则直接从数据库中获取。

public static class CacheHelper
{
    private static IMemoryCache? _memoryCache;
    private static RoleService _roleService;

    public static void Configure(IMemoryCache memoryCache, RoleService roleService)
    {
        _memoryCache = memoryCache;
        _roleService = roleService;
    }

    public static Dictionary<Permissions, byte> GetRolesPermissions(int roleId)
    {
        if (_memoryCache.TryGetValue(roleId, out string permissions))
        {
            return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
        } 
        else
        {
            string alsoPermissions = _roleService.Get(roleId).Permissions;
            _memoryCache.Set(roleId, alsoPermissions);
            return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
        }
    }
}

The configuration gets set up in Program.cs:配置在 Program.cs 中设置:

CacheHelper.Configure(app.Services.GetRequiredService<IMemoryCache>(), serviceScope.ServiceProvider.GetRequiredService<RoleService>());

Except I get an error when it tries to get the data:除了尝试获取数据时出现错误:

System.ObjectDisposedException: 'Cannot access a disposed context instance... System.ObjectDisposedException: '无法访问已释放的上下文实例...

I have not seen this with any of my other many services running smoothly.我的其他许多服务运行平稳时,我还没有看到这一点。

I think it has something to do with the way I'm configuring the service for the static class... What is the correct way to get at the service from within the static class? I think it has something to do with the way I'm configuring the service for the static class... What is the correct way to get at the service from within the static class?

A unit of work pattern essentially helps cover scoping a DbContext, and there are several examples & implementations out there.工作单元模式本质上有助于覆盖 DbContext 的范围,并且有几个示例和实现。 The one I use for EF is the DbContextScope from Medhime which was developed for EF6 though there are forks for EF Core.我用于 EF 的是 Medhime 的 DbContextScope,它是为 EF6 开发的,尽管有用于 EF Core 的分支。 ( https://github.com/mehdime/DbContextScope ) https://github.com/mehdime/DbContextScope

At a very basic level to write yourself and have full transparency to the workings would be to inject a DbContextFactory, though the goal would be to have the DbContext's scope (lifetime) managed at a high enough level, and a common desire for teams is to avoid adding EF references to their top-level projects.在一个非常基本的级别上,编写自己并对工作完全透明是注入一个 DbContextFactory,尽管目标是将 DbContext 的 scope(生命周期)管理在足够高的级别,团队的共同愿望是避免将 EF 引用添加到其顶级项目。 It also makes sense to abstract the DbContext/DbSets in the event that you want to unit test your business logic as they can be somewhat clumsy to mock.如果您想对业务逻辑进行单元测试,那么抽象 DbContext/DbSet 也是有意义的,因为模拟它们可能有些笨拙。

The very simple example: (No UoW, just scoping a DbContext)非常简单的示例:(没有 UoW,只是确定 DbContext 的范围)

public static Dictionary<Permissions, byte> GetRolesPermissions(int roleId)
{
    if (_memoryCache.TryGetValue(roleId, out string permissions))
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    
    using(var context = new ServiceDbContext())
    {
        string alsoPermissions = _roleService.GetPermissionsForRole(roleId, context);
        _memoryCache.Set(roleId, alsoPermissions);
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    }
}

This scopes a DbContext instance and provides it to any calls that will need a DbContext.这限定了一个 DbContext 实例并将其提供给任何需要 DbContext 的调用。 If any of those services make modifications, a context.SaveChanges() can be applied at the end of the scope, handling any exceptions.如果这些服务中的任何一个进行了修改,则可以在 scope 的末尾应用context.SaveChanges() ,处理任何异常。

The downsides of this approach is that the DbContext is not injected so it cannot be substituted to test this service.这种方法的缺点是未注入 DbContext,因此无法替代它来测试此服务。 It also requires that the service methods all need to be passed a reference for the DbContext since they cannot neatly accept an injected dependency.它还要求所有服务方法都需要传递 DbContext 的引用,因为它们不能巧妙地接受注入的依赖项。 This ensures that where calls to multiple services and such are done with the same DbContext instance.这确保了使用相同的 DbContext 实例完成对多个服务的调用等。

The next example: (DbContextScopeFactory)下一个示例:(DbContextScopeFactory)

public static Dictionary<Permissions, byte> GetRolesPermissions(int roleId)
{
    if (_memoryCache.TryGetValue(roleId, out string permissions))
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    
    using(var contextScope = _contextScopeFactory.Create())
    {
        string alsoPermissions = _roleService.GetPermissionsForRole(roleId, contextScope);
        _memoryCache.Set(roleId, alsoPermissions);
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    }
}

A ContextScope is a simple wrapper interface and implementation for the DbContext(s) which can expose a new instance of a DbContext as well as an overarching SaveChanges() method. ContextScope 是 DbContext(s) 的简单包装接口和实现,它可以公开 DbContext 的新实例以及总体 SaveChanges() 方法。 This essentially forms a Unit of work.这本质上是 forms 一个工作单元。 The ContextScopeFactory can be injected, though in the case of a Singleton pattern static implementation that's not a big issue/concern.可以注入 ContextScopeFactory,尽管在 Singleton 模式static实现的情况下,这不是一个大问题/担忧。 The downside is that this scope still needs to be passed to each service method.缺点是这个 scope 仍然需要传递给每个服务方法。

Unit of Work: (Medhime DbContextScope)工作单元:(Medhime DbContextScope)

public static Dictionary<Permissions, byte> GetRolesPermissions(int roleId)
{
    if (_memoryCache.TryGetValue(roleId, out string permissions))
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    
    using(var contextScope = _dbContextScopeFactory.Create())
    {
        string alsoPermissions = _roleService.GetPermissionsForRole(roleId);
        _memoryCache.Set(roleId, alsoPermissions);
        return JsonSerializer.Deserialize<Dictionary<Permissions, byte>>(permissions);
    }
}

This looks very similar, however note that the DbContextScope does not need to be passed into the service method.这看起来非常相似,但请注意,不需要将 DbContextScope 传递到服务方法中。 The DbContextScope is provided by an injected DbContextScopeFactory similar to the previous example, however the service accepts an injected IAmbientDbContextScopeLocator which it uses to resolve a DbContextScope and get the relevant DbContext instance or instances. DbContextScope 由注入的 DbContextScopeFactory 提供,类似于前面的示例,但是服务接受注入的IAmbientDbContextScopeLocator ,它用于解析 DbContextScope 并获取相关的 DbContext 实例。

Example Service:示例服务:

public class RoleService : IRoleService
{
    private readonly IAmbientDbContextScopeLocator _contextScopeLocator = null;

    private ServiceDbContext Context
    {
        get { return _contextScopeLocator.Get<ServiceDbContext>(); }
    }

    public RoleService(IAmbientDbContextScopeLocator contextScopeLocator)
    {
        _contextScopeLocator = contextScopeLocator ?? throw new ArgumentNullException("contextScopeLocator");
    }

    // ...
}

Here your service can interact with a DbContext instance so long as it is called within a scope.在这里,您的服务可以与 DbContext 实例交互,只要它在 scope 中被调用。 Medhime's implementation accommodates both a Read/Write context scope ( Create ) and a read-only one ( CreateReadOnly ) so that implementations have a bit more control over whether calls should be updating data or not. Medhime 的实现同时容纳了读/写上下文 scope ( Create ) 和只读上下文 ( CreateReadOnly ),因此实现可以更好地控制调用是否应该更新数据。 You can also create scopes within scopes for more fine control where you might want to perform a DB operation (such as logging) without "poisoning" or being dependent on whether the outer scope is committed successfully or not.您还可以在范围内创建范围以进行更精细的控制,您可能希望在不“中毒”或依赖外部 scope 是否成功提交的情况下执行数据库操作(例如日志记录)。 ( DbContextScopeOption.ForceCreateNew ) ( DbContextScopeOption.ForceCreateNew )

Services can even call SaveChanges() on the DbContext instance within the scope to validate & handle exceptions, but it is at the scope level (DbContextScope) outside of all of the calls that ultimately commits the changes or not to the DB /w DbContextScope.SaveChanges() .服务甚至可以在 scope 内的 DbContext 实例上调用SaveChanges()来验证和处理异常,但它处于 scope 级别 (DbContextScope) 的所有调用之外,最终将更改提交或不提交到 DB /w DbContextScope.SaveChanges()

It can take a moment to wrap your head around the Scope Factory and Ambient Scope Locator, but it has been by far the sleekest and most non-intrusive implementation for a unit of work that allows for more fine grained control of the "scope" of the work unit.可能需要一点时间来了解 Scope 工厂和环境 Scope 定位器,但它是迄今为止最时尚、最非侵入性的工作单元实现,可以更精细地控制“范围”工作单位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 c#-将DataContext放置在ASP.NET Core调度程序中 - c# - DataContext disposed in ASP.NET Core scheduler 更新数据库引发 FK 级联错误(EF Core / C# / ASP.NET Core) - Update-Database throwing FK cascade error (EF Core / C# / ASP.NET Core) 无法访问ASP.NET Core后台服务中的已处置对象 - Cannot access a disposed object in ASP.NET Core background service 在asp.net核心中部署作用域服务之前如何执行方法? - How to execute method before scoped service is disposed in asp.net core? ASP.NET Core 2.1,C#应用程序,C#Web服务,访问SQL数据库 - asp.net core 2.1, C# app, c# web service, access SQL database C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET 核心中已处置的 object - C#: Cannot access a disposed object in ASP.NET Core when injecting DbContext with IQueryable ASP.NET Core - IServiceProvider什么时候处理掉? - ASP.NET Core - When is IServiceProvider disposed? 如何在 c# asp.net 内核中使用 web 服务 - How to consume a web service in c# asp.net core 在ASP.NET Core中具有EF Core的Razor Pages-更新相关数据-使用c#的8之7 - Razor Pages with EF Core in ASP.NET Core - Update Related Data - 7 of 8 with c# 扩展方法 C# ASP.NET 核心 - Extension Method C# ASP.NET Core
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM