Botframework-sdk: [Question] Using delegate factories with constructor injection in dialogs

Created on 24 May 2017  路  3Comments  路  Source: microsoft/botframework-sdk

System Information (Required)

  • SDK Platform: C#
  • SDK Version: 3.8.1
  • Development Environment: localhost

Issue Description

I am doing dependency injection with Autofac. Everything goes great until I am trying to instantiate a dialog in another one using a delegate factory method (in order to call context.Forward on it), injected by Autofac.

Example Code

private readonly Func<MyDialog> _myDialogFactory;

public RootDialog(Func<MyDialog> myDialogFactory)
{
    SetField.NotNull(out _myDialogFactory, nameof(_myDialogFactory), myDialogFactory);
}

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{

...

var myDialog = _myDialogFactory();
await context.Forward(myDialog, ... );

...

}

Results

Since RootDialog is marked as [Serializable] I obviously get the following exception:

Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator鈥檚 assembly.

I know that a possible workaround would be to simply use the service locator pattern and do something like this. But I consider service locator an anti-pattern, so I am strictly against this solution.

How can I make it work using only dependency injection, like I am trying to do in the above example?

Most helpful comment

Thanks to Will's great advice, I was able to come up with the following solution:

builder.Register(context => new Func<MyDialog>(() => new MyDialog())).Keyed<Func<MyDialog>>(FiberModule.Key_DoNotSerialize).As<Func<MyDialog>>().SingleInstance();

The idea here is that in the Module where I am registering RootDialog and MyDialog with Autofac, I don't let Autofac to automatically generate the factory delegates for me, because those will end up in Autofac's assembly itself, hence the exception message in my previous comment. This way, I am registering my own factory delegate, overriding the automatically generated one, marking mine as a non-serializable singleton and now it works like a charm! :)

All 3 comments

Two ideas

  1. Wrap the delegate factory in a closure created from your assembly instead (this might just delay the issue - I wonder if you can hide service locator here)
  2. Use a serialization surrogate e.g. https://github.com/Microsoft/BotBuilder/blob/497252e8d9949be20baa2cebaa6ce56de04461cf/CSharp/Library/Microsoft.Bot.Builder.Autofac/Fibers/FiberModule.cs#L196

Thanks to Will's great advice, I was able to come up with the following solution:

builder.Register(context => new Func<MyDialog>(() => new MyDialog())).Keyed<Func<MyDialog>>(FiberModule.Key_DoNotSerialize).As<Func<MyDialog>>().SingleInstance();

The idea here is that in the Module where I am registering RootDialog and MyDialog with Autofac, I don't let Autofac to automatically generate the factory delegates for me, because those will end up in Autofac's assembly itself, hence the exception message in my previous comment. This way, I am registering my own factory delegate, overriding the automatically generated one, marking mine as a non-serializable singleton and now it works like a charm! :)

I'm struggling to solve similar situation like this for hours and using FiberModule.Key_DoNotSerialize works perfectly. Thank you @willportnoy and @peterbozso

Was this page helpful?
0 / 5 - 0 ratings