Информация об изменениях

Сообщение Aspect Generator (по мотивам "Новости C#12") от 22.11.2023 3:05

Изменено 22.11.2023 3:07 IT

Aspect Generator (по мотивам "Новости C#12")
В данном
Автор: Serginio1
Дата: 13.04.23
топике народ обсуждает новую экспериментальную фичу C# — Interceptors. При этом некоторые коллеги выразили сомнения, что это фича рабочая в частности для AOP.

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

По сути это реинкарнация аспектов из BLToolkit, кроме реализации самих аспектов. Сами аспекты предлагается реализовывать пользователям, т.к. с этим генератором это делается максимально просто (хотелось в это верить).

Пример для OpenTelemetry:

using System;
using System.Diagnostics;

using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace Aspects
{
    /// <summary>
    /// Initializes OpenTelemetry.
    /// </summary>
    static class OpenTelemetryFactory
    {
        public static TracerProvider? Create()
        {
            return Sdk.CreateTracerProviderBuilder()
                .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
                .AddSource("Sample.Aspect")
                .AddConsoleExporter()
                .Build();
        }
    }

    /// <summary>
    /// Metrics aspect.
    /// </summary>
    [Aspect(
        OnUsing   = nameof(OnUsing),
        OnFinally = nameof(OnFinally)
        )]
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    sealed class MetricsAttribute : Attribute
    {
        static readonly ActivitySource _activitySource = new("Sample.Aspect");

        public static Activity? OnUsing(InterceptCallInfo info)
        {
            var activity = _activitySource.StartActivity(info.MemberInfo.Name);

            info.Tag = activity;

            return activity;
        }

        public static void OnFinally(InterceptCallInfo info)
        {
            if (info is { Tag: Activity activity, Exception: var ex })
                activity.SetStatus(ex is null ? ActivityStatusCode.Ok : ActivityStatusCode.Error);
        }
    }
}


Пример использования:

using System;
using System.Threading;

using Aspects;

namespace OpenTelemetryAspect
{
    static class Program
    {
        static void Main()
        {
            using var _ = OpenTelemetryFactory.Create();

            Method1();
            Method2();
            Method1();

            try
            {
                ExceptionMethod();
            }
            catch
            {
                // ignored
            }
        }

        [Metrics]
        public static void Method1()
        {
            Thread.Sleep(100);
        }

        [Metrics]
        public static void Method2()
        {
            Thread.Sleep(200);
        }

        [Metrics]
        public static void ExceptionMethod()
        {
            throw new();
        }
    }
}
Aspect Generator (по мотивам "Новости C#12")
В данном
Автор: Serginio1
Дата: 13.04.23
топике народ обсуждает новую экспериментальную фичу C# — Interceptors. При этом некоторые коллеги выразили сомнения, что это фича рабочая в частности для AOP.

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

По сути это реинкарнация аспектов из BLToolkit, кроме реализации самих аспектов. Сами аспекты предлагается реализовывать пользователям, т.к. с этим генератором это делается максимально просто (хотелось бы в это верить).

Пример для OpenTelemetry:

using System;
using System.Diagnostics;

using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace Aspects
{
    /// <summary>
    /// Initializes OpenTelemetry.
    /// </summary>
    static class OpenTelemetryFactory
    {
        public static TracerProvider? Create()
        {
            return Sdk.CreateTracerProviderBuilder()
                .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
                .AddSource("Sample.Aspect")
                .AddConsoleExporter()
                .Build();
        }
    }

    /// <summary>
    /// Metrics aspect.
    /// </summary>
    [Aspect(
        OnUsing   = nameof(OnUsing),
        OnFinally = nameof(OnFinally)
        )]
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    sealed class MetricsAttribute : Attribute
    {
        static readonly ActivitySource _activitySource = new("Sample.Aspect");

        public static Activity? OnUsing(InterceptCallInfo info)
        {
            var activity = _activitySource.StartActivity(info.MemberInfo.Name);

            info.Tag = activity;

            return activity;
        }

        public static void OnFinally(InterceptCallInfo info)
        {
            if (info is { Tag: Activity activity, Exception: var ex })
                activity.SetStatus(ex is null ? ActivityStatusCode.Ok : ActivityStatusCode.Error);
        }
    }
}


Пример использования:

using System;
using System.Threading;

using Aspects;

namespace OpenTelemetryAspect
{
    static class Program
    {
        static void Main()
        {
            using var _ = OpenTelemetryFactory.Create();

            Method1();
            Method2();
            Method1();

            try
            {
                ExceptionMethod();
            }
            catch
            {
                // ignored
            }
        }

        [Metrics]
        public static void Method1()
        {
            Thread.Sleep(100);
        }

        [Metrics]
        public static void Method2()
        {
            Thread.Sleep(200);
        }

        [Metrics]
        public static void ExceptionMethod()
        {
            throw new();
        }
    }
}