0

I had an issue which is I am trying to Log Interceptor using Autofac.But logaspect did not intercept. Indeed aspectInterceptor selector did not intercept when click to add method. So you can see my flow ,

After saw Jonathan's comment , I want to ask is the problem maybe asyc methods?

Pogram.cs

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureContainer<ContainerBuilder>(builder =>
                {
                    builder.RegisterModule(new AutofacResolverModule());
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(LogLevel.Trace);
                });
    }

//--------

AutofacResolverModule.cs

public class AutofacResolverModule : Module
{
    public AutofacResolverModule()
    {
        
    }
    protected override void Load(ContainerBuilder builder)
    {

        builder.RegisterGeneric(typeof(Repository<,>)).As(typeof(IRepository<,>));
        builder.RegisterGeneric(typeof(BaseService<,,>)).As(typeof(IBaseService<,,>));
        builder.RegisterType<FileLogger>();



        #region AutofacInterceptorHelper
        var assembly = Assembly.GetExecutingAssembly();
        builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces()
            .EnableInterfaceInterceptors(new ProxyGenerationOptions()
            {
                Selector = new AspectInterceptorSelector()
            }).SingleInstance().InstancePerDependency();
        #endregion
    }
}

//---------

AspectInterceptorSelector.cs

public class AspectInterceptorSelector: IInterceptorSelector
    {
        public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
        {
            var classAttributes = type.GetCustomAttributes<MethodInterceptorBaseAttribute>(true).ToList();
            var methodAttributes =
                type.GetMethod(method.Name)?.GetCustomAttributes<MethodInterceptorBaseAttribute>(true);
            if (methodAttributes != null)
            {
                classAttributes.AddRange(methodAttributes);
            }

            //classAttributes.Add(new LogAspect(typeof(FileLogger)));

            return classAttributes.OrderBy(x => x.Priority).ToArray();

        }
    }

BaseService.cs

   This class is generic base class and my purpose is all post methods logged into .txt file , is there any problem to use this log aspect into a generic class ?     

public class BaseService<TEntity, TPrimaryKey, TEntityDto> : IBaseService<TEntity, TPrimaryKey, TEntityDto> where TEntity : BaseEntity<TPrimaryKey>, new()
where TEntityDto : IDto
{
    private readonly IRepository<TEntity, TPrimaryKey> _repository;
    private readonly IMapper _mapper;
    public BaseService(IRepository<TEntity, TPrimaryKey> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }


   [LogAspect(typeof(FileLogger))]
    public async Task<IResult> Add(TEntityDto entityDto)
    {
        var entity = _mapper.Map<TEntity>(entityDto);
        var result = await _repository.Add(entity);
        return result == null ? new Result(false, ErrorMessages.CreateMessage) : new Result(true, SuccessMessages.CreateMessage, result.Id);


    }


    public async Task<IResult> Find(Expression<Func<TEntity, bool>> predicate)
    {
        var result = await _repository.Find(predicate);
        return result == null ? new Result(true, ErrorMessages.GetMessage) : new Result(true, _mapper.Map<List<ExampleDto>>(result));
    }

    public async Task<IResult> GetAll(Expression<Func<TEntity, bool>> predicate = null)
    {
        var result = predicate == null ? await _repository.GetAll() : await _repository.GetAll(predicate);
        return result == null ? new Result(true, ErrorMessages.GetMessage) : new Result(true, _mapper.Map<List<ExampleDto>>(result));

    }

    public async Task<IResult> HardDelete(TPrimaryKey Id)
    {
        var entity = await _repository.Find(x => x.Id.Equals(Id));
        var result = await _repository.HardDelete(entity);
        return result == 0 ? new Result(false, ErrorMessages.DeleteMessage) : new Result(true, SuccessMessages.DeleteMessage);
    }

    public async Task<IResult> Delete(TPrimaryKey Id)
    {
        var entity = await _repository.Find(x => x.Id.Equals(Id));
        var result = await _repository.Delete(entity);
        return result == 0 ? new Result(false, ErrorMessages.DeleteMessage) : new Result(true, SuccessMessages.DeleteMessage);
    }

    public async Task<IResult> Update(TEntityDto entityDto)
    {
        var entity = _mapper.Map<TEntity>(entityDto);
        var result = await _repository.Update(entity);
        return result == null ? new Result(false, ErrorMessages.UpdateMessage) : new Result(true, SuccessMessages.UpdateMessage, result.Id);
    }
}

LogAspect.cs

public class LogAspect : MethodInterceptor
{
    private readonly LoggerServiceBase _loggerServiceBase;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public LogAspect(Type loggerService)
    {
        if (loggerService.BaseType != typeof(LoggerServiceBase))
        {
            throw new ArgumentException("Wrong Type");
        }

        _loggerServiceBase = (LoggerServiceBase)ServiceTool.ServiceProvider.GetService(loggerService);
        _httpContextAccessor = ServiceTool.ServiceProvider.GetService<IHttpContextAccessor>();
    }

    protected override void OnBefore(IInvocation invocation)
    {
        _loggerServiceBase?.Info(GetLogDetail(invocation));
    }

    private string GetLogDetail(IInvocation invocation)
    {
        var logParameters = new List<LogParameters>();
        for (var i = 0; i < invocation.Arguments.Length; i++)
        {
            logParameters.Add(new LogParameters
            {
                Name = invocation.GetConcreteMethod().GetParameters()[i].Name,
                Value = invocation.Arguments[i],
                Type = invocation.Arguments[i].GetType().Name,
            });
        }

        var logDetail = new LogDetails
        {
            MethodName = invocation.Method.Name,
            Parameters = logParameters,
            User = (_httpContextAccessor.HttpContext == null ||
                    _httpContextAccessor.HttpContext.User.Identity.Name == null)
                ? "?"
                : _httpContextAccessor.HttpContext.User.Identity.Name
        };
        return JsonConvert.SerializeObject(logDetail);
    }
}

MethodInterceptor.cs

 public abstract class MethodInterceptor: MethodInterceptorBaseAttribute
{

    public override void Intercept(IInvocation invocation)
    {
        var isSuccess = true;
        OnBefore(invocation);
        try
        {
            invocation.Proceed();
            var result = invocation.ReturnValue as Task;
            result?.Wait();
        }
        catch (Exception e)
        {
            isSuccess = false;
            OnException(invocation, e);
            throw;
        }
        finally
        {
            if (isSuccess)
            {
                OnSuccess(invocation);
            }
        }

        OnAfter(invocation);
    }

    protected virtual void OnBefore(IInvocation invocation)
    {
    }

    protected virtual void OnAfter(IInvocation invocation)
    {
    }

    protected virtual void OnException(IInvocation invocation, Exception e)
    {
    }

    protected virtual void OnSuccess(IInvocation invocation)
    {
    }
}

MethodInterceptorBaseAttribute.cs

 [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method , AllowMultiple = true,Inherited = true)]
    public abstract class MethodInterceptorBaseAttribute:Attribute, IInterceptor
    {
        public int Priority { get; set; }
        public virtual void Intercept(IInvocation invocation)
        {
            
        }
    }

So i can not find this solutions for almost a month , any ideas ?

  • 2
    I haven't looked at your issue specifically, but have you read the DynamicProxy documentation on async? You are likely making incorrect assumptions about when code is run, DynamicProxy only sees your async method split into multiple methods by the compiler. https://github.com/castleproject/Core/blob/master/docs/dynamicproxy-async-interception.md – Jonathon Rossi Dec 12 '21 at 13:20
  • Jonathon i am not sure Dynamic proxy is the problem , maybe i am a bit of unaware but can you be more spesific for me ? – Ahmet Tarık Bostan Dec 12 '21 at 15:45
  • Unfortunately no, I don't use Autofac, just thought I'd make you aware of the DP documentation as async interception catches most people. Unfortunately your question is unlikely to get an answer as there are a lot of parts to understand. If you can, refine your question to the bare minimum to reproduce. – Jonathon Rossi Dec 12 '21 at 22:25
  • I concur with earlier comments: async is likely part of the problem, but also, this needs to be cut down to a more minimal repro. Try without a selector. Try without assembly scanning. Try with only one method on the class being intercepted. Try with a much simpler interceptor. Few folks will have time to try debugging this amount of complexity - I know I don't. – Travis Illig Dec 13 '21 at 13:56

1 Answers1

0

Tarık. In my opinion, your fault is not registering generic types to assemblies configuration. You should register generic types in AutofacResolverModule.cs

builder.RegisterAssemblyOpenGenericTypes(assembly).AsImplementedInterfaces()
        .EnableInterfaceInterceptors(new ProxyGenerationOptions()
        {
            Selector = new AspectInterceptorSelector()
        }).SingleInstance().InstancePerDependency();