Web development experience with .NET has never seen a drastic change like this since its birth day. Yes, I’m talking about ASP.NET 5 :) I have been putting my toes into this water for a while now and a few days ago, I started a new blog post series about ASP.NET 5 (with hopes that I will continue this time :)). To be more specific, I’m planning on writing about the things I am actually excited about this new cloud optimized (TM) runtime. Those things could be anything which will come from ASP.NET GitHub account: things I like about the development process, Visual Studio tooling experience for ASP.NET 5, bowels of .NET Execution Runtime, tiny little things about the frameworks like MVC, Identity, Entity Framework.
In this very exciting post, I would like to talk about build only dependencies whose code can be compiled into target project.
BIG ASS CAUTION! At the time of this writing, I am using DNX 1.0.0-beta5-11611 version. As things are moving really fast in this new world, it’s very likely that the things explained here will have been changed as you read this post. So, be aware of this and try to explore the things that are changed to figure out what are the corresponding new things.
Also, inside this post I am referencing a lot of things from ASP.NET GitHub repositories. In order to be sure that the links won’t break in the future, I’m actually referring them by getting permanent links to the files on GitHub. So, these links are actually referring the files from the latest commit at the time of this writing and they have a potential to be changed, too. Read the "Getting permanent links to files" post to figure what this actually is.
From the start of NuGet, it has been a real pain to have source file dependencies. There are some examples of this like TaskHelpers.Sources. When you install this package, it will end up inside your codebase.
The nice thing about this type of source dependencies is that you don’t need to fight with DLL hell. You can have one version of this package and your consumer can have another version of it. As the source files you pull down from NuGet has no public members, there will be no problems whatsoever as the code is compiled into their assembly separately. However, there are several problems with the way we are getting them in:
So, it wasn’t that good of an approach we had there but ASP.NET 5 has a top notch solution this problem: build only dependencies.
These are the kind of dependencies that you can pull in and it will just be compiled into your stuff. As you can also guess, it won’t be shown as a dependency. Let’s see an example!
One of the packages that support this concept is Microsoft.Framework.CommandLineUtils package. You can pull this down as a build-only dependency by declaring it inside your project.json file as below:
{ "version": "1.0.0-*", "dependencies": { "Microsoft.Framework.CommandLineUtils": { "version": "1.0.0-beta5-11611", "type": "build" } }, // ... }
Notice the type field there. That indicates the type of the dependency. Let’s stop here and without doing anything else further, run dnu pack to get a NuGet package out. When we look at the manifest of the generated NuGet package, we won’t see any sign of the build dependency there:
Makes sense. Let’s peak inside the assembly now.
That’s what I expected to see. All the stuff distributed with that packages is compiled into my target assembly. As you can guess, I can use these stuff inside my project without any problems:
using Microsoft.Framework.Runtime.Common.CommandLine; namespace AspNet5CommandLineSample { public class Program { public void Main(string[] args) { var app = new CommandLineApplication(); } } }
You may ask that ASP.NET 5 applications can work without assemblies on disk. That’s true and at that point, this will end up being compiled into the target assembly in-memory.
If you look at what I committed to my source control system, it’s barely nothing which solves one of the biggest pains of source packages.
Generating libraries which can be consumed as a build only dependency is also fairly simple but there are some little things which doesn’t make sense. Assuming I have a library called AspNet5Utils and it has the following internal type:
namespace AspNet5Utils { internal static class StringExtensions { internal static string Suffix(this string value, string suffix) { return $"{value}-{suffix}"; } } }
If you want this type to end up as a build dependency, you need to declare this as shared inside the project.json file.
{ "version": "1.0.0-*", "shared": "**/*.cs", "dependencies": { }, // ... }
Doing this will give a hint to dnu pack command to pack these types into the shared folder inside the NuGet package.
Notice that there is also an assembly generated there. Maybe there is a reason behind why this is there but as I don’t have any type which ends up inside an assembly, I would expect this to not have one at all. Indeed, if you decompile the assembly, you will see that nothing is there:
In order to consume this package, you don’t actually need to distribute this through NuGet if you only want to consume this inside the same solution. As the dependency consumption is unified in ASP.NET 5, this can easy be a project dependency as you would expect:
{ "version": "1.0.0-*", "dependencies": { "AspNet5Utils": { "version": "", "type": "build" } }, // .. }
In my opinion, this is one of the many powerful and yet simple concepts that ASP.NET 5 has brought to us. Enjoy!