Single Message Handler Factory using Autofac - c#

So I have a marker interface called IMessage.Then I have classes like:
public class MessageA: IMessage
{
}
Then I have message handlers defined as:
internal interface IMessageHandler<in T> where T: IMessage
{
void Handle(T message);
}
public class MessageAHandler : IMessageHandler<MessageA>
{
public void Handle(T message)
{
//Some logic here
}
}
I want to re-route these messages that I get to the corresponding message handlers when I get a new message. Example:
public class MessageReceiver
{
public void ReceiveMessage(IMessage message)
{
//somehow resolve the appropiate message handler here
messageHandler.Handle(message);
}
}
I can accomplish this right now by using factories like below but I need to have a dependency on each a new factory per different type of message. So I'm wondering if there is a way to create a single factory that will be smart enough to resolve the appropiate message handler?
public class MessageReceiver
{
private readonly Func<IMessageHandler<MessageA>> _messageAFactory;
public MessageReceiver(Func<IMessageHandler<MessageA>> messageAFactory)
{
_messageAFactory= messageAFactory;
}
public void ReceiveMessage(IMessage message)
{
if (message is MessageA)
{
var messageHandler = _messageAFactory();
messageHandler.Handle(message as MessageA);
}
// Add more if-statements here for more messages
}
}
Autofac Registration
public class InfrastructureModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//Register the types in the infrastructure assembly
builder.RegisterAssemblyTypes(ThisAssembly).AsImplementedInterfaces()
.InstancePerLifetimeScope();
//Register the message handlers
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(x => x.IsAssignableFrom(typeof(IMessageHandler<IMessage>)))
.InstancePerDependency().AsImplementedInterfaces();
}
}

First I'll make a small implementation for your messages, just a basic handled flag, in order to test
public class MessageA : IMessage
{
public bool Handled
{
get;
private set;
}
public void MarkAsHandled()
{
this.Handled = true;
}
}
public class MessageB : IMessage
{
public bool Handled
{
get;
private set;
}
public void MarkAsHandled()
{
this.Handled = true;
}
}
Now let's implement both handlers as:
public class MessageAHandler : IMessageHandler<MessageA>
{
public void Handle(MessageA message)
{
message.MarkAsHandled();
}
}
public class MessageBHandler : IMessageHandler<MessageB>
{
public void Handle(MessageB message)
{
message.MarkAsHandled();
}
}
As a side note, you might want to mark your IMessageHandler interface as public (I get compiler error if visibility is set as internal).
Now let's add a small handler:
public interface IMessageHandler
{
Type MessageType { get; }
void Handle(IMessage message);
}
public class MessageHandlerAdapter<T> : IMessageHandler where T : IMessage
{
private readonly Func<IMessageHandler<T>> handlerFactory;
public MessageHandlerAdapter(Func<IMessageHandler<T>> handlerFactory)
{
this.handlerFactory = handlerFactory;
}
public void Handle(IMessage message)
{
var handler = handlerFactory();
handler.Handle((T)message);
}
public Type MessageType
{
get { return typeof(T); }
}
}
We can now implement MessageReceiver this way:
public class MessageReceiver
{
private readonly IEnumerable<IMessageHandler> handlers;
public MessageReceiver(IEnumerable<IMessageHandler> handlers)
{
this.handlers = handlers;
}
public void ReceiveMessage(IMessage message)
{
var handler = this.handlers.Where(h => h.MessageType == message.GetType()).FirstOrDefault();
if (handler != null)
{
handler.Handle(message);
}
else
{
//Do something here, no handler found for message type
}
}
}
Now to test that our messages are processed properly, here is a small test:
[TestClass]
public class TestSelector
{
private IContainer container;
[TestMethod]
public void TestMethod()
{
var processor = container.Resolve<MessageReceiver>();
MessageA ma = new MessageA();
MessageB mb = new MessageB();
processor.ReceiveMessage(ma);
processor.ReceiveMessage(mb);
Assert.AreEqual(ma.Handled, true);
Assert.AreEqual(mb.Handled, true);
}
}
And we need to modify registration a bit, if opting for manual registration, we do as follow:
public TestSelector()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<MessageAHandler>().As<IMessageHandler<MessageA>>();
containerBuilder.RegisterType<MessageBHandler>().As<IMessageHandler<MessageB>>();
containerBuilder.RegisterType<MessageHandlerAdapter<MessageA>>().As<IMessageHandler>();
containerBuilder.RegisterType<MessageHandlerAdapter<MessageB>>().As<IMessageHandler>();
containerBuilder.RegisterType<MessageReceiver>();
this.container = containerBuilder.Build();
}
In here, we now need to register one handler and the relevant adapter.
It is also of course possible to perform assembly scan, but this requires a little bit more plumbing, since using:
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(x => x.IsAssignableFrom(typeof(IMessageHandler<IMessage>)))
.InstancePerDependency().AsImplementedInterfaces();
will not work
typeof(MessageAHandler).IsAssignableFrom(typeof(IMessageHandler<IMessage>))
will return false, since MessageAHandler implements IMessageHandler, not IMessageHandler
To do automatic discovery and registration, here is a snippet:
public TestSelector()
{
var containerBuilder = new ContainerBuilder();
Func<Type, Type> GetHandlerInterface = (t) => t.GetInterfaces()
.Where(iface => iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IMessageHandler<>)).FirstOrDefault();
var handlerTypes = typeof(IMessage).Assembly.GetTypes()
.Where(type => type.IsClass
&& !type.IsAbstract
&& GetHandlerInterface(type) != null);
foreach (Type handlerType in handlerTypes)
{
Type messageType = GetHandlerInterface(handlerType).GetGenericArguments()[0];
var genericHandler = typeof(MessageHandlerAdapter<>).MakeGenericType(messageType);
containerBuilder.RegisterType(handlerType).AsImplementedInterfaces();
containerBuilder.RegisterType(genericHandler).As<IMessageHandler>();
}
containerBuilder.RegisterType<MessageReceiver>();
this.container = containerBuilder.Build();
}

For anyone who is still looking for better solution for auto dispatching to appropriate message handlers registered, there is a nice implementation via MediatR.This is awesome library which can dispatch messages to appropriate registered handlers, and has capability to post messages to multiples handlers.
It is best suited for CQRS scenarios and also for Async Web API, refer CQRS using MediatR . There is a nice support when using DI container like Autofac and StructuredMap, Refer to wiki page of MediatR wiki for full details on DI support.

Related

Why can Autofac not create this class that takes a non-generic implementation of a generic interface as a constructor parameter?

Using Autofac for injection.
Given
interface IStateMachine<TState, TTrigger> { }
class ConcreteStateMachine : IStateMachine<MachineState, Trigger> { }
builder.RegisterType<ConcreteStateMachine>().As<IStateMachine<MachineState, Trigger>>();
class Consumer { Consumer(IStateMachine<MachineState, Trigger> machine) { } }
Why does container.Resolve<Consumer>(); fail with this exception:
Unhandled exception.
Autofac.Core.DependencyResolutionException: An exception was thrown while activating Consumer.
---> Autofac.Core.DependencyResolutionException: An exception was thrown while invoking the
constructor 'Void .ctor(IStateMachine`2[MachineState,Trigger])' on type 'Consumer'.
---> System.NullReferenceException: Object reference not set to an instance of an object.
Full code contrived example:
Requires nuget for Autofac and Stateless
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Autofac;
using Stateless;
public class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<Consumer>()
.InstancePerLifetimeScope();
builder.RegisterType<ConcreteStateMachine>()
.As<IStateMachine<MachineState, Trigger>>()
.InstancePerLifetimeScope();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var consumer = scope.Resolve<Consumer>();
consumer.TurnOn();
consumer.GetData();
consumer.TurnOff();
}
}
}
public class Consumer
{
protected IStateMachine<MachineState, Trigger> Machine;
public Consumer(IStateMachine<MachineState, Trigger> machine)
{
Machine = machine;
Machine.Register(MachineState.Off, () => Debug.WriteLine("State Off"));
Machine.Register(MachineState.On, () => Debug.WriteLine("State On"));
}
public List<string> GetData()
{
if (!Machine.IsInState(MachineState.On)) throw new InvalidOperationException("Can't GetData when machine is off!");
Debug.WriteLine("Getting Data");
return new List<String> {"Data", "Data", "Data"};
}
public void TurnOn()
{
Machine.Fire(Trigger.TurnOn);
}
public void TurnOff()
{
Machine.Fire(Trigger.TurnOff);
}
}
public class ConcreteStateMachine : IStateMachine<MachineState, Trigger>
{
protected StateMachine<MachineState, Trigger> Machine;
public MachineState CurrentState { get; set; }
public void Fire(Trigger trigger)
{
Machine.Fire(trigger);
}
public async Task FireAsync(Trigger trigger)
{
await Machine.FireAsync(trigger);
}
public bool IsInState(MachineState state)
{
return Machine.IsInState(state);
}
public void Register(MachineState state, Action callback)
{
Machine.Configure(state)
.OnEntry(() => callback.Invoke());
}
public void RegisterAsync(MachineState state, Func<Task> callback)
{
Machine.Configure(state)
.OnEntryAsync(async () => await callback.Invoke());
}
public void Start()
{
ConfigureMachine();
Machine.Activate();
}
protected void ConfigureMachine()
{
Machine = new StateMachine<MachineState, Trigger>(MachineState.Off);
Machine.Configure(MachineState.Off)
.Permit(Trigger.TurnOn, MachineState.On);
Machine.Configure(MachineState.On)
.Permit(Trigger.TurnOff, MachineState.Off);
}
}
public interface IStateMachine<TState, TTrigger>
{
TState CurrentState { get; set; }
bool IsInState(TState state);
void Fire(TTrigger trigger);
Task FireAsync(TTrigger trigger);
void Start();
void Register(TState state, Action callback);
void RegisterAsync(TState state, Func<Task> callback);
}
public enum MachineState
{
Off,
On
}
public enum Trigger
{
TurnOff,
TurnOn
}
I've tried to find an example of this type of injection using Autofac and I can't find anyone else who's had a similar problem.
The container will resolve an instance of ConcreteStateMachine directly.
As in container.Resolve<IStateMachine<MachineState, Trigger>>() correctly provides a ConcreteStateMachine, but Autofac doesn't seem to know how to send an instance to Consumer.
Machine field of ConcreteStateMachine is not initialized and is null.
So it fails with NullReferenceException in:
public void Register(MachineState state, Action callback)
{
Machine.Configure(state)
.OnEntry(() => callback.Invoke());
}
Just a case of introducing a bug while creating the contrived example...
I never called Machine.Start() in the constructor of Consumer, which instantiates the internal StateMachine, prior to calling Register, which invokes a method on the underlying StateMachine. This caused an exception to be thrown while creating Consumer and was not in fact an Autofac configuration issue.
Sorry!

Autofac not resolving interfaces in other project

I'm trying to write a generic command bus (Part of class library) that uses different commands and handlers in each of my services.
The following code produces the following exception:
System.Exception: Command does not have any handler RegisterUserCommand
I was under the impression passing the the ExecutingAssemblies of my UserService would allow the Container to resolve the handler in my UserService but apparently not.
Am I doing something wrong?
CommandBus:
public interface ICommandBus
{
void Send<T>(T Command) where T : ICommand;
}
public class CommandBus : ICommandBus
{
private IContainer Container { get; set; }
public CommandBus(Assembly assembly)
{
Container = new CommandBusContainerConfig().Configure(assembly);
}
public void Send<TCommand>(TCommand command) where TCommand : ICommand
{
var handlers = Container.Resolve<IEnumerable<ICommandHandler<TCommand>>>().ToList();
if (handlers.Count == 1)
{
handlers[0].Handle(command);
}
else if (handlers.Count == 0)
{
throw new System.Exception($"Command does not have any handler {command.GetType().Name}");
}
else
{
throw new System.Exception($"Too many registred handlers - {handlers.Count} for command {command.GetType().Name}");
}
}
}
ContainerBuilder:
public class CommandBusContainerConfig : IContainerConfig
{
public IContainer Configure(Assembly executingAssembly)
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsAssignableTo<ICommandHandler>())
.AsImplementedInterfaces();
builder.Register<Func<Type, ICommandHandler>>(c =>
{
var ctx = c.Resolve<IComponentContext>();
return t =>
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
return (ICommandHandler)ctx.Resolve(handlerType);
};
});
return builder.Build();
}
}
In my UserService(ASP.Net Core 3), which is a different project that references the above CommandBus:
public class RegisterUserCommand : ICommand
{
public readonly string Name;
public readonly Address Address;
public string MobileNumber;
public string EmailAddress;
public RegisterUserCommand(Guid messageId, string name, string mobileNumber, string emailAddress, Address address)
{
Name = name;
Address = address;
MobileNumber = mobileNumber;
EmailAddress = emailAddress;
}
CommandHandler:
public class RegisterUserComnmandHandler : ICommandHandler<RegisterUserCommand>
{
public void Handle(RegisterUserCommand command)
{
Console.WriteLine($"Create user {command.Name} {command.MobileNumber} - handler");
}
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ICommandBus>(new CommandBus(Assembly.GetExecutingAssembly()));
}
Controller:
private readonly ICommandBus _commandBus;
public UsersController(ICommandBus commandBus) {
_commandBus = commandBus;
}
// POST api/values
[HttpPost]
public async Task<IActionResult> Post([FromBody]RegisterUserCommand command)
{
if (ModelState.IsValid)
{
CommandBus commandBus = new CommandBus(Assembly.GetExecutingAssembly());
commandBus.Send(command);
_commandBus.Send(Command); //Same result as above
// return result
return Ok(command);
}
return BadRequest();
}
Thanks,
The main error is here :
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsAssignableTo<ICommandHandler>())
.AsImplementedInterfaces();
RegisterUserComnmandHandler is not a ICommandHandler but a ICommandHandler<RegisterUserCommand>. Instead of IsAssignableTo<> method you can use the IsClosedTypeOf which is an Autofac extension which do exactly what you can.
builder.RegisterAssemblyTypes(executingAssembly)
.Where(x => x.IsClosedTypeOf(typeof(ICommandHandler<>)))
.AsImplementedInterfaces();
By the way, in your code sample you are using another Container. Most of the time it is always simple to have a single container for the whole application. To get things organised you can use autofac module. You are also resolving straight from the container and not using scope this means that your instance graph won't be disposed at the end of the operation but will stay for the whole lifetime of the container.
In your controller, I saw that you are building a new CommandBus for each request, which will create a new container. Building a new container is a heavy operation and you should avoid doing it often but only once of the startup of the application.
Also I don't get the point of this registration :
builder.Register<Func<Type, ICommandHandler>>(c =>
{
var ctx = c.Resolve<IComponentContext>();
return t =>
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
return (ICommandHandler)ctx.Resolve(handlerType);
};
});
It doesn't looks you need it and it seems useless to me
This took me a while to figure out. But my CommandHandler interface was incorrectly defined. It should look like:
public interface ICommandHandler { }
public interface ICommandHandler<T> : ICommandHandler where T : ICommand
{
void Handle(T command);
}
}
When trying to resolve the CommandHandler in the Autofac configuration class, the .Where(x => x.IsAssignableTo<ICommandHandler>()) was failing because the class was assignable to ICommandHandle<T> not ICommandHandler

Autofac how to register and resolve generic interface at runtime?

I am having trouble resolving a generic interface implementation at runtime. I am working with an eventbus that will resolve eventhandlers depending on the type of the event. When I try to resolve the eventhandlers without a generic implementation everything works as expected. I want to implement a generic interface so I could have a base class that handles specific type of events.
I had the following situation before I created a generic implementation:
public interface IEvent
{
Guid EntityId { get; set; }
}
public interface IEventHandler<TEvent> where TEvent : IEvent
{
Task Handle(TEvent #event);
}
public class EventBus
{
private readonly IComponentContext _context;
public EventBus(IComponentContext context)
{
_context = context;
}
public async Task HandleEvent<TEvent>(TEvent #event) where TEvent : IEvent
{
var handler = _context.Resolve<IEventHandler<TEvent>>();
await handler.Handle(#event);
}
}
I register the eventHandlers as follows:
builder.RegisterAssemblyTypes(ThisAssembly).AsClosedTypesOf(typeof(IEventHandler<>));
Example Implementation :
public class FooEventHandler :
IEventHandler<FooArchivedEvent>,
IEventHandler<FooRestoredEvent>,
IEventHandler<FooSomethingElseHappenedEvent>
{
private readonly IRepository<Foo> _repository;
public FooEventHandler(IRepository<Foo> repository)
{
_repository = repository;
}
public async Task Handle(FooArchivedEvent #event)
{
var Foo = await _repository.Get(#event.EntityId);
Foo.Archive();
}
public async Task Handle(FooRestoredEvent #event)
{
var Foo = await _repository.Get(#event.EntityId);
Foo.Restore();
}
public async Task Handle(FooSomethingElseHappenedEvent #event)
{
// do something else with Foo
}
}
public class BarEventHandler :
IEventHandler<BarArchivedEvent>,
IEventHandler<BarRestoredEvent>
{
private readonly IRepository<Bar> _repository;
public BarEventHandler(IRepository<Bar> repository)
{
_repository = repository;
}
public async Task Handle(BarArchivedEvent #event)
{
var Bar = await _repository.Get(#event.EntityId);
Bar.Archive();
}
public async Task Handle(BarRestoredEvent #event)
{
var Bar = await _repository.Get(#event.EntityId);
Bar.Restore();
}
}
When I pass a FooArchivedEvent to the eventbus the eventbus will resolve the required eventhandler. As you can see I have some duplicate code that I want to resolve in a base eventhandler. This is what I tried before I created the generic implementation (which won't compile):
public class BaseEventHandler<TEntity, TArchivedEvent, TRestoredEvent> :
IEventHandler<TArchivedEvent>,
IEventHandler<TRestoredEvent>
where TEntity : class
where TArchivedEvent : IEvent
where TRestoredEvent : IEvent
{
public Task Handle(TArchivedEvent #event)
{
throw new NotImplementedException();
}
public Task Handle(TRestoredEvent #event)
{
throw new NotImplementedException();
}
}
So I created a generic base class that does compile, however I can't figure out how to resolve the generic eventhandler in the eventbus.
Generic base class:
public abstract class BaseEventHandler<TEntity> :
IEventHandler<IArchivedEvent<TEntity>>,
IEventHandler<IRestoredEvent<TEntity>>
where TEntity : Archivable
{
protected readonly IRepository<TEntity> _repository;
public BaseEventHandler(IRepository<TEntity> repository)
{
_repository = repository;
}
public virtual async Task Handle(IArchivedEvent<TEntity> #event)
{
var entity = await _repository.Get(#event.EntityId);
entity.Archive();
}
public async virtual Task Handle(IRestoredEvent<TEntity> #event)
{
var entity = await _repository.Get(#event.EntityId);
entity.Archive();
}
}
My new FooEventHandler will now look like this:
public class FooEventHandler : BaseEventHandler<Foo>,
IEventHandler<FooSomethingElseHappenedEvent>
{
public FooEventHandler(IRepository<Foo> repository) : base(repository)
{
}
public Task Handle(FooSomethingElseHappenedEvent #event)
{
// do something else with Foo
}
}
Now when I pass a FooArchivedEvent to the eventbus the eventbus can't resolve the eventhandler. Is there something I need to do in the registration part or is it not possible to resolve a generic implementation like this ?
I ended up writing a method that checks every interface of a type for a registered handler:
private IEventHandler<TEvent> GetHandler<TEvent>(Type type = null) where TEvent : IEvent
{
object handler;
type = type ?? typeof(TEvent);
if (_container.TryResolve(typeof(IEventHandler<>).MakeGenericType(type), out handler))
{
return (IEventHandler<TEvent>)handler;
}
else
{
foreach (var t in type.GetInterfaces())
{
var h = GetHandler<TEvent>(t);
if (h != null)
return h;
}
}
return null;
}

Inject Dependency into Core Module of a ASP Boilerplate project

I have NotificationJob class where I have all the functions related to Notification Feature for my .Net Core application. It has some injected dependencies from Domain services. I am having a problem trying to inject INotificationJob interface of the class into the CoreModule of the project.
I initially tried injecting the interface directly into the CoreModule but failed so I created another module in the same file called NotificationModule where I inject INotificationJob interface. Then I try to link it with the CoreModule using [DependsOn(typeof(oasisCoreModule))] annotation.
Core Module of the project
[DependsOn(
typeof(AbpZeroCoreModule),
typeof(AbpHangfireAspNetCoreModule),
typeof(AbpWebCommonModule)
)]
public class oasisCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true;
Configuration.BackgroundJobs.UseHangfire();
Configuration.Auditing.IsEnabledForAnonymousUsers = true;
// Declare entity types
Configuration.Modules.Zero().EntityTypes.Tenant = typeof(Tenant);
Configuration.Modules.Zero().EntityTypes.Role = typeof(Role);
Configuration.Modules.Zero().EntityTypes.User = typeof(User);
oasisLocalizationConfigurer.Configure(Configuration.Localization);
// Enable this line to create a multi-tenant application.
Configuration.MultiTenancy.IsEnabled = oasisConsts.MultiTenancyEnabled;
// Configure roles
AppRoleConfig.Configure(Configuration.Modules.Zero().RoleManagement);
Configuration.Settings.Providers.Add<AppSettingProvider>();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(oasisCoreModule).GetAssembly());
}
public override void PostInitialize()
{
IocManager.Resolve<AppTimes>().StartupTime = Clock.Now;
}
}
// This is the custom module that I created in the same file as the core module.
[DependsOn(typeof(oasisCoreModule))]
public class NotificationModule : AbpModule
{
INotificationJob _job;
public NotificationModule(INotificationJob job)
{
_job = job;
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
public override void PostInitialize()
{
_job.Loop();
}
}
INotificationJob Interface I am Injecting into the NotificationModule
public interface INotificationJob: IDomainService
{
void Loop();
void CheckTickets();
void CheckReminders(string email, string ticket);
}
Class Implementation of INotificationJob Interface
public class NotificationJob: DomainService, INotificationJob
{
private readonly ITicketRefManager _ticketRefManager;
private readonly IClientManager _clientManager;
private readonly IEmailManager _emailManager;
public NotificationJob(
ITicketRefManager ticketRefManager,
IClientManager clientManager,
IEmailManager emailManager,
)
{
_ticketRefManager = ticketRefManager;
_clientManager = clientManager;
_emailManager = emailManager;
}
public void Loop()
{
RecurringJob.AddOrUpdate(() => CheckTickets(), Cron.Minutely);
}
}
When I run the solution, I am presented with an error saying as shown:
Are there any other steps that I need to take to complete the Dependency Injection process? Or are the steps that I described flawed?
I'm not sure what you're trying to do with you "interface injecting", but you can try this if I understand correctly what you're trying to do :
Core Module
[...]
public override void PostInitialize()
{
var recurrentJobs = IocManager.Resolve<NotificationJob>();
RecurringJob.RemoveIfExists("JobName");
RecurringJob.AddOrUpdate("JobName", () => recurrentJobs.CheckTickets(), Cron.Minutely);
}
Your class
public class NotificationJob : ISingletonDependency
{
private readonly ITicketRefManager _ticketRefManager;
private readonly IClientManager _clientManager;
private readonly IEmailManager _emailManager;
public NotificationJob(
ITicketRefManager ticketRefManager,
IClientManager clientManager,
IEmailManager emailManager,
)
{
_ticketRefManager = ticketRefManager;
_clientManager = clientManager;
_emailManager = emailManager;
}
public void CheckTickets()
{
//Do something
}
}
Does it helps ?

Using Autofac with Dynamic Proxy that output message automatic

public interface ILog
{
void Write(string msg);
}
public class MyLog : ILog
{
public void Write(string msg)
{
Console.WriteLine(msg);
}
}
public interface ICanLog
{
ILog Log { get; set; }
}
public interface IMyClass
{
void Test();
}
public class MyClass : IMyClass, ICanLog
{
public ILog Log { get; set; }
public void Test()
{
Log.Write("Test");
}
}
I am using Autofac with Castle DynamicProxy,
and try to let MyClass Test Method output "BEGIN"/"END" automatic.
public class MyLogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("BEGIN");
invocation.Proceed();
Console.WriteLine("END");
}
}
The following is test code:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<MyLog>().As<ILog>();
builder.Register(c =>
{
ProxyGenerator g = new ProxyGenerator();
object proxy = g.CreateClassProxy(typeof(MyClass), new MyLogInterceptor());
ICanLog proxyICanLog = (ICanLog)proxy;
proxyICanLog.Log = c.Resolve<ILog>();
return proxy;
}).As<IMyClass>();
using (var container = builder.Build())
{
objectContext.Container = container;
IMyClass myclass = container.Resolve<IMyClass>();
myclass.Test();
}
But result no output "BEGIN"/"END", why ?
and if I create AutoLogModule that try build Log Property Instance automatic
public class AutoLogModule : Autofac.Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
var type = registration.Activator.LimitType;
if (HasPropertyDependencyOnClass(type))
{
registration.Activated += InjectClassViaProperty;
}
}
private bool HasPropertyDependencyOnClass(Type type)
{
return type.GetProperties().Any(property => property.CanWrite && property.PropertyType==typeof(ILog));
}
private void InjectClassViaProperty(object sender, ActivatedEventArgs<object> evt)
{
var type = evt.Instance.GetType();
var propertyInfo = type.GetProperties().First(x => x.CanWrite && x.PropertyType==typeof(ILog));
ILog log = new MyLog();
propertyInfo.SetValue(evt.Instance, log, null);
}
}
The following is test code:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<MyLog>().As<ILog>();
builder.RegisterModule(new AutoLogModule());
builder.Register(c =>
{
ProxyGenerator g = new ProxyGenerator();
object proxy = g.CreateClassProxy(typeof(MyClass), new MyLogInterceptor());
//ICanLog proxyICanLog = (ICanLog)proxy;
//proxyICanLog.Log = c.Resolve<ILog>();
return proxy;
}).As<IMyClass>();
using (var container = builder.Build())
{
objectContext.Container = container;
IMyClass myclass = container.Resolve<IMyClass>();
myclass.Test();
}
The result is Test Method throw
"Object reference not set to an instance of an object."
in Log.Write("Test")
How to write this feature?
I know this is a rather old post but as I was trying to accomplish the same thing with Autofac and I found the documentation that helped me to achieve it. I will answer just in case it helps someone else.
In my case I'm using Autofac 4.92 and and extra package for DynamicProxy called Autofac.Extras.DynamicProxy 4.5.0 as the documentations sates.
I see a difference where you register your Interceptors. Even though what you are doing is what I would have done initially; is not what Autofac Documentation currently says about how to Register Interceptors:
builder.RegisterType<MyClass>().As<IMyClass>().EnableInterfaceInterceptors();
// Typed registration
builder.Register(c => new MyLogInterceptor ();
Lastly, you need to Associate Interceptors with Types to be Intercepted:
[Intercept(typeof(MyLogInterceptor))]
public class MyClass : IMyClass, ICanLog
{
public ILog Log { get; set; }
public void Test()
{
Log.Write("Test");
}
}
I hope this answer may help. In any case, the Autofac documentation explains step by step how to do it just in case my code may mistakenly skip some relevant part.

Categories