Два разных EF dbContexts не работают в одном модульном тесте - PullRequest
0 голосов
/ 18 мая 2018

Я использую: EF 6.2, VisualStudio 2017, nUnit 2.6.3.13283 (модульный тест), Unity 5.8.5 (как IoC).

Проблема появляется, когда я хочудля проверки двух разных DbContexts в одном и том же UnitTest.

Первый контекст:

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; }
}

Второй контекст:

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());
        }
    }
}

Когда я создаю экземпляр вышеупомянутого контекста отдельно, передавая строку подключения - они оба работают нормально.

Однако, если они являются частьютот же класс модульного тестирования - они не могут быть запущены.Первый контекст устанавливает своего провайдера по умолчанию (скажем, для SQL), а следующий DbContext (скажем, для SQLite) не может установить его провайдера.

Если MS SQL dbcontext идет первым, то SQLite dbcontext получает следующее исключение:

System.InvalidOperationException: 'Невозможно завершить операцию.Предоставленный SqlConnection не указывает начальный каталог или AttachDBFileName. '

Если SQLite идет первым, тогда контекст MS SQL получает:

System.InvalidOperationException:' Тип хранилища'date' не найден в манифесте поставщика SQLite '

Мне просто интересно, что мне здесь не хватает.Будь то nUnit специфично (какой-то кеш).Или это действительно обычное место для провайдеров EF, которое можно установить только один раз.

Я вообще не использую App.config - просто передаю строку конфигурации из некоторого сохраненного места.

1 Ответ

0 голосов
/ 08 июня 2018

@ Bit @ programtreasures

Нашли ответ.Основной причиной была неспособность EF обрабатывать несколько DBConfiguration одновременно (возможно, в памяти), даже если они являются частями различных DbContexts.

Подробнее здесь: https://msdn.microsoft.com/en-us/data/jj680699

ИтакЯ только что создал общий контекст:

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);
        }
    }
}

И контекст БД MS SQL:

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; }
    }
}

И 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; }
    }
}

Тогда я могу запустить модультест, как показано ниже:

[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);
    }
}
...