0

I have a parent-child class structure where parent class in constructor creates instance of the child class and use one of child class async methods with .Result approach.

public class ParentClass
{
    private readonly ChildClass _childClass;
    public ParentClass()
    {
        _childClass = new ChildClass();
        bool result = _childClass.GetSomething().Result;
    }
}



public class ChildClass
{
    public ChildClass()
    {

    }

    public async Task<bool> GetSomething()
    {
        return await Task.FromResult<bool>(false);
    }
}

My goal is to remove .Result and change it with normal await keyword. I can create static async method, there create instance of the child class, later call constructor and pass instance.

public class ParentClass2
{
    private readonly ChildClass _childClass;

    private ParentClass2(ChildClass childClass, bool result)
    {
        _childClass = childClass;
    }
    public static async Task<ParentClass2> GetInstance()
    {
        ChildClass childClass = new ChildClass();
        bool result = await childClass.GetSomething();
        return new ParentClass2(childClass, result);

    }
}

But then I have 2 problems:

  1. The more parents of parents [..of parents] I have - the more code I have to modify in a same way.

  2. Highest level classes use DI. And with async I can't do this

     services.AddScoped<ParentClass2>(async p =>
     {
         var instance = await ParentClass2.GetInstance();
         return instance;
     });
    

Any suggestions?

  • 3
    `async` constructors are not allowed: [async-oop-2-constructors](https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html) - article by **Stephen Cleary**. But his article does offer alternative design. I think you could do this in a better way, blocking a thread inside a constructor seems like a bad idea with your current design, and `async` constructors are not supported. – Ryan Wilson Apr 11 '22 at 19:40
  • Related: [Can constructors be async?](https://stackoverflow.com/questions/8145479/can-constructors-be-async) – Theodor Zoulias Apr 11 '22 at 20:16
  • 2
    It is not clear what exactly you want to achieve. If you want to get rid of `.Result` - the factory method approach that your provided may solve the issue. If you want to execute some async logic during the instantiation in DI - most likely your DI container would not allow it. In order to do it, you may extract some `InitAsync` method and call it explicitly when you get the object from DI container. – GoodboY Apr 11 '22 at 20:21
  • Also see this: https://stackoverflow.com/questions/45924027/avoiding-all-di-antipatterns-for-types-requiring-asynchronous-initialization – Steven Apr 12 '22 at 14:41
  • And this: https://stackoverflow.com/questions/43240405/async-provider-in-net-core-di – Steven Apr 12 '22 at 14:42

1 Answers1

1

When you deal with dependency injection and asynchronous initialization, you have to make compromises.

In general, I approach this one of two ways.

If the asynchronous initialization is done once at startup, you can extract the asynchronous call out before the DI is set up, run it synchronously, and inject the results into the constructors for your types at DI setup.

The other option is to essentially do the asynchronous initialization on-demand whenever any other method is called. The constructor could start it, and every method would (asynchronously) wait for the initialization to complete. This does have the side effect that the entire API has to be asynchronous. Either that, or you change the DI type to be an asynchronous factory which then asynchronously creates the type with your actual API.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810