I'm using: EF 6.2, VisualStudio 2017, nUnit 2.6.3.13283 (unit test), Unity 5.8.5 (as IoC).
The problem appears when I want to test two different DbContexts in the same UnitTest.
First Context:
public class MsSqlConfiguration : System.Data.Entity.DbConfiguration
{
public MsSqlConfiguration()
{
this.SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.SqlConnectionFactory());
this.SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(MsSqlConfiguration))]
public class SqlDbContext: DbContext
{
public SqlDbContext(string connectonString) : base(connectonString)
{}
public DbSet<SomeClass> SomeField { get; set; }
}
Second context:
public class SQLiteProviderInvariantName : IProviderInvariantName
{
public static readonly SQLiteProviderInvariantName Instance = new SQLiteProviderInvariantName();
private SQLiteProviderInvariantName() { }
public const string ProviderName = "System.Data.SQLite.EF6";
public string Name { get { return ProviderName; } }
}
class SQLiteDbDependencyResolver : IDbDependencyResolver
{
public object GetService(Type type, object key)
{
if (type == typeof(IProviderInvariantName)) return SQLiteProviderInvariantName.Instance;
if (type == typeof(DbProviderFactory)) return SQLiteProviderFactory.Instance;
return SQLiteProviderFactory.Instance.GetService(type);
}
public IEnumerable<object> GetServices(Type type, object key)
{
var service = GetService(type, key);
if (service != null) yield return service;
}
}
public class SQLiteConfiguration : System.Data.Entity.DbConfiguration
{
public SQLiteConfiguration()
{
AddDependencyResolver(new SQLiteDbDependencyResolver());
SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance);
SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
SetProviderServices("System.Data.SQLite", (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices)));
}
}
[DbConfigurationType(typeof(SQLiteConfiguration))]
public class SqlDbContext : DbContext
{
public SqlDbContext (string connectonString) : base(connectonString)
{
}
public DbSet<SomeClass> SomeField{ get; set; }
}
UnitTest:
[TestFixture]
class DbContextIntegrationTests
{
[Test]
public void CanReadFromMsSqlDatabase()
{
using (var context = IocContainer.Instance.Resolve<MsSqlDbContext>(someConnString))
{
Assert.DoesNotThrow(() => context.SomeField.FirstOrDefault());
}
}
[Test]
public void CanReadFromSqliteDatabase()
{
using (var context2 = IocContainer.Instance.Resolve<SqliteDbContext>(someConnString2))
{
Assert.DoesNotThrow(() => context2.Somefield.FirstOrDefault());
}
}
}
When I instantiate the above context separately by passing connection string - they both works fine.
However if they are a part of the same unit test class - they can't be run. First context set it's provider as default (let say SQL one) and the next one DbContext (say SQLite one) can't set it's provider.
If MS SQL dbcontext goes first, then SQLite dbcontext get next exception:
System.InvalidOperationException: 'Unable to complete operation. The supplied SqlConnection does not specify an initial catalog or AttachDBFileName.'
If SQLite goes first, then MS SQL context gets:
System.InvalidOperationException: 'The store type 'date' could not be found in the SQLite provider manifest'
I'm just wondering what I'm missing here. Whether it's nUnit specific (some cache). Or it's indeed some common place for EF providers which can be set only once.
I'm not using App.config at all - just passing config string from some saved place.
@Bit @programtreasures
Found the answer. The root cause was in inability of EF to handle multiple DBConfiguration at the same time (probably in memory) even if they are parts of different DbContexts.
More details here: https://msdn.microsoft.com/en-us/data/jj680699
So I've just created a common context:
using System.Data.Entity.Core.Common;
using System.Data.SQLite;
using System.Data.SQLite.EF6;
namespace ClassLibrary1
{
public class commonConfig : System.Data.Entity.DbConfiguration
{
public commonConfig()
{
SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.SqlConnectionFactory());
SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance);
SetProviderServices("System.Data.SQLite", (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices)));
SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
}
}
}
And MS SQL DB context:
using System.Data.Entity;
using System.Data.SqlClient;
namespace ClassLibrary1
{
[DbConfigurationType(typeof(commonConfig))]
public class MsSqlDbContext : DbContext
{
public MsSqlDbContext(SqlConnection existingConnection,
bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)
{
DbConfiguration.SetConfiguration(new commonConfig());
}
public DbSet<SomeTableEntity> SomeTable { get; set; }
}
}
And SqliteDbContext:
using System.Data.Entity;
using System.Data.SQLite;
namespace ClassLibrary1
{
[DbConfigurationType(typeof(commonConfig))]
public class SqliteDbContext : DbContext
{
public SqliteDbContext(SQLiteConnection existingConnection,
bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)
{
DbConfiguration.SetConfiguration(new commonConfig());
}
public DbSet<SomeDbTableEntity> SomeTable { get; set; }
}
}
Then I can run unit test like the below:
[TestMethod]
public void TestMethod()
{
using (var context1 = new SqliteDbContext(new SQLiteConnection(
@"C:\db.sqlite"), true
))
{
Console.WriteLine("SQLITE" + Environment.NewLine);
Console.Write(context1.SomeTable.FirstOrDefault().SomeRecord);
Console.WriteLine(Environment.NewLine);
}
using (var context2 =
new MsSqlDbContext(
new SqlConnection(@"Data Source=localhost;Initial Catalog=SomeDatabase;Integrated Security=True")
, true)
)
{
Console.WriteLine("MS SQL" + Environment.NewLine);
Console.Write(context2.SomeTable.FirstOrDefault().SomeRecord);
Console.WriteLine(Environment.NewLine);
}
}