Packing assemblies to a single file for Righthand.Dataset.Visualizer

A while ago I’ve created Righthand.DataSet.Visualizer, an advanced DataSet visualizer. Today I’ve added support for displaying a single table as well. It wasn’t a big deal but I guess people will find it useful.

Now, there is one things I weren’t too happy about until today: I reference a lot of DevExpress assemblies and I have to redistribute all those assemblies along mine two (my visualizer comes in form of two assemblies). Which makes a lot of assemblies in total and even worse, if two visualizers use slightly different DevExpress versions you are in for a trouble.

So I’ve decided to pack everything to a single file. ILMerge, a free assembly merging tool from Microsoft, won’t work for me since it has problems with reference to Microsoft.VisualStudio.DebuggerVisualizers even though I don’t want to redistribute it. So I tried Xenocode Postbuild for .NET which does all sort of hacky things with .net assemblies, it even allows to create a setup that doesn’t require .net framework installed at the target machine. Among other features (obfuscation, optimization, etc.) it provides an assembly merging option that I successfully used in my case. Here are the required steps for my case:

1. Start Xenocode Postbuild for .NET, click on Application tab. Use Add… button to add required assemblies to pack together (you can add assemblies individually or pick most important ones and then use Scan Dependencies button to add referenced ones):

application

2. If you want only to pack assemblies then use Null – For test and debugging purposes or any other preset you want, just make sure you set other options appropriately.

presets

 

 

 

3. On the Protection tab I did uncheck all metadata obfuscation (since I am not after obfuscation here) by right clicking on the root node and selecting Unselect Tree menu item. I don’t use any Disassembler Suppression either. I left moderate code obfuscation (level 3 in scale 0..4), just for testing – this option shouldn’t cause any trouble since it should keep functionality the same. If there are problems with the later it means that the tool sucks heavily.

protection

4. Clear all checkboxes in Optimize tab.

5. On the Output tab I made sure that Single application executable option in Link and Code Generation group is selected and Righthand.DebugerVisualizer.Dataset.Visualizer assembly is the main one. I also selected .\Setup for the output folder.

output

6. By clicking Xenocode Application button the final, single file, output is written to the disk.

And that’s it. I got a single file with all required assemblies packed together. Just that easy. Note that I intentionally used only a fraction of Postbuild power.

If you use frequently Postbuild you should also consider using Final Builder tool, an automated build and release management tool that supports Postbuild out of the box (I am sure other such tools support Postbuild as well).

And finally, here is the updated visualizer:

RightHand.DebugerVisualizer.Dataset.Visualizer 0.9.14.zip (7.60 mb)

Let me know if there are any problems or if you have any improvement wish.

Using XtraChart ASP.NET control in an ASP.NET MVC project

The problem

These days I am building an ASP.NET MVC project that requires charting. The problem is that there are no native ASP.NET MVC charting controls out there. The only one I am aware of is the one from Syncfusion . The problem with it is that it is in a beta and I didn’t have time to test it out. BTW, Syncfusion is the only 3rd party control provider (in beta though) for ASP.NET MVC AFAIK.

The solution

Anyway, back to my problem – charting in ASP.NET MVC. It is quite obvious that I have to use an ASP.NET control for the job. And since I already own and I am used to XtraCharts I’ve decided to give it a try (alternatives include use of Microsoft’s free ASP.NET Chart Control (which works in ASP.NET MVC for sure) and other ASP.NET charting controls might work as well). And guess what: XtraCharts works without many problems. As an image provider that is – I don’t need callbacks nor I think they are supported in this scenario.

Creating a test project

There are couple of extra steps necessary to use XtraCharts in ASP.NET MVC project. Here are steps necessary to create a test project.

Requirements: XtraCharts v9.1.

1. Drop a WebChartControl toolbox item (residing in DX.9.1.: Data Toolbox’ tab) on the Index view (I tested with the view (aspx) but it should work with partial view (ascx) as well). You’ll get this code:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%></h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.































































</p>































































<dxchartsui:WebChartControl ID="WebChartControl1" runat="server">































































</dxchartsui:WebChartControl>































































</asp:Content>
































It might happen that all the necessary assemblies won’t be referenced automatically. In this case add manually these (v9.1.x) assemblies:

  1. DevExpress.Web
  2. DevExpress.Charts.Core
  3. DevExpress.XtraCharts

2. Adjust WebChartControl’s settings either directly or through designer (yes, you can use its designer!). I’ll create a 3D pie chart with a single series bound to Name/Value pair. Disable both EnableCallbacks and EnableViewState.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%></h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.































































</p>































































<dxchartsui:WebChartControl ID="someChart" runat="server" EnableViewState="false" EnableCallbacks="false"































































DiagramTypeName="SimpleDiagram3D" Height="200px" Width="300px">































































<SeriesSerializable>































































<series argumentdatamember="Name" labeltypename="Pie3DSeriesLabel" name="Series 1"































































pointoptionstypename="PiePointOptions" seriesviewtypename="Pie3DSeriesView" valuedatamembersserializable="Value">































































<view hiddenserializablestring="to be serialized"></view>































































<label hiddenserializablestring="to be serialized" linevisible="True" overlappingoptionstypename="OverlappingOptions">































































<fillstyle filloptionstypename="SolidFillOptions">































































<options hiddenserializablestring="to be serialized"></options>































































</fillstyle>































































</label>































































<pointoptions hiddenserializablestring="to be serialized"></pointoptions>































































<legendpointoptions hiddenserializablestring="to be serialized"></legendpointoptions>































































</series>































































</SeriesSerializable>































































<SeriesTemplate LabelTypeName="Pie3DSeriesLabel" PointOptionsTypeName="PiePointOptions"































































SeriesViewTypeName="Pie3DSeriesView">































































<View HiddenSerializableString="to be serialized">































































</View>































































<Label HiddenSerializableString="to be serialized" LineVisible="True" OverlappingOptionsTypeName="OverlappingOptions">































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized"></Options>































































</FillStyle>































































</Label>































































<PointOptions HiddenSerializableString="to be serialized">































































</PointOptions>































































<LegendPointOptions HiddenSerializableString="to be serialized">































































</LegendPointOptions>































































</SeriesTemplate>































































<Diagram RotationMatrixSerializable="1;0;0;0;0;0.5;-0.866025403784439;0;0;0.866025403784439;0.5;0;0;0;0;1">































































</Diagram>































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized"></Options>































































</FillStyle>































































</dxchartsui:WebChartControl>































































</asp:Content>
































































3. Add the following registration on the top of the page:

<%@ Register Assembly="DevExpress.XtraCharts.v9.1, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts" TagPrefix="dx" %>

and change nodes of type <series> to type <dx:series>. Otherwise you’ll have a naming conflict.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts" TagPrefix="dx" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%></h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.































































</p>































































<dxchartsui:WebChartControl ID="someChart" runat="server" EnableViewState="false" EnableCallbacks="false"































































DiagramTypeName="SimpleDiagram3D" Height="200px" Width="300px">































































<SeriesSerializable>































































<dx:series argumentdatamember="Name" labeltypename="Pie3DSeriesLabel" name="Series 1"































































pointoptionstypename="PiePointOptions" seriesviewtypename="Pie3DSeriesView" valuedatamembersserializable="Value">































































<view hiddenserializablestring="to be serialized"></view>































































<label hiddenserializablestring="to be serialized" linevisible="True" overlappingoptionstypename="OverlappingOptions">































































<fillstyle filloptionstypename="SolidFillOptions">































































<options hiddenserializablestring="to be serialized"></options>































































</fillstyle>































































</label>































































<pointoptions hiddenserializablestring="to be serialized"></pointoptions>































































<legendpointoptions hiddenserializablestring="to be serialized"></legendpointoptions>































































</dx:series>































































</SeriesSerializable>































































<SeriesTemplate LabelTypeName="Pie3DSeriesLabel" PointOptionsTypeName="PiePointOptions"































































SeriesViewTypeName="Pie3DSeriesView">































































<View HiddenSerializableString="to be serialized">































































</View>































































<Label HiddenSerializableString="to be serialized" LineVisible="True" OverlappingOptionsTypeName="OverlappingOptions">































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized"></Options>































































</FillStyle>































































</Label>































































<PointOptions HiddenSerializableString="to be serialized">































































</PointOptions>































































<LegendPointOptions HiddenSerializableString="to be serialized">































































</LegendPointOptions>































































</SeriesTemplate>































































<Diagram RotationMatrixSerializable="1;0;0;0;0;0.5;-0.866025403784439;0;0;0.866025403784439;0.5;0;0;0;0;1">































































</Diagram>































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized"></Options>































































</FillStyle>































































</dxchartsui:WebChartControl>































































</asp:Content>
































































4. Under Models folder create RhData class that will hold data:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplication10.Models
{
public class RhData
{
public string Name { get; set; }
public int Value { get; set; }
}
}

5. Add class Index.aspx.cs next to Index.aspx file. This will serve as codebehind file. Also add Page_Load method, create and bind data to chart right in there. Note that you have to declare chart instance field as well since the designer won’t do it for you.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication10.Models;
namespace MvcApplication10.Views.Home
{
public class Index: ViewPage
{
protected global::DevExpress.XtraCharts.Web.WebChartControl someChart;
protected void Page_Load(object sender, EventArgs e)
{
List<RhData> data = new List<RhData>();
data.Add(new RhData { Name = "One", Value = 1 });
data.Add(new RhData { Name = "Two", Value = 2 });
someChart.DataSource = data;
someChart.DataBind();
}
}
}

Note: The proper MVC to provide data way would be through the model, however for the sake of simplicity I took this shortcut. The binding itself has to be done in Page_Load event.

6. Wire up the code behind class to the view by adding CodeBehind attribute and modifying Inherits one.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcApplication10.Views.Home.Index" CodeBehind="Index.aspx.cs" %>

 

 

The result

If everything goes well you should see such output.

demo

This will do just fine until there is an MvcXtraChart control I guess.

XtraChartsMvc.zip (241.46 kb) (Updated) Note: Built with DevExpress v9.1.3.0. If you use other version then you should run DX' ProjectConverter tool or just do a search and replace of "9.1.3.0" with your version.

Shout it


Strong typing routes in ASP.NET MVC

Let’s say you have Test action in Home controller like this:

public ActionResult Test(int id, int tubo)
{
return View("Index");
}

And you want to create a hyperlink to that action on your view. So, did you ever wonder why do you have to write code like:

< %= Html.ActionLink("Classic Action", "Test", "Home", new { tubo=8, id=77 }, null) %>

[Note: I had to insert a space after each less than character (<) becaue of formatting issues in IE7. For some reason the code disappeared when there was no space after <. Firefox didn't show any such issues. If you try running the code contained in the code snippets you'll have to remove those spaces.]

(I won’t even consider lambda version because it is awfully slow). This version has two problems:

  1. It isn’t strong typed by any mean.
  2. The anonymous class holding id and tubo values isn’t the best performer either.

To solve the second issue you might opt for RouteValueDictionary instead of anonymous class, like this:

< %= Html.ActionLink("Classic Action", "Test", "Home", 
new RouteValueDictionary {  { "tubo", 8 } ,{ "id", 77 } }, null) %>

This version is less readable but slightly better performer.

The bigger problem is the first one, which wasn’t possible to overcome, unless you wanted to get rid of performance (by using lambdas). Until now, that is. I created a solution that would provide best of both worlds: strong typing and performances.

< %= Html.LinkTestOnHome("TestLink", 8, 77)%>

Note the advantages:

  1. Parameters are strongly typed
  2. The most performing call (currently to ActionLink) is handled internally (with possibility to improve).

Sounds good? If yes, read futher.

Instructions

The generator comes in a form of DXCore/CodeRush plugin and there is a single requirement: you’ll need the best Visual Studio productivity tool CodeRush. Or even better, this plugin requires only DXCore, which is a free subset of CodeRush (untested scenario at this point though). In other words, this is a free lunch albeit you should really take a look at CodeRush and all of its super features. You won’t regret it.

Here are the steps to test it out:

1. Unzip the attached MvcStrongTyping0.1.zip file into [Program Files]\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins folder (on a 64 bit Windows it should look like C:\Program Files (x86)\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins).

2. Start an instance of Visual Studio 2008 and open (or create a new) ASP.NET MVC project.

3. Add a root folder named Helpers in the ASP.NET MVC project if it doesn’t exist yet.

4. Create a file named UrlExtensions.cs in Helpers folder.

5. Open the file and/or make it the active document.

6. WARNING: Any content in active document gets deleted and replaced by new one during this operation. Click DevExpress\Create MVC Routes menu entry.

route1
7. Include [Rootnamespace].Helpers (check out UrlExtensions class’ namespace) namespace to the web.config file in the <namespaces> node:

<system.web>
...
<pages>
...
<namespaces>
...
<add namespace="[YourRootNamespace].Helpers"/>
< /namespaces>
< /pages>
...
< /system.web >

That’s it. There should be a lot of code generated to UrlExtenstions.cs file and you are able now to use both new methods (or their overloads):

< %= Html.LinkTestOnHome("TestLink", 8, 77)%>
< %= Url.ActionTestOnHome(8, 77) %>

Generated code

For the Test action there should be this code (shortened) present:

...
namespace [YourRootNamespace].Helpers
{
public static class UrlExtensions
{
...
#region Fixed params action for Home.Test
public static string ActionTestOnHome(this UrlHelper helper, object routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, object routeValues, 
string protocol)...
#endregion
#region Fixed params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
object routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
object routeValues, object htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
string protocol, string hostName, 
string fragment, object routeValues, object htmlAttributes) ...
#endregion
#region Variable params for action Home.Test
public static string ActionTestOnHome(this UrlHelper helper, int tubo)
public static string ActionTestOnHome(this UrlHelper helper, int tubo, 
RouteValueDictionary routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, 
RouteValueDictionary routeValues, 
string protocol, string hostName) ...
#endregion
#region Variable params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
RouteValueDictionary routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
string protocol, string hostName, string fragment, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
#endregion
#region Variable params for action Home.Test
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id, 
RouteValueDictionary routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id, 
RouteValueDictionary routeValues, string protocol, string hostName) ...
#endregion
#region Variable params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, RouteValueDictionary routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, 
string protocol, string hostName, string fragment, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
#endregion
}
}

That’s quite a lot of overloaded methods (and this is just an example for a single action!).
First note the name of the method that corresponds to the ActionOnController pair.
Next, there are basically two groups:
  • Fixed parameters – just strong typed links to the action (without any explicit action parameter)
  • Variable parameters – strong typed links with various combinations of action’s input parameters

Both groups are subdivided into two groups:

  • Actions – generates string representation of matching URL through UrlHelper.Action
  • Links – generates links through HtmlHelper.ActionLink method

Both internal calls might be optimized further and perhaps they will be in the future. At this point it is important that the methods work.

 

Implementation details

The magic of the autogenerated code comes from DXCore’s StructuralParser, which allowed me easy parsing of the project’s code (something that should be provided by Visual Studio from the beginning). And by easy I mean really easy and straightforward. I can’t praise it enough. On the other hand if it wasn’t for DXCore my plugin wouldn’t be even possible. At least not in time that I can allocate to this project.

So, all it took me was a couple of advices from Mark Miller (who is an incredibly helpful person btw) on where to start with parser and a bunch of basic operations of building a DXCore plugin. All in all it took me around 4 hours of my time. Thanks to my friend Miha for method naming suggestion. If it wasn’t for him the method names would be a lot worse. And thanks also to Whiletrue guys for their asp.net mvc performance presentation.

So, here it is. Let me know what do you think about it and don’t forget, this is an early, an experimental version of the plugin. All feedback is appreciated.

MvcStrongTyping0.1.zip (9.62 kb)

See also my previous article on strong typing the views.

Autoimplement base class constructors with CodeRush

How often do you derive a class and re-create all of the base constructors? This is not an uncommon task, let's take a custom exception class for example:

public class MyException: Exception { }

If you right click on Exception and pick Go to Definition from the context menu you'll see that the Exception class has four constructors:

public class Exception : ISerializable, _Exception { public Exception(); public Exception(string message); protected Exception(SerializationInfo info, StreamingContext context); public Exception(string message, Exception innerException); }

And you better re-create all four in your derived MyException class if you want to follow the design guidelines. This is how MyException should look like (without custom code that is):

public class MyException: Exception { public MyException() { } public MyException(string message) : base(message) { } public MyException(string message, Exception innerException) : base(message, innerException) { } protected MyException(SerializationInfo info, StreamingContext context) : base(info, context) { } }

This is a straightforward derived Exception class definition and needless to say it is quite boring to create all those constructors again and again. That's why [CodeRush] has a one-click base constructors recreation feature. Position the caret somewhere on the Exception word and invoke [CodeRush] with keyboard shortcut (by default the shortcut is Ctrl+CEDILLA) and instantly all of those boring-to-recreate constructors appear from nowhere. Brilliant.

Visual Studio, CodeRush and Silverlight 2

If your Visual Studio 2005 or 2008 freezes while creating a Silverlight project, or any other project then you might try switching off [CodeRush] add in. Yep, it causes Visual Studio to freeze when creating a shinny new Silverlight 2.0 application. I am sure that [DevEx] folks will fix the problem very soon but for the time being just start Visual Studio, go to Tools\Add-in Manager... and uncheck DevExpress Tools item (I guess that will be a hard step to [CodeRush] addicted people, but don't worry, it is only temporal). You can run Silverlight project wizard now and you can re-enable [CodeRush] once the project is created.

The workaround and the fact that [CodeRush] is the culprit has been found in this thread.

Developer Express steps into WPF

[DevEx], my favorite 3rd party .net component vendor, has just made its first public step into Windows Presentation Foundation world. They released a beta version of their charting product DXCharts for WPF (hey, where are those Xtra, Xpress, Express prefixes - "DX for WPF", shhh, boring ;-)). Anyway, everything is as one would expected in WPF - animations and 3D make it look good. Though it is just a first step and many features (i.e. many chart types), we are used from WinForms world, are missing at this time. But this is normal as they usually concentrate on good foundations at first and only then they add all those additional features.

Here is quick glimpse at the demo that comes with the product:

image

If you are entitled to [DevEx] beta previews, go check client center for the beta bits.

ASPxGridView, MS Ajax and XYDataSource

If you use [DevEx] ASPxGridView within MS Ajax' UpdatePanel (ASPxGridView.EnableCallBacks="False") you should be aware that you should perform DataBind() method within OnInit method (Init event). Otherwise editing just won't work, or better, it works, just the modifications aren't persisted. It took me some time to pinpoint the problem as ASPxGridView worked just fine outside UpdatePanel. I had to put it within UpdatePanel because I needed to refresh other controls as well (otherwise grid refreshes just itself).

Note that when ASPxGridView is hosted in UpdatePanel the nice error reporting feature (see the picture below) won't work either - instead you'll get script error reported by browser. IOW you have to handle errors by yourself.

image

Cool automatic error feedback

See also this support thread.