15

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.

3
  • 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<IFoo> 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?) Commented Jan 25, 2019 at 7: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. Commented Jan 26, 2019 at 2:57
  • I'd definitely keep your dependences taking IFooService directly, and then they'd separately call GetFooAsync from inside a normal (non-Constructor) method. If you're worried about overhead of many dependencies calling GetFooAsync within a given scoped instance, you could always internally cache the value in FooService. Commented Feb 29, 2024 at 17:58

5 Answers 5

2

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>>();

If you want to run code when creating the IFoo , you can use the following code:

var fooTask = HttpContext.RequestServices.GetService<Task<IFoo>>();
/*your Code*/
var foo = await fooTask; /* OR var foo = fooTask.GetAwaiter().GetResult()*/
Sign up to request clarification or add additional context in comments.

2 Comments

This is okay, but you still need to await. I think OP is trying to avoid asynchronous code in the parts of the app that consume IFoo.
yes , its need to await or GetAwaiter().GetResult()
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>.

1 Comment

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
0

What I usually do in this case is implement a IHostedService do to the asynchronous work I need at startup and store it in some singleton state. As long as any services that use IFoo are registered after the loading service, the value will be populated when they access the singleton (otherwise, you can throw InvalidOperationException).

Comments

-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;
});

3 Comments

.Result has almost identical effects to .GetAwaiter().GetResult() except it's slightly more prone to deadlocks. Why would you suggest it?
@Damien_The_Unbeliever Sorry for Result, I just want to point out op used a action which is not defined in IFooService.
@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.
-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 .

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.