7

I have a Wallet class that I get from a repository. I'm trying to properly register both in Autofac so classes using the wallet could have a proper instance injected. The problem is that the repository uses an async method (returning Task). Does Autofac support such cases?

This doesn't work:

cb.RegisterType<WalletRepository>()
    .As<IWalletRepository>()
    .SingleInstance();
cb.Register(async c => await c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath));
cb.RegisterType<ViewModel>()
    .AsSelf().
    .SingleInstance();

Somewhere in the app I just have:

class ViewModel
{
    public ViewModel(Wallet wallet)
    {
        //nothing fancy here
    }
}

When calling container.Resolve<ViewModel>() i get an exception saying Wallet is not registered.

Pein
  • 1,216
  • 9
  • 16
  • Could you include the consuming code, or at least the ctor and example method using the injected dependency? – James Manning Apr 06 '13 at 20:14
  • Not answering the question but I'd say your WalletRepository shouldn't need IO to be initialized in the first place. Try initializing it lazily on the first use of one of its methods. – usr Apr 06 '13 at 20:31
  • It's not a repository initialized with IO but a Wallet object (they are loaded from files). – Pein Apr 06 '13 at 21:34
  • 2
    Why do you even have async initialization when you're then resolving the view model synchronously? – svick Apr 06 '13 at 23:14
  • 1
    @Pein loading from a file *is* IO. Try loading lazily and those problems go away. – usr Apr 07 '13 at 09:44

3 Answers3

10

Unless I'm mistaken Autofac doesn't have any specific support for async factories. You can still make it work, but you have to write some boilerplate code, because constructor injection won't work. You also have to be more literal and say you want Task<T> instead of T. The whole code would look something like this:

cb.RegisterType<WalletRepository>()
  .As<IWalletRepository>()
  .SingleInstance();
cb.Register(c => c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath));
cb.Register(async c => new ViewModel(await c.Resolve<Task<Wallet>>()))
  .SingleInstance();
var container = cb.Build();
var viewModel = await container.Resolve<Task<ViewModel>>();

It's possible Autofac has some extensibility points to make this code simpler, but I don't know enough about it to help you with that.

svick
  • 236,525
  • 50
  • 385
  • 514
  • It looks like capturing "c" is prohibited: https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html#service-resolution – astef May 11 '19 at 17:47
  • @astef My code does not use `c` after the lambda is invoked and returns a `Task`, so I think it should be okay. – svick May 12 '19 at 09:39
2

This might cause a deadlock. Autofac is using locks in some of the logic that handles the resolving for different lifetime scopes. so if the the async factory operation will try to resolve something in another thread you will get a deadlock

0

One option is just to make the registration do the blocking required to convert the async call into it's result.

ie:

cb.Register(c => c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath).Result); // nb: blocking call

This means that AutoFac will be forced to do the blocking when the dependencies are resolved, and the client of the service need not know that the service factory was async.

Whether this is a good idea will depend largely on what you are doing at the time of course.

piers7
  • 4,174
  • 34
  • 47
  • 1
    `.Result` should be avoid because it wraps up exceptions. use: `.ConfigureAwait(false).GetAwaiter().GetResult();` instead. source: https://stackoverflow.com/questions/33835063/difference-between-getawaiter-and-configureawait – Vetras May 21 '18 at 14:31
  • 1
    Valid code, but you should only do this if you're "forced" to by third party code you cannot modify. Object graph construction should follow simple-constructor semantics, and complete synchronously and quickly wherever possible. Blocking on asynchronous code [has it's own dangers](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) as well. – Marc L. Aug 15 '18 at 13:13