Task.Factory.StarNew and ApplicationDbContext Upda

2019-08-22 07:45发布

问题:

I need to update a table of my ApplicationDbContext within a Task; however, I'm not getting it done. Here is the error message I've got:

ex.Message = "Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose...

I understand it has something to do with threading which I'm not so familiar.

Here's the code:

  [HttpPost]
        public ActionResult WebHook([FromBody] BotRequest data)
        {

         Task.Factory.StartNew(() =>
                {

                //Read value from Table
                ContextWatsonFB contextWatsonFB = _context.ContextWatsonFB.Where(m => m.RecipientId == recipientid).FirstOrDefault();

                if (contextWatsonFB == null)
                {
                    contextWatsonFB = new ContextWatsonFB()
                    {
                        RecipientId = recipientid
                    };
                    _context.Add(contextWatsonFB);
                    _context.SaveChanges();

                }
                else
                {
                    if (!string.IsNullOrEmpty(contextWatsonFB.Context))
                    {
                        model = JsonConvert.DeserializeObject<Context>(contextWatsonFB.Context);
                    }

                }

                ///DO SOME STUFF ////////////////

                ///Here I need to update my table using some values processed above in "some stuff"

                ContextWatsonFB contextWatsonFB = _context.ContextWatsonFB.Where(m => m.RecipientId == recipientid).FirstOrDefault();

                contextWatsonFB.Context = JsonConvert.SerializeObject(context);
                _context.Update(contextWatsonFB);
                _context.SaveChanges();

                }
        }

As you can figure it out, its a webhook connection for Facebook, which requires the process to be handled in a Task. Within "some stuff", basically I'm consuming IBM Watson Conversation service whom persist a conversation "context" that I'm not able to send back and forth to Facebook, that's why I figure it out to persist such data in a table to keep the differences among multiple request from facebook messenger.

回答1:

Luckily, the following code did the trick:

        private readonly IServiceProvider _provider;

        public FacebookBotController(ApplicationDbContext context, IServiceProvider provider)
        {
            _provider = provider;

}

  [HttpPost]
        public ActionResult WebHook([FromBody] BotRequest data)
        {

            if (data == null || data?.entry?.Count == 0)
            {
                return new StatusCodeResult(StatusCodes.Status204NoContent);
            }


            try
            {

                var task = Task.Factory.StartNew(async () =>
                {

                    using (IServiceScope scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                    {
                        ApplicationDbContext _contx = _provider.GetService<ApplicationDbContext>();

                        ContextWatsonFB contextWatsonFB = await _contx.ContextWatsonFB.Where(m => m.SenderId == senderId).FirstOrDefaultAsync();

                        if (contextWatsonFB == null)
                        {
                            context = null;
                        }
                        else
                        {
                            context = JsonConvert.DeserializeObject<Context>(contextWatsonFB.Context);
                        }

                    }

}
}