MEF dependency questions - mef

Simple example.
Application A has a class library C which is used through out.
Application A uses MEF to discover and load plug in modules P1 and P2 from a plug in directory. One assembly per plug in.
P1 and P2 both have a dependency on C (The class library).
The build process will ensure the version of C used by P1 is identical to the version referenced by A.
I assume that I won't end up with multiple copies of the same assembly loaded at once? By default I end up with a copy of C in the Plugin directory as well as A's application directory.

To ensure you don't end up with duplicate assemblies, you could change the Copy Local property to false of the contract (C) library in your plugin projects, that way on build, it won't be copied to the output directory.
You should be fine I think, as the CLR won't load the same assembly twice thanks to the Fusion loader rules - the first being to see if the target assembly is already loaded in the AppDomain. BUT, you have to be careful, because any code using Assembly.LoadFrom may result in exceptions occurring if it is actually finding that the assemblies have different locations on disk.

Related

NuGet: references to assemblies in runtimes folder not added

I have a project that targets two different operating systems/frameworks:
net461 on Windows and
netcoreapp2.0 on OSX
I'm trying to figure out how to correctly package this for NuGet. According to this post I should be able to package them like this:
/runtimes/win/lib/net461/myassembly.dll
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll
By when I add the NuGet package to another project, the packaged assemblies aren't added as references to the target project.
Then I read somewhere that you also need to add reference libraries to the /ref folder so I tried this:
/runtimes/win/lib/net461/myassembly.dll
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll
/ref/net461/myassembly.dll
/ref/netcoreapp2.0/myassembly.dll
In this case the assemblies get added as a reference to the target project and I can build it, but the required assemblies aren't copied to the output folder.
The documentation on all this is extremely vague and I'm fairly lost.
What am I missing?
Associated NuGet Issue: https://github.com/NuGet/Home/issues/7316
Update: I've put together a sample project that demonstrates what I'm trying to achieve. In particular see the bottom of the readme, titled "NuGet Packaging".
This is what I've finally figured out/guessed (because as best I can tell there's no official documentation for some of this)
Files added to the /runtimes folder aren't automatically added as references to the target project.
The /ref and /runtime folder should be used in conjunction with each other and only for the .NET Core target. As best I can .NET Framework targets apparently don't support these folders.
The /ref folder is for compile time references and anything added here will be added as a reference to the target project.
Assemblies in the /ref folder don't need to have an implementation - every public API could just throw a not implemented exception. In practice however you typically just take a copy of one of the implementation assemblies and declare it as the compile time API.
I've read (but haven't tested myself) that assemblies in the /ref folder must be "Any CPU" builds. You can use CorFlags utility to patch an implementation assembly for this if necessary.
The /runtimes folder is used to provide an implementation assemblies for any references included in the /ref folder. These assemblies are used at runtime and during deployment.
The /runtimes folder can include additional assemblies that are only required at runtime and don't need to be seen by the client project. These additional assemblies won't be included as references in the target project but will be available for run/deployment.
As mentioned by others, the files in the /runtimes folder aren't copied to the output folder of the build. Instead config files are placed there that tell the runtime how to locate the /runtimes files from the NuGet cache.
For .NET Framework targets (ie: net461) just use the /lib folder as there's no other runtimes for .NET aside from Windows anyway.
Putting this all together, my original example, should have looked like this:
/lib/net461/myassembly.dll (net461/Windows Compile and Runtime)
/runtimes/osx/lib/netcoreapp2.0/myassembly.dll (netcore/OSX Runtime)
/runtimes/win/lib/netcoreapp2.0/myassembly.dll (netcore/Win Runtime)
/ref/netcoreapp2.0/myassembly.dll (netcore/* Compile Time)
I spent a fair amount of time trying your project on OSX in both Visual Studio for Mac and VS Code. I'll try to stick with factual observations without getting into "why don't you do X instead".
The runtimes/{rid}/lib/{tfm}/*.dll paths look ok
target="lib/{tfm}/..." assemblies are automatically referenced, runtimes/... are not
Using target framework of netstandard seems like it would make your package work in both netcoreapp and netstandard projects (e.g. use target="lib/netstandard1.6/..."). Compare with this
runtimes/ seems to be intended for platform-dependent assemblies you'll load at runtime. For example, 32/64-bit native assemblies in runtimes/win-x64/native/ and runtimes/win-x86/native/) loaded with AssemblyLoadContext (another post by McMaster)
Using separate slns for Windows and OSX, or separate platform-specific projects that reference platform-agnostic projects (like Xamarin) would obviate some of the configuration wrangling
I found no documentation on target="ref/...", but you can add Explicit Assembly <references> (inside the nuspec <metadata> block)
Packaged assemblies won't appear in the output directory, but when prepared for distribution with dotnet publish they'll be included:
.NET Core and .NETSTANDARD don't copy dependencies to output directory, they are mapped using deps.json which points to relative paths from local NuGet cache.
This has been a very useful thread to get more information and hints on how to create a NuGet package that references native DLLs, and is consumed in both .NET Framework as well as .NET Core / modern .NET libraries / applications.
My experience so far has been that if this library (let's call it library A) only targets .NET Standard, consuming this library in a .NET Core / 5.0 or 6.0 application does lead to the native assemblies being pulled in correctly from the runtimes folder. In a .NET Framework 4.7 application however, this does not appear to be the case. Unless the runtime is explicitly specified when compiling, e.g.:
dotnet build ... --runtime win-x86
When using library A in a .NET Core or .NET 5.0/6.0 application however, this runtime identifier is not required - all runtimes are made available and the right one is selected at runtime.
If you want library A to be consumed in applications that also target .NET Framework, and you don't want the user to have to specify the runtime explicitly, then it seems to be necessary to:
Target both .NET Standard and .NET Framework
Ensure that the native assemblies end up in the following folder structure in the NuGet package:
"lib/{tfm}/..."
While the .NET documentation referenced by tm1 earlier here talks about how to get this to work using nuspec files, it is less clear how to do so in the SDK .csproj format. I managed to do this in the NLoptNet project, see the relevant .csproj file here. Final relevant point (in addition to the two bullets above):
Use "<None Include" rather than "<None Update" to add the native assemblies
So far - this works, but there is one quirk - as you can see here it generates warning MSB3246 when consuming library A in a .NET Framework application. See also this Stack Overflow post. This leads me to believe that maybe the above is not the right approach, and therefore to some questions:
Is this the intended way to consume library A in .NET Core, modern .NET and .NET Framework applications?
Should one always specify the runtime identifier when using dotnet build / dotnet test?
Can you try to target .NET Standard 2.0 instead of net461 and netcoreapp2.0? Libraries built against netstandard2.0 should work with .NET Core 2.0 and .NET Framework 4.6.1: https://learn.microsoft.com/en-us/dotnet/standard/net-standard
Are you using the new csproj format? If so it has built in support for multiple target frameworks.
For example running dotnet pack against a .csproj file with this content:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.1;netstandard2.0</TargetFrameworks>
</PropertyGroup>
</Project>
will result in a .nupkg that works for .NET Framework 4.6.1, .NET Core 2.1 and .NET Standard 2.0.
Various trick can then be used to include specific parts for each framework depending on what's available.
I'm trying to solve the same problem.
The solution proposed by you works fine, but there is one question ...
The case of Win and net46 is clear. And now I need to add a reference to the assembly in the project for a netcoreapp for the Win and for Linux. The problem is that this is a DIFFERENT assembly with the SAME name. Those my package looks like this:
/lib/net461/myassembly1.dll (net461/Windows Compile and Runtime)
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll (netcore/Ubuntu Runtime)
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll (netcore/Win Runtime)
/ref/netcoreapp2.0/???
Update: Actually, the myassembly1.dll and myassembly2.dll are both called myassembly.dll. But to show that one is assembled for Windows, and the second one for Linux, I will leave here such a name.
The most interesting thing is that I tried to put any assembly in the folder ref, and it works on both Windows and Linux.
This version works on both systems
/lib/net461/myassembly1.dll
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll
/ref/netcoreapp2.0/myassembly1.dll
And this too
/lib/net461/myassembly1.dll
/runtimes/ubuntu/lib/netcoreapp2.0/myassembly2.dll
/runtimes/win/lib/netcoreapp2.0/myassembly1.dll
/ref/netcoreapp2.0/myassembly2.dll
But I think this is not right and I was wrong somewhere.

CRM could not load file or assembly to my project

I added a reference itextsharp.dll to my plugins project, when running my plugins using plugin-registration tool I get this exception:
Could not load file or assembly or one of its dependencies. The system
cannot find the file specified
I tried removing the ref and adding it again, cleaning and then adding it to my project from different places.
Is there restrictions in plugin registration tool about adding non crm dlls? why ? how to solve it?
This is not going to work - you cannot reference external assemblies from CRM plugins that are registered in database. If you want to do this, you will have to merge your external dll with your plugin assembly. You have to remember that adding assembly as reference is not automatically making your referenced assembly available for your base assembly, therefore if you register your plugin assembly in CRM, system is not going to "magically" find somewhere your external assembly (in your case - "itextsharp.dll"). If this is not Online system, you can add your assembly to GAC, or register all your assemblies on Disk instead of database (not recommended approach). If you want to register them in database, you will have to merge everything in one assembly using ILMerge for example.
You can't reference something in a plugin unless it's in the bin of the CRM.
To make it work you need to ILmerge your reference with the plugin. Install this package in your project: MSBuild.ILMerge.Task. Then build. It will work instantly. The package will merge everything in the bin after the build. So make sure every other references are marked "Copy Local = false". Otherwise, you'll have a crazy big assembly.
Finally, Microsoft released a solution for this. You can build a nupgk file and register dependent assemblies.
Here are the white paper and my post about this;
Microsoft : Microsoft White Paper
My summary: Here is the link

sbt multi project: create resource in another sub-project

I have an sbt project with two sub-projects, A and B. A produces a standalone scala-based executable exe. When exe is run, it will produce a file out.xml. I want this file to be part of resources for project B. I do not want B to include any references to A's code, all I want is the out.xml file to be part of it. I suspect that http://www.scala-sbt.org/0.13.5/docs/Howto/generatefiles.html should be a good starting point, but I can't get my head around on how to split it between two projects. Any takers?
Since A is a dependency of the build process, which needs to run the executable to generate your xml file you would list it as a libraryDepencency in project/[something].sbt or project/project/[something].scala. This would make it available to code you put in build.sbt or project/[something].scala but not make it a transitive dependency of the resulting artifact of project B.
(Or you could of course make project A a sbt-plugin itself, or create yet another project which is a plugin depending on A that runs the executable.)

How to force p2 to set singleton=false in bundle iu definition

I'd like to force p2 so set singleton='false' to some of my bundle that are defined as singleton in their manifest.
I know that multiple singleton plugins cannot be activated at the same time in an Eclipse runtime, but I would like them to be installed (no in the OSGI sense), meaning present on the disc. I have implemented my own configurator to activate the required bundle according to some license file.
I have tried to modify the content.xml manually to add the singleton='false' attribute to all my bundle IUs and it works fine. But I'd like to know if there is a way using a p2.inf file ?
Thanks for any lead.
The standard publishers that come with p2 cater for the standard Eclipse installation use case. The aim is to create an installation that will run, and fail the installation if it would fail at runtime. Therefore, I'd be surprised to see support for your use case in the standard p2 publishers.
Note however that you are free to write your own publishers. p2 (i.e. the entire project, not just the p2 publishers) aims to support all kinds of different installation scenarios and not only OSGi/Eclipse installations.

Referencing a Compiled Business Layer in a DotNetNuke Module

I was wondering if it is possible to create a DNN module that references a compiled business layer, instead of using just the data provider approach that's included in the default project. How would such a project be deployed? Just copy the compiled assembly that I'm referencing into the website bin folder?
Yes, you can reference assemblies in the bin from your module's project.