To prevent having to call .Result
on Channel
, you will have to await it. For instance:
var channel = await Channel;
GetAllResponse getAllResponse =
await channel.GetAllAsync(new Google.Protobuf.WellKnownTypes.Empty());
Side note: As I mentioned in the comments, you should typically want to prevent doing anything that involves I/O during object resolution, becuase it makes object resolution fragile and untestable. Instead, you should be able to compose your object graphs with con confidence, as expressed here by Mark Seemann. You can do this by postponing the creation of the Task by hiding it behind an abstraction. For instance:
public interface IGrpcChannelProvider
{
Task<GrpcChannel> Channel { get; }
}
This allows you to move all the registration code into an implementation for IGrpcChannelProvider
:
public sealed class GrpcChannelProvider : IGrpcChannelProvider, IDisposable
{
private readonly IConfiguration config;
private readonly IAccessTokenProvider authenticationService;
private readonly Lazy<Task<GrpcChannel>> channel;
public GrpcChannelProvider(
IConfiguration config, IAccessTokenProvider authenticationService)
{
this.config = config;
this.authenticationService = authenticationService;
this.channel = new Lazy<Task<GrpcChannel>>(this.CreateChannel);
}
public Task<GrpcChannel> Channel => this.channel.Value;
public void Dispose()
{
if (this.channel.IsValueCreated) this.channel.Value.Dispose();
}
// This is your original code
private async Task<GrpcChannel> CreateChannel()
{
#if DEBUG
var baseUri = "http://localhost:8999/";
#else
var baseUri = "[mysite]";
#endif
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
var tokenResult = await this.authenticationService.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
var credentials = CallCredentials.FromInterceptor((context, metadata) =>
{
if (!string.IsNullOrEmpty(token.Value))
{
metadata.Add("Authorization", $"Bearer {token.Value}");
}
return Task.CompletedTask;
});
var channel = GrpcChannel.ForAddress(baseUri,
new GrpcChannelOptions
{
HttpClient = httpClient,
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});
var client = new GrpcServices.GrpcServicesClient(channel);
return client;
}
}
}
This component can be registered as follows:
services.AddSingleton<IGrpcChannelProvider, GrpcChannelProvider>();
Or -in case caching the channel for the duration of the app domain causes security concerns- register the component as scoped:
services.AddScoped<IGrpcChannelProvider, GrpcChannelProvider>();
In the view, inject this IGrpcChannelProvider
instead of the channel:
@inject IGrpcChannelProvider Provider
And use it as follows:
var channel = await Provider.Channel;
GetAllResponse getAllResponse =
await channel.GetAllAsync(new Google.Protobuf.WellKnownTypes.Empty());
To take it one step further, you might even want to prevent doing any calls on services inside your Razor page, but instead rely on a pre-populated model:
@model AllResponseModel
@model AllResponseModel
GetAllResponse getAllResponse = Model.AllResponses;
Now you can inject IGrpcChannelProvider
into the Razor AllResponseModel
instead.
CLICK HERE to find out more related problems solutions.