How to enable forward compatibility on a reusable .NET library? - c#

I'm in the process of creating a new minor release of a toy project of mine. This project is released on NuGet and is compatible with .NET 4.0 and up. Some of the new features I'm introducing require .NET 4.5 (users should be able to resolve IReadOnlyCollection<T> and IReadOnlyList<T>, both interfaces that were introduced in .NET 4.5), but I need to keep the project compatible with .NET 4.0, since not all developers can easily migrate to the latest .NET framework.
So the problem I’m facing is how to solve this ‘forward-compatibility’ problem. There are two solutions I’ve thought about, but both are not very attractive, so hopefully anybody can give me some ideas or guidance here.
Here are the two solutions I came up with:
Solution 1: Use #if compiler directives and build a DLL per .NET framework version and ship those versions using the NuGet packages and download at the project site.
Downside of this method is that when developers update their Visual Studio project from .NET 4.0 to .NET 4.5, they don't automatically get the .NET 4.5 version (with .NET 4.5 specific features). This violates the Principle of least astonishment and would leave developers dazed why the feature is not working, when they try using it a few months later.
Solution 2: Use one single DLL and emit type's on the fly that implement both new interfaces when they exist in the current app domain. This allows shipping a single DLL to the user and allows features to come available when the developer switches .NET framework versions in their project. This will make things 'just work'. This is the direction I’m currently heading btw.
Since I need to return a type that needs to implement the interfaces, the downside is that that type must be created at runtime using Reflection.Emit, ModuleBuilder, TypeBuilder, and the like. This is seriously nasty shizzle. But besides that, since this type must be created in a new (anonymous) assembly, I must make some internal types public (a type it needs to inherit from and an interface it needs to implement). Making those internal types public pollutes the API of the project and will disallow me from making changes to those types.
I believe these are my options, but I might be missing something obvious. So my question is, am I missing a possibility? Is there a way to circumvent the problems for solution 1 or would it be better to go with the hardcore root of runtime type emitting?

Have you thought about another custom assembly with the missing items in it? Then you test if a type/method exists (that would only exist in .net 4.5) and if it does, you load the assembly in.
That way you can keep the exact same methods and classes, and save yourself pain of doing all of that crazy emit (not to mention the perf hit you will take if you find yourself doing that much).

I have a project called Dynamitey that allows you to load a type at runtime and called it's static methods and constructors with the DLR. Which would be much less messy than a lot of reflection or emitting code to load an api that is not necessarily available.
dynamic bigIntType = new DynamicObjects.LateType("System.Numerics.BigInteger, System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
if (bigIntType.IsAvailable)
{
var one = bigIntType.#new(1);
var two = bigIntType.#new(2);
Assert.IsFalse(one.IsEven);
Assert.AreEqual(true, two.IsEven);
var tParsed = bigIntType.Parse("4");
Assert.AreEqual(true, tParsed.IsEven);
}
I also have a project called ImpromptuInterface, that will emit proxy types for interfaces around objects that duck callable match it (also uses DLR).
var targetType =Type.GetType("System.Collections.Generic.IReadOnlyList`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
var list = new List<string>{"lala", "la","lala"};
object readonlyList;
if(targetType != null){
readonlyList = Impromptu.DynamicActLike(list, targetType);
}

Related

Could not load file or assembly System.ValueTuple Version=4.0.3.0

I am getting the following runtime exception, except I think my case is different than the dozen or so I've seen so far, most of which are a couple years old.
Could not load file or assembly 'System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
We had been using System.ValueTuple and ran into these common issues back when they first incorporated the library into the core language. Since then we have removed all references to the old nuget package and all projects target 4.7.
Since then we have been successfully using the ValueTuple constructs throughout our solution without any problems. Until now, I have a specific call that fails, while other calls that return a ValueTuple succeed, from within the same method. I don't know what the difference could be since all the calls use custom objects serialized across a SignalR hub through an interface like:
Task<(MyObject myobj, Guid myguid)> GetStuffd(Guid id);
I bumped all our projects to 4.7.2. I removed every binding redirect in every app.config file. Still nothing. And since other ValueTuple calls work from the same project, I don't think I'm on the right track with these kinds of solutions. Any ideas?
The problem was actually server-side and removing the binding redirect from my host service easily solved the problem.
It's worth noting that a new .Net Standard 2.0 library was the catalyst here. My call down into a .Net Standard class library is what prompted the issue. This is what was different from other calls already using ValueTuple.
Clearing the binding redirects was indeed the solution after all.

Decompiling .NET assemblies only shows throw null; [duplicate]

I have a project that uses System.Runtime.Serialization assembly. I am using the type DataContractSerializer from that assembly, but I have a problem.
There are two assemblies:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\System.Runtime.Serialization.dll
C:\Windows\Microsoft.net\Framework\v4.0.30319\System.Runtime.Serialization.dll
Both of them have the same version - v4.0.30319. The first one have 429kb size, and the second one 1037kb. I used reflector to see the list of classes, and the first one doesn't have the class that I need (DataContractSerializerSettings). However, the second one does have it.
Why are there some big difference in size and classes for that assembly? Will it be ok, if I use the second one, instead of the first?
.NET version 4.0 made a big change in the way framework reference assemblies are done. Previously, the reference assembly was a simple copy of the runtime assembly, the one stored in the GAC. That however caused some painful problems. Notable is the WaitHandle.WaitOne(int) overload, it was added in the .NET 2.0 Service Pack 2 update (aka .NET 3.5). Programmers used it without noticing that it was an added method, the mscorlib assembly version number was still 2.0.0.0. But then discovered their program failed when running on an unpatched version of .NET 2.0. Very nasty kaboom, MissingMethodException without a hint why such a common method could be missing.
To prevent this kind of breakage, the .NET 4.0 reference assemblies are kept separate, in the "%programfiles%\Reference Assemblies" directory as you found out. And they are special assemblies, they only contain the metadata with all the IL stripped out. Which is why the assembly is so much smaller.
Microsoft now can improve the .NET 4 code and add public classes and methods without causing this kind of breakage. And have done so profusely, updates 4.01, 4.02 and 4.03 have shipped since the original 4.0 release.
The reason you are having trouble with the DataContractSerializerSetting class is thus easily explained, it just doesn't appear in the reference assembly. It got added, probably in one of those incremental updates. And you should not try, your program will break on a machine that doesn't have the update. You should wait until .NET 4.5, the version that added it to the reference assembly. You can invoke DLL Hell if you really want to.

How do I create and use a .NET metadata-only 'Reference Assembly'?

Since version 3.0, .NET installs a bunch of different 'reference assemblies' under C:\Program Files\Reference Assemblies\Microsoft...., to support different profiles (say .NET 3.5 client profile, Silverlight profile). Each of these is a proper .NET assembly that contains only metadata - no IL code - and each assembly is marked with the ReferenceAssemblyAttribute. The metadata is restricted to those types and member available under the applicable profile - that's how intellisense shows a restricted set of types and members. The reference assemblies are not used at runtime.
I learnt a bit about it from this blog post.
I'd like to create and use such a reference assembly for my library.
How do I create a metadata-only assembly - is there some compiler flag or ildasm post-processor?
Are there attributes that control which types are exported to different 'profiles'?
How does the reference assembly resolution at runtime - if I had the reference assembly present in my application directory instead of the 'real' assembly, and not in the GAC at all, would probing continue and my AssemblyResolve event fire so that I can supply the actual assembly at runtime?
Any ideas or pointers to where I could learn more about this would be greatly appreciated.
Update: Looking around a bit, I see the .NET 3.0 'reference assemblies' do seem to have some code, and the Reference Assembly attribute was only added in .NET 4.0. So the behaviour might have changed a bit with the new runtime.
Why? For my Excel-DNA ( http://exceldna.codeplex.com ) add-in library, I create single-file .xll add-in by packing the referenced assemblies into the .xll file as resources. The packed assemblies include the user's add-in code, as well as the Excel-DNA managed library (which might be referenced by the user's assembly).
It sounds rather complicated, but works wonderfully well most of the time - the add-in is a single small file, so no installation of distribution issues. I run into (not unexpected) problems because of different versions - if there is an old version of the Excel-DNA managed library as a file, the runtime will load that instead of the packed one (I never get a chance to interfere with the loading).
I hope to make a reference assembly for my Excel-DNA managed part that users can point to when compiling their add-ins. But if they mistakenly have a version of this assembly at runtime, the runtime should fail to load it, and give me a chance to load the real assembly from resources.
To create a reference assembly, you would add this line to your AssemblyInfo.cs file:
[assembly: ReferenceAssembly]
To load others, you can reference them as usual from your VisualStudio project references, or dynamically at runtime using:
Assembly.ReflectionOnlyLoad()
or
Assembly.ReflectionOnlyLoadFrom()
If you have added a reference to a metadata/reference assembly using VisualStudio, then intellisense and building your project will work just fine, however if you try to execute your application against one, you will get an error:
System.BadImageFormatException: Cannot load a reference assembly for execution.
So the expectation is that at runtime you would substitute in a real assembly that has the same metadata signature.
If you have loaded an assembly dynamically with Assembly.ReflectionOnlyLoad() then you can only do all the reflection operations against it (read the types, methods, properties, attributes, etc, but can not dynamically invoke any of them).
I am curious as to what your use case is for creating a metadata-only assembly. I've never had to do that before, and would love to know if you have found some interesting use for them...
If you are still interested in this possibility, I've made a fork of the il-repack project based on Mono.Cecil which accepts a "/meta" command line argument to generate a metadata only assembly for the public and protected types.
https://github.com/KarimLUCCIN/il-repack/tree/xna
(I tried it on the full XNA Framework and its working afaik ...)
Yes, this is new for .NET 4.0. I'm fairly sure this was done to avoid the nasty versioning problems in the .NET 2.0 service packs. Best example is the WaitHandle.WaitOne(int) overload, added and documented in SP2. A popular overload because it avoids having to guess at the proper value for *exitContext" in the WaitOne(int, bool) overload. Problem is, the program bombs when it is run on a version of 2.0 that's older than SP2. Not a happy diagnostic either. Isolating the reference assemblies ensures that this can't happen again.
I think those reference assemblies were created by starting from a copy of the compiled assemblies (like it was done in previous versions) and running them through a tool that strips the IL from the assembly. That tool is however not available to us, nothing in the bin/netfx 4.0 tools Windows 7.1 SDK subdirectory that could do this. Not exactly a tool that gets used often so it is probably not production quality :)
You might have luck with the Cecil Library (from Mono); I think the implementation allows ILMerge functionality, it might just as well write metadata only assemblies.
I have scanned the code base (documentation is sparse), but haven't found any obvious clues yet...
YYMV

How do I add a reference to an assembly that wasn't built with the Silverlight Runtime?

I am looking for a possible solution where I can add ShapeMap.dll as a reference,
but when I try to add the reference I get an error stating:
You can't add reference to ShapeMap.dll, as it was not build against the Silverlight runtime. Silverlight projects will only work with Silverlight Assemblies"
What do I do now?
While Silverlight code may look and smell like good old .NET-backed logic, the runtime for Silverlight is different from that supporting regular .NET applications.
It is useful to think of the Silverlight runtime as a subset of the .NET runtime: Silverlight is meant to run in a "sandbox" whereby many the unsafe features such as direct access to the file system are not allowed.
For this reason, one can only add Silverlight assemblies to a Silverlight project.
The error you're getting is therefore as said: the version of ShapeMap.dll you have wasn't build for Silverlight runtime.
There are two ways out of this situation:
find or build a Silverlight-backed version of the DLL
somehow refactor the Silverlight application so that it leverages the features of the DLL by way of WebServices (if that makes sense, for the name ShapeMap.dll indicates that this may deal with UI objects which are hard/impossible to deal with remotely)
To get a Silverlight-backed version of the DLL:
First choice: It may just be that you can get the binary of the Silverlight version of the assembly where you found the .NET version.
Second choice: it may be that you can get the the source code of the [.NET targeting] DLL.
If so you can try -and I stress "TRY"- to make a Silverlight assembly out of it. The problem may be that the source code uses .NET-only idioms/API calls and you'll then need to convert these; several just .NET-to-SL "gotchas" can easily be converted, others are absolute roadblocks (eg. direct access to the file system, registry etc.), although, it may be possible to merely comment-out the offending sections of the code, if, somehow the Silverlight was not going to use the underlying features of the DLL.
Now... for sake of full disclosure...
there are a few techniques which allow "fooling" Visual Studio so that .NET assembly be accepted within a SilverLight project. See for example "Reusing .NET assemblies in Silverlight". Beware, however, that while very insightful as to the nature of the .NET and Silverlight runtimes, and possibly useful in some cases, these techniques are undocumented and, more importantly, depending on the subset of .NET API used by the DLL, may merely allow to get over over the build-time blocking, to fall into various run-time errors (when/if the application makes calls into the DLL to methods which in turn invoke .NET-only methods of the runtime).
If you have access to the source files for that assembly (dll), create a new Silverlight Class Library project and add all the existing source files to your new project. Then attempt to build the project. Depending on the amount of dependencies you may succeed in building a silverlight compatible version of the assembly.
If you don't have the source code, then sorry you're out of luck.
Silverlight works in a "subset" of the .net framework, some stuff is organized differently and works not like a regular WPF application (like that everything needs to be async in order to keep the UI responsive). You can see it as a "protected" .net environment, and therefor you may not reference or use non-silverlight dll's.
Like the previous answer states, use the source code and copy paste it into a SL library project, compile, and use that.

.Net Add-ins and versioning

Our media center add-in is shipped as a single DLL which lives in the GAC (mediabrowser.dll), we allow users to write extensions for our add-in by referencing our DLL and accessing the pre-defined extensibility points.
On load we search through a plug-in directory, load all the assemblies in the directory, search the assemblies for a type implementing IPlugin and execute initialiaztion routine on an instance of the plugin. I am aware that this is not the most robust design (for example: we may want to look at appdomain isolation for plugin later on) but it works alright now.
As it stands, this seems to be working fine, except for one big caveat.
When plugin writers compile their plugins the plugin references mediabrowser.dll with a specific version. Later on when we revise our dll (to fix bugs or add features) all addins that were written against earlier versions of mediabrowser.dll break.
I have thought of a few solutions to this problem (note the assembly is in the GAC):
Ship a publisher policy with with mediabrowser.dll that will redirect all earlier compatible versions of mediabrowser.dll to the current version (this must also live in the GAC).
Ship a separate assembly which contains all the fixed extension points and contracts, be extra prudent about changing this assembly, have plugin writers link against this assembly. (but still look at using publisher policies for non-breaking changes to the interfaces)
Let a third party worry about this stuff and leverage MEF or some other framework that takes care of this kind of stuff.
Hookup AppDomain.CurrentDomain.AssemblyResolve and resolve the earlier versions of the assembly to the current version. This will only work if the assembly of that specific version is not in the GAC.
Are there any other solutions to this problem that I am missing?
Update I ended up going with option 4.
I see you have picked an answer but if you are still open to ideas there is another option to consider (the very one used by the .NET framework): do not increment your assembly version between builds (but do increment your assembly build number).
This will allow your assembly to retain it's same strong name, not breaking plugin compat, and still allow you to distinguish builds from each other (using the assembly build number).
You can see this in action in .NET 2.0 through 3.5. Those releases all use the assembly version 2.0.50727 but have distinct build versions.
As long as you do not break your interface contracts (which you should never do anyway) this approach is quite reasonable.

Categories

Resources