Why are some nuget package references required by applications? - c#

I have a question that has been bothering me for awhile. I ran across this problem a few years back when I was dealing with writing a generic logging wrapper around some hosted provider instances using log4net.
The idea was simple enough, I wanted to write a logging and metrics that hid all the implementation in a separate visual studio project so when you wanted to add any telemetry support to another application you could just include the project, new up an instance of the logger and start logging using generic calls. If you ever switched providers or tweak logging settings, it wouldn't require any changes to the host applications.
This creates a strong decoupling point, where the main application used an interface in a logging class library, but would know nothing about the packages or providers that the logging class library was using to do the real work.
When I did this and tried out using Loggly's nuget package and log4net, I found that the calling application had to have a ref to the nuget package or else the dependent assembly would not be copied to the build directory. At the time I just wrote this off as something odd that they Loggly engineers were doing. But I have since encountered the same behavior in some, but not all other packages. (DogstatsD doesn't have a problem, Raygun does, etc..)
I have noticed that some nuget packages in assemblies are automatically copied into the parent output directory, but when I look for the setting that controls this, I cannot find it.
I have written dozens of class libraries over the years, and I have never had a problem with 'chained dependency assemblies (a refs b, b refs c, etc.) resolving when I build. It only seems to be some nuget packages that are a problem.
How do I force nuget packages referenced by a class library project to copy into the build directory without an explicit reference in the application?

Ok, I figured this one out.
This is a problem only for the Log4Net & Loggly wrapper assembly combo in particular because it is referenced entirely at runtime. Log4net loads up its required log appenders at runtime, and because .net doesn't see a ref to the assembly at build time, it assumes that it isn't being used and omits copying the required assembly to the bin directory. The solution when you know this is simple, just write an empty dummy method in the referenced library that you can call in the main application. This will cause .net to include the assembly in the build.
While this problem is specific to the Log4net library, it could occur anywhere that you are using an assembly that is only used with runtime reflection.

Related

How to resolve "sub-assemblies" in ASP.Net Core

I have been working on a library which can perform various email actions with either EWS or MS Graph.
I had great success implementing EWS because of the limited amount of dependencies required for the EWS API. However when I implemented MS Graph into the library the amount of external dependencies grew ALOT, I went from (estimated) 2-3 external dependencies to 15-17.
(By external dependencies I am refering to dependencies which does not come standard with .Net framework 4.8)
All these depedencies are installed with the nuget package manager.
While I only had EWS implemented I had no issues to get ILMerge to merge the final DLL with the external dependencies, but as soon as I implemented Graph ILMerge would no longer work.
This is a problem as the library was originally developed for use in an application called Kofax TotalAgility (KTA), but this application would only store the single DLL as a blob in its internal database. This meant that when KTA read the dll for classes and methods (KTA gives an overview of available methods to be executed along with input and return parameters) the application would fail telling me that it could not resolve "Azure.Identity.dll".
After SO MUCH STRUGGELING to get ILMerge to work, but with no luck, I yielded and tried another approach.
I decided to develop an ASP.net Core application which would expose the methods in the DLL as a REST service, this in theory was an okay solution as KTA has excellent REST service support.
This new REST service worked on the premise that controllers would have post mappings for each method, and the controller would call my library.
Having the library external would be preferred as I might need it as standalone later.
Now having developed the REST API I created some Unit tests to test the different endpoints, to make sure everything worked as it should.
These told me that the EWS implementation still works as intended, but the Graph implentation throws an error: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Identity.Client, Version=4.39.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae'. The system cannot find the file specified. File name: 'Microsoft.Identity.Client, Version=4.39.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae'
I held my hopes high as the missing dependency was no longer the "Azure.Identity.dll", hoping that copying the missing DLLs to the IIS Application folder (next to the executable) would fix the issue, this however did not help anything.
I have since not been able to find any other solution.
I have thought about an internal assembly resolver which would be a C# event run everytime a dependency should be resolved. I would subscribe to this event in the class contructor, so that it would be ready whenever a method was called.
This probably would fix the issue in ASP.net Core, but it would not be fixed in KTA. If KTA should load classes and methods then the library is never "instantiated", I have tried doing the assembly resolve solution using a static constructor, but this does not help.
To sum up here is a list of things I have tried:
AssemblyResolver in a static contructor
RegAsm all the depedencies needed
Putting all depedencies next to the executable
Uploading all the dependencies to KTA Store
Merging all dependencies using ILMerge and ILMergeGUI
Embedding interop types (long shot, but I am desperate)
Trying to merge only dependencies that could not be resolved
Copying gacutil from my development machine to the target machine to use that instead of RegAsm (also a long shot)
Loading depedencies directly as an embedded resource
The optimal solution I am looking for is a library that is free of external dependencies.
But I can settle for a solution to my dependency problem in ASP.net.
I hope you can help.

Missing DLL's own dependencies in consuming projects

I created a class library project using C# and .Net.
In this project I used two external dependencies(to be more specific: Microsoft.Win32.Registry(4.6.0) and System.Data.SqlClient(4.7.0) Nuget packages).
After I build this project, I can see the generated DLL file under /bin/debug folder.
Now I want to import this generated DLL in another project and consume its methods. Once imported and I run this project, it complains about not being able to find those two external dependencies I had in class library project.
As a temporary fix, I can import these two missing references in this project and it will work fine and as expected. But this is not what I want(and I guess is not a clean solution as well).
I want to know why the dependencies of class library project is not reflected in generated dll file? And is there any way to fix this?
Many thanks for your help.
If your class library is in the same solution or source control repository as the app that's using it, you should use a project-to-project reference, rather than referencing the assembly directly. As the docs say, this way it automatically detects changes to the class library when you compile the app, but what the docs didn't say is that dependencies flow though as well.
Otherwise, as Lance Li wrote, you should create a NuGet package from your class library. Unfortunately there's a bit of a barrier to get started. Creating the package is easy, but then you need to publish the nupkg file somewhere. For early development (before the package is ready to be shared), the easiest option is to use a local file feed. You'll then need a nuget.config in the app that will use the package to add that local feed as a source, then you can install the package in your consuming project, which will bring dependencies.
As you can see, for development, this is slow and difficult because if your consuming app finds a bug in your package, or if you're trying to develop a new feature in both the consuming app and class library at the same time, it means every time you make code changes to class library, you need to increment the version number, pack a package, publish the package, then update the package version in the consuming project. It's far, far easier to use a ProjectReference which lets you simply edit code, compile, run. Nothing else to think about.
See this, the way you reference that assembly is not a recommended way when both the projects are in same machine.
You're using the file reference(Add reference => browse...). And that's why you have to import these two missing references in this project manually.
So I suggest you add the project reference, if both the two projects are in same solution, you can right-click current project=>add reference=>project tab find that assembly you need.(instead of browsing...)
If the referenced project is not in same solution. Right-click solution in solution explorer=>add existing project to import it. Then add project reference.

.NET custom dependencies (no nuget)

So, I'm having trouble adding a git project to my net Core solution, and after spending hours trying to figure this out and being uncapable of finding a solution online, I decided to ask here.
I have a forked github repo (link) in which I modified some files to suit my needs, but I simply can't seem to get it to work with my current project.
The problem I'm having is that normally, when I want a package for a .NET project, I usually simply go to nuget and fetch the necessary dependencies. This is usually very simple and straight forward. But now that I have these modified files, I'm unsure on how to proceed.
I have tried adding it as a submodule, but after I built the project, I got an exception saying that the dll could not be found.
Then I've tried adding the dll itself as a reference, but the ImGui.dll depends on a C dll which couldn't be found then (nor added to the project).
Finally, I've tried adding the csproj as a project of my solution, but that didn't work either
Do you know what am I doing wrong here? Am I missing a key piece or is it just something obvious I'm not seeing? It can't be this hard to get it to work
From the look of it, that repository produces a DLL (output type Class Library). So modify it to your liking, and use the sample program build (ImGui.NET.SampleProgram) to test your changes. Once you're happy, build the DLL project (ImGui.NET) and use the resulting DLL as a Reference in your own app.
In Visual Studio:
Solution Explorer>YourApp>References>Right Click>Add Reference...>Locate your DLL
This means you should also keep track of your modifications to the ImGui.NET project itself, since you may/will be required to maintain this in the future.
Hope this gets you started -- update your question with more specific issues once you're underway.
Edit:
Like #CoolBots mentions, I probably misread your question. Seems like the build depends on cimgui.dll, which you can hotlink from the ImGui repo along with your custom DLL. In fact, the demo app is using cimgui.dll, cimgui.dylib and cimgui.so. Regardless of linking method, you want the files to copy into your build folder. I don't believe subfolder /bin is necessary.
You can find all the cimgui dependencies for various operating systems in the ~/ImGui.NET/deps/cimgui folder.
The demo also utilizes NuGet packages Velrid and Velrid.StartupUtilities.
Depending on your own codebase, you may or may not require these NuGet packages along with the aforementioned class library.

loading multiple.net assemblies with same AssemblyVersion from a plugin

I'm in dll hell.
I'm building a plugin for a huge, ancient and very powerful software suite called ANSYS. They have a plugin framework. I had hoped that they would magically handle everything for me via AssemblyContexts or AppDomains or some other clever dotnet device that I don't understand. They do not.
The result is that I've created an application that depends on GRPC.core 1.16.0 via nuget. I wrote a little application that drives my plugin with a winform host. It loads and works perfectly, finding my library in ~/myproject/bin/debug/grpc.core.1.1.16.dll that exists right beside the class-library that is my plugin, no problem.
When I run my plugin in the ANSYS process space, which happens to also depend on grpc 1.0.0.0, the linker finds C:\Program FIles\ANSYS\...\WIN64\grpc.core.dll. No Good.
One odd thing about the Nuget GRPC package is that it adds a reference with a "reference version" of 1.0.0.0, where most other nuget packages have their reference version match the nuget package version. If i manually change the reference version the compiler wont find the library.
<Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad">
<HintPath>..\packages\Grpc.Core.1.16.1\lib\net45\Grpc.Core.dll</HintPath>
</Reference>
edit: the key is in the above line. The Nuget published Grpc.core artifact is at AssemblyInformationVersion=1.16.1.0, AssemblyFileVersion=1.16.1.0, AssemblyVersion=1.0.0.0. I logged this as a request against GRPC. More Below.
Thus I need to tell the runtime linking facilities not to use grpc.core...dll found in ANSYS's own binary directoryWhats more, there is exactly one dll (and its dependents) that I wish to load from my parent processes context: and that's ANSYS API dlls themselves, which are probably already in the GAC. In my project I've included this as a non-nuget reference with "build action: do not copy" selected.
So my questions:
is there something simple and easy I can do at runtime to tell the runtime-linker "when somebody loads a type from an assembly you think should be grpc.core, do not load 1.0.0.0, find 1.16.0.0 exactly"?
the runtime was already matching the needed library by "strong
name". The problem is that the 1.16.0 is a misnomer. That version
string was informational, but the assembly itself was version
1.0.0.0. Fusion was loading the library I wanted by exact match already.
is there something smarter I can do with appdomains or contexts or another C# device to explictly enter some kind of nested scope? Could I go so far as to log this as a bug in ANSYS's API?
I've tried digging into this myself, but I'm not a dotnet expert and finding out whether I'm looking at a nuget package configuration option --which isn't relevant to me, or an old-fashioned dotnet runtime option, has been very tricky.
update 1:
I've tried using AppDomain.CreateDomain, and it does indeed solve my problem, but it also requires me to provide a marshalling strategy for the already-loaded API objects. In other words, if you're programming against a plugin framework that has an api similar to:
public void DoMyPluginsFunctionality(ApiProvidedInputContext context){
var myPlugin = AppDomain.Create(
strongName: "MyCompany.MyPlugin.; Version=1.2.3.4 ...",
baseDirectory: "C:\\Program Files\\MyPlugin\\bin"
)
//success! MyCompany.MyPlugin loads the version of GRPC I want!
myPlugin.unWrapAsDynamicProxy().doFunctionality(context)
//error: No marshalling strategy and/or not serializable and/or swizzling errors
}
Then the runtime will require you to marshall (serialize) the context variable, because .net will not let you share memory across AppDomain boundaries.
So my new question:
- given I cant use AppDomains myself
- given that Grpc.core is always published as AssemblyVersion=1.0.0.0
What are my options?
Stop using newer features of GRPC.core and live in fear of my parent processes dependencies
use a strategy similar to shading. Is there something like shading in the .net world?
Edit the published binary's version metadata. Can I dynamically edit a published binaries version?
rebuild GRPC myself with the version string updated --effectively a private fork of GRPC.
update 2:
The GRPC build system seems like its quite large and well maintained, so I'm hoping I can simply build it and change a vcproj file to include an updated version string.
Unfortunately it also seems quite complex, and I haven't quite got the targeting/cross-compiling (x64 targeting x86) worked out.

How to deploy a DLL that is constantly changing?

I have created several small applications that use my own DLL. The problem is, this DLL is constantly changing. My current solution to this problem is that I have a Setup project in the class library solution that creates and registers the DLL. In all my applications I then have to open the solution and re-reference the newly created/registered DLL. Then I have to re-compile their setup projects, uninstall the old applications, and then re-install the new application.
There has to be a better way and I'm just not sure because I'm fairly new to all this. I have looked into ClickOnce but I don't think that will solve my issue as I cannot publish a class library. I have looked into checking version numbers but I must be doing something wrong because it doesn't work either.
I understand that once a DLL is created and being used in an application it should essentially not be touched. I do not have that option in this situation. It is constantly updated. Done.
So, is there a better way? A point in the direction of a guide or related question/answer/forum would be greatly appreciated.
Edit: The DLL is not constantly changing during runtime but it is constantly evolving to allow more functionality and detail within the other applications. Also, one big thing I guess I should have mentioned is the Public interface is constantly chaning - usually adding new methods.
Make sure the references to your DLL specify SpecificVersion=false. Then just deploy each new version into the GAC and that should do the trick.
Eventually, you can also manually force versions using Binding Redirection.
A solution you can try is to use a single solution for your project and reference the project wherever it needs to go.
Check out NuGet
You could set up an internal Nuget repository (really just a folder that stores nupkg files.) Then when you build a new DLL, you can update the apps as needed in studio. This would ensure it had the latest version. They shouldn't need a redployment unless there are bugs in the DLL that you're fixing.
One solution is as follows:
Physically separate the interface from the implementation. e.g. AssemblyA is the interface, the application (AssemblyB say) knows only the interface at compile time. The implementation (AssemblyC) also knows/references AssemblyA of course. The point being that AssemblyB does not reference AssemblyC. This will require you to use an IoC container (like MS Unity 2.0 but there are many others) in order to resolve and instantiate your concretes at runtime.
Write an update process that finds the new AssemblyC.dll, replaces the local copy and uses reflection along with the IoCContainer to 'load' the new implementation at what ever interval you require, typically app start up.
The above relies on your interface being stable. If it isn't, you may be able to write a (more) stable Facade.

Categories

Resources