Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Using and Debugging External Source Code Packages in ASP.NET 5


:P
On this page:

bugIn my last ASP.NET 5 post I talked about using the new strongly typed Configuration system in ASP.NET 5. While I was working with the example code, I ran into a DNX runtime library bug that caused Enum values to not get parsed properly from the configuration data into the strongly typed classes. My first instinct was to take a look and see why this particular code wasn't working.

One of the really cool aspects of the new DNX runtimes is that project dependencies are loaded from  'packages', and these packages can be either loaded from NuGet or from a source code location. As long as the source code follows the packaging rules – a root src folder with src with subfolders that contain project.json files – those packages in the folder can be discovered and used directly from source code, just like a package downloaded from NuGet. Thanks to the Roslyn compiler technology, compilation is now fast enough to make it possible to compile code on the fly, and that's what makes it possible to go directly from source code to executing in memory code without a requirement for intermediate physical assemblies.

Now that ASP.NET is fully open source, we can pull down a project from GitHub as source code, point the project package configuration to look at the root folder and then add the package as source code in your own project. Once you do this you can use the debugger to step into the code and even make changes to the underlying code – the project essentially becomes part of your own project. This takes the sting out of trying to check out external source code in your own applications.

In this post I'll demonstrate how you can debug and change an external Microsoft package from source code and integrate it into your own project with only a few simple steps. The cool thing about this – in terms of what we're used to in full framework .NET – is that this is soooo much easier to do with the DNX and this feature opens up a host of new capabilities and potential for much richer sharing of code.

Let's see how this works.

Setting the Stage – A Bug in Microsoft Runtime Library Code

So to set the stage here in my last post I was talking about strongly typed configuration values and I had set up a strongly typed class with some complex configuration values in the class. DNX can map the configuration store values from the source store to the strongly typed classes. However, I ran into bug in one of the Microsoft runtime libraries as  I found that Enum values were not properly parsing from the config file.

To sum it up: I have a configuration class like this:

public class LoggingConfiguration
{
    public LogModes LogMode { get; set; } = LogModes.TextFile;
    public string LogFile { get; set; } = "~/logs/ApplicationLog.txt";
    public string ConnectionString { get; set; }
    public int LogDays { get; set; } = 7;
}

I then inject the configuration class into a Controller class:

public HomeController(IOptions<AppSettings> appSettings,
                      IOptions<LoggingConfiguration> loggingConfig)
  

and then try to read it from within the controller code:

var mode = LoggingConfiguration.Options.LogMode;
string logFile = LoggingConfiguration.Options.LogFile;
int days = LoggingConfiguration.Options.LogDays;

For more info on how this all works see the previous article, but long story short all configuration values properly set from the config.json file that holds the custom config values, except for the Enum value which is never set from the config file. There's a bug in beta4 that fails to parse the Enum value.

Let's see what the problem is by looking at the source code.

The Hard Part – Finding the right Project

So I know the problem is in one of the Microsoft runtime libraries. It would seem logical that you find the code for this in the Configuration project. This is where I started, but after browsing through the source code I quickly found that it doesn't contain anything related to actual assignment of resources. The Configuration pieces deal with retrieving content from configuration stores and parsing them into key value pair based dictionaries. The actual project that holds the code responsible for taking the dictionary data and assigning it to an options model is the Options project. Yeah, totally obvious that!

I managed to find the code by guessing at a few projects and doing a search – in this case for SetProperty() (initially) and SetValue(). Like I said finding the right project to import is the hardest part and the best way to find is likely to blast out a question on Twitter and have somebody from the team tell you were stuff lives or else plan spending a while browsing source code and guessing at where stuff lives.

Downloading the Source Code Project

With the hardest part out of the way and the right project in place lets see how we can use that project in our code. First clone or fork the project from GitHub.

git clone https://github.com/aspnet/Options.git

which gives you a local copy of the project.

Because these libraries are actively worked on you'll want to switch to a version of the branch that matches the version you're running with in my case the Beta 4 tag. The ASP.NET team is tagging milestones so it's easy to jump to the correct place in the repository by jumping to that tag. In the future you may be able to more easily use newer branches but in this case there has been a complete overhaul of the options library and the newer code would not run.

So to switch to the right code base switch to the beta 4 tag:

Branches

Or from the Command Line:

git tag -l

to list all tags and find the Beta 4 tag:

git checkout tags/1.0.0-beta4

And I'm now on the active codebase that matches the version I'm running.

Linking the Source Code to your Project

If you want to add the library into your project now you need to first establish a project link by pointing at the src  folder of the package. Note that the src folder can contain multiple projects (sub-folders with project.json files in them) and each of those become an available package that you can import into your project.

But first you have to let the compiler know where to look for packages – or source code based packages in this case. You need to let it know where to find this new 'package source' we want to link to our project.

The place to do this is the global.json file which lives at the solution root folder (the one that sits above the src folder) and which is global to the entire solution. In there you add the path to the src folder to the projects array:

SourceLink

The packages found at this folder take priority over NuGet packages loaded from the NuGet feeds, so effectively local packages and source code trumps NuGet packages coming from your feed. Keep this in mind because this can have some unintended consequences if you carry an older codebase that you've patched forward – it can affect not just the immediate code and project you changed but all those projects in the solution.

Adding Source Package References

The running application already has a reference to the Options library as it's a dependency of some other component. IOW, there's already a reference to it in my project – somewhere. I couldn't actually find what references it – there are too many freaking packages and no way to search the tree – this is something badly needed Microsoft (here's a User Voice). Anyway there's a reference *somewhere* already and because the package is referenced in the local package sources you don't have to do anything to get it to run from source code.

However, if you want to debug the code you'll need to explicitly add the Reference to your DNX project so the project gets pulled into your solution to debug. You do this by adding it to the dependencies node like any other package:

"dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-beta4",
"Microsoft.Framework.OptionsModel": "1.0.0-beta4" },

Note that when you type inside of Visual Studio you'll get version completion. Visual Studio will peak into all of the project.json files in the src folder and provide Intellisense for each of the projects available. In this case there's a single package, but if you had multiple packages they would all show up for autocompletion. Source code packages are treated with the same level of integrity as binary packages and that's awesome!

Once you've added the project.json entry you now have a referenced project in your solution. Visual Studio will add the project to your existing project list and show the project as source project in the references section:

Library Project

As well as the project added into your solution:

AddedProject

You can now select the code you want to debug. In my case the code that's the problem is in OptionsServices.cs, so I set a break point in the code and go ahead and run it:

DebuggerCode

And sure enough I can now debug into the DNX runtime component. As you can see the debugger works and I can step through the Microsoft code like any other code, which in effect it is. It's simply an external project reference. What's new and nice about this is that the DNX treats this external package like it would like a binary package, but it is also immediately debuggable as source code.

Fixing the Bug

So the original problem I was trying to troubleshoot was that Enums values weren't parsing.  Based on the code above you can probably spot the problem: Convert.ChangeType() doesn't support Enum conversions. When you step through the code above with an Enum value, the exception handler kicks in and the value is not set leaving the default configuration value intact.

That's easy enough to fix with the following code:

try
{
    object value;
    if (propertyType.IsEnum)
        value = Enum.Parse(propertyType, configValue);
    else
        value = Convert.ChangeType(configValue, propertyType);

    prop.SetValue(obj, value);                    
}
catch
{
    // Ignore errors
}

I can make this change and re-run my application and – voila: the Enum value now properly parses. Yay!

Let's Review

I've just made a change in a Microsoft provided runtime library and integrated that change into my running application. If I now compile and package this application for deployment the change I made would be shipped up right into a deployed application.

Think about of how much of a pain that's been in the past, and it's now – pretty straightforward and easy. You can make temporary changes like this and integrate those changes into your own projects with what is essentially a private copy of the project. Since the code is in source control you can also keep checking Microsoft's repository by updating and seeing if they fixed the problem in their code base, or if you're willing to put in the effort and paperwork, you can just as easily submit a pull requests.

Now mind you, this particular bug has been addressed in post beta4 versions of the OptionsModel project. In fact the entire options model hierarchy has been re-shuffled so newer versions won't actually work in a beta 4 project unless you update all the dependencies.

But this exercise clearly demonstrates that you can easily fix a bug in the framework and run with that private bug fix in your own projects without a hassle. If Microsoft updates their code, and you want to keep running with your's instead – you can do that. If Microsoft fixes the bug without breaking anything else – you can remove your private patch and simply revert back to the NuGet packages. It's easy to move back and forth simply by adding and removing external package paths in global.json.

The fact that you can debug framework code in real source code, rather than source from symbol servers is pretty awesome. With source code you have the actual up to date source code and it's part of your project where you can browse and change it. That's huge and makes it actually realistic to debug and modify source code as well as making it much easier to contribute bug fixes back to Microsoft (or any other library provider) as Pull Requests.

Summary of Steps To Add an external Source Code Project

As a summary lets look at the steps you need to do this again:

  1. Find the Package Source that you need to work on
  2. Clone the Git Repository to your local Disk
  3. Add project's path to the local Package Sources in global.json
  4. Add any of the source package names to your project.json dependencies
  5. Go nuts with the source code

Out of those steps the most time consuming one for DNX components likely is finding the right GitHub project to clone. And this should be easier in the future when the DNX source code has settled a bit and hopefully gets a bit more consolidated. It should also be much easier for third party libraries which aren't likely to be as crazy fragmented as the DNX runtimes are currently.

Much More than a Nice-To-Demo Feature!

It's great to see that source code as a package solution has become a first class citizen. I was talking to Glenn Condron when I was researching into this subject based on his awesome DNX Deep Dive talk at Build (go watch it for lots of little useful tidbits) and both of us were remarking that although this comes off as nice-to-demo feature, it really has great practical potential in real life developer work flows. Where it was painful in the past to import external code into your running solutions, it now takes 3 simple steps to get external source code linked and ready to run.

I think this is a key feature for contributing to open source projects because it makes so much easier to work with source code that you don't own. Clone from Git, add a solution reference and add the project to your existing package references and you can start making changes to code in the realistic contexts of your own projects. The latter part is the key here – making it easy to use external project code in your actual projects. It's a big improvement over how things work with today's .NET projects.

Resources

Posted in ASPNET5  ASP.NET  NuGet  

The Voices of Reason


 

Codematrix
November 29, 2015

# re: Using and Debugging External Source Code Packages in ASP.NET 5

Nice work Rick! You made it so simple to understand.

Ross

Jeff Bailey
March 13, 2017

# re: Using and Debugging External Source Code Packages in ASP.NET 5

Does this still work with the csproj netcore VS2017 world? I can't find any documentation.


Rick Strahl
March 14, 2017

# re: Using and Debugging External Source Code Packages in ASP.NET 5

@Jeff - Looks like this is not really possible anymore although the feature may come back. Mentioned on the Community Standup today actually: https://youtu.be/qlVE5iDjBIg?t=6m35s


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024