Adding dll reference in .Net standard 2.0 project - c#

I want to add Dll reference of system assembliesSystem.Runtime.Caching assembly in my .net Standard 2.0 project(using VS2019). Due to some policy restrictions nuget usage is not possible so we need to add a dll reference(assuming target system has same version of .net framework installed),
How can add this reference so that it works in different systems without using path?
I tried following without any success:
Adding with reference manager dialog. This adds hint path(relative) which might not work in some systems(or when directory is changed).
Adding <PackageReference> adds nuget package which is restricted hence can not be used.
Tried adding override to Microsoft.Common.targets as
<AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == ''">{GAC}</AssemblySearchPaths> in csproj so that GAC is searched first.
Note: Third party package management or nuget local feed is not being accepted.
Also msbuild.common.targets file has {TargetFrameworkDirectory} option for resolving assemblies, so I tried adding <Reference Include="System.Xml"> to check if msbuild would search in target framework binaries but I still get same error.
In old style .net framework csproj structure(not SDK style) Adding something like <Reference Include="System.Xml"> used to work at runtime and compile time both, but it does not anymore.
Does DLL reference as mentioned above does not work in .net standard and core anymore?

You don't want to fight with current .NET/MSBuild architecture and NuGet. If you cannot create a local NuGet server you need to provide your path to libraries on your own. MSBuild is smart enough to find packages but if you will not provide any feed he will not resolve all of them. Under the hood, MSBuild has hardcoded paths that search for libraries as a fallback but these paths change over time when the framework evolves.
.NET Framework isn't equal to .NET Core, it's the standalone solution not related to Windows anymore so you shouldn't assume it will work as the previous framework.
Only one stable solution is to include all libs inside the project folder.
Reference still works but you need to supply a path like that(otherwise MSBuild probably will struggle to find libraries):
<ItemGroup>
<Reference Include="lib\$(TargetFramework)\*.dll" HintPath="%(FilePath)" />
</ItemGroup>
I think you should speak with your client and tell him he needs a more robust solution than fighting with a chosen framework because it's detrimental for him and you.

Related

utilizing C# microsoft assemblies that are not in the GAC

My computer only has VS2019 installed with .net versions up to 4.8. I'm attempting to use the Microsoft.Build.Construction assemblies in a program i'm working on. When i go to add the reference->Assemblies, the only versions i have are 4.0.0.0. Based on my research, VS stopped adding assemblies to the GAC, so i'm forced to manually add the DLL from C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin in order to utilize the Version 15.1.0.0 that at least supports the SolutionFile calls.
So my question is this: should i push those versions to the GAC in order to be able to use them, or is there a better way to access non-GAC assemblies than how i did.
For MSBuild assemblies, you must follow the guidance of Microsoft strictly to avoid all side efforts. In general, the NuGet packages are recommended.
Change MSBuild references
To make sure that MSBuild loads from a central location, you must not distribute its assemblies with your application.
The mechanism for changing your project to avoid loading MSBuild from a central location depends on how you reference MSBuild.
Use NuGet packages (preferred)
For detailed information, please check out the long reference
Forget the GAC, just take the DLL in the Bin folder, copy it to your project folder in /References, and reference it from there. This way if you use version control, the specific version of the DLL follows the source code.
EDIT: As mentioned by Lex below, this is not a suitable solution for MSBuild assemblies, you should use the Nuget package instead.

Unable to consolidate NuGet package transitive dependency versions in two NET Standard projects

Adding EF Core to a NET Standard project introduces transitive dependency versions incompatible with NuGet packages in other projects
I have a solution with multiple .NET Standard 2.0 projects.
One Project A uses the Google.Protobuf (3.11.2) NuGet package, that depends on
System.Memory (4.5.3)
System.Buffers (4.4.0)
System.Numerics.Vectors (4.4.0)
System.Runtime.CompilerServices.Unsafe (4.5.2)
A few other projects also depend on System.Memory and all use the same dependency versions.
Another Project B uses Microsoft.EntityFrameworkCore (3.1.0) NuGet package that depends on
System.Memory (4.5.3)
System.Buffers (4.5.0)
System.Numerics.Vectors (4.5.0)
System.Runtime.CompilerServices.Unsafe (4.7.0)
Even though the System.Memory version is (4.5.3) in both cases, it depends on System.Buffers, System.Numerics.Vectors and System.Runtime.CompilerServices.Unsafe and their versions differ.
When I run the application that uses these projects (a Microsoft Prism WPF .NET Framework 4.8 app that uses Unity IoC) UnityContainer throws the following exception:
System.IO.FileLoadException: 'Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.
After searching for a solution I added this to my NuGet.Config:
<config>
<add key="DependencyVersion" value="Highest" />
</config>
In both, %appdata%\Nuget and in the root folder of the .sln file.
I also deleted the %userprofile%\.nuget\packages folder.
Then I removed the NuGet packages from the projects and added them back again, but their dependecies come with the same versions as before.
If I navigate to "Manage NuGet Packages for Solution..." in Visual Studio and choose "Consolidate" it just says "No packages found"
I managed to reproduce the problem.
I created two new .net standard 2.0 project class libraries.
On the first I added EF Core.
On the second I added Google protobuf.
Both same versions as you mention.
For EF core I created a new class that just inherits from DbContext.
For Protobuf I just created an empty class as I am not familiar on how to use it.
I was still able to replicate the problem though.
Then I created a console app .net framework 4.7.2 referencing the above two projects.
I instantiated the two classes in the Console App and got error System.IO.FileLoadException: 'Could not load file or assembly...
How I resolved it
I went to all three projects and added this line to the csproj.
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
to the Property Group.
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
After that I built and ran again and no error appeared.
Please let me know your results.
Even if my solution does not work for you, I believe it is good practice to have it.
To quote Oren.
"Using .NET Standard requires you to use PackageReference to eliminate the pain of “lots of packages” as well as properly handle transitive dependencies. While you may be able to use .NET Standard without PackageReference, I wouldn’t recommend it."
Also Hanselman mentions:
"The "full" Framework projects are using the older .csproj format and by default, they use package.config to manage dependencies. The newer projects can reference Packages as first-class references. So we need to tell ALL projects in this solution to manage and restore their packages as "PackageReferences.""
Here are my sources.
https://www.hanselman.com/blog/ReferencingNETStandardAssembliesFromBothNETCoreAndNETFramework.aspx
https://oren.codes/2017/04/23/using-xamarin-forms-with-net-standard-vs-2017-edition/
Updated according to Sommen's extra info from the github issues
Kudos to Sommen for providing this extra info. Also Kudos to Immo Landwerth for providing this Info at GitHub.
I will provide as is the Workarounds that already exist in the Github page just for complecity as advised by the OP jinjinov.
Taken from GitHub Issues
Workarounds
Regular .NET Framework projects
Enable automatic binding redirects in the root .NET Framework application
Make sure your root application project doesn't use packages.config but uses PackageReference for NuGet packages:
If you currently don't have packages.config, simply add
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>.
If you currently do have a packages.config, convert the contents to package references in the project file. The syntax is like this:
<PackageReference Include="package-id" Version="package-version" />
ASP.NET web applications and web sites
Web applications and web sites don't support automatic binding redirect generation. In order to resolve binding conflicts, you need to double click the warning in the error list and Visual Studio will add them to your web.config file
In web application projects, you should enable PackageReference like mentioned above. In web sites, you cannot use PackageReference as there is no project file. In that case, you need to install all NuGet packages into your web site that any of the direct or indirect project references depend on.
Unit test projects
By default, binding redirects aren't added to class library projects. This is problematic for unit testing projects as they are essentially like apps. So in addition to what's outlined in automatic binding redirects you also need to specify GenerateBindingRedirectsOutputType:
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
There is also a discussion section that provides more information -> GitHub discussion
Yeah, welcome to the struggle.
Like PanosKarajohn pointed out using packagereference instead of packages.config helps with this. Unfortunately, that is Vs2017 and up and for some of us that is not yet in sight.
The problem is actually explained pretty much here:
https://github.com/dotnet/announcements/issues/31
You need to use binding redirects to redirect all the version numbers to the highest on you got and then pray that everything plays nice with each other.
I use the Microsoft.aspnetcore.signalR package in a .net 4.6.1 project and you have the same issues.

Why is my .NET framework app looking for the wrong version of the .NET core/standard platform extension assembly, and how do I fix it?

From the .NET APIs catalog, I understand that the Microsoft.Win32.Registry class is declared in the .NET Standard + Platform Extensions 2.0 package in an assembly Microsoft.Win32.Registry, Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a.
I've created a class library which targets .NET Standard 2.0, and here's a simple class:
public class NetStandardClass
{
public string GetHklmRegValue()
{
var lmKey = Microsoft.Win32.Registry.LocalMachine;
var softwareKey = lmKey.OpenSubKey("Software");
return "value";
}
}
I've created a .NET Framework 4.7.2 console application which references my above class library:
class Program
{
static void Main(string[] args)
{
string value = new ClassLibrary2.NetStandardClass().GetHklmRegValue();
}
}
When I run this on Windows, this throws a run-time exception:
System.IO.FileNotFoundException: 'Could not load file or assembly
'Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The
system cannot find the file specified.'
Based on what I've read, having assembly load issues in this scenario is somewhat of a known issue. The prescribed work-around is to enable automatic binding redirects and make sure my .NET Framework application is using PackageReference rather than Project.Config. I have done this with my projects from which I shared the above code, but I'm still getting the error. What confuses me most, though, is that the error is indicating the .NET Core / .NET Core + Platform Extensions assembly (Version=4.1.3.0, PublicKeyToken=b03f5f7f11d50a3a) rather than the .NET Standard (Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a) or .NET Framework (Version=4.0.0.0, PublicKeyToken=b77a5c561934e089) versions from the APIs catalog:
This is further corroborated by the Microsoft.Win32.Registry.DLL that is in the output directory:
Based on further reading, I can make a little progress by doing either of the following:
Add <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to the .NET Standard class library proj file
-- or --
Add the Microsoft.Win32.Registry NuGet package directly to my .NET Framework console application.
Either of these results in loading some version of the assembly, but then I get some odd behavior: I get an NRE for the LocalMachine property, which shouldn't happen.
So here are the questions:
1.) Since my project is a .NET Framework application, why is it not using the Microsoft.Win32.Registry class in the .NET Framework API, specifically the mscorlib assembly that the same APIs catalog refers to?
2.) Why isn't the "work around" in the GitHub post not working for me?
3.) Why is it seemingly looking for the .NET Core / ... extensions version of the assembly?
4.) Why when I explicitly export the NuGet Microsoft.Win32.Registry assembly in the .NET Standard class library or directly reference the package in the .NET Framework console application does it result in the strange behavior where Microsoft.Win32.Registry.LocalMachine is null, which should never be the case on a Windows machine?
The following answers make an assumption of having the newest version of Visual Studio 2019 (v16.4.3 at time of writing) installed, as this may have some effect on the outcome.
Question 1):
1.) Since my project is a .NET Framework application, why is it not using the Microsoft.Win32.Registry class in the .NET Framework API, specifically the mscorlib assembly that the same APIs catalog refers to?
This will actually use the v4.0.0.0 mscorlib Registry class when the projects are set up in either of the following manners:
Option 1
Class library: Target Framework = netstandard2.0, NuGet packages = Microsoft.Windows.Registry (v4.5.0)
Console app: Target Framework = net472, NuGet is set to "packages.config" mode, NuGet packages = Microsoft.Windows.Registry (v4.5.0) [and also AccessControls and Principal.Windows, as they are dependencies]
NOTE: Here, if you don't add the Microsoft.Windows.Registry package, you typically will get the runtime error looking for version 4.1.1.0 of the Registry dll. But I believe the version it looks for is based on what the current .NET Core SDK version you have installed.
Option 2 [I think this is the one you really want]
Class library: Target Framework = netstandard2.0, NuGet packages = Microsoft.Windows.Registry (v4.5.0)
Console app: Target Framework = net472, NuGet is set to "PackageReference" mode, NuGet packages = None
NOTE: In VS2019, if you have the option for "Allow format selection on first package install" checked, then it will allow you to choose to use the PackageReference style, where NuGet packages are referenced in the project file instead of packages.config. Typically you have to install any one NuGet package just to set this mode, but afterward can uninstall that package and it will stay in that mode. I believe you could also set the default mode as well, before you first create your net472 project.
NOTE: Here, the PackageReference mode seems to help resolve the NuGet dependencies on the other .NET Standard 2.0 class library, where as the package.config mode requires you to do it yourself it seems.
This should be easily reproducible, however, things that can cause some sort issue can be any of:
- older versions of VS2019 being used
- skipping binding redirects setting turned on for NuGet in VS
- auto binding redirects turned off for the .NET 4.7.2 project
- not "rebuilding" the solution after package or reference changes
- not restarting the computer after installing/updating .NET SDK's or VS2019 updates
- still having a packages.config file
I'd also like to note that in Option 1 above, I found in testing this out that if you don't add the Microsoft.Windows.Registry package, it fails on runtime looking for version 4.1.1.0 of the registry dll. But, I was able to get it to fail looking for runtime 4.1.3.0 by first installing Microsoft.Windows.Registry 4.7.0, and then I uninstalled it (thereby leaving the two dependent packages AccessControl and Principal.Windows), and without rebuilding the project: if I run it, it fails on runtime with the 4.1.3.0 version being the one it's looking for. Rebuilding it reverts back to 4.1.1.0. This remains the same even if I remove the two dependent packages. Note: this also works if you simply remove the references to the dll's in the project, rather than uninstalling the NuGet packages.
Question 2):
2.) Why isn't the "work around" in the GitHub post not working for me?
I have a feeling this is happening because you may have an older version of VS2019 than 16.4.3. I found that when I was using an older version, the PackageReference mode still resulted in the runtime error. When I updated (sorry, I am not sure which exact revision actually fixes it) VS2019 to 16.4.3, this seems to now just work. I am not sure if this is some sort of unexpected interaction with the various SDK's (perhaps some being more recently released but not supported by an older revision of VS). It could also be an issue if the packages.config file is still lingering around.
Another issue could potentially be interference by other NuGet packages that may be installed and have different library version requirements.
Question 3):
3.) Why is it seemingly looking for the .NET Core / ... extensions version of the assembly?
In a .NET 4.7.2 project that references a .NET Standard 2.0 (.NET standard projects are .NET Core projects by default), it will utilize the .NET Core framework, not the .NET framework. So any references to the Registry are not by default available. You need the Microsoft.Windows.Registry packages (at the least) to allow use of the Registry, which I believe has the ability to act as a shim to the .NET 4.7.2 mscorlib verison of the library if available, but use the 4.1.1.0 version as a fallback (or 4.1.3.0 version if you're referencing from a .NET Core project instead).
Question 4):
4.) Why when I explicitly export the NuGet Microsoft.Win32.Registry assembly in the .NET Standard class library or directly reference the
package in the .NET Framework console application does it result in
the strange behavior where Microsoft.Win32.Registry.LocalMachine is
null, which should never be the case on a Windows machine?
I haven't personally tested this, and didn't run into this issue when I tested the above, but my feeling on this is that the dll's are likely missing their dependent dll's. But thinking about that further, would likely just result in another runtime error if that's the case. I think the issue is that they aren't intended to be directly exported and something may be missing along the way.
I'd also note that if you run this on any platform other than Windows, the registry is likely to come back as null since I think it wouldn't exist on, say, a Linux runtime.
Other Notes:
I get a general sense that this sort of thing has been a little buggy with VS and .NET Core in general referencing to/from .NET Framework, and that there's progress being made regularly to improve this.
I found also that there are some surprising issues I ran into that I didn't expect. For example, creating a .NET Standard console app, referencing the .NET Standard class library, and still getting the runtime error, no matter what packages I installed on the console app. You would think the exact same target framework would just work without any special configurations, but it doesn't seem to. But if you create a .NET Core console app instead, it does work properly. It's a bit mystifying, but there's always a technical explanation somewhere in the mix.

Adding .NET Standard libraries to 4.7.1 lib adds loads of references, some broken

As I need to import a library targeting .NET Standard 2, I had upgraded my library to .NET 4.7.1, as I understood from this MS video that should avoid this issue: https://www.youtube.com/watch?v=u67Eu_IgEMs
However, adding .NET standard now results in dozens of System.xxx references, rather than a single reference to .NET Standard (as per the video).
Worse still, several of the references have been added but the underlying file appears to be missing generating warnings, e.g.
Warning The referenced component 'Microsoft.Win32.Primitives' could not be found.
Warning The referenced component 'System.IO.FileSystem' could not be found.
Warning The referenced component 'System.Security.Cryptography.X509Certificates' could not be found.
Warning The referenced component 'System.Globalization.Calendars' could not be found.
Warning The referenced component 'System.Security.Cryptography.Encoding' could not be found.
Warning The referenced component 'System.Security.Cryptography.Primitives' could not be found.
Warning The referenced component 'System.IO.Compression.ZipFile' could not be found.
Warning The referenced component 'System.Console' could not be found.
I even re-created the demo project in video and got the same result - no single reference to .NET Standard, lots of DLL references instead.
I've tried a NUGET update-package -reinstall and downgraded and upgraded to .NET standard 2.0 and 2.0.1 as well
The answer I'm creating for my own question is:
Does your .NET Framework project use packages.config ? If it does, DO NOT reference .NET Standard libraries. The package/reference/binding-redirect in VS 2017 is horribly broken if you introduce .NET Standard. Trying to fix it will cause more problems (I've wasted several days trying). Expect to have assemblies which don't load despite being present, lots of warnings and a broken app.
If you use System.Net.Http, plan on spending several days in Google and GitHub issues trying to get that to work.
If you are able to upgrade to packageReferences, this should fix the problem. But if your project contains packages that import content, like JQuery or Bootstrap be aware that these no longer work and you'll instead spend more time trying to fix those references and migrate to npm or bower, along with fixing TypeScript compilation too. No thanks.
Ideally you'd be using the 2017 csproj format but that's not compatible with WinForms, ASP.NET or Windows Services - so tough if you've got a legacy project.
Because of some issues with the implementation of the .NET Standard 2.0 support on .NET Framework 4.7.1, additional files are required to be deployed to your bin folder.
This issue is described as a known issue here.
The number of files copied to the output folder will be 0 when you are targeting or running on .NET Framework 4.7.2.
Please also make sure you are using the latest Visual Studio (at least version 15.6.3) because some of the changes required to make this scenario work are available there.
I had an absolutely the same issue. I was trying to install Microsoft.Azure.ServiceBus package on the empty console .NET Framework 4.7.1 project and got all these broken references.
As far as I understood the root cause is https://github.com/dotnet/standard/issues/567 and the possible workaround described here https://github.com/dotnet/corefx/issues/29622#issuecomment-396753264
So I just replaced broken references like
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
</Reference>
in my .csproj file to
<Reference Include="System.Security.Cryptography.Primitives"/>
and it had worked because this assembly is the part of .NET Framework 4.7.1. Also I deleted all binding redirects from the .config file regarding the broken references.
Also, I've found an interesting fact. There was a reference
<Reference Include="System.Runtime.Serialization.Primitives, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.Serialization.Primitives.4.3.0\lib\net46\System.Runtime.Serialization.Primitives.dll</HintPath>
</Reference>
and it was not broken, because this assembly exists in the .../MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net471\lib folder. So I wonder, could it be the problem of MS build?
FWIW, I was using Visual Studio 15.7.5, and was manually fixing up all of my binding redirects (to remove them). However, I noticed that my colleague had Visual Studio 15.9.4 and on the project properties screen there is now a 'Auto generate binding redirects'. I'd previously set this in the csproj manually. But, updating to VS 15.9.4 and re-building the projects got rid of all of the binding redirects for me.

Using two different versions of same the NuGet package

I wanted to use two different version same library (OpenCVSharp 2.x and OpenCVSharp 3.x).
I downloaded those two packages both to the separate project (let's call it OCV2Wrapper and OCV3Wrapper) and reference both wrappers in my project. I had to renamed libraries from one package (2.x) and reference them manual because: Can we add 2 different versions of same package in NuGet. I read about external aliases and I used external alias in one of the wrappers (2.x in my case).
But I have some major problems:
My renamed libraries are not copied to the launch project build (that one which reference both wrappers), but is in build of the 2.x wrapper
It doesn't work because yet it says it cannot find a type from my 2.x wrapper even when I manually copy my renamed libraries from 2.x wrapper.
What is the correct approach for this scenario in C#?
I want to use both wrappers in solution because the 2.x version contains algorithms (SIFT and SURF) and 3.x version contains algorithms (Kaze and AKaze).
I can live that with both packages coming from somewhere other than NuGet, but I prefer that 3.x comes from NuGet and the 2.x version is manually configured.
As already stated, there is nothing wrong with referencing 2 different versions of a NuGet package, as long as it's in different Visual Studio Projects that those references are made.
But this is also where the easy part ends, but I think there are a few options left. Depending on your needs, I see the following options.
Create a post build step which registers the multi-versioned assemblies into the GAC. As long as each assembly have different assembly version, the CLR will pick up the right assembly from the GAC when needed.
Create a post build step which copies the different assemblies into a subfolder of your application bin folder like bin/package-v1 and bin/package-v2. Then you can, in your application, override the AssemblyResolve event as described here: https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx. This will make it possible for you to load the assembly in the right version at the time of need.
If you don't want to play around with AssemblyResolve, then you can also modify your web/app.config to do assembly redirect/probing as described here: https://msdn.microsoft.com/en-us/library/4191fzwb(v=vs.110).aspx
Hope this helps a bit, so you don't have to modify third party source code next time.
OK so, I solve this by downloading whole sourcecode for 2.X wrapper version.
Renamed its namespace to ABCDEF2 where ABCDEF was original namespace. Build my own nuget package with my own key and... publish it to our private nuget server.
This is such a lame solution but there is no other way than manually downloading the original packages and reference it directly with different filename etc and you loose nuget advantages.

Categories

Resources