これは Entity Framework への私の最初の進出であり、EF5 とリポジトリ パターンを使用した作業プロジェクトがあります。ライブ DB に対して統合テストを実行したいと考えています。既存の本番データベースのスナップショットを作成し、テストを実行するたびに新しいスナップショットを再作成するストアド プロシージャを作成しました。私の質問は、「単体テスト モード」でコンテキストをこのデータベース スナップショットに切り替える方法です。私の 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") に変更することです。問題は私の 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] 部分で何らかの方法でテスト接続文字列に切り替えられるようにしたいと考えています。