Я использую IDbCommandTreeInterceptor
для реализации функциональности soft-delete. Внутри стандартного метода TreeCreated
я проверяю, содержит ли данная команда запроса модели с атрибутом soft-delete. Если они это сделают, и пользователь попросил также получить мягкий удаленный объект --- я вызываю своего посетителя с мягким удалением с помощью querySoftDeleted
= true
. Это приведет к тому, что мой запрос вернет весь объект, те с true
и те, у кого false
значения в свойстве IsDeleted
.
public class SoftDeleteInterceptor : IDbCommandTreeInterceptor {
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) {
...
bool shouldFetchSoftDeleted = context != null && context.ShouldFetchSoftDeleted;
this.visitor = new SoftDeleteQueryVisitor(ignoredTypes, shouldFetchSoftDeleted);
var newQuery = queryCommand.Query.Accept(this.visitor);
...
}
}
public class SoftDeleteQueryVisitor {
...
public override DbExpression Visit(DbScanExpression expression)
{
// Skip filter if all soft deleted items should be fetched
if (this.shouldFetchSoftDeleted)
return base.Visit(expression);
...
// TODO Apply `IsDeleted` filter.
}
}
Проблема возникает, когда я пытаюсь восстановить все объекты (с мягким удалением), а затем с тем же самым объектом запроса, который не удаляется только. Что-то вроде этого:
context.ShouldFetchSoftDeleted = true;
var retrievedObj= context.Objects.Find(obj.Id);
И затем в новом экземпляре контекста (не в том же контексте)
var retrievedObj= context.Objects.Find(obj.Id);
Второй раз, ShouldFetchSoftDeleted
установлен в false, все отлично, но EF решает, что этот запрос был таким же, как и раньше, и извлекал его из кеша. Полученный запрос не содержит фильтра и, таким образом, возвращает все объекты (soft-deleted и not). Кэш не очищается, когда контекст расположен.
Теперь вопрос заключается в том, есть ли способ, в идеале, пометить построенный DbCommand
так, чтобы он не был кэширован. Это можно сделать? Или есть способ принудительно выполнить перекомпиляцию запроса?
Есть способы избежать кеширования, но я бы предпочел не менять каждый запрос в приложении, чтобы исправить это.
Подробнее о кешировании Query Plan можно найти здесь.
Изменить 1
Я использую новый контекст для каждого запроса - не должно быть проблем с кэшированием объектов.
Изменить 2
Вот журнал базы данных. Первый вызов - с soft-delete, а второй - без него. ...
части идентичны, поэтому я исключил их из журнала. Вы можете видеть, что оба запроса идентичны. Сначала вызывается CreateTree
, и приведенное дерево кэшируется так, что при выполнении дерево извлекается из кеша, и мой флаг soft-delete не применяется повторно, когда это должно быть.
Opened connection at 16.5.2015. 2:34:25 +02:00
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[IsDeleted] AS [IsDeleted],
...
FROM [dbo].[Items] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0
-- p__linq__0: '1' (Type = Int64, IsNullable = false)
-- Executing at 16.5.2015. 2:34:25 +02:00
-- Completed in 22 ms with result: SqlDataReader
Closed connection at 16.5.2015. 2:34:25 +02:00
The thread 0x1008 has exited with code 259 (0x103).
The thread 0x1204 has exited with code 259 (0x103).
The thread 0xf94 has exited with code 259 (0x103).
Opened connection at 16.5.2015. 2:34:32 +02:00
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[IsDeleted] AS [IsDeleted],
...
FROM [dbo].[Items] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0
-- p__linq__0: '1' (Type = Int64, IsNullable = false)
-- Executing at 16.5.2015. 2:34:32 +02:00
-- Completed in 16 ms with result: SqlDataReader
Closed connection at 16.5.2015. 2:34:32 +02:00
'vstest.executionengine.x86.exe' (CLR v4.0.30319: UnitTestAdapter: Running test): Loaded 'C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.DebuggerVisualizers\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.DebuggerVisualizers.dll'. Cannot find or open the PDB file.
Как я уже сказал, я выполнил каждый запрос в своем собственном контексте следующим образом:
using (var context = new MockContext())
{
// Test overrided behaviour
// This should return just deleted entity
// Enable soft-delete retrieval
context.ShouldFetchSoftDeleted = true;
// Request 1 goes here
// context.Items.Where(...).ToList()
}
using (var context = new MockContext())
{
// Request 2 goes here
// context.Items.Where(...).ToList()
}