Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface in .NETStandard 1.1 library does not have implementation in .NET 4.61

I'm defining an interface in a .NETStandard library that is a factory for a HttpClient instances:

namespace Ofl.Net.Http
{
    public interface IHttpClientFactory
    {
        Task<HttpClient> CreateAsync(HttpMessageHandler handler, 
            bool disposeHandler, CancellationToken cancellationToken);
    }
}

The <FrameworkTarget> element in the .csproj file is set to netstandard1.1 (HttpClient requires 1.1).

.NETStandard.Library 1.6.1 is referenced by default (although not explicitly specified in the .csproj file).

In the same solution, I start with a .NET Standard project and set the <FrameworkTargets> element to netcoreapp1.1;net461.

I have some tests to test just creating an implementation of the interface (I wouldn't normally test this, but this reduces it to the smallest self contained reproducible example).

To aid in this, I have a private implementation of IHttpClientFactory:

private class HttpClientFactory : IHttpClientFactory
{
    #region Implementation of IHttpClientFactory

    public Task<HttpClient> CreateAsync(HttpMessageHandler handler, 
        bool disposeHandler, CancellationToken cancellationToken)
    {
        // Return a new client.
        return Task.FromResult(new HttpClient());
    }

    #endregion
}

Then the tests:

[Fact]
public async Task Test_CreateAsync_Interface_Async()
{
    // Cancellation token.
    CancellationToken cancellationToken = CancellationToken.None;

    // The client factory.
    IHttpClientFactory factory = new HttpClientFactory();

    // Not null.
    Assert.NotNull(factory);

    // Create client.
    HttpClient client = await factory.CreateAsync(null, true,
        cancellationToken).ConfigureAwait(false);

    // Not null.
    Assert.NotNull(client);
}

[Fact]
public async Task Test_CreateAsync_Concrete_Async()
{
    // Cancellation token.
    CancellationToken cancellationToken = CancellationToken.None;

    // The client factory.
    var factory = new HttpClientFactory();

    // Not null.
    Assert.NotNull(factory);

    // Create client.
    HttpClient client = await factory.CreateAsync(null, true,
        cancellationToken).ConfigureAwait(false);

    // Not null.
    Assert.NotNull(client);
}

Basically, making a call through a variable with a type of the interface implementation, and a variable with a type of the implementing class (that doesn't matter though).

When running the tests (through Resharper, or dotnet test) in .NETCoreApp 1.1 framework has all tests passing. However, when running on the .NET Framework 4.6.1, I get a System.TypeLoadException stating that there is no implementation:

System.TypeLoadException : Method 'CreateAsync' in type 'HttpClientFactory' from assembly 'Ofl.Net.Http.Abstractions.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
   at Ofl.Net.Http.Abstractions.Tests.HttpClientFactoryTests.<Test_CreateAsync_Interface_Async>d__1.MoveNext()
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine](TStateMachine& stateMachine)
   at Ofl.Net.Http.Abstractions.Tests.HttpClientFactoryTests.Test_CreateAsync_Interface_Async()

Why doesn't the test pass when running on the .NET Framework 4.6.1? There's most certainly an implementation.

I thought it could be because System.Net.Http wasn't referenced in the test project (not necessary in the interface definition because .NETStandard 1.6.1 is referenced, which references System.Net.Http) so I made sure to add that, but the test still fails under the .NET Framework 4.6.1.

The full setup can be found here:

https://github.com/OneFrameLink/Ofl.Net.Http.Abstractions/tree/c2bc5499b86e29c2e0a91282ca7dabcc1acc1176

Info

VS.NET 2017 .NET CLI: 1.1.0 .NET Desktop Framework: 4.61 .NET Core: 1.1

like image 558
casperOne Avatar asked May 13 '17 16:05

casperOne


1 Answers

This is because no .dll.config file with the correct binding redirects is generated for your assembly and the Test SDK doesn't set the required properties to do this automatically.

You can work around this by adding this to your test's .csproj file:

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
  </PropertyGroup>

You'll see that this causes a bin\Debug\net461\Ofl.Net.Http.Abstractions.Tests.dll.config file to be generated that includes binding redirects for System.Net.Http and your tests should run properly.

The upcoming tooling for .net core / cli 2.0 and the test sdk will include a few changes that should fix this but this may be considered a bug in the current tooling.

like image 107
Martin Ullrich Avatar answered Oct 13 '22 00:10

Martin Ullrich