Stackexchange.redis: Could not load file or assembly System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0

Created on 18 Apr 2019  路  6Comments  路  Source: StackExchange/StackExchange.Redis

Hello,

I have been struggling with this for a few days now, I just can't figure it out.
I am working on a project with StackExchange.Redis 2.0.600 (tried 2.0.601, same issue even after clearing obj/ and build/ folders on 2 different machines).

When the project calls for StackExchange.Redis.ConnectionMultiplexer.Connect(), it throws the following exception:

Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of
its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
   at System.ReadOnlySpan`1..ctor(T[] array, Int32 start, Int32 length)
   at StackExchange.Redis.ConnectionMultiplexer.ActivateAllServers(LogProxy log)
   at StackExchange.Redis.ConnectionMultiplexer.<ReconfigureAsync>d__142.MoveNext()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at StackExchange.Redis.ConnectionMultiplexer.ConnectImpl(Object configuration, TextWriter log)

I've been looking for similar issues on GH, but still haven't found a solution yet.
I even tried restarting from scratch stripping down everything to the bare source code and the least required dependencies but got no luck.

Here is the list of the dependencies:

    <PackageReference Include="ini-parser" Version="2.5.2" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
    <PackageReference Include="StackExchange.Redis" Version="2.0.600" />
    <PackageReference Include="System.Net.Http" Version="4.3.4" />

I've tried adding several versions of System.Runtime.CompilerServices.Unsafe, restoring packages through NuGET everytime but none of that ever worked! This is driving me crazy, because on Mono it DOES work!

God bless anyone who is able to give me a piece of advice for this.

Matt.

EDIT: Attaching Core.csproj for reference

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{69F22F59-03ED-4DB6-B1E8-248D8C549D50}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>NZR</RootNamespace>
    <AssemblyName>Core</AssemblyName>
    <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>x64</PlatformTarget>
    <Prefer32bit>false</Prefer32bit>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32bit>false</Prefer32bit>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>x64</PlatformTarget>
    <Prefer32bit>false</Prefer32bit>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32bit>false</Prefer32bit>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <!-- ... .cs source files ... -->
  </ItemGroup>
  <ItemGroup>
    <Content Include=".gitignore" />
    <Content Include="Core.nuspec" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="ini-parser" Version="2.5.2" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
    <PackageReference Include="StackExchange.Redis" Version="2.0.601" />
    <PackageReference Include="System.Net.Http" Version="4.3.4" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Most helpful comment

Hi @mgravell,

Thanks for the tip. For anyone experiencing the same issue, I found a fix.
I tried force-loading all assemblies at the beginning using code from https://stackoverflow.com/a/26191959.

Here is the code I used:

using System;
using System.Linq;
using System.Reflection;

namespace Utils.Debug
{
    /// <summary>
    /// Debug tool to force load all assemblies in the current AppDomain
    /// thus allowing deep debugging of failing reference dependencies.
    /// Code inspired by https://stackoverflow.com/a/26191959.
    /// </summary>
    public class CAssemblyDebugger
    {
        /// <summary>
        /// Force load all referenced assemblies in the current AppDomain
        /// </summary>
        public static void ForceLoadAllAssemblies()
        {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                ForceLoadAssemblyAndItsDependencies(assembly);
            }
        }

        /// <summary>
        /// Force load an assembly and its dependencies in the current AppDomain
        /// </summary>
        /// <param name="assembly">The assembly to force load</param>
        public static void ForceLoadAssemblyAndItsDependencies(Assembly assembly)
        {
            foreach (AssemblyName name in assembly.GetReferencedAssemblies())
            {
                if (AppDomain.CurrentDomain.GetAssemblies().All(a => a.FullName != name.FullName))
                {
                    ForceLoadAssemblyAndItsDependencies(Assembly.Load(name));
                }
            }
        }
    }
}

As expected, the code threw several interesting exceptions.
I found out that when trying to resolve StackExchange.Redis references, the environment was picking wrong versions of System.Memory and System.Threading.Tasks.Extensions, which in turn were picking the old System.Runtime.CompilerServices.Unsafe 4.0.4.0.

I simply added those 2 dependencies using version >= 4.5.0 (minimum req for 4.7.1) to force the environment to pick them while resolving references therefore using the right version of Unsafe shipped with .Net.

All 6 comments

It sounds like you need an assembly-binding redirect for Unsafe via app.config / web.config; Sorry, but we can't control the bindings, and sometimes nuget and/or dotnet gets them wrong.

Hi @mgravell,

Thanks for the tip. For anyone experiencing the same issue, I found a fix.
I tried force-loading all assemblies at the beginning using code from https://stackoverflow.com/a/26191959.

Here is the code I used:

using System;
using System.Linq;
using System.Reflection;

namespace Utils.Debug
{
    /// <summary>
    /// Debug tool to force load all assemblies in the current AppDomain
    /// thus allowing deep debugging of failing reference dependencies.
    /// Code inspired by https://stackoverflow.com/a/26191959.
    /// </summary>
    public class CAssemblyDebugger
    {
        /// <summary>
        /// Force load all referenced assemblies in the current AppDomain
        /// </summary>
        public static void ForceLoadAllAssemblies()
        {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                ForceLoadAssemblyAndItsDependencies(assembly);
            }
        }

        /// <summary>
        /// Force load an assembly and its dependencies in the current AppDomain
        /// </summary>
        /// <param name="assembly">The assembly to force load</param>
        public static void ForceLoadAssemblyAndItsDependencies(Assembly assembly)
        {
            foreach (AssemblyName name in assembly.GetReferencedAssemblies())
            {
                if (AppDomain.CurrentDomain.GetAssemblies().All(a => a.FullName != name.FullName))
                {
                    ForceLoadAssemblyAndItsDependencies(Assembly.Load(name));
                }
            }
        }
    }
}

As expected, the code threw several interesting exceptions.
I found out that when trying to resolve StackExchange.Redis references, the environment was picking wrong versions of System.Memory and System.Threading.Tasks.Extensions, which in turn were picking the old System.Runtime.CompilerServices.Unsafe 4.0.4.0.

I simply added those 2 dependencies using version >= 4.5.0 (minimum req for 4.7.1) to force the environment to pick them while resolving references therefore using the right version of Unsafe shipped with .Net.

A fix would be outstanding. I have been bashing my head all week regarding the same problem. I tried everything. I must try what was mentioned above.

Thanks both!

Hi @ZeusInTexas,

I am still struggling with this and would appreciate checking a couple of points.

Do you add references for three assemblies - System.Memory, System.Threading.Tasks.Extensions and StackExchange.Redis. Then load these individually into the app domain recursively as the thread mentioned above indicates.

This would be a huge help.

Best wishes
Fig

@lefig I added the dependencies from NuGet, which automatically adds them as references in the .csproj if I'm not mistaken.

Please also see #1130 which includes the binding redirect example and a workaround using 4.6-preview (the bug in Unsafe is fixed in 4.6, but it isn't releases yet)

Was this page helpful?
0 / 5 - 0 ratings