0

我在使用泛型按类型创建类的新实例时遇到问题。我想要做的是拥有一个可以使用 DBSet<> 或 FakeDBSet<> 创建的数据库上下文。FakeDBSet 将用于测试代码。我目前有一个完整的假数据上下文,但这是一种浪费,因为唯一真正的区别是使用的 DBSet。我已经研究过使用 Activator.CreateInstance() 没有太多运气。

例子:

public class Album {}
public class Artist {}

public class MusicStoreContext
{
    public IDbSet<Album> Albums { get; set; }
    public IDbSet<Artist> Artists { get; set; }

    public MusicStoreContext(Type dbSetType)
    {
        Albums = new (dbSetType)<Album>;
        Artists = new (dbSetType)<Artist>;
    }

}

public class Startup
{
    public Startup()
    {
        // Production code would do something like this:
        MusicStoreContext context = new MusicStoreContext(typeof(DbSet<>));

        // Test code would do something like this:
        MusicStoreContext testContext = new MusicStoreContext(typeof(FakeDbSet<>));
    }
}

我也尝试过这样的事情:

public class MusicStoreContext<T> where T : IDBSet
{
    public IDbSet<Album> Albums { get; set; }
    public IDbSet<Artist> Artists { get; set; }
    ...

由于乔恩的建议,这是我想出的有效方法:

public class MusicStoreContext
{
    private IDbSet<Album> _Albums;
    private IDbSet<Artist> _Artists;

    public IDbSet<Album> Albums { get {return _Albums;} }
    public IDbSet<Artist> Artists { get {return _Artists; }

    public MusicStoreContext(Type dbSetType)
    {
        Albums = new (dbSetType)<Album>;
        Artists = new (dbSetType)<Artist>;
    }

    public TaxDocumentsContext() : base() 
    {
        CreateDbSets(new ProductionDbSetProvider());
    }

    public TaxDocumentsContext(IDbSetProvider provider)
    {
        CreateDbSets(provider);
    }

    private void CreateDbSets(IDbSetProvider provider)
    {
        provider.CreateDbSet<Album>(this, ref _Albums);
        provider.CreateDbSet<Artist>(this, ref _Artists);
    }

}

对于 DbSetProvider:

public interface IDbSetProvider
{
    void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class;
}

public class FakeDbSetProvider : IDbSetProvider
{
    public void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class
    {
        dbSet = new FakeDbSet<T>();
    }

}

public class ProductionDbSetProvider : IDbSetProvider
{
    public void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class
    {
        dbSet = context.Set<T>();
    }
}

现在我可以轻松地测试,而无需使用 FakeDbSet 从此处访问数据库: http ://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with -a-通用存储库/

4

1 回答 1

6

编辑:如果有问题的集合有适当的构造函数,您可以使用Type.MakeGenericType创建相关的构造类型并使用以下方法调用构造函数Activator.CreateInstance

Type albumType = dbSetType.MakeGenericType(typeof(Album));
Albums = (IDbSet<Album>) Activator.CreateInstance(albumType);

或者,您可以传入DbSetProvider具有通用方法的(或其他):

public IDbSet<T> CreateDbSet<T>()

那么你会有 aProductionDbSetProvider和 a FakeDbSetProvider

public MusicStoreContext(DbSetProvider provider)
{
    Albums = provider.CreateDbSet<Album>();
    Artists = provider.CreateDbSet<Artist>();
}

就我个人而言,这对我来说感觉更干净,但是 YMMV。

于 2012-05-31T15:52:47.067 回答