how do you use a function within a class injected as a task without using callingresult?

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.

Leave a Comment

Your email address will not be published.

Scroll to Top