How does csc/Roslyn resolve assembly version? - c#

I have a console program that has two direct dependencies: ClosedXML v0.97 and ClosedXML.Report 0.2.4. The ClosedXML.Report dependency also depends on ClosedXML, but in a version 0.95.
When I try to compile the program in NET6 (basically only a reference to a class in the ClosedXml.Report),
_ = new XLTemplate(new System.IO.MemoryStream());
I get a compiler error
CS0012 The type 'IXLWorkbook' is defined in an assembly that is not referenced. You must add a reference to assembly 'ClosedXML, Version=0.95.0.0, Culture=neutral, PublicKeyToken=null'.
Why doesn't csc.exe recognize the 0.97 version to use and requests 0.95 (the indirect dependency)? The assembly version of package is same as the nuget version.
Nuget uses direct-dependency-wins mechanism and I though .net core also uses that for assemlby resolution. Is a mechanism different? How does roslyn resolve which version to use and when to throw an error?
Relevant piece of MSBuild
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\Roslyn\csc.exe
/reference:C:\Users\username\.nuget\packages\closedxml\0.97.0\lib\netstandard2.0\ClosedXML.dll
/reference:C:\Users\username\.nuget\packages\closedxml.report\0.2.4\lib\netstandard2.0\ClosedXML.Report.dll

One possible explanation for this behavior would be if IXLWorkbook, which is apparently required by the call to new XLTemplate() no longer exists in Version 0.97 of ClosedXML.
If possible, try to rebuild ClosedXML.Report with ClosedXML 0.97. Since these apparently are early versions (Version < 1.0) breaking changes between versions seem possible. Also, can you make sure that the output bin directory actually contains Version 0.97?

Related

DLL version mismatch issue

* Introduction to the Issue
I am using a software that uses the .Net framework to perform some tasks.
We are trying to use the Mailkit.dll file but when using it we are faced with the message:
Internal : Could not execute code stage because exception thrown by code stage: Could not load file or assembly 'System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
and from what I have concluded so far the Mailkit.dll depends on Mimekit.dll that depends on System.memory.dll, something like that.
Mailkit.dll > Mimekit.dll > System.Memory.dll
Version details:
.Net framework installed on machine: 4.7.3190
.Net framework used by application: 4.7
Mailkit.dll version: 3.4.1
Mimekit.dll Version: 3.4.1
System.Memory.dll in application folder Version:4.6.28619.01
I don't Know what's the issue or why this is happening but I am pretty sure it has to do with version issue so any help is welcomed.
The version numbers you provided are a good starting point.
For .NETFramework 4.7, MailKit v3.4.1 depends on MimeKit v3.4.1 which depends on System.Memory >= v4.5.5.
System.Memory with NuGet version v4.5.5 has an AssemblyFileVersion of 4.6.31308.01 (the number that shows in windows explorer) but an AssemblyVersion of 4.0.1.2. The assembly number is what really matters when the CLR looks for assemblies. The CLR looks for v4.0.1.2 when loading MimeKit but can only find v4.0.1.1.
The version that ends up in your output is older than the version required. I found that the version that is actually in your output is from System.Memory v4.5.4. AssemblyFileVersion: 4.6.28619.01. AssemblyVersion: 4.0.1.1.
This is probably happening if you are referencing System.Memory nuget package directly. If you do have a direct package reference to System.Memory, then you need to upgrade it.
If you were using SDK styled projects, you would get an error preventing this from happening. But you should still be getting a build warning about version conflicts detected.

Fixing "They type 'X' exists in both 'Y' and 'Z'" C# error for System.ServiceModel in LINQPad

If I:
Create a new C# query in LINQPad 6.
Add the System.ServiceModel.Http NuGet package or another package that references it.
Try to instantiate a class from the System.ServiceModel namespace, for example System.ServiceModel.BasicHttpBinding.
Leading to the following .linq file:
<Query Kind="Expression">
<NuGetReference>System.ServiceModel.Http</NuGetReference>
</Query>
new System.ServiceModel.BasicHttpBinding()
Then I get a compile error:
CS0433 The type 'BasicHttpBinding' exists in both 'System.Private.ServiceModel, Version=4.7.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' and 'System.ServiceModel.Http, Version=4.7.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
If I create a project in Visual Studio and add the same NuGet package and code, I do not get this error.
According to the C# Language reference for the error, it should be possible to resolve by using the -reference compiler option or by not referencing one of the assemblies. However, I can't seem to find a way to use this compiler option in LINQPad, nor can I find any way to remove the assembly reference to System.Private.ServiceModel.
How can I fix the error?
This is a bug in LINQPad, triggered by an obscure scenario. The System.Private.ServiceModel package contains a lib folder with an assembly which is required at runtime, and a ref folder with a underscore.underscore file which indicates that no assemblies should be referenced by the compiler. Because LINQPad finds no reference assemblies, it feeds the compiler the assembly in the lib folder, which causes the error.
I've got a fix ready and regression tests are currently running. The fix will likely make it into the 6.11.2 beta build, which should be released in a day or two.

Why Does Command Line C# Interactive (csi.exe) Ignore Explicit Assembly Reference?

Microsoft (R) Visual C# Interactive Compiler version 2.9.0.63208
Windows 7 64 bit
NBitcoin 4.1.1.68
====
System.Security.Cryptography.Algorithms version 4.3.0.0 has an SHA256Managed class that I want to use in C# Interactive (csi.exe).
I added that assembly to the GAC with the gacutil -i [path_to_dll] command. I launched csi with an /r:[path_to_dll_dir]/System.Security.Cryptography.Algorithms.dll command line option.
On top of that, after csi started, I also did an #r "[path_to_dll]" reference. Belt and suspenders type stuff. I know. But I guess I was hoping the overkill would force it to do the right thing.
My application uses a class from a third-party library. The following code was copied pretty much verbatim from the third-party method my app calls. If I run the following code in csi, it works fine...
using System;
using System.Security.Cryptography;
byte[] b = Guid.NewGuid().ToByteArray();
var sha = new SHA256Managed();
byte[] c = sha.ComputeHash(b, 0, 15);
Now, here's the thing. That third-party class defines a method that calls SHA256Managed.ComputeHash(byte[], int, int) exactly like the code above.
For the sake of discussion, let's refer to the third party class and method as Foo.m().
The problem is, when I call the third party Foo.m() from csi, csi balks with...
Could not load type 'System.Security.Cryptography.SHA256Managed' from assembly 'System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
+ Third.Party.Foo.m(byte[], int, int)
Remember I explicitly added the version 4.3.0.0 crypto algorithm assembly into the GAC. Plus I explictly referenced the dll with both #r and /r:. So what gives?
I can see in the FusLogVw binding logs that csi is loading the version 4.0.0.0 assembly; in spite of me explicitly insisting on using version 4.3.0.0. System.Security.Cryptography.Algorithms version 4.0.0.0 does not have an SHA256Managed class.
Please help me figure out how to make csi use the assembly I tell it to use? Bear in mind, that it uses the right version for code written directly in csi. So why is it not using the correct assembly version for the exact same code in a third-party library?
TL;DR: The csi newb that I am, after installing the third-party lib for the first time two days ago, I copied its dll plus all its dependencies into a subdirectory of csi's bin directory to get csi to find and load it.
Long Answer: After bouncing some ideas off of the knowledgeable and helpful C# expert #PetSerAl, I eventually figured out what the answer to my original question was.
A FusLogVw Assembly Binding Log Entry that I encountered very soon after installing the third-party lib, lead to me putting the third-party dll into a sub directory of csi's bin dir. That appears to have inadvertently thwarted csi's assembly lookup mechanism from doing a complete probe of all the locations it should have looked for assemblies. As a result, csi was finding and referencing its own preinstalled older version of System.Security.Cryptography.Algorithms which is distributed with csi as part of the original VS 2017 installation.
Consequently, that made csi ignore both the subsequent /r: and #r references to the Algorithm dll that I was passing it. So it wasn't finding the non-existent SHA256Managed type in its own, older 4.0.0.0 version of the assembly it had in its bin dir. That's what caused the assembly binder error reported in the original post.
Removing the third-party assembly and co from csi's bin subdir did the trick. From there, getting csi to work correctly with the third-party library was just a matter of launching csi with the right assembly references...
C:\>csi /u:NBitcoin /u:NBitcoin.Crypto /u:System.Security.Cryptography /lib:[my_vs_2017_project_dir]\bin\Release\net461;[my_vs_2017_home_dir]\2017\Community\MSBuild\15.0\bin\Roslyn\ /r:System.Data.dll /r:System.Drawing.dll /r:System.IO.Compression.FileSystem.dll /r:System.Numerics.dll /r:System.Runtime.Serialization.dll /r:System.Xml.dll /r:System.Xml.Linq.dll /r:Microsoft.Extensions.Logging.Abstractions.dll /r:[my_vs_2017_project_dir]\bin\Release\net461\Microsoft.Extensions.Logging.Abstractions.dll /r:[my_vs_2017_project_dir]\bin\Release\net461\Newtonsoft.Json.dll /r:[my_vs_2017_project_dir]\bin\Release\net461\System.Buffers.dll /r:[my_nuget_repo_pkgs_dir]\system.net.http\4.3.4\lib\net46\System.Net.Http.dll /r:[my_vs_2017_project_dir]\bin\Release\net461\System.Net.Requests.dll /r:[my_nuget_repo_pkgs_dir]\nbitcoin\4.1.1.68\lib\net461\NBitcoin.dll /r:[my_nuget_repo_pkgs_dir]\system.security.cryptography.algorithms\4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll /r:[my_nuget_repo_pkgs_dir]\system.security.cryptography.csp\4.3.0\lib\net46\System.Security.Cryptography.Csp.dll /r:[my_nuget_repo_pkgs_dir]\system.security.cryptography.encoding\4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll /r:[my_nuget_repo_pkgs_dir]\system.security.cryptography.primitives\4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll /r:[my_nuget_repo_pkgs_dir]\system.security.cryptography.x509certificates\4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
Microsoft (R) Visual C# Interactive Compiler version 2.9.0.63208
Copyright (C) Microsoft Corporation. All rights reserved.
Type "#help" for more information.
>
> Console.WriteLine("Hello World! " + new Key().GetWif(Network.Main));
Hello World! L2emt8acTdsGMXzJfAMSARUvKvP8xUCULcQbpiY76EpeyvKyrir2
>
I'm pretty sure there's a less verbose way to pass all those /r: and /u: options on csi startup. But just getting it to find and load the correct assemblies is the main thing.

Why do I receive a warning about an assembly reference that appears to be identical in version and culture

I am using Xamarin on a Mac and have built up a series of .netstandard1.3 libraries. One of which is referencing a few external packages:
NETStandard.Library
Newtonsoft.Json
System.Linq.Queryable
System.Reactive
System.Security.Principal
When I build the project (library) it builds but with the following warning:
/Library/Frameworks/Mono.framework/Versions/4.8.1/lib/mono/xbuild/14.0/bin/Microsoft.CSharp.targets (CoreCompile target) ->
CSC: warning CS1702: Assuming assembly reference
System.Runtime.Serialization.Primitives, Version=4.1.1.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' matches assembly
System.Runtime.Serialization.Primitives, Version=4.1.1.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
You may need to supply runtime policy
I'm not directly referencing this specific library anywhere, but even if I were the version and public key token appear to be identical so why the complaint? How do I get rid of this warning and why am I getting it?
Despite having the same assembly identity, there are different implementations of System.Runtime.Serialization.Primitives.dll presumably because of runtime specific behavior / implementation. MSBuild is not sure which to use. You may target your library to multiple runtimes, or distribute it as a NuGet package which shifts the responsibility of picking a target runtime to the library or app consuming your package. You can download the package, change it to a .zip and take a peak inside to see what I mean.

Adding MetadataReference in Roslyn Vs Microsoft.CodeAnalysis

Roslyn version 1.2.* has a function called MetadataReference.CreateAssemblyReference() which takes the display name of the assembly and returns the appropriate MetadataReference object. For example I was able to add reference to various assemblies as follows:
Compilation compilation = Compilation.Create("HelloWorld")
.AddReferences(MetadataReference.CreateAssemblyReference("mscorlib"),
MetadataReference.CreateAssemblyReference("System.Linq"),
MetadataReference.CreateAssemblyReference("System.Data.Linq"),
MetadataReference.CreateAssemblyReference("System.Data"),
MetadataReference.CreateAssemblyReference("System.Data.DataSetExtensions"),
MetadataReference.CreateAssemblyReference("System.Xml"),
MetadataReference.CreateAssemblyReference("System.Xml.Linq"),
MetadataReference.CreateAssemblyReference("System"),
MetadataReference.CreateAssemblyReference("System.Core")
//MetadataReference.CreateAssemblyReference("System.Core"),
/*MetadataReference.CreateAssemblyReference("System")*/)
.AddSyntaxTrees(tree);
This however does not seem possible with the Microsoft.CodeAnalysis package (this is the latest package that one can install from Nuget). This package has a few functions inside MetadataReference - but they either require an Assembly or a file path.
Does that above mentioned simpler function exist in newer compiler packages?
You can load the assembly with the CLR loader and find out where it was loaded from:
typeof(DataSetExtensions).Assembly.Location

Categories