Как создать регистратор, который регистрирует входные параметры и реакцию метода [duplicate]

Другой общий случай, когда можно получить это исключение, включает в себя насмешливые классы во время модульного тестирования. Независимо от используемой насмешливой структуры, вы должны убедиться, что все соответствующие уровни иерархии классов должным образом высмеиваются. В частности, все свойства HttpContext, на которые ссылается тестируемый код, должны быть изделены.

См. « Исключение NullReferenceException при проверке пользовательского AuthorizationAttribute » для несколько подробного примера.

137
задан Journeyman 25 August 2008 в 10:14
поделиться

14 ответов

C # не является ориентированным на АОП языком. У него есть некоторые функции AOP, и вы можете эмулировать некоторые другие, но AOP с C # является болезненным.

Я искал способы сделать именно то, что вы хотели сделать, и я не нашел простого способа сделать это.

Как я понимаю, это то, что вы хотите сделать:

[Log()]
public void Method1(String name, Int32 value);

, и для этого у вас есть две основные опции

  1. Наследуйте свой класс от MarshalByRefObject или ContextBoundObject и определите атрибут, который наследуется от IMessageSink. Эта статья имеет хороший пример. Вы должны не считаться с тем, что с помощью MarshalByRefObject производительность будет падать, как черт, и я имею в виду это, я говорю о потерях в 10 раз, поэтому тщательно подумайте, прежде чем пытаться это сделать.
  2. Другой вариант вводить код напрямую. В runtime, то есть вам придется использовать рефлексию для «чтения» каждого класса, получения его атрибутов и ввода соответствующего вызова (и в этом отношении я думаю, что вы не могли использовать метод Reflection.Emit, поскольку я думаю, что Reflection.Emit wouldn 't позволить вам вставлять новый код внутри уже существующего метода). Во время разработки это будет означать создание расширения для компилятора CLR, о котором я честно не знаю, как это делается.

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

64
ответ дан Jorge Córdoba 31 August 2018 в 15:39
поделиться
  • 1
    Другими словами, 'ouch' – johnc 29 December 2008 в 05:23
  • 2
    Я должен указать, что если бы у вас были первые функции класса, тогда функцию можно было бы обрабатывать так же, как и любую другую переменную, и вы могли бы использовать «hook» метода. он делает то, что хочет. – RCIX 26 August 2009 в 05:18
  • 3
    Третьей альтернативой является создание основанных на наследовании прокси-серверов во время выполнения с использованием Reflection.Emit. Это подход , выбранный Spring.NET . Однако для этого потребуются виртуальные методы на Traced и не подходит для использования без какого-либо контейнера IOC, поэтому я понимаю, почему этот параметр отсутствует в вашем списке. – Marijn 18 November 2011 в 12:39
  • 4
    ваш второй вариант в основном «Напишите части структуры AOP, которые вам нужны вручную». который затем должен привести к ошибкам «О, подождите, может быть, я должен использовать сторонний вариант, созданный специально для решения проблемы, которую я имею, вместо того, чтобы идти вниз, не-inveted-here-road». – Rune FS 9 August 2013 в 10:42
  • 5
    @jorge Можете ли вы предоставить пример / ссылку для достижения этой цели, используя инфраструктуру Injection / IoC для зависимостей, например nInject – Charanraj Golla 3 October 2013 в 02:30

Взгляните на это - Довольно тяжелый материал .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx

Essential .net - У дон-бокса была глава о том, что вам нужно назвать перехватом. Я прочесал некоторые из них здесь (Извините за цвет шрифтов - тогда у меня была темная тема ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html

4
ответ дан Gishu 31 August 2018 в 15:39
поделиться

Сначала вам нужно изменить свой класс для реализации интерфейса (а не для реализации MarshalByRefObject).

interface ITraced {
    void Method1();
    void Method2()
}
class Traced: ITraced { .... }

Затем вам понадобится общий объект-оболочка на основе RealProxy, чтобы украсить любой интерфейс, чтобы позволить перехватывать любые вызов декорированного объекта.

class MethodLogInterceptor: RealProxy
{
     public MethodLogInterceptor(Type interfaceType, object decorated) 
         : base(interfaceType)
     {
          _decorated = decorated;
     }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall.MethodBase;
        Console.WriteLine("Precall " + methodInfo.Name);
        var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
        Console.WriteLine("Postcall " + methodInfo.Name);

        return new ReturnMessage(result, null, 0,
            methodCall.LogicalCallContext, methodCall);
    }
}

Теперь мы готовы перехватить вызовы метода Method1 и Method2 из файла ITraced

 public class Caller 
 {
     public static void Call() 
     {
         ITraced traced = (ITraced)new MethodLogInterceptor(typeof(ITraced), new Traced()).GetTransparentProxy();
         traced.Method1();
         traced.Method2(); 
     }
 }
4
ответ дан Ibrahim ben Salah 31 August 2018 в 15:39
поделиться

Лучшее, что вы можете сделать до того, как C # 6 с выпуском 'nameof' будет использовать медленные выражения StackTrace и linq.

Например. для такого метода

    public void MyMethod(int age, string name)
    {
        log.DebugTrace(() => age, () => name);

        //do your stuff
    }

Такая строка может быть создана в вашем файле журнала

Method 'MyMethod' parameters age: 20 name: Mike

Вот реализация:

    //TODO: replace with 'nameof' in C# 6
    public static void DebugTrace(this ILog log, params Expression<Func<object>>[] args)
    {
        #if DEBUG

        var method = (new StackTrace()).GetFrame(1).GetMethod();

        var parameters = new List<string>();

        foreach(var arg in args)
        {
            MemberExpression memberExpression = null;
            if (arg.Body is MemberExpression)
                memberExpression = (MemberExpression)arg.Body;

            if (arg.Body is UnaryExpression && ((UnaryExpression)arg.Body).Operand is MemberExpression)
                memberExpression = (MemberExpression)((UnaryExpression)arg.Body).Operand;

            parameters.Add(memberExpression == null ? "NA" : memberExpression.Member.Name + ": " + arg.Compile().DynamicInvoke().ToString());
        }

        log.Debug(string.Format("Method '{0}' parameters {1}", method.Name, string.Join(" ", parameters)));

        #endif
    }
-2
ответ дан irriss 31 August 2018 в 15:39
поделиться
  • 1
    Это не соответствует требованию, определенному в его второй точке. – Ted Bigham 28 October 2015 в 23:34

Я нашел другой способ, который может быть проще ...

Объявить метод InvokeMethod

[WebMethod]
    public object InvokeMethod(string methodName, Dictionary<string, object> methodArguments)
    {
        try
        {
            string lowerMethodName = '_' + methodName.ToLowerInvariant();
            List<object> tempParams = new List<object>();
            foreach (MethodInfo methodInfo in serviceMethods.Where(methodInfo => methodInfo.Name.ToLowerInvariant() == lowerMethodName))
            {
                ParameterInfo[] parameters = methodInfo.GetParameters();
                if (parameters.Length != methodArguments.Count()) continue;
                else foreach (ParameterInfo parameter in parameters)
                    {
                        object argument = null;
                        if (methodArguments.TryGetValue(parameter.Name, out argument))
                        {
                            if (parameter.ParameterType.IsValueType)
                            {
                                System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(parameter.ParameterType);
                                argument = tc.ConvertFrom(argument);

                            }
                            tempParams.Insert(parameter.Position, argument);

                        }
                        else goto ContinueLoop;
                    }

                foreach (object attribute in methodInfo.GetCustomAttributes(true))
                {
                    if (attribute is YourAttributeClass)
                    {
                        RequiresPermissionAttribute attrib = attribute as YourAttributeClass;
                        YourAttributeClass.YourMethod();//Mine throws an ex
                    }
                }

                return methodInfo.Invoke(this, tempParams.ToArray());
            ContinueLoop:
                continue;
            }
            return null;
        }
        catch
        {
            throw;
        }
    }

Затем я определяю свои методы следующим образом

[WebMethod]
    public void BroadcastMessage(string Message)
    {
        //MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
        //return;
        InvokeMethod("BroadcastMessage", new Dictionary<string, object>() { {"Message", Message} });
    }

    [RequiresPermission("editUser")]
    void _BroadcastMessage(string Message)
    {
        MessageBus.GetInstance().SendAll("<span class='system'>Web Service Broadcast: <b>" + Message + "</b></span>");
        return;
    }

Теперь у меня есть проверка во время выполнения без инъекции зависимостей ...

Нет ошибок на сайте:)

Надеюсь, вы согласитесь, что это меньше, чем тогда AOP Framework или из MarshalByRefObject или с использованием удаленных или прокси-классов.

4
ответ дан Jay 31 August 2018 в 15:39
поделиться
  1. Напишите свою собственную библиотеку AOP.
  2. Используйте рефлексию для создания прокси-сервера регистрации через свои экземпляры (не уверен, что вы можете сделать это, не изменяя часть вашего существующего кода).
  3. Перепишите сборку и введите код регистрации (в основном то же самое, что и 1).
  4. Устанавливайте CLR и добавьте ведение журнала на этом уровне (я думаю, что это самое сложное решение для реализации, не уверен, что у вас есть необходимые крючки в CLR, хотя).
0
ответ дан kokos 31 August 2018 в 15:39
поделиться

Я не знаю решения, но мой подход был бы следующим.

Украсить класс (или его методы) с помощью настраиваемого атрибута. Где-то еще в программе, пусть функция инициализации отражает все типы, читает методы, украшенные атрибутами, и вводит некоторый IL-код в этот метод. Фактически было бы более целесообразно заменить метод на заглушку, которая вызывает LogStart, фактический метод, а затем LogEnd. Кроме того, я не знаю, можете ли вы изменить методы с использованием отражения, поэтому было бы более практичным заменить весь тип.

2
ответ дан Konrad Rudolph 31 August 2018 в 15:39
поделиться

вам нужно исправить ошибку Айенде за то, как он это сделал: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx

1
ответ дан Liao 31 August 2018 в 15:39
поделиться
  • 1
    LOL, комментарии в этом блоге ссылаются на этот вопрос ... – RCIX 21 November 2009 в 07:55

Вы можете достичь этого с помощью функции Перехват контейнера DI, такого как Castle Windsor . Действительно, можно сконфигурировать контейнер таким образом, чтобы все классы, у которых есть метод, украшенный определенным атрибутом, были перехвачены.

Что касается пункта № 3, OP попросил решение без рамки AOP. Я предположил в следующем ответе, что следует избегать: Aspect, JointPoint, PointCut и т. Д. Согласно документации перехвата от CastleWindsor , ни один из них не требуется для выполнения того, что задано.

Настроить общую регистрацию Interceptor на основе наличия атрибута:

public class RequireInterception : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        if (HasAMethodDecoratedByLoggingAttribute(model.Implementation))
        {
            model.Interceptors.Add(new InterceptorReference(typeof(ConsoleLoggingInterceptor)));
            model.Interceptors.Add(new InterceptorReference(typeof(NLogInterceptor)));
        }
    }

    private bool HasAMethodDecoratedByLoggingAttribute(Type implementation)
    {
        foreach (var memberInfo in implementation.GetMembers())
        {
            var attribute = memberInfo.GetCustomAttributes(typeof(LogAttribute)).FirstOrDefault() as LogAttribute;
            if (attribute != null)
            {
                return true;
            }
        }

        return false;
    }
}

Добавить созданный IContributeComponentModelConstruction в контейнер

container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());

И вы можете делать все, что хотите в самом перехватчике

public class ConsoleLoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.Writeline("Log before executing");
        invocation.Proceed();
        Console.Writeline("Log after executing");
    }
}

Добавить атрибут регистрации для вашего метода в журнал

 public class Traced 
 {
     [Log]
     public void Method1(String name, Int32 value) { }

     [Log]
     public void Method2(Object object) { }
 }

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

45
ответ дан plog17 31 August 2018 в 15:39
поделиться
  • 1
    вы также можете вводить вещи с ICorDebug, но это супер зло – Sam Saffron 7 May 2009 в 10:30

Вы можете использовать фреймворк с открытым исходным кодом CInject в CodePlex. Вы можете написать минимальный код для создания Инжектора и заставить его быстро перехватить любой код с помощью CInject. Кроме того, поскольку это Open Source, вы также можете расширить его.

Или вы можете следовать шагам, указанным в этой статье, в методах перехвата вызова с использованием IL и создать свой собственный перехватчик с использованием классов Reflection.Emit в C #.

2
ответ дан Puneet Ghanshani 31 August 2018 в 15:39
поделиться

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

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

1
ответ дан Stacy A 31 August 2018 в 15:39
поделиться

Если вы пишете класс - вызовите его Трассировка - который реализует интерфейс IDisposable, вы можете обернуть все тела метода в

Using( Tracing tracing = new Tracing() ){ ... method body ...}

. В классе Tracing вы можете обработать логику следов в методе constructor / Dispose, соответственно, в классе Tracing, чтобы отслеживать ввод и выход из методов. Такие, что:

    public class Traced 
    {
        public void Method1(String name, Int32 value) {
            using(Tracing tracer = new Tracing()) 
            {
                [... method body ...]
            }
        }

        public void Method2(Object object) { 
            using(Tracing tracer = new Tracing())
            {
                [... method body ...]
            }
        }
    }
11
ответ дан Steen 31 August 2018 в 15:39
поделиться

AOP является обязательным для реализации чистого кода, однако, если вы хотите окружить блок на C #, общие методы имеют относительно простое использование. (с понятием intelli и строго типизированным кодом). Конечно, он НЕ может быть альтернативой AOP.

Хотя PostSHarp имеют мало проблем с ошибкой (я не уверен, что буду использовать на производстве ), это хороший материал.

Общий класс обертки,

public class Wrapper
{
    public static Exception TryCatch(Action actionToWrap, Action<Exception> exceptionHandler = null)
    {
        Exception retval = null;
        try
        {
            actionToWrap();
        }
        catch (Exception exception)
        {
            retval = exception;
            if (exceptionHandler != null)
            {
                exceptionHandler(retval);
            }
        }
        return retval;
    }

    public static Exception LogOnError(Action actionToWrap, string errorMessage = "", Action<Exception> afterExceptionHandled = null)
    {
        return Wrapper.TryCatch(actionToWrap, (e) =>
        {
            if (afterExceptionHandled != null)
            {
                afterExceptionHandled(e);
            }
        });
    }
}

использование может быть таким (с понятием intelli)

var exception = Wrapper.LogOnError(() =>
{
  MessageBox.Show("test");
  throw new Exception("test");
}, "Hata");
2
ответ дан takrl 31 August 2018 в 15:39
поделиться

Если вы хотите отслеживать свои методы без ограничений (без адаптации кода, без AOP Framework, без дубликата кода), позвольте мне сказать вам, вам нужно немного волшебства ...

Серьезно, я решил он реализует AOP Framework, работающий во время выполнения.

Вы можете найти здесь: NConcern .NET AOP Framework

Я решил создать эту AOP Framework, чтобы дать реагировать на подобные потребности. это простая библиотека очень легкая. Вы можете увидеть пример журнала на домашней странице.

Если вы не хотите использовать стороннюю сборку, вы можете просмотреть источник кода (с открытым исходным кодом) и скопировать оба файла Aspect. Directory.cs и Aspect.Directory.Entry.cs для адаптации в соответствии с вашими пожеланиями. Классы тезисов позволяют заменять ваши методы во время выполнения. Я просто попрошу вас уважать лицензию.

Надеюсь, вы найдете то, что вам нужно, или убедите вас, наконец, использовать AOP Framework.

3
ответ дан Tony THONG 31 August 2018 в 15:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: