Grpc.Auth: Could not load type 'Grpc.Core.CallCredentials' from assembly 'Grpc.Core.Api'

Viewed 997

Context

I'm trying to use Google's Cloud Natural Language API. I have my service account key JSON file, and am trying to write a simple .NET Core application (more specifically an Azure Function using .NET Core) that will take in some text and use the sentiment analysis function from the Natural Language API and return a couple values.

I'm basing my implementation off Google's documentation, specifically the code section under the heading:

Passing the path to the service account key in code

The following is my application:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Google.Cloud.Language.V1;
using Google.Apis.Auth.OAuth2;
using Grpc.Auth;

namespace Project.Function
{
    public static class GoogleNLAPI
    {
        [FunctionName("GoogleNLAPI")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Admin, "get", "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext context)
        {
            string content = req.Query["content"];

            var path = Path.Combine(context.FunctionAppDirectory, "{FILE-NAME}");
            var credential = GoogleCredential.FromFile(path)
                .CreateScoped(LanguageServiceClient.DefaultScopes);
            var channel = new Grpc.Core.Channel(
                LanguageServiceClient.DefaultEndpoint.ToString(),
                credential.ToChannelCredentials()
            );
            var languageClient = LanguageServiceClient.Create(channel);
            var response = languageClient.AnalyzeSentiment(new Document()
            {
                Content = content,
                Type = Document.Types.Type.PlainText
            });
            var sentiment = response.DocumentSentiment;

            return new OkObjectResult($"Score: {sentiment.Score}\nMagnitude: {sentiment.Magnitude}");
        }
    }
}

And the .csproj file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
    <RootNamespace>google_nl_api</RootNamespace>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.28"/>
    <PackageReference Include="Google.Cloud.Language.V1" Version="1.2.0"/>
    <PackageReference Include="Google.Apis.Auth" Version="1.40.2"/>
    <PackageReference Include="Grpc.Auth" Version="1.21.0"/>
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="{FILE-NAME}">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

Issue

When I run this, I get the following error:

Exception while executing function: GoogleNLAPI. Grpc.Auth: Could not load type 'Grpc.Core.CallCredentials' from assembly 'Grpc.Core.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad'.

I'm using Grpc.Auth because without it I am unable to use .ToChannelCredentials(), which seems to be the method where the error is occurring.

3 Answers

more specifically an Azure Function using .NET Core

That's the problem, unfortunately.

At least in the emulator, there's a problem where the emulator contains an older version of Grpc.Core than 1.21.0. Normally that would only be a problem if new features were being used, but around 1.19.0 (IIRC), Grpc.Core was split into Grpc.Core and Grpc.Core.Api, with type forwarding handling the compatibility issue. That's fine until you use code that expects the split to be present, but finds that the old version of Grpc.Core has been loaded instead.

This issue has been reported to Microsoft but I haven't seen any workarounds yet. Note that you don't even need any direct reference to Google.Apis.Auth or Grpc.Auth - see the repro I posted in the final comment on that issue.

In addition to the above answer from @JonSkeet the link (https://github.com/Azure/azure-functions-host/issues/4527) provides a work around to add the following lines to the Azure Function .csproj file that will resolve the immediate issue:

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
   <!--https://github.com/Azure/azure-functions-host/issues/3568-->
   <!--<Exec Command="copy $(OutDir)bin\runtimes\win\lib\netstandard2.0\System.Private.ServiceModel.dll $(OutDir)bin\System.Private.ServiceModel.dll" />-->
   <Exec Command="copy $(OutDir)$(ProjectName).deps.json $(OutDir)bin\function.deps.json" />
 </Target>
 <Target Name="PostPublish" BeforeTargets="AfterPublish">
   <!--https://github.com/Azure/azure-functions-host/issues/3568-->
   <!--<Exec Command="copy $(PublishDir)bin\runtimes\win\lib\netstandard2.0\System.Private.ServiceModel.dll $(PublishDir)bin\System.Private.ServiceModel.dll" />-->
   <Exec Command="copy $(PublishDir)$(ProjectName).deps.json $(PublishDir)bin\function.deps.json" />
 </Target>

I had the same issue, I had to upgrade my package

"Microsoft.NET.Sdk.Functions" Version="1.0.29" -> "Microsoft.NET.Sdk.Functions" Version="1.0.31"
Related