Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting external dependencies in Microsoft Bot Framework Dialog using Autofac

I've been trying to pass a service to a LuisDialog from the MessagesController like so:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
...
await Conversation.SendAsync(activity, () => new ActionDialog(botService));

The botService is injected into the MessageController using dependency injection.

When I start a bot conversation I get an error:

Type 'ThetaBot.Services.BotService' in Assembly 'ThetaBot.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

Looking around for a solution I can find: https://github.com/Microsoft/BotBuilder/issues/106

I understand your question better now. We have a similar issue with service objects that we want to instantiate from the container rather than from the serialized blob. Here is how we register those objects in the container - we apply special handling during deserialiation for all objects with the key Key_DoNotSerialize:

builder
        .RegisterType<BotToUserQueue>()
        .Keyed<IBotToUser>(FiberModule.Key_DoNotSerialize)
        .AsSelf()
        .As<IBotToUser>()
        .SingleInstance();

However I cannot find an example or documentation that details how to register your own dependencies into the existing Bot Framework modules.

I also found https://github.com/Microsoft/BotBuilder/issues/252 which indicates that it should be possible to instantiate the dialogs from the container.

I have tried this suggestion:

Func<IDialog<object>> makeRoot = () => actionDialog;
await Conversation.SendAsync(activity, makeRoot);

Together with:

builder
            .RegisterType<ActionDialog>()
            .Keyed<ActionDialog>(FiberModule.Key_DoNotSerialize)
            .AsSelf()
            .As<ActionDialog>()
            .SingleInstance();

This does not work.

I have also tried:

var builder = new ContainerBuilder();
builder.RegisterModule(new DialogModule_MakeRoot());

// My application module
builder.RegisterModule(new ApplicationModule());

using (var container = builder.Build())
using (var scope = DialogModule.BeginLifetimeScope(container, activity))
{
    await Conversation.SendAsync(activity, () => scope.Resolve<ActionDialog>());
}

Together with the following in the ApplicationModule:

            builder
            .RegisterType<ActionDialog>()
            .Keyed<ActionDialog>(FiberModule.Key_DoNotSerialize)
            .AsSelf()
            .As<ActionDialog>()
            .SingleInstance();

This does not work and I encounter the same issue.

If I simply mark all the services and their dependencies as serializable I can get this to work without the need to use FiberModule.Key_DoNotSerialize.

The question is - what is the correct / preferred / recommended way to register and inject dependencies into Bot Framework Dialogs?

like image 728
jim.taylor.1974 Avatar asked Aug 07 '16 22:08

jim.taylor.1974


1 Answers

In the Global.asax.cs, you can do do the following to register your services/dialogs:

ContainerBuilder builder = new ContainerBuilder();

builder.RegisterType<IntroDialog>()
  .As<IDialog<object>>()
  .InstancePerDependency();

builder.RegisterType<JobsMapper>()
    .Keyed<IMapper<DocumentSearchResult, GenericSearchResult>>(FiberModule.Key_DoNotSerialize)
    .AsImplementedInterfaces()
    .SingleInstance();

builder.RegisterType<AzureSearchClient>()
    .Keyed<ISearchClient>(FiberModule.Key_DoNotSerialize)
    .AsImplementedInterfaces()
    .SingleInstance();

builder.Update(Conversation.Container);

In your controller, you can then resolve your main dialog as:

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
    await Conversation.SendAsync(activity, () => scope.Resolve<IDialog<object>>());
}
like image 161
Ezequiel Jadib Avatar answered Oct 10 '22 09:10

Ezequiel Jadib