这是我第一次涉足实体框架,我有一个使用 EF5 和存储库模式的工作项目。我想对实时数据库进行集成测试。我为我现有的生产数据库制作了一个快照,并编写了一个存储过程来在每次我想运行测试时重新创建一个新的快照。我的问题是如何在“单元测试模式”时将我的上下文切换到这个数据库快照?在我的 app.config 中,我既有实时连接字符串,也有测试连接字符串:
<connectionStrings>
<add name="ReportingDbContext" connectionString="Server=LiveServer;Database=UnifiedReporting;User Id='myuser';Password='mypass';Trusted_Connection=False" providerName="System.Data.SqlClient" />
<add name="TestingDbContext" connectionString="Server=LiveServer;Database=UnifiedReportingSnapshot;User Id='myuser';Password='mypass';Trusted_Connection=False" providerName="System.Data.SqlClient" />
</connectionStrings>
就目前而言,我的 DbContext 包含我想要使用的实体,如下所示:
public class ReportingDbContext : DbContext
{
public ReportingDbContext() : base("name=ReportingDbContext") // as per my app.config
{
}
// inventory
public DbSet<ComputerEntity> Computers { get; set; }
public DbSet<NetworkAdapterEntity> NetworkAdapters { get; set; }
// ... plus a whole bunch more
}
我认为我需要做的是将 base("name=ReportingDbContext") 更改为 ("name=TestingDbContext"),但考虑到我的 Repository/UnitOfWork 设置,我不知道我该怎么做。这个问题可能在我的 UnitOfWork 中:
public interface IUnitOfWork : IDisposable
{
void Commit();
// inventory
IRepository<ComputerEntity> Computers { get; }
IRepository<NetworkAdapterEntity> NetworkAdapters { get; }
// ... plus a bunch more
}
public class UnitOfWork : IUnitOfWork
{
private readonly ReportingDbContext _dbContext = null;
public UnitOfWork()
{
_dbContext = new ReportingDbContext();
}
public void Commit()
{
_dbContext.SaveChanges();
}
// Inventory
public IRepository<ComputerEntity> Computers {get { return new Repository<ComputerEntity>(_dbContext); }}
public IRepository<NetworkAdapterEntity> NetworkAdapters { get { return new Repository<NetworkAdapterEntity>(_dbContext); } }
// ... lots more
}
这个 UnitOfWork 很棒的是,我可以对我的所有存储库做一堆东西,并一次保存它,而无需一堆上下文浮动来同步。它可能与这个问题相关,也可能不相关,但这就是我的 UnitOfWork 使用存储库的方式。只有 1 个存储库类,但可以提供所需的任何实体类型:
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
T GetById(int id);
void Remove(T entity);
void Add(T newEntity);
}
public class Repository<T> : IRepository<T> where T : class
{
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public Repository(DbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException("dbContext");
}
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
public IQueryable<T> GetAll()
{
return DbSet;
}
// ... more implementation of the interface, nothing fancy
}
使用这个魔法的端点在我的 WCF 服务中。这是我想要实际运行集成测试的地方。我的服务中的一个特定方法初始化了一个工作单元并使用它来做事。UnitOfWork 在新建时会创建一个 ReportingDbContext,而这个 ReportingDbContext 又引用“name=ReportingDbContext”的连接字符串。经过大量阅读,我认为答案是使用 Unity 或 Ninject 之类的 IoC 容器(以前没有使用过,但我想使用),而我一直坚持如何在这种情况下实现 IoC。这是我在 WCF 服务中使用的示例方法,它似乎对实时数据库连接字符串进行了硬编码:
public ComputerDTO GetComputerDetails(string hostname, string client)
{
// don't worry about the return type, it's defined elsewhere
using (var uoW = new UnitOfWork())
{
var repo = uoW.Computers;
var computer = repo.Find(x => x.Hostname == hostname && x.CompanyEntity.Name == client).FirstOrDefault();
// do stuff
}
}
如果可能的话,我想将我的连接字符串保留在我的 app.config 中,并且能够在我的 WCF 服务中方法的 NUnit 测试的 [SetUp] 部分期间以某种方式切换到测试连接字符串。