Как проверить представления базы данных с помощью Entity Framework Core в провайдере БД памяти? - PullRequest
6 голосов
/ 27 марта 2019

Когда я пытался добавить объекты в представления, выдается исключение, говорящее unable to track an instance of type because it is a query type. Есть ли способ обойти это?

1 Ответ

3 голосов
/ 09 апреля 2019

Типы запросов доступны только для чтения по определению (для всех поставщиков баз данных, не только для памяти):

  • Никогда не отслеживаются на предмет изменений в DbContext и, следовательно, никогда не вставляются, не обновляются и не удаляются в базе данных.

Однако, в дополнение к их обычным сценариям использования

  • Отображение в представления базы данных.
  • Отображение в таблицы, для которых не определен первичный ключ.

они позволяют

  • Отображение на запросы, определенные в модели.

или другими словами

  • Может быть сопоставлен с определяющим запросом - Определяющим запросом является вторичный запрос, объявленный в модели, который действует как источник данных для типа запроса.

, что достигается с помощью ToQuery свободный API:

Настраивает запрос, используемый для предоставления данных для типа запроса.

Таким образом, для тестирования типов запросов с использованием базы данных в памяти следует использовать определяющий запрос возможность сопоставления.

Например, внутри OnModelCreating override вы можете добавить что-то вроде этого:

if (Database.IsInMemory())
{
   // In memory test query type mappings
    modelBuilder.Query<MyQueryType>().ToQuery(() => LINQ_query);
    // ... similar for other query types
}
else
{
    // Database query type mappings
    modelBuilder.Query<MyQueryType>().ToView("MyQueryTypeView");
    // ... 
}

, где LINQ_query - это обычный запрос LINQ, обращающийся к контексту DbSet с и DbQuery с и проецирующий на MyQueryType.

Затем тест передает данные соответствующим объектам, а запросы, использующие DbQuery s, извлекают данные из определяющего запроса.


Вышеуказанный метод должен быть рекомендован для проверки представлений в базе данных памяти.

Просто для полноты можно напрямую снабжать данные DbQuery данными (в основном имитируя их), создавая своего рода хранилище запросов, но со следующим ограничением - это должно быть shared (static), поскольку в настоящее время EF Core неправильно обрабатывает элементы контекста БД (как это делает глобальный фильтр запросов), кроме DbSet<T> и DbQuery<T>.

Примерно так:

public static class FakeQueryProvider
{
    static Dictionary<Type, IQueryable> queries = new Dictionary<Type, IQueryable>();

    public static void SetQuery<T>(IQueryable<T> query)
    {
        lock (queries)
            queries[typeof(T)] = query;
    }

    public static IQueryable<T> GetQuery<T>()
    {
        lock (queries)
            return queries.TryGetValue(typeof(T), out var query) ? (IQueryable<T>)query : Enumerable.Empty<T>().AsQueryable();
    }

    public static QueryTypeBuilder<T> ToFakeQuery<T>(this QueryTypeBuilder<T> builder)
        where T : class
    { 
        return builder.ToQuery(() => GetQuery<T>());
    }
}

тогда вместо

.ToQuery(() => LINQ_query);

вы бы использовали

.ToFakeQuery(); 

и будет кормить его внутри теста вот так

 List<MyQueryType> data = ...;
 FakeQueryProvider.SetQuery(data.AsQueryable());

Тем не менее, я рекомендую первый подход, поскольку общее хранилище ограничивает возможность параллельного запуска MyQueryType связанных тестов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...