EF6 DbContext IOC 依赖
EF6 DbContext IOC Dependency
我在 Windows 服务/控制台应用程序中使用 EF6。我已经成功
为我的业务层接口和实现实现了 IOC/DI。
使用构造函数注入。我也在使用对象数据库、任务并行库。为了更好的表现,我很高兴。
还使用 System.IO.Abstractions 使我的代码更易于测试。
EF6 使用 .tt 文件为所有域实体创建 POCO 类,这些文件相当
便利。为了执行数据库查询,我在每个地方都写了
using(var db = new MyContext())
{
// code reading from/writing to database
...
...
}
我知道这是不正确的做法,并且会在我的代码的各个地方发出噪音。
我想让它松耦合。现在进行我的数据库操作 - 我是
对如何前进以使其更具可测试性和松散耦合感到困惑。任何人都可以给我一个很好的例子,可以参考的文章。
我想要实现的 2 件主要事情是对
连接字符串配置(用于各种服务器部署)并在我的代码中 DbContext
非常松散耦合。
为了解决解耦(和测试)问题,您可以为您的 DbContext
(IMyDbContext
) 创建自己的接口,并重新公开所有类型化实体 DbSets
、SaveChanges()
,可能还有其他一些方法。你也应该让这个界面Disposable
。
public interface IMyDbContext : IDisposable
{
IDbSet<Foo> Foos { get; set; }
IDbSet<Bar> Bars { get; set; }
int SaveChanges();
DbEntityEntry<T> Entry<T>(T entity) where T : class;
}
(您也可以考虑接口的只读和读写版本)
然后更改您的具体 DbContext
以实现此接口。您现在与 DbContext
合理分离(用于单元测试等),但仍然可以使用 IQueryable
、固有工作单元和 DbContext
提供的缓存.
然后,这里有两个选项可以将 IMyDbContext
注入您的业务/服务 类
IDbContext
的构造函数注入
或
- 创建用于创建具体
DbContext
的工厂方法和工厂接口,然后对 IMyDbContextFactory
工厂接口进行构造函数注入(再次需要接口,而不是具体工厂用于模拟测试目的)。
这里的选择取决于您需要用 DbContext
做什么。 #1 在 IoC 容器中配置可能很棘手,因为您需要将生命周期管理移交给容器。但是,如果可以为每个请求配置新实例,那么这在 Web 应用程序中可能是有益的,这样如果请求(假定为单线程)可以将其用作缓存。
我个人更喜欢#2,因为它允许直接管理上下文:
using(var db = _myContextFactory.CreateDB())
{
db.SaveChanges();
}
但很明显,我们随后失去了缓存等长期存在的上下文的任何潜在好处。但如果需要,还有许多其他缓存替代技术。
一个警告:DbContext
根本不是线程安全的 - 如果您使用 TPL,请确保每个任务都获得自己的 DbContext
实例 - 例如使用 Parallel.For
/ ForEach
的 localinit
重载来实例化它。
我在 Windows 服务/控制台应用程序中使用 EF6。我已经成功 为我的业务层接口和实现实现了 IOC/DI。 使用构造函数注入。我也在使用对象数据库、任务并行库。为了更好的表现,我很高兴。
还使用 System.IO.Abstractions 使我的代码更易于测试。
EF6 使用 .tt 文件为所有域实体创建 POCO 类,这些文件相当 便利。为了执行数据库查询,我在每个地方都写了
using(var db = new MyContext())
{
// code reading from/writing to database
...
...
}
我知道这是不正确的做法,并且会在我的代码的各个地方发出噪音。 我想让它松耦合。现在进行我的数据库操作 - 我是 对如何前进以使其更具可测试性和松散耦合感到困惑。任何人都可以给我一个很好的例子,可以参考的文章。
我想要实现的 2 件主要事情是对
连接字符串配置(用于各种服务器部署)并在我的代码中 DbContext
非常松散耦合。
为了解决解耦(和测试)问题,您可以为您的 DbContext
(IMyDbContext
) 创建自己的接口,并重新公开所有类型化实体 DbSets
、SaveChanges()
,可能还有其他一些方法。你也应该让这个界面Disposable
。
public interface IMyDbContext : IDisposable
{
IDbSet<Foo> Foos { get; set; }
IDbSet<Bar> Bars { get; set; }
int SaveChanges();
DbEntityEntry<T> Entry<T>(T entity) where T : class;
}
(您也可以考虑接口的只读和读写版本)
然后更改您的具体 DbContext
以实现此接口。您现在与 DbContext
合理分离(用于单元测试等),但仍然可以使用 IQueryable
、固有工作单元和 DbContext
提供的缓存.
然后,这里有两个选项可以将 IMyDbContext
注入您的业务/服务 类
IDbContext
的构造函数注入
或
- 创建用于创建具体
DbContext
的工厂方法和工厂接口,然后对IMyDbContextFactory
工厂接口进行构造函数注入(再次需要接口,而不是具体工厂用于模拟测试目的)。
这里的选择取决于您需要用 DbContext
做什么。 #1 在 IoC 容器中配置可能很棘手,因为您需要将生命周期管理移交给容器。但是,如果可以为每个请求配置新实例,那么这在 Web 应用程序中可能是有益的,这样如果请求(假定为单线程)可以将其用作缓存。
我个人更喜欢#2,因为它允许直接管理上下文:
using(var db = _myContextFactory.CreateDB())
{
db.SaveChanges();
}
但很明显,我们随后失去了缓存等长期存在的上下文的任何潜在好处。但如果需要,还有许多其他缓存替代技术。
一个警告:DbContext
根本不是线程安全的 - 如果您使用 TPL,请确保每个任务都获得自己的 DbContext
实例 - 例如使用 Parallel.For
/ ForEach
的 localinit
重载来实例化它。