C#:在Task中使用依赖注入的Service/EFContext

dotnet core时代,依赖注入基本已经成为标配了,这就不多说了.

前几天在做某个功能的时候遇到在Task中使用EF DbContext的问题,学艺不精的我被困扰了不短的一段时间,

于是有了这个文章.

先说一下代码结构和场景.

首先有一个HouseDbContext,代码大概是下面这样:

public class HouseDbContext : DbContext
{
public HouseDbContext(DbContextOptions<HouseDbContext> options)
: base(options)
{
}
public DbSet<Notice> Notices { get; set; }
}

接着已经在StarUp.cs中初始化并注入了,注入代码是这样的:


services.AddDbContextPool<HouseDbContext>(options =>
{
options.UseLoggerFactory(loggerFactory);
options.UseMySql(Configuration["MySQLString"].ToString());
});

有一个NoticeService.cs 会用到HouseDbContext 进行增删查改

public class NoticeService
{ private readonly HouseDbContext _dataContext; public NoticeService(HouseDbContext dataContext)
{
_dataContext = dataContext;
} public Notice FindNotice(long id)
{
var notice = _dataContext.Notices.FirstOrDefault(n =>n.Id == id);
return notice;
}
}

当然我们也需要把NoticeService注入到容器中,类似这样


services.AddScoped<NoticeService,NoticeService>();

现在一切都是很美好的,也能正常查询出Notice

然后某一天来了,有个需求是Update Notice之后需要把Notice同步到另外一个地方,例如Elasticsearch?

代码如下:


public class NoticeService
{ private readonly HouseDbContext _dataContext; public NoticeService(HouseDbContext dataContext)
{
_dataContext = dataContext;
} public Notice FindNotice(long id)
{
var notice = _dataContext.Notices.FirstOrDefault(n =>n.Id == id);
return notice;
} public void Save(Notice notice)
{
_dataContext.Notices.Add(notice);
_dataContext.SaveChanges();
Task.Run(() =>
{
try
{
var one = _dataContext.Notices.FirstOrDefault(n =>n.Id == notice.Id);
// write to other
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
}
}

然后一跑...

代码炸了...

恭喜你获得跨线程使用EF DbContext导致上下文不同步的异常.

错误大概长这样.


System.ObjectDisposedException: 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() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'HouseDbContext'.

估计现在整个人都不好了.

这个撒意思呢?


无法访问被释放的对象。 这种错误的一个常见原因是使用从依赖注入中解决的上下文,然后在应用程序的其他地方尝试使用相同的上下文实例。 如果您在上下文上调用Dispose(),或者在using语句中包装上下文,可能会发生这种情况。如果使用依赖项注入,则应该让依赖项注入容器处理上下文实例。

用人话来说是什么意思呢?

这里的HouseDbContext是依赖注入进来的,生命周期由容器本身管理;

在Task.Run中再次使用HouseDbContext实例中由于已经切换了线程了,

HouseDbContext实例已经被释放掉了,无法再继续使用同一个实例,我们应该自己初始化HouseDbContext来用.

到这里的话,上次我做的时候心生一计:

既然我们不能直接从构造函数注入的HouseDbContext实例的话,我们是不是可以直接从依赖注入容器中拿一个实例回来呢?

那在dotnet core里面可以用个什么从容器中取出实例呢?

答案是:IServiceProvider

代码如下:


public class NoticeService
{ private readonly HouseDbContext _dataContext; private readonly IServiceProvider _serviceProvider; public NoticeService(HouseDbContext dataContext,IServiceProvider serviceProvider)
{
_dataContext = dataContext;
_serviceProvider = serviceProvider;
} public Notice FindNotice(long id)
{
var notice = _dataContext.Notices.FirstOrDefault(n => n.Id == id);
Task.Run(() =>
{
try
{
var context = _serviceProvider.GetService<HouseDbContext>();
var one = context.Notices.FirstOrDefault(n => n.Id == notice.Id);
Console.WriteLine(notice.Id);
// write to other
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
return notice;
} public void Save(Notice notice)
{
_dataContext.Notices.Add(notice);
_dataContext.SaveChanges();
Task.Run(() =>
{
try
{
var one = _dataContext.Notices.FirstOrDefault(n => n.Id == notice.Id);
// write to other
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
}
}

跑一下看看...

然而事实告诉我,实例是能拿得到,然而还是会炸,错误是一样的.

原因其实还是一样的,这里已经不受依赖注入托管了,人家的上下文你别想用了.

那咋办呢...

在EF6,还可以直接new HouseDbContext 一个字符串进去初始化,在EF Core这里,已经不能这样玩了.

那可咋办呢?

翻了好多资料都没看到有人介绍过咋办,最后居然还是在官网教程里面找到了样例.

先看代码...

 Task.Run(() =>
{
try
{
var optionsBuilder = new DbContextOptionsBuilder<HouseDbContext>();
// appConfiguration.MySQLString appConfiguration是配置类,MySQLString为连接字符串
optionsBuilder.UseMySql(appConfiguration.MySQLString);
using (var context = new HouseDbContext(optionsBuilder.Options))
{
var one = context.Notices.FirstOrDefault(n => n.Id == notice.Id);
// 当然你也可以直接初始化其他的Service
var nService = new NoticeService(context,null);
var one =nService.FindOne(notice.Id);
} }catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});

教程代码在:Configuring a DbContext

大功告成...

dotnet core在Task中使用依赖注入的Service/EFContext的更多相关文章

  1. 在.NET Core控制台程序中使用依赖注入

    之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...

  2. ASP.NET Core - 在ActionFilter中使用依赖注入

    上次ActionFilter引发的一个EF异常,本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文档.发现除了IActionFilter.I ...

  3. ASP.NET Core 新建线程中使用依赖注入的问题

    问题来自博问的一个提问 .net core 多线程数据保存的时候DbContext被释放 . TCPService 通过构造函数注入了 ContentService , ContentService ...

  4. 如何在.NET Core控制台程序中使用依赖注入

    背景介绍 依赖注入(Dependency Injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度.在.NET Core MVC中 我们可以在Startup.cs文件的Co ...

  5. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  6. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

  7. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(三)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  8. 深入理解net core中的依赖注入、Singleton、Scoped、Transient(二)

    相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...

  9. ASP.NET Core 中文文档 第四章 MVC(3.8)视图中的依赖注入

    原文:Dependency injection into views 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:孟帅洋(书缘) ASP.NET Core 支持在视图中使用 依赖 ...

随机推荐

  1. JavaScript权威指南第01章 JavaScript 概述

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/huangbin10025/article/details/27951767 JavaScript 概 ...

  2. Java并发案例02---生产者消费者问题

    package example; import java.util.LinkedList; import java.util.concurrent.TimeUnit; public class MyC ...

  3. POJ2724 Purifying Machine

    嘟嘟嘟 扒下来的题意:迈克有一台可以净化奶酪的机器,用二进制表示净化的奶酪的编号.但是,在某些二进制串中可能包含有\(*\).例如\(01*100\),\(*\)其实就代表可以取\(0\),\(1\) ...

  4. 随手练——POJ - 2676 数独 (回溯法)

    POJ - 2676 : http://poj.org/problem?id=2676: 解题思想 (大力出奇迹): 1. 依次在空格里面填上“1~9”,并检查这个数字是否合法(其所在的行.列,以及3 ...

  5. Linux下安装PHP并在nginx服务器中进行配置的详细方法

    先介绍一下使用的环境:centos 7.4, PHP 7.0 , nginx 1.12 Linux系统版本可以通过命令:lsb_release -a 查看. 现在开始步入正题了! 1.  首先查看一下 ...

  6. Kali-linux绘制网络结构图

    CaseFile工具用来绘制网络结构图.使用该工具能快速添加和连接,并能以图形界面形式灵活的构建网络结构图.本节将介绍Maltego CaseFile的使用. 在使用CaseFile工具之前,需要修改 ...

  7. [译] MVP模式的14条规则

    笔者在前文<MVP和MVC>中提到了两者的区别,以及MVP日趋流行的原因:即随着各种给力UI框架的发布,View的功能越来越强,已经足以完成一些简单的不需要与后台或其他view交互的eve ...

  8. pgAdmin4 重置布局

    pgAdmin4 是网页版客户端,而且在其网页上可以通过拖拽改变布局.当布局混乱时可以通过 菜单File--Reset Layout功能来重置. 如果这篇随笔只是为了说以上这句话就没必要了,而且你根据 ...

  9. Windows 安装 MySQL 8.0.11

    下载并解压 从官方网站下载最新安装包 解压到目标安装目录 新建配置文件 在安装目录新建my.ini文件 添加如下内容(需修改为自己的配置) #----------------------------- ...

  10. Linux系统初学-第二课 linux基础知识

    一.用户与群组 Linux是多人多任务的操作系统,每个用户有一个主目录(或者叫家目录 /home),其他用户可以浏览,但是能否查看文件要看具体的权限设置.文件拥有者可以修改权限,选择是否允许其他用户进 ...