Could someone assist me with one question please?
I have two services.
GAuth:
public class GAuth : IGAuth
{
public async Task<UserCredential> AuthorizeAsync(ClientSecrets clientSecrets)
{
using (var cts = new CancellationTokenSource())
{
var localServerCodeReceiver = new LocalServerCodeReceiver();
cts.CancelAfter(TimeSpan.FromMinutes(1));
return await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
_scopes,
User,
cts.Token,
new FileDataStore(string.Empty), localServerCodeReceiver
);
}
}
}
GDrive:
public class GDrive : IGDrive
{
private readonly DriveService _driveService;
public GDrive(UserCredential userCredential)
{
_driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = string.Empty
});
}
}
And registration part:
container.Register<IGAuth, GAuth>(Lifestyle.Singleton);
container.Register<IGDrive, GDrive>(Lifestyle.Singleton);
As you can see GAuth
service returns UserCredentials
object after authorization. And this UserCredentials
is required for GDrive
service.
It's simplified example, but in general I need to initialize GDrive
service with correct UserCredentials
object after user press Auth
button on the application form.
Is there any way to do that?
Thanks in advance.
From a design perspective, injection constructors should be simple, fast and reliable. This isn't the case in your case, since the building of the object graph depends on I/O.
Instead, you should postpone the IO till after the constructor has ran.
There are two things that come to my mind that you can do. Either you inject the IGAuth
into the GDrive and make sure it is called after the constructor has ran, or you inject a lazy async UserCredential
that can be requested after the constructor has ran.
Here's an example of the latter:
public class GDrive : IGDrive
{
private readonly Lazy<Task<DriveService>> _driveService;
public GDrive(Lazy<Task<UserCredential>> userCredential)
{
_driveService = new Lazy<Task<DriveService>>(async () =>
new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = await userCredential.Value,
ApplicationName = string.Empty
}));
}
public async Task SomeMethod()
{
var service = await _driveService.Value;
service.DoSomeStuff();
}
}
You can configure this GDrive
as follows:
var auth = new GAuth();
var credentials = new Lazy<Task<UserCredential>>(
() => auth.AuthorizeAsync(new ClientSecrets()));
container.RegisterSingleton<IGDrive>(new GDrive(credentials));
The other option is to inject the GAuth
into the GDrive
. That would result in something as follows:
public class GDrive : IGDrive
{
private readonly Lazy<Task<DriveService>> _driveService;
public GDrive(IGAuth auth)
{
_driveService = new Lazy<Task<DriveService>>(async () =>
new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = await auth.AuthorizeAsync(new ClientSecrets()),
ApplicationName = string.Empty
}));
}
public async Task SomeMethod()
{
var service = await _driveService.Value;
service.DoSomeStuff();
}
}
Note that in both cases a Lazy<Async<T>>
is created that will ensure that the asynchronous operation is only triggered when Lazy<T>.Value
is called for the first time.