I'm trying to follow RAII pattern in my service classes, meaning that when an object is constructed, it is fully initialized. However, I'm facing difficulties with asynchronous APIs. The structure of class in question looks like following
class ServiceProvider : IServiceProvider // Is only used through this interface
{
public int ImportantValue { get; set; }
public event EventHandler ImportantValueUpdated;
public ServiceProvider(IDependency1 dep1, IDependency2 dep2)
{
// IDependency1 provide an input value to calculate ImportantValue
// IDependency2 provide an async algorithm to calculate ImportantValue
}
}
I'm also targeting to get rid of side-effects in ImportantValue
getter, to make it thread-safe.
Now users of ServiceProvider
will create an instance of it, subscribe to an event of ImportantValue
change, and get the initial ImportantValue
. And here comes the problem, with the initial value. Since the ImportantValue
is calculated asynchronously, the class cannot be fully initialized in constructor. It may be okay to have this value as null initially, but then I need to have some place where it will be calculated first time. A natural place for that could be the ImportantValue
's getter, but I'm targeting to make it thread-safe and with no side-effects.
So I'm basically stuck with these contradictions. Could you please help me and offer some alternative? Having value initialized in constructor while nice is not really necessary, but no side-effects and thread-safety of property is mandatory.
Thanks in advance.
EDIT: One more thing to add. I'm using Ninject for instantiation, and as far as I understand, it doesn't support async methods to create a binding. While approach with initiating some Task-based operation in constructor will work, I cannot await its result.
I.e. two next approaches (offered as answers so far) will not compile, since Task is returned, not my object:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
or
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var sp = new ServiceProvider();
await sp.InitializeAsync();
})
Simple binding will work, but I'm not awaiting the result of asynchronous initialization started in constructor, as proposed by Stephen Cleary:
Kernel.Bind<IServiceProvider>().To<ServiceProvider>();
... and that's not looking good for me.
I have a blog post that describes several approaches to
async
construction.I recommend the asynchronous factory method as described by Reed, but sometimes that's not possible (e.g., dependency injection). In these cases, you can use an asynchronous initialization pattern like this:
You can then construct the type normally, but keep in mind that construction only starts the asynchronous initialization. When you need the type to be initialized, your code can do:
Note that if
Initialization
is already complete, execution (synchronously) continues past theawait
.If you do want an actual asynchronous property, I have a blog post for that, too. Your situation sounds like it may benefit from
AsyncLazy<T>
:One potential option would be to move this to a factory method instead of using a constructor.
Your factory method could then return a
Task<ServiceProvider>
, which would allow you to perform the initialization asynchronously, but not return the constructedServiceProvider
untilImportantValue
has been (asynchronously) computed.This would allow your users to write code like:
This is a slight modification to @StephenCleary pattern of async initialization.
The difference being the caller doesn't need to 'remember' to
await
theInitializationTask
, or even know anything about theinitializationTask
(in fact it is now changed to private).The way it works is that in every method that uses the initialized data there is an initial call to
await _initializationTask
. This returns instantly the second time around - because the_initializationTask
object itself will have a boolean set (IsCompleted
which the 'await' mechanism checks) - so don't worry about it initializing multiple times.The only catch I'm aware of is you mustn't forget to call it in every method that uses the data.
You could use my AsyncContainer IoC container which supports the exact same scenario as you.
It also supports other handy scenarios such as async initializers, run-time conditional factories, depend on async and sync factory functions
I know this is an old question, but it's the first which appears on Google and, quite frankly, the accepted answer is a poor answer. You should never force a delay just so you can use the await operator.
A better approach to an initialization method:
This will use the async framework to initialize your object, but then it will return a boolean value.
Why is this a better approach? First off, you're not forcing a delay in your code which IMHO totally defeats the purpose of using the async framework. Second, it's a good rule of thumb to return something from an async method. This way, you know if your async method actually worked/did what it was supposed to. Returning just Task is the equivalent of returning void on a non-async method.