3

We have a websolution with autofac. Now we want to reuse things in a windows service/console app where things are only available when a message comes in from an enterprise bus.

I have the following service to reuse

public SettingsService : ISettingsService {
    public readonly ITenantIdentifer _tenantIdentifier
    public SettingsService (ITenantIdentifier tenantIdentifier) {
       this._tenantIdentifier =tenantIdentifier; 
    }
    // do other stuff
}

The current working setup

the ITenantIdentifier for the webcontext is simply registered for the webapplication using builder.RegisterType<WebTenantIdentifier>().As<ITenantIdentifier>();.
Evething works fine.

Our enterprise bus

The enterprise bus can not resolve the ITenantIdentifier until the message is available. So we created a MessageTenantIdentifier and registered a factory.

 public class MessageTenantIdentifier : ITenantIdentifier
 {
    public delegate MessageTenantIdentifier Factory(int tenantId);
public MessageTenantIdentifier(int tenantId, IOtherDependency stuff)
    {
        _tenantId = tenantId;
        // ...
    }
}

// somewhere else the this is registered     
builder.RegisterType<MessageTenantIdentifier >().As<ITenantIdentifier>().AsSelf();
builder.RegisterGeneratedFactory<MessageTenantIdentifier.Factory>();

The problem The factory can only be used when the message is being handled in a

public class MsgTypeHandler : IHandleMessages<MsgType>
{
    public MsgTypeHandler(ISettingsService settingsService, MessageTenantIdentifier factory) { ...}
    public async Task Handle(MsgType message)
    {
        var tenantId = message.TenantId;
        // THIS IS THE MOMENT I CAN CONFIGURE THE MessageTenantIdentifier
        var tenantIdentifier = factory.Invoke(tenantId);

        // but this factory is not used against the ISettingsService.  The service to be reused.  <== THE REAL PROBLEM
    }
}

The question

So, how can I solve this issue? E.g. how should I setup the registration of the MessageTenantIdentifier in the servicebus? Or is my dependency setup just plain wrong?

mnwsmit
  • 1,198
  • 2
  • 15
  • 31
dampee
  • 3,392
  • 1
  • 21
  • 37

1 Answers1

2

If the MsgTypeHandler class needs an ISettingsService, but the entire object graph can't be resolved until the tenant ID is available, that means that the MsgTypeHandler is the Composition Root. That's OK, but that means that this is where you resolve your entire object graph, so don't inject individual services here; instead, inject the factory you need:

public class MsgTypeHandler : IHandleMessages<MsgType>
{
    public MsgTypeHandler(ISettingsServiceFactory factory) {...}

    public async Task Handle(MsgType message)
    {
        var tenantId = message.TenantId;
        ISettingsService svc = this.factory.Create(tenantId);

        // User svc here...
    }
}
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Ok, So I have to write my own factory method because the composition root can't depend on the DI-framework in this case. (Still wondering if autofac has no way of resolving this)... – dampee Mar 12 '16 at 16:54
  • @dampee You can [write the factory in various ways](http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory), including letting your DI Container take care of the details. – Mark Seemann Mar 12 '16 at 16:56
  • I get it. I really need to read a book on the topic. Is your book still a good reference, or would you advice any other? – dampee Mar 12 '16 at 17:16
  • 1
    @dampee I'd still recommend mine; it's deliberately written to be as timeless as possible. – Mark Seemann Mar 12 '16 at 18:37