【问题标题】:How should I cache data from database to be thread safe by EF Core?我应该如何缓存数据库中的数据以确保 EF Core 的线程安全?
【发布时间】:2022-01-25 19:20:23
【问题描述】:

我有一个数据列表,它并不大(大约 400 项)。它存储在MySQL数据库中,我想将其缓存到ASP.NET Core中以防止经常读取它。更重要的是,数据会每小时刷新一次。

这是我的代码:

public class IPCheckService
{
        readonly SIComponent.Database.BLContext Context;       

        public IPCheckService(SIComponent.Database.BLContext Context)
        { 
            this.Context = Context;
        }       

        List<SIComponent.Database.IPAndUAModel> _IPBlackList = null;

        public List<SIComponent.Database.IPAndUAModel> IPBlackList 
        {
            get  
            {
                if (DateTime.Now - LastUpdateTime > TimeSpan.FromHours(2))
                {                                    
                    if (Context.IpBlackList.Count() != _IPRangeBlackList.Count)
                    {
                        _IPRangeBlackList = new List<SIComponent.Database.IPAndUAModel>(Context.IpBlackList);
                    }                
                    LastUpdateTime = DateTime.Now;
                }

                if (_IPBlackList == null)
                {
                    _IPBlackList = new List<SIComponent.Database.IPAndUAModel>(Context.IPBlackList);
                }

                return _IPBlackList;
            }
        }     
           
        DateTime LastUpdateTime = DateTime.Now;       
    }

更重要的是,上面的代码是服务的一部分,是一个单例服务。

程序运行后,我做的异常中间件有时会捕捉到这个异常:

在创建模型时尝试使用它。不能以任何利用正在创建的模型的方式在“OnModelCreating”内部使用 DbContext 实例。

在 Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
在 Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
在 Microsoft.EntityFrameworkCore.DbContext.get_Model()
在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.CheckState()
在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable.GetEnumerator()
在 System.Collections.Generic.List1..ctor(IEnumerable1 集合)
在 C:\Project\abc\Services\IPCheckService.cs:第 12 行中的 abc.Services.IPCheckService.get_IPBlackList()
在 C:\Project\abc\Middlewares\IPAccessCheckMiddleware.cs:line 701 中的 abc.Middlewares.IPAccessCheck(IPModel IP) 在 lambda_method9(闭包,对象)
在 Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper 映射器,ObjectMethodExecutor 执行器,对象控制器,Object[] 参数)
在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker 调用程序,ValueTask1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeNextActionFilterAsync&gt;g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&amp; next, Scope&amp; scope, Object&amp; state, Boolean&amp; isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextResourceFilter&gt;g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State&amp; next, Scope&amp; scope, Object&amp; state, Boolean&amp; isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeAsync&gt;g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeAsync&gt;g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.&lt;Invoke&gt;g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.&lt;Invoke&gt;g__AwaitMatcher|8_0(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task1 matcherTask) 在 C:\Project\abc\Middlewares\ExceptionMiddleware.cs:line 26 中的 abc.Middlewares.ExceptionMiddleware.Invoke(HttpContext context) 处

我阅读了一些关于这个异常的文章,发现这似乎是因为DbContext 和相关类的实例成员不能保证是线程安全的。

但是,我没有找到有关如何解决此问题的文章。我该如何解决?谢谢。

【问题讨论】:

    标签: asp.net-core .net-core entity-framework-core


    【解决方案1】:

    您不能以这种方式使用 DbContext。它通常注册为范围服务,但您正试图将其注入单音。多线程也有问题,你必须注意检索数据。

    这是我的变种:

    public class IPCheckService
    {
        private readonly IServiceProvider _serviceProvider;       
        private DateTime _lastUpdateTime;       
        private List<SIComponent.Database.IPAndUAModel> _IPBlackList = null;
    
        public IPCheckService(IServiceProvider serviceProvider)
        { 
            _serviceProvider = serviceProvider;
        }       
    
        public IReadOnlyList<SIComponent.Database.IPAndUAModel> IPBlackList 
        {
            get  
            {
                // you need lock because several threads may access this property concurrently.
                lock(this)
                {
                    if (_IPBlackList != null && (DateTime.Now - _lastUpdateTime).TotalHours < 1)
                    {
                        return  _IPBlackList;
                    }
    
                    // context will be create within scope and automatically disposed
                    using (var scope = serviceProvider.CreateScope())
                    {   
                        var context = scope.ServiceProvider.GetService<SIComponent.Database.BLContext>();
    
                        _IPBlackList = context.IPBlackList.ToList();           
                        _lastUpdateTime = DateTime.Now;
                    }
    
                    return _IPBlackList
                }
            }
        }     
    }
    

    【讨论】:

    • 非常感谢。我认为应该是对的。让我试试看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2017-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多