DryIoc にInterceptor 機能を追加する

## Interceptor(Aspect)

DI によって注入されるオブジェクトの動作に「横断的関心」を組み込む。
主要なものにログ取得、トランザクション処理がある。

Aspect使わない場合

```csharp
public int Foo(){
    logger.LogTrace($"{nameof(Foo)} start");
    DoSomething();
    using(var transaction = context.Database.BeginTransaction()){    
        var data = context.Foos.Find(1);
        logger.LogDebug($"data id = {data.Id} name = {data.Name}")
        data.Name ="updated";
        context.SaveChanges();
        transaction.Commit();
        logger.LogDebug($"{nameof(Foo)} commit")
        
        return 1;
    }
}
```

となって、特にLog出力は処理の本質には関係ない(=Log出力を削除しても処理結果は同じ)のに処理の可読性を下げてしまう。

Aspect使うと

```csharp
[TransactionMethod]
public int Foo(){
    DoSomething();

        var data = context.Foos.Find(1);
        logger.LogDebug($"data id = {data.Id} name = {data.Name}")

        data.Name ="updated";
        context.SaveChanges();
        transaction.Commit();
        
        return 1;
    }
}
```
で済み、デバッグ用出力以外の「本質に関係ない」コードを除去できる。

他にも、メソッドの前後に必ず実行する処理を挟み込んだり継承によらず処理を書き換えたりできるようになったりする。

## DryIoc によるInterceptor 

Castle.DynamicProxy.IInterceptor の実装を作る。


```csharp
// Interceptor 自体のコード
    public class TraceInterceptor : IInterceptor
    {
        ILogger logger = LogManager.GetCurrentClassLogger(); // 例えばNLogに出力 
        public void Intercept(IInvocation invocation)
        {
             logger.Trace($"{invocation.Method.Name} start");
            invocation.Proceed();
             logger.Trace($"{invocation.Method.Name} end");
        }
    }
```

Interceptor を適用できるようにする

```csharp
    public static class DryIocInterception
    {
        public static void Intercept<TService, TInterceptor>(this IRegistrator registrator, object serviceKey = null)
          where TInterceptor : class, IInterceptor =>
          registrator.Intercept<TInterceptor>(typeof(TService), serviceKey);

        public static void Intercept<TInterceptor>(this IRegistrator registrator, Type serviceType, object serviceKey = null)
            where TInterceptor : class, IInterceptor =>
            registrator.Intercept(serviceType, Parameters.Of.Type(typeof(IInterceptor[]), typeof(TInterceptor[])), serviceKey);

        public static void Intercept(this IRegistrator registrator, Type serviceType, Type[] interceptors, object serviceKey = null)
        {
            registrator.Intercept(
               serviceType,
               Parameters.Of.Type(
                   typeof(IInterceptor[]),
                       request =>
                       {
                           var list = new List<IInterceptor>();
                           foreach(var t in interceptors)
                           {
                               list.Add((IInterceptor)request.Container.Resolve(t));
                           }
                           return list.ToArray();
                       }),
               serviceKey);
        }

        public static void Intercept(this IRegistrator registrator,
            Type serviceType, ParameterSelector interceptorsParameterSelector, object serviceKey = null)
        {
            Type proxyType;
            if (serviceType.IsInterface())
                proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(
                    serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
            else if (serviceType.IsClass())
                proxyType = ProxyBuilder.CreateClassProxyTypeWithTarget(
                    serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
            else
                throw new ArgumentException(
                    $"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface");

            registrator.Register(serviceType, proxyType,
                made: Made.Of(
                    pt => pt.PublicConstructors().FindFirst(ctor => ctor.GetParameters().Length != 0),
                    interceptorsParameterSelector),
                setup: Setup.DecoratorOf(useDecorateeReuse: true, decorateeServiceKey: serviceKey));
        }

        private static DefaultProxyBuilder ProxyBuilder => _proxyBuilder ?? (_proxyBuilder = new DefaultProxyBuilder());
        private static DefaultProxyBuilder _proxyBuilder;


        public static void Intercept<TInterceptor>(this IRegistrator registrator, Func<Type, bool> predicate)
           where TInterceptor : class, IInterceptor
        {
            foreach (var registration in registrator.GetServiceRegistrations().Where(r => predicate(r.ServiceType)))
            {
                registrator.Intercept<TInterceptor>(registration.ServiceType);
            }
        }
    }
```

DIコンテナを構成する

```csharp
        public static IServiceProvider CreateServiceProvider()
        {
            var services = new ServiceCollection();
            services.AddLogging(loggingBuilder => {
                loggingBuilder.ClearProviders();
                loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                loggingBuilder.AddNLog();
            });

            var container = new DryIoc.Container(rules => rules.With()).WithDependencyInjectionAdapter(services);

            container.Register<TraceInterceptor>(reuse: Reuse.Transient);
            container.Register<TraceInterceptorAsync>(reuse: Reuse.Transient);

            container.Register<IService, Service>(reuse: Reuse.Scoped);
            container.Intercept(typeof(IService), new Type[] { typeof(TraceInterceptor), typeof(TransactionInterceptor) });

            return container.BuildServiceProvider();
        }

```

使う

```csharp
            var provider = CreateServiceProvider();
            using(var scope = provider.CreateScope())
            {
                var service = scope.ServiceProvider.GetRequiredService<Services.IService>();
                Console.WriteLine(service.DoSomething());

            }
```

## その他

AutofacにもUnity Container にもInterceptor注入の仕組みがあるのだけど DryIoc はいろいろと自前でやる必要があったりする。

ここのサンプルでは1サービスに複数のInterceptor を登録できるようにしている。
オリジナルは https://github.com/dadhi/DryIoc/blob/master/test/DryIoc.IssuesTests/Interception/DryIocInterception.cs

追加したのは

```csharp
        public static void Intercept(this IRegistrator registrator, Type serviceType, Type[] interceptors, object serviceKey = null)
        {
            registrator.Intercept(
               serviceType,
               Parameters.Of.Type(
                   typeof(IInterceptor[]),
                       request =>
                       {
                           var list = new List<IInterceptor>();
                           foreach(var t in interceptors)
                           {
                               list.Add((IInterceptor)request.Container.Resolve(t));
                           }
                           return list.ToArray();
                       }),
               serviceKey);
        }
```

`Parameters.Of.Type` が注入する Interceptor を取り出せればよい。