13

I have a service which is used to get some information and the method has a bunch of async calls in the chain.

public interface IFooService
{
    Task<IFoo> GetFooAsync();
}

The concrete class,

public class FooService : IFooService
{
    public async Task<IFoo> GetFooAsync()
    {
        // whole bunch of awaits on async calls and return IFoo at last
    }
}

I register this service on StartUp,

services.AddTransient<IFooService, FooService>();

Several other services are injected with this service. One among them,

public class BarService : IBarService
{
    private readonly IFooService _fooService;

    public BarService(IFooService fooService)
    {
        _fooService = fooService;
    }

    public async Task<IBar> GetBarAsync()
    {
        var foo = await _fooService.GetFooAsync();

        // additional calls & processing
        var bar = SomeOtherMethod(foo);

        return bar;
    }
}

IFoo is integral to the application and used across several services. Most of my code is async just due to this one IFooService and the one method it has which returns IFoo.

Considering this use case, I would like to be able to just inject IFoo to all other services as opposed to injecting them with IFooService.

I gave this a shot,

services.AddTransient<IFoo>(provider => 
{
    var fooService = provider.GetService<IFooService>();
    var foo = fooService.GetFooAsync().GetAwaiter().GetResult();
    return foo;
});

but it raises a red flag to me as I'm doing sync over async and I'm unsure if this will cause any issues like race conditions. Would startup be blocked by doing this. Im looking for a clean way to handle this, any recommendation for when we need something like this? Thank you for your help.

  • Anything you do here is going to fall down to either hiding a blocking call somewhere or having to expose them, at the least, to a `Task` rather than an `IFoo`. Why is it a problem that most of your code is now async? (It may "only" be in the service of `IFooService` currently but does your service truly not have any other opportunities for async work to come up?) – Damien_The_Unbeliever Jan 25 '19 at 07:40
  • @Damien_The_Unbeliever So yes there are other async calls that I await on within GetFooAsync(). One approach I had in mind is to make a non async version - `GetFoo()` or just continue injecting `IFooService` and other services can always await on `GetFooAsync`. But I'm wondering how such a use case is generally handled where you really want to call async code within `AddTransient` or `AddScoped` or even `AddSingleton`. – Emmanuel Ponnudurai Jan 26 '19 at 02:57

4 Answers4

0

I guess what you want is an async constructor, which is not recommended.

here is more info about it and contains a solution using Lazy<T>.

ikesnowy
  • 1
  • 1
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/32776717) – user16217248 Sep 28 '22 at 21:01
0

Your code is not bad , but can use folowing code :

services.AddTransient<Task<IFoo>>(provider => 
{
    var fooService = provider.GetService<IFooService>();
    return fooService.GetFooAsync();
});

can use its for example following code :

var foo = await HttpContext.RequestServices.GetService<Task<IFoo>>();
BQF
  • 33
  • 7
-2

but it raises a red flag to me as I'm doing sync over async

For this red flag, it is caused by that you call method GetFoo which is not defined in IFooService, it is not related with async or sync method.

Try the method which is defined

services.AddTransient<IFoo>(provider =>
{
    var fooService = provider.GetService<IFooService>();
    var foo = fooService.GetFooAsync().Result;
    return foo;
});
Edward
  • 28,296
  • 11
  • 76
  • 121
  • 2
    `.Result` has almost identical effects to `.GetAwaiter().GetResult()` except it's slightly more prone to deadlocks. Why would you suggest it? – Damien_The_Unbeliever Jan 25 '19 at 07:37
  • @Damien_The_Unbeliever Sorry for `Result`, I just want to point out op used a action which is not defined in `IFooService`. – Edward Jan 25 '19 at 07:49
  • @TaoZhou It was a typo. I edited the question and I indeed need to call `GetFooAsync` but unfortunately, I'm afraid that is not the answer to the question I'm asking. – Emmanuel Ponnudurai Jan 26 '19 at 02:49
-2

2 Other options

  1. Inject Func<Task> as the transient, the caller can await !

  2. Task.Run( async () await abc).Wait(); // this deadlocks much less
    due to creating a task and having less issues .

user1496062
  • 1,309
  • 1
  • 7
  • 22