如何使用泛型按类型创建类的实例

时间:2012-05-31 15:48:18

标签: c# generics tdd datacontext

我在使用泛型创建类的新实例时遇到问题。我想要做的是拥有一个可以使用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; }
    ...

感谢Jon的建议,以下是我提出的建议:

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-generic-repository/

1 个答案:

答案 0 :(得分:6)

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

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

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

public IDbSet<T> CreateDbSet<T>()

然后你有一个ProductionDbSetProvider和一个FakeDbSetProvider

public MusicStoreContext(DbSetProvider provider)
{
    Albums = provider.CreateDbSet<Album>();
    Artists = provider.CreateDbSet<Artist>();
}
个人觉得我觉得更干净,但YMMV。