A screenshot from
LINQPad showing an example use of this code.
Introduction
In attempting to write software for Windows Mobile using Visual Studio C# Express or SharpDevelop, I found that there is some common code that is generated by the designers which does not work on Windows Mobile. Instead of fixing it by hand every time I changed something on the form, I wanted to write a general refactoring tool that I could run from the command line (or the "Tools" menu) and fix the problem code.
I got something very ugly working, using (or misusing) the NRefactory library from SharpDevelop. However, I found that sometimes I needed to use Reflection, and so I needed to know which libraries were referenced in the project. At this point, I had never used LINQ, but I started to see how LINQ to XML could save a lot of code.
Then, as I was working on it, the code to get project properties evolved into the general purpose LINQ to Visual Studio solution that I present here. There is nothing in this code that deals with refactoring. It does read only property queries against Visual Studio projects. Hopefully, at some point, my refactoring code will be at a decent stage and I can post an article on that.
Background
As I already stated, I had never used LINQ until a few days ago. Not all of this code is very pretty, and there may be a simpler/better way to implement some of the features in this code. If there is, I would appreciate advice. Having said that, when I ran a series of queries against the many solution files / projects on my computer, it performed very well.
Using the Code
To use the demo application, simply start it from the command line, passing a directory you want to scan recursively for solution files and projects. It outputs a summary of each solution/project to output.txt in the application directory. In the code for the demo application, there are several other queries you can try that are commented out. I recommend that you look at those to see some of the possibilities of this library.
Add a reference to LinqToVisualStudioSolution.dll that is found in the demo download. Add a using
directive for the LinqToVisualStudioSolution
namespace. Now, you are ready to use the code.
using System;
using LinqToVisualStudioSolution;
class Program
{
static void Main(string[] args)
{
Solution solution = new Solution(@"D:\temp\Test.sln");
var projects = (from project in solution
where project.OutputType == OutputType.Library
select project);
foreach (var project in projects)
{
Console.Write("Project: ");
Console.WriteLine(project.ProjectName);
Console.WriteLine(project.OutputPathToAssemblyFull);
Console.WriteLine();
if (project.Errors.Count > 0)
{
Console.ForegroundColor = ConsoleColor.Red;
foreach (string err in project.Errors)
{
Console.WriteLine(err);
}
Console.ResetColor();
}
}
}
}
Points of Interest
The hardest part of my code was getting the Element(s)
and Attribute(s)
features working so that the user can access any property in the project, not only the common ones. I had to create a class, PElement
, that inherits from XElement
. The reason for this is to keep the user from having to include the namespace in his queries. So instead of writing the following:
XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
var projects = (from project in solution
where project.Element(ns + "PropertyGroup") != null &&
project.Element(ns + "PropertyGroup")
.Element(ns + "AssemblyName") != null &&
project.Element(ns + "PropertyGroup")
.Element(ns + "AssemblyName").Value == "Test"
select project);
you can forget about the XNamespace
altogether.
In order to accomplish this, I had to write a couple of extension methods for the IEnumerable<PElement>
class. These can be found at the beginning of the PElement.cs file.
I also wrote an extension method for the IEnumerable<Reference>
class so I could have a custom Contains
method. Now, you can search for projects that contain certain references.
var projects = (from project in solution
where project.References.Contains("mscorlib")
select project);
Conclusion
I had fun writing this code and learned a lot from it, including how powerful LINQ is. Hopefully, this will be of some use to others.