Настройка EF для выброса, если доступ к свойству навигации не загружен (и отложенная загрузка отключена) - PullRequest
3 голосов
/ 09 февраля 2012

У нас есть несколько приложений, которые в настоящее время используют модель EF с включенной отложенной загрузкой.Когда я отключаю ленивую загрузку (чтобы избежать неявных загрузок и большинства наших N + 1 выборов), я бы предпочел получить доступ к «должен быть загружен» (или вручную () для ссылки)бросить исключение вместо возврата null (поскольку конкретное исключение для этого было бы лучше и легче отлаживать, чем null ref).

В настоящее время я склоняюсь к тому, чтобы просто изменить шаблон t4 для этого (так,если reference.IsLoaded == false, throw), но подумал, не является ли это уже решенной проблемой, либо в коробке, либо с помощью другого проекта.

Бонусные баллы за любые ссылки на плагины / расширения / etc, которые могутИсходный анализ и выявление таких проблем.:)

Ответы [ 2 ]

2 голосов
/ 28 апреля 2016

Я хотел сделать то же самое (добавить ленивую загрузку) по нескольким причинам, связанным с производительностью - я хотел избежать синхронизации запросов, потому что они блокируют поток, а в некоторых местах я хочу избежать загрузки полной сущности, а вместо этого просто загрузить свойства, которые нужны коду.

Просто отключить отложенную загрузку недостаточно, потому что некоторые объекты имеют свойства, которые на законных основаниях могут быть нулевыми, и я не хочу путать «нулевой, потому что он нулевой» с «нулевым, потому что мы решили не загружать его».

Я также хотел опционально добавлять ленивую загрузку в некоторые конкретные пути кода, где я знаю, что ленивая загрузка проблематична.

Ниже мое решение.

В моем классе DbContext добавьте это свойство:

class AnimalContext : DbContext
{
   public bool ThrowOnSyncQuery { get; set; }
}

Где-нибудь при запуске моего кода, запустите это:

// Optionally don't let EF execute sync queries
DbInterception.Add(new ThrowOnSyncQueryInterceptor());

Код для ThrowOnSyncQueryInterceptor выглядит следующим образом:

public class ThrowOnSyncQueryInterceptor : IDbCommandInterceptor
{
    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        OptionallyThrowOnSyncQuery(interceptionContext);
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }

    private void OptionallyThrowOnSyncQuery<T>(DbCommandInterceptionContext<T> interceptionContext)
    {
        // Short-cut return on async queries.
        if (interceptionContext.IsAsync)
        {
            return;
        }

        // Throw if ThrowOnSyncQuery is enabled
        AnimalContext context = interceptionContext.DbContexts.OfType<AnimalContext>().SingleOrDefault();
        if (context != null && context.ThrowOnSyncQuery)
        {
            throw new InvalidOperationException("Sync query is disallowed in this context.");
        }
    }
}

Тогда в коде, который использует AnimalContext

using (AnimalContext context = new AnimalContext(_connectionString))
{
    // Disable lazy loading and sync queries in this code path
    context.ThrowOnSyncQuery = true;

    // Async queries still work fine
    var dogs = await context.Dogs.Where(d => d.Breed == "Corgi").ToListAsync();

    // ... blah blah business logic ...
}
0 голосов
/ 27 февраля 2012

Вам не нужно изменять T4. Основываясь на упоминании "T4", я предполагаю, что вы используете EDMX. Окно свойств контейнера имеет свойство lazyloadingenabled. При создании новой модели устанавливается значение true. Вы можете изменить его на false. Шаблон T4 увидит это и добавит код в ctor.

Также, если вы используете шаблоны POCO от Microsoft, они добавят виртуальное ключевое слово в ваши свойства навигации. Virtual + lazyloadingenabled - это необходимая комбинация для получения отложенной загрузки. Если вы удалите виртуальное ключевое слово, свойство никогда не будет загружено с отложенной загрузкой, даже если включена отложенная загрузка.

НТН Julie

...