Displaying Project Dependencies and Solution Folder Dependencies

January 30, 2011

While I was writing the introductory blog to VS Solution Dependency Visualizer, I already had a lot of ideas about what functionality could be included in the future.

Displaying Project Dependencies

I do not know of an easy way to find which projects in a Visual Studio solution reference a specific project.

Upon analysis of a solution, VS Solution Dependency Visualizer will now display all references of a project once it is selected in the project tree:

Displaying Solution Folder Dependencies

Solution folders can be used to group projects into function groups or architectural layers. To make sure the dependencies are correct between layers and groups, generate a Solution Folders Dependency chart

The latest release of VS Solution Dependency Viewer is available for download here.


Adding Formatted Text to RichTextBox

January 28, 2011

RichTextBox provides the method AppendText() inherited from TextBoxBase to programmatically add  text to the editor control, but there seems to be no native method to append formatted text.

A small extension class adds this functionality:

public static class RichTextBoxExtensions
{
  const string NewLine = "\r\n";

  public static void AppendLine(this RichTextBox ed)
  {
    ed.AppendText(NewLine);
  }

  public static void AppendLine(this RichTextBox ed, string s)
  {
    ed.AppendText(s + NewLine);
  }

  public static void AppendBoldLine(this RichTextBox ed, string s)
  {
    int ss = ed.SelectionStart;
    ed.AppendText(s);
    int sl = ed.SelectionStart - ss + 1;

    Font bold = new Font(ed.Font, FontStyle.Bold);
    ed.Select(ss, sl);
    ed.SelectionFont = bold;
    ed.AppendText(NewLine);
  }
}

Visual Studio Solution Dependency Visualizer

January 26, 2011

The result of my evaluation of project dependency visualizers was that while some of the tested applications worked on some solutions, none of them could handle all my test cases. Moreover, the generated graphs were simply too large to be useful.

On the other hand, my application dbscript already contains a graph module to generate data diagrams, which could be re-used for displaying project dependencies.

Time to write my own ;)

My new application, VS Solution Dependency Visualizer, analyzes C# project dependencies within a Visual Studio solution file, and is tested to work on any VS version from 2003 to 2010.

It is a stand-alone .Net application, but can also be invoked from the context menu of a .sln file.

In a hierarchical view of solution folders and projects, developers select the projects to be analyzed:

After analysis, simply generate a .png file, or a .dia chart, and the default viewer for the generated file opens automatically.

See some screenshots and output samples in the product’s gallery.

The initial release of VS Solution Dependency Viewer is available for download here.


Comparison of Visual Studio Project Dependency Visualizers

January 26, 2011

If you work in a Visual Studio solution that contains lots of projects (in my case, about 90), you occasionally need to check your project dependencies: in a multi-layered architecture, a project may only reference projects of certain layers, but not of others.

A quick search gave me 3 tools that visualize VS solution dependencies

All of them make use of the GraphViz library, so their output looks similar.

I tested with a couple of .sln files dating back from VS2003 up to the current VS2010. These solution files are from C# software projects that I created or that I was working on, so your experience may be different.

depcharter

After installation depcharter, which also includes GraphViz, depcharter can be added to the context menu of .sln files by adding a .reg file to the registry.

However, I never accomplished to generate a chart using depcharter. It crashes on web site projects, and otherwise ends with the messages “No dependencies, nothing to do” or “Error generation diagram”.

DependencyViewer

DependencyViewer is a WPF application that opens a selected solution file, allows the user to select/deselect the projects to be included in the chart, and select an output filename. After generating the graph, the resulting .png file is automatically opened in the default viewer.

DependencyViewer crashes on some solution (my guess is either web site projects and/or spaces in paths), but successfully generates png charts:

Dependency Visualizer

Dependency Visualizer installs itself as context menu on .sln files, and provides a separate configuration application to adjust some settings, such as inclusion of referenced assemblies and .Net libraries in the chart.

Upon right-click, Visualize Dependencies, it generates a .png and a .svg chart in a newly created Dependencies subdirectory of the solution’s directory.

Unfortunately it only supports Visual Studio 2005 and 2008 solution files.

Conclusion

Not all visualizers support all kinds of projects in solution files. (Disclaimer: any program error or crash may be caused by my handling of the applications)

Generally speaking, charts generated GraphViz are quite huge: they range from 500  pixels wide for simple solutions (10 projects), up to 12.000 pixels wide for a solution containing 90 projects.


vdproj: Add Application to Context Menu of Existing File Type in VS Setup Project

January 24, 2011

The registry associates file extensions with file types and their applications.

Taking Visual Studio as an example, the key HKCR\.sln points to the file type VisualStudio.Launcher.sln (HKCR being the abbreviation for HKEY_CLASSES_ROOT).

Under HKCR\VisualStudio.Launcher.sln\Shell, the registry lists all commands that are available from Explorer’s context menu on a .sln file.

I investigated how to add my application to this key using a Visual Studio Setup Project (.vdproj), especially, how to retrieve the installation directory selected by the user during setup.

In the Registry view of the Setup Project

  • locate the HKEY_CLASSES_ROOT key.
  • select New Key and add the file type, e.g. VisualStudio.Launcher.sln.
  • select New Key, and add “Shell”
  • select New Key, and add either “[ProductName]” or the text you want to display in the context menu
  • select New Key, and add “Command”
  • in the right-hand pane, Select New/String Value, and delete the name of the key. The name will now be displayed as “(Default)”.
  • select the default key, go to the Properties window, and set the value to “[TARGETDIR]myprogram.exe” “%1″.

(The double quotes have to be typed in the property value to handle spaces in executable and document paths).

Upon installation, your program will now appear in the context menu of the files associated with this type.

The difficulty for me was to find the macros that can be used in a setup project. That’s because Microsoft does not refer to them as installer macros or installer variables, but rather properties.

Here is the complete list of Setup Project Macros.


Altering XML Schema Collections

January 23, 2011

SQL Server first implemented XML Schema Collections in version 2005.

However, support for XML schema collections is not exactly developer-friendly: Once a schema collection has been created in a database, the ALTER XML SCHEMA COLLECTION command can only add to an existing schema, but nothing can be removed from it, or changed:

Use the ALTER XML SCHEMA COLLECTION to add new XML schemas whose namespaces are not already in the XML schema collection, or add new components to existing namespaces in the collection.

Change or removal of schema “components” (i.e. elements and attributes) is only possible by dropping and then re-creating the XML schema collection. This is not as straight-forward as it seems, and this lack of efficiency keeps developers from using XML-based data, as this Stack Overflow question shows.

To drop an XML schema collection, the following steps are necessary:

  • If a table column references the schema collection (i.e. typed XML), it has to be converted to plain XML type
  • If the table column has a default constraint, drop the default constraint
  • If a procedure or function has typed XML parameters, the procedure or function has to be dropped
  • If a function has typed XML parameters, the function has to be dropped
  • If there is an XML index on a column referencing the schema collection, the index (primary and secondary indexes) has to be dropped
  • If there are computed columns based on a typed XML column, the computed columns have to be dropped
  • If there are indexes on these computed columns, the indexes have to be dropped
  • If there are schema-bound views, functions, or procedures based on tables containing typed XML columns, these objects have to be dropped

Of course, all these DROP commands have to be executed in the correct order.

After creating the new XML schema collection, all the dropped objects can be re-created using information initially stored in the SQL Server catalog views.


Get Name of Nested Property as String Value

January 21, 2011

I presented a method to retrieve the property name referenced by a lambda expression in a previous post.

Reader Jacek asked,

is there any way for nested property (eg. Studies.ID or Studies.Class.Name) to convert to string?

Nice question, and I tried to solve it.

Starting with a simple data model

public class Foo
{
  public int FooID { get; set; }
  public string Name { get; set; }
}
public class Bar
{
  public string Name { get; set; }
  public Foo BarFoo { get; set; }
}
public class Baz
{
  public string Name { get; set; }
  public Bar BazBar { get; set; }
}

we want to convert an expression such as b => b.BazBar.BarFoo.Name into its string representation.

public static string GetPropertyName(this LambdaExpression expression)
{

First, we handle type casts, as in the original solution:

  if (expression.Body is UnaryExpression)
  {
    UnaryExpression unex = (UnaryExpression)expression.Body;
    if (unex.NodeType == ExpressionType.Convert)
    {
      Expression ex = unex.Operand;
      MemberExpression mex = (MemberExpression)ex;
      return mex.Member.Name;
    }
  }

Keep a reference of the parameter, and set path (i.e. concatenated property names) to empty

  MemberExpression memberExpression = (MemberExpression)expression.Body;
  MemberExpression memberExpressionOrg = memberExpression;

  string Path = "";

The sequence b.BazBar.BarFoo is stored as an expression of ExpressionType MemberAccess, which is an object of the non-public class System.Reflection.RuntimePropertyInfo (google), as the debugger tells us.

Since the class is not public, we need to access the Member property using reflection, in this case PropertyInfo.GetValue:

  while (memberExpression.Expression.NodeType == ExpressionType.MemberAccess)
  {
    var propInfo = memberExpression.Expression
      .GetType().GetProperty("Member");
    var propValue = propInfo.GetValue(memberExpression.Expression, null) 
      as PropertyInfo;
    Path = propValue.Name + "." + Path;

    memberExpression = memberExpression.Expression as MemberExpression;
  }

  return Path + memberExpressionOrg.Member.Name;
}

The variable Path stores the sequence of property names as we iterate through the expression tree. Finally, the last property name is added.

Using this code, the line

Console.WriteLine(
  ExpressionExtensions.GetPropertyName(
    (Expression<Func<Baz, string>>)(b => b.BazBar.BarFoo.Name)));

produces this output:

BazBar.BarFoo.Name

Updating a Single Column in Linq to SQL – Summary

January 16, 2011

The standard way to update a record using Linq to SQL is to load a record from the database, assign the new column values, and submit the changes.

This method is undesirable if your table contains long text or binary columns, since they are loaded into the Linq to SQL object without being used.

Starting with a table containing nvarchar(max) and varbinary(max) columns

CREATE TABLE [dbo].[FooMax](
  [OID] [int] IDENTITY(1,1) NOT NULL,
  [ID] [nvarchar](50) NOT NULL,
  [SomeText] [nvarchar](max) NULL,
  [SomeBinary] [varbinary](max) NULL,
  CONSTRAINT [PK_FooMax] PRIMARY KEY CLUSTERED ([OID] ASC)
)

I listed a couple of methods to reduce database access to the columns actually required in the UPDATE process:

The dbml mappings for these solutions


Updating a Single Column in Linq to SQL using Attach

January 16, 2011

This answer on Stack Overflow shows another way to update specific columns of a database record in Linq to SQL:

Call the table’s Attach() method passing the new values and a dummy set of old values.

In the .dbml file, the table’s columns (except for the primary key column(s)) need to have their UpdateCheck property set to Never (default is Always).

To update only specific columns, you need to instantiate two table objects: one containing the new values to be set, and one containing values different from the values to be set. The different values need not be the actual values of the record:

using (var database = new DataContext())
{
  database.FooMaxNevers.Attach(
    new FooMaxNever() { OID = OID, ID = newID }, 
    new FooMaxNever() { OID = OID, ID = "some random value" });
  database.SubmitChanges();
}

This code generates the following SQL statement

UPDATE [dbo].[FooMax]
SET   [ID]  = @p1
WHERE [OID] = @p0

Note that this method does not retrieve the current values and instantiate an object based on the actual database record, and therefore does not generate a SELECT statement.


Updating a Single Column in Linq to SQL using an Alias

January 16, 2011

This answer on Stack Overflow shows another way to update specific columns of a database record in Linq to SQL: Map the table in the .dbml file a second time, but omit the long columns.

using (var database = new DataContext())
{
  var fooID = database.FooMaxIDs
    .Where(foo => foo.OID == OID).FirstOrDefault();
  fooID.ID = newID;
  database.SubmitChanges();
}

This generates the following SQL statements:

SELECT TOP (1) [t0].[OID], [t0].[ID]
FROM [dbo].[FooMax] AS [t0]
WHERE [t0].[OID] = @p0

UPDATE [dbo].[FooMax]
SET [ID] = @p2
WHERE ([OID] = @p0) AND ([ID] = @p1)

Follow

Get every new post delivered to your Inbox.