首页 新闻 会员 周边

Microsoft.EntityFrameworkCore异常System.InvalidOperationException

0
悬赏园豆:200 [已解决问题] 解决于 2018-08-17 12:58

场景描述:

单位项目目前使用Masstransit rabbitmq 做发布订阅

订阅方接收到消息后会做一些增删改动作

数据操作用的EntityFramworkCore,DbContext使用Autofac注入的,范围是InstancePerLifetimeScope

问题:

目前发现如果出现并发的情况,数据增删改会出现异常

System.InvalidOperationException

A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

代码:

发布方

 public CommonOutputDto SystemLoginOut(SystemLoginOutRequest systemLoginOutRequest)
        {
            var result = new CommonOutputDto() { IsSuccess = true };
            foreach (var distributorID in systemLoginOutRequest.Distributors)
            {
                var model = _ssoAuthTokenRepository.Table.Where(x => x.DISTRIBUTOR_ID.Equals(distributorID));
                if (model.Count() > 0)
                {
                    _ssoAuthTokenRepository.Delete(model);
                }
                //向mq发送信息
                _busControl.Publish(new SSOAuthTokenConsumer()
                {
                    DistributorID = distributorID,
                    tokenConsumerType = ToKenConsumerType.DeleteAll
                });
            }
            return result;
        }

订阅方

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            var appSettings = Configuration.Get<AppSettings>();
            if (appSettings.tokenConfig.AuthCenterType == AuthCenterType.Slave)
            {
        //订阅
var _busControl = BusConfiguration.CreateBus(appSettings, (cfg, host) => cfg.ReceiveEndpoint(host, appSettings.rabbitMQConfig.QueueName, e => { e.Consumer<ToKenConsumer>(); })); applicationLifetime.ApplicationStarted.Register(_busControl.Start); applicationLifetime.ApplicationStopped.Register(_busControl.Stop); } app.SwaggerConfigBuilder(); app.UseMiddleware(typeof(ApiErrorHandlingMiddleware)); app.UseMvc(); app.SwaggerConfigBuilder(); }

TokenConsumer实现   注:lock是我的临时解决办法 但我还是想找到问题所在

 public class ToKenConsumer : IConsumer<SSOAuthTokenConsumer>
    {
        private static readonly object ConsumerLock = new object();
        public Task Consume(ConsumeContext<SSOAuthTokenConsumer> context)
        {
            lock (ConsumerLock)
            {
                try
                {
                    var ssoService = EngineContext.Current.Resolve<ISSOService>();
                    ssoService.ResetSlaveToken(context.Message);
                }
                catch (Exception ex)
                {
                    LogHelper.Error(context.Message.tokenConsumerType.ToString() + "  " + ex.Message, ex);
                }
                return Task.CompletedTask;
            }
        }
    }

注入的DBContext

builder.RegisterType<OracleContext>().As<IDbContext>().InstancePerLifetimeScope();
ssoService的实现
复制代码
        private readonly IRepository<SSOAuthToken> _ssoAuthTokenRepository;
        private readonly IDbContext _dbContext;
     
   public SSOService(
             IDbContext dbContext,
            IRepository<SSOAuthToken> ssoAuthTokenRepository)
        {
            this._dbContext = dbContext;
            this._ssoAuthTokenRepository = ssoAuthTokenRepository;
        }

public void ResetSlaveToken(SSOAuthTokenConsumer ssoAuthTokenConsumer)
        {
            switch (ssoAuthTokenConsumer.tokenConsumerType)
            {
                case ToKenConsumerType.Add:
                    {
                        var model = new SSOAuthToken()
                        {
                            ID = GetSeqID(),
                            CREATE_DATE = ssoAuthTokenConsumer.ssoAuthToken.CREATE_DATE,
                            DISTRIBUTOR_ID = ssoAuthTokenConsumer.ssoAuthToken.DISTRIBUTOR_ID,
                            TOKEN = ssoAuthTokenConsumer.ssoAuthToken.TOKEN,
                            TOKEN_TYPE = ssoAuthTokenConsumer.ssoAuthToken.TOKEN_TYPE
                        };
                        _ssoAuthTokenRepository.Insert(model);
                        break;
                    }
                case ToKenConsumerType.Update:
                    {
                        var token = ssoAuthTokenConsumer.ssoAuthToken.TOKEN;
                        var did = ssoAuthTokenConsumer.ssoAuthToken.DISTRIBUTOR_ID;
                        var model = _ssoAuthTokenRepository.Table.Where(x => x.TOKEN.Equals(token) &&
                        x.DISTRIBUTOR_ID.Equals(did)).FirstOrDefault();
                        if (model != null)
                        {
                            model.TOKEN = ssoAuthTokenConsumer.ssoAuthToken.TOKEN;
                            model.DISTRIBUTOR_ID = ssoAuthTokenConsumer.ssoAuthToken.DISTRIBUTOR_ID;
                            model.CREATE_DATE = ssoAuthTokenConsumer.ssoAuthToken.CREATE_DATE;
                            model.TOKEN_TYPE = ssoAuthTokenConsumer.ssoAuthToken.TOKEN_TYPE;
                            _ssoAuthTokenRepository.Update(model);
                        }
                        break;
                    }
                case ToKenConsumerType.Delete:
                    {
                        var token = ssoAuthTokenConsumer.ssoAuthToken.TOKEN;
                        var did = ssoAuthTokenConsumer.ssoAuthToken.DISTRIBUTOR_ID;
                        var model = _ssoAuthTokenRepository.Table.Where(x => x.TOKEN.Equals(token) &&
                        x.DISTRIBUTOR_ID.Equals(did)).FirstOrDefault();
                        if (model != null)
                        {
                            _ssoAuthTokenRepository.Delete(model);
                        }
                        break;
                    }
                case ToKenConsumerType.DeleteAll:
                    {
                        var did = ssoAuthTokenConsumer.DistributorID;
                        var model = _ssoAuthTokenRepository.Table.Where(x => x.DISTRIBUTOR_ID.Equals(did)).ToList();
                        if (model.Count() > 0)
                        {
                            _ssoAuthTokenRepository.Delete(model);
                        }
                        break;
                    }
            }
            ResetRedisToken(ssoAuthTokenConsumer.DistributorID);
        }
复制代码

 

请各路大神帮我看看 为什么会报这个错误 我百度了很久 但都是介绍"context before a previous asynchronous operation" 这个错误,可是我并没有用异步。

拜托各位了 新注册的号 还没有豆豆 - -!

 
232111的主页 232111 | 初学一级 | 园豆:22
提问于:2018-08-15 23:00
< >
分享
最佳答案
1

ssoService 是不是被注册为单例了?

收获园豆:200
dudu | 高人七级 |园豆:30994 | 2018-08-16 08:20

园主 你感觉是这句的问题 "var ssoService = EngineContext.Current.Resolve<ISSOService>();" ?

232111 | 园豆:22 (初学一级) | 2018-08-16 08:22

@232111: 代码中如何注册 ISSOService 的?

dudu | 园豆:30994 (高人七级) | 2018-08-16 08:26

@232111: 建议提供一下依赖注入部分的代码

dudu | 园豆:30994 (高人七级) | 2018-08-16 08:28

@dudu: 

注入类里是这么写的 

 builder.RegisterType<SSOService>().As<ISSOService>().InstancePerLifetimeScope();

但是我在TokenConsumer里并不是通过构造函数使用 ,而是new出来的,因为Consumer构造函数不允许有参数

var ssoService = EngineContext.Current.Resolve<ISSOService>();
232111 | 园豆:22 (初学一级) | 2018-08-16 08:30

@232111: IRepository<SSOAuthToken> 是如何注册的?

dudu | 园豆:30994 (高人七级) | 2018-08-16 08:38

@dudu: 

builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
232111 | 园豆:22 (初学一级) | 2018-08-16 08:39

@232111: 出现这个问题是由于通一个 DbContext 实例被多个线程使用,建议把 InstancePerLifetimeScope 都改为 InstancePerHttpRequest 试试

dudu | 园豆:30994 (高人七级) | 2018-08-16 08:47

@dudu: 嗯 谢谢园主 我试试  再问个问题 我第一次使用autofac  一直以为InstancePerLifetimeScope的作用域就是当前请求的上下文 不对吗?

232111 | 园豆:22 (初学一级) | 2018-08-16 08:49

@232111: 我对 autofac 也不熟悉,我们都用的是 .net core 内置的依赖注入,InstancePerHttpRequest 与 InstancePerLifetimeScope 的区别详见 Autofac - InstancePerHttpRequest vs InstancePerLifetimeScope

dudu | 园豆:30994 (高人七级) | 2018-08-16 08:51

@dudu: 

园主,我的问题按你这篇文章解决的

https://www.cnblogs.com/dudu/p/9353369.html

谢谢

232111 | 园豆:22 (初学一级) | 2018-08-17 12:58
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册