I’ve been working on three interleaved projects RoslynDom, CodeFirst Strong-typed Metadata and ExpansionFirst Templates. Also, Jim Christopher (aka beefarino) built a PowerShell provider. This post is an overview of these projects and a roadmap of how they relate to each other.
You can find the short version here.
In the roadmap, blue indicates full (almost) test coverage and that the library has had more than one user, orange indicates preliminary released code, and grey indicates code that it’s really not ready to go and not yet available.
I’m working left to right, waiting to complete some features of the RoslynDom library until I have the full set of projects available in preliminary form.
.NET Compiler Services, or Roslyn, does exactly what it was intended to do, which is exactly what we want it to do. It’s a very good compiler, now released as open source, and exposing all of its internals. It’s great that we get access to the internal trees, but it’s not happy code for you and I to use – it’s compiler internals.
At the same time, these trees hold a wealth of information we want – it’s more complete information than reflection, holds design information like comments and XML documentation, and it’s available even when the source code doesn’t compile.
When you and I ask questions about our code, we ask simple things – what are the classes in this file? We don’t care about whitespace, or precisely how we defined namespaces. In fact, most of the time, we don’t even care about namespaces at all. And we certainly don’t care whether a piece of information is available in the syntactic or semantic tree or whether attributes were defined with this style or that style.
RoslynDom wraps the Roslyn compiler trees and exposes the information in a programmer friendly way. Goals include
- Easy access to the tree in the way(s) programmers think about code as a hierarchy
- Easy access to common information about the code as parameters
- Access to the applicable SyntaxNode when you need it
- Access to the applicable Symbol when you need it
- Planned: Access to the full logical model – solution to smallest code detail
(Currently, file down to member)
- Planned: A kludged public annotation/design time attribute system until we get a real one
(Currently, attribute support only)
- Planned: Ability to morph and output changes
You can get the source code on GitHub, and there’s a RoslynDomExampleTests project which shows how to do about 20 common things.
The project is also available via NuGet. It’s preliminary, use cautiously. Download with the Visual Studio NuGet package manager.
Jim Christopher created a PowerShell provider for RoslynDom. PowerShell providers allow you to access the underlying tree of information in the same way you access the file system. IOW, you can mount your source code as though it was a drive.
I’m really happy about the RoslynDom-Provider. It shows one way to use a .NET Compiler Platform/library to access the information that’s otherwise locked into the compiler trees. It’s also another way for you to find out about the amazing power of PowerShell providers. If you’re new to PowerShell, and you’re a Pluralsight subscriber, check out “Discovering PowerShell with Mark Minasi”. It uses Active Directory as the underlying problem and a few parts may be slow for a developer, but it will give you the gist of it. Follow up with Jim Christopher’s “Everyday PowerShell for Developers” and “PowerShell Gotchas.” If you’d rather read, there are a boatload of awesome books including PowerShell Deep Dives and Windows PowerShell for Developers, and too many Internet sites for me to keep straight.
Code-first Strong-typed Metadata
As a first step, I have samples in runtime T4. These run from the command line at present. These templates inherit from a generic base class that has a property named Meta. This property is typed to the underlying strong-typed metadata item – in the samples either CodeFirstSemanticLog or CodeFirstClass. The EventSource template and problem is significantly more complex, but avoids some extra mind twisting with a strong-typed metadata class around a class. These templates are preliminary and do not handle all scenarios.
While there are a couple of ways to solve a metaprogramming expansion or code first problem, I’ve settled on an alternate file extension. The code-first minimal description is in a file with a .cfcs extension. Because I lie to Visual Studio and tell it that this is a C# file (Tools/Options/Text Editor/File Extensions) I get nice IntelliSense for most features (more work to be done later). But because MSBuild doesn’t see it as a C# file, the .cfcs file is ignored as a source file in compilation.
Generation produces an actual source code file in a file with a .g.cs extension. This file becomes part of your project. This is the “real” code and you debug in this “real” code because it’s all the compiler and debugger know about. As a result
- You write is the minimal code that only you can write
- You understand your application through either the minimal or expanded code
- You easily recognize expanded code via a .g.cs extension
- You can place the minimal and expanded code side by side to understand the expansion
- You debug in real code
- You protect the generated code by allowing only the build server to check in these files
Again this happens because there are two clearly differentiated files in your project – the .cfcs file and the .g.cs file.
The intent is to have this automated as part of your normal development pipeline, through one or more mechanism – build, custom tools, VS extension/PowerShell. The pipeline part is not done yet, but you can grab the necessary pieces from the console application in the example.
You can also find more here.
You can get this project on GitHub.
I’ll add this to NuGet when the samples are in a more accessible from your Visual Studio project.
T4 has brought us a very long way. It, and CodeSmith have had the lion’s share of code generation templating in the .NET world for about a decade. I have enormous respect for people like Gareth Jones who wrote it and kept it alive and Oleg Sych who taught so many people to use it. But i think it’s time to move on. Look for more upcoming on this – my current bits are so preliminary that I’ll wait to post.
I look forward to sharing the unfinished pieces of this roadmap in the coming weeks and months.
I’d like to offer a special thanks to the folks in my April DevIntersection workshop. The challenges of explaining the .NET Compiler Platform/Roslyn pieces to you let me to take a step back and isolate those pieces from the rest of the work. While this put me way behind schedule, in the end I think it’s valuable both in simplifying the metaprogramming steps and in offering a wrapper for the .NET Compiler Platform/Roslyn.