Обнаружить или передать метод вызова в Entity Framework 6 и Entity Framework Core через IDbCommandInterceptor - PullRequest
0 голосов
/ 20 июня 2020

Меня интересует запись имени метода, инициировавшего запрос Entity Framework. Я буду использовать эту функциональность как в Entity Framework 6, так и в Entity Framework Core.

Рассмотрим следующий класс:

namespace MyCompany.MyApp
{
    public class MyDataClass
    {
        // ... snip ...
        
        public Person GetPerson(long id)
        {
            return _dbContext
                .Person
                .FirstOrDefault(p => p.Id == id);
        }
    }
}

И следующий перехватчик:

public class EFCommandInterceptor : IDbCommandInterceptor
{
    // ... snip other interface methods ...

    public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    {
        var callingMehtod = GetCallingMethod() // <--- I'd like this to return the string "MyCompany.MyApp.MyDataClass.GetPerson"
    }
}

Is есть ли способ определить, какой метод инициировал запрос, в результате которого был вызван перехватчик?

Поскольку я собираюсь использовать эту функцию для отслеживания и анализа производительности, я не могу отправить и записать все Environment.StackTrace для каждого запроса, проходящего через систему.

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

Другой приемлемый подход может выглядеть примерно так:

namespace MyCompany.MyApp
{
    public class MyDataClass
    {
        // ... snip ...
        
        public Person GetPerson(long id)
        {
            return _dbContext
                .Person
                .WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
                .FirstOrDefault(p => p.Id == id);
        }
    }
}

намерение с приведенным выше примером сможет позже получить дополнительный контекст через DbCommandInterceptionContext внутри IDbCommandInterceptor

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

Ответы [ 2 ]

2 голосов
/ 20 июня 2020

С EF Core вы можете использовать TagWith

var result = db.Albums
               .TagWith(MethodBase.GetCurrentMethod().Name)
               .ToList();
1 голос
/ 20 июня 2020

Вы можете просто зарегистрировать информацию перед запуском запроса, например:

    public Person GetPerson(long id)
    {
        EfDebug.SetCurrentMethodName();
        return _dbContext
            .Person
            .WithExtraInterceptorContext(new { CallingMethod = "MyCompany.MyApp.MyDataClass.GetPerson"}) // <--- Can something like this be done?
            .FirstOrDefault(p => p.Id == id);
    }

. . .

   static class EfDebugExtensions
    {
        public static AsyncLocal<string> CurrentMethodName = new AsyncLocal<string>();
        public static void SetCurrentMethodName([CallerMemberName] string caller = "")
        {
            CurrentMethodName.Value = caller;
        }
        public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source,[CallerMemberName] string caller = "")
        {
            CurrentMethodName.Value = caller;
            var rv = Enumerable.FirstOrDefault(source);
            return rv;
        }
        public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source, [CallerMemberName] string caller = "")
        {
            CurrentMethodName.Value = caller;
            var rv = Enumerable.ToList(source);
            return rv;
        }

    }
...