NT Konferenca 2012 lectures schedule for mobile devices

I’ve created a simple ASP MVC 4/jQuery Mobile UI web site that helps you with NT Konferenca 2012 lectures schedule if you have a mobile device (now, who doesn’t?). Note that it isn’t a well thought, thoroughly and carefully crafted site. Instead it is a quick and simple one, actually it took me an hour (thanks to wonderful tools – VS2010, NuGet and technologies - .net 4.0, OData, WCF Data Service, jQuery Mobile UI, ASP MVC 4 and so forth).

Feel free to browse the lectures schedule through ntk.rthand.com. I might add additional features in next days – feedback me if you’d like to see a feature.

Integrating MvcMiniProfiler and LLBLGenPro

MvcMiniProfiler is a lean and mean mini profiler for MVC 3 that shows the profiling results on each page displayed at runtime. Besides the custom steps you can seed everywhere in your code it supports database profiling as well. Out of the box are supported ado.net, Linq to SQL and Entity Framework. LLBLGenPro, my favorite ORM, isn’t supported though and it won’t work just like that.

Luckily, it turns out, it requires just a little effort to integrate MvcMiniProfiler into LLBLGenPro.

How does MvcMiniProfiler database profiling works

The way it works is that it wraps DbConnection, DbCommand and other Db[Stuff] and thus records the execution time by tracking their inner workings. Here is an example for MvcMiniProfiler documentation about how to start:

public static DbConnection GetOpenConnection()
{
    var cnn = CreateRealConnection(); // A SqlConnection, SqliteConnection ... or whatever

    // wrap the connection with a profiling connection that tracks timings 
    return MvcMiniProfiler.Data.ProfiledDbConnection.Get(cnn, MiniProfiler.Current);
}

If client calls DbConnection.CreateCommand on an ProfiledDbConnection instance returned from previous method it will get a wrapped whatever command original connection returns and so on. There is also a way to manually create DbCommand through ProfiledDbCommand constructor.

The support for Linq To SQL and Entity Framework is done in a similar manner.

This gets us to the point, why can’t I just use the same approach with LLBLGenPro?

Integrate MvcMiniProfiler with LLBLGenPro – why doesn’t work with same approach

The major problem with LLBLGenPro and MvcMiniProfiler integration is that LLBLGenPro doesn’t use DbConnection.CreateCommand method to create commands from existing connection. Instead it creates an instance of proper DbCommand derived class and assigns a connection to it. Thus it won’t work because it would try to assign a ProfiledDbConnection to a i.e. SqlCommand class.

So a bit more work is required to match them.

The code for adapter scenario

1. Create a DynamicQueryEngine derived class. Note: this class is database specific, thus if you work with i.e. SQL Server you’ll find it in SD.LLBLGen.Pro.DQE.SqlServer.NET20.dll assembly.

public class ProfilingDynamicQueryEngine : DynamicQueryEngine
{
    protected override DbCommand CreateCommand()
    {
         DbCommand cmd = base.CreateCommand();
         ProfiledDbCommand pCmd = new ProfiledDbCommand(cmd, null, MiniProfiler.Current);
         return pCmd;
    }
}

Here the DbCommand creation is overriden. Note that I wrap the original cmd and pass a current MiniProfiler instance as arguments to ProfiledDbCommand constructor, while I pass a null for the connection instance because it will be assigned later.

2. Derive from DataAccessAdapter class. Note: this class is generated from a template and you’ll find it in DBSpecificLayer project generated by LLBLGenPro.

public class DataAccessAdapterEx: DataAccessAdapter
{    
    protected override System.Data.Common.DbConnection CreateNewPhysicalConnection(string connectionString)
    {
        DbConnection conn = base.CreateNewPhysicalConnection(connectionString);
        // return ProfiledDbConnection.Get(conn); Pre MvcMiniProfiler 1.9
        return new ProfiledDbConnection(conn, MiniProfiler.Current);
} protected override DynamicQueryEngineBase CreateDynamicQueryEngine() { return PostProcessNewDynamicQueryEngine(new ProfilingDynamicQueryEngine()); } }

Within CreateDynamicQueryEngine I pass the class I’ve created in step #1. CreateNewPhysicalConnection will return a wrapped connection.

Instead of using DataAccessAdapter you should use the one created in step #2 - DataAccessAdapterEx. That’s it.

Conclusion

As it turns out, integrating MvcMiniProfiler with LLBLGenPro is quite easy. And the required coding might be added to LLBLGenPro templates by modifying them, so you won’t have to manually add the same code each time.

Let me know if you have feedback.

Update 19.9.2011: Updated the code because MvcMiniProiler introduced a breaking change in v1.9 (instead of ProfiledDbConnection.Get static method a constructor has to be used - thanks for swift response from David from LLBLGenPro support team)

The slides and code from my “Making asp.net mvc applications strong typed” presentation

Yesterday I held a presentation (as the part of Bleeding Edge 2009 conference) on how to make ASP.NET MVC applications strong typed by using CodeSmith and CodeRush (actually by using its free DXCore part). Attendees were great and the presentation went well. Attached are the slides in Slovene and source code in C#.

If you are interested in the topic you might read my previous blog posts as well:

Thanks everybody for attending the presentation.

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


RenderPartialToString missing in ASP.NET MVC

If I had to pick the most important feature that is missing from ASP.NET MVC it would be RenderPartialToString. This method should render a partial view to a string and not to internal result. Why? Simple, when you have to do with AJAX and partial updates it is convenient to pack one ore more partial results into a JSON among with other related data.

So, RenderPartialToString would be my most important feature missing in ASP.NET MVC. Luckily ASP.NET MVC is very extensible and Kevin hacked up a solution. Make sure you also read the comments, specially one from Jon Kruger (where he fix a bug or two) and one from Kyle Simpson (where he describes a workaround around setting a proper content type).

Implemented all the code and fixes mentioned above I am able to return a JSON object containing more than one partial result and related data. But still, this looks like a hack and performance might be a problem (or not, no idea – did anybody check it out?). Anyway, I really hope that we’ll see RenderPartialToString in a future version or, even better, in a service pack – as soon as possible that is.

And of course, I've enhanced my strong typed views template with Render[PartialView]ToString strong typed methods as well.

Enhancing strong typed views in ASP.NET MVC

I’ve added new constructs for replacing

< % Html.RenderPartial("~/Shared/LogOnUserControl.ascx", model); %>
 
with these
< % Html.RenderPartial().Shared.LogOnUserControl(model); %>
 
where model argument is strong typed of course. There are other overloads as well.

Notes:

  • due to IE7 formatting issues I am adding an extra space after < character
  • I can’t get rid of parenthesis after RenderPartial due to extension methods limitation (there are no extension properties)

For more details read my previous article Strong typing views in ASP.NET MVC. The new template is attached below.

StrongTypedViews.zip (2.60 kb)

Other related articles:

Strong typing routes in ASP.NET MVC

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.

Strong typing views in ASP.NET MVC

Did you ever wonder why do you have to write code like:

public ActionResult SomeAction() {   ... return View("SomeView", model); }


instead of this beauty:

public ActionResult SomeAction()
{    
...
  return Views.Home.SomeAction(model);
}

Note the advantages:

  1. Path to view is strong typed. No more “SomeView”.
  2. model argument is strong typed as well. No more passing a wrong typed only to discover the error at runtime.
  3. You can’t pass a non-performing argument anymore. This happens when you pass a view name and that view isn’t stored where MVC expects it to be: in a default folder (with the same name as controller under Views folder). Instead MVC will search for the view using various file-system locations and thus loosing valuable time. Lesson learned from Whiletrue's ASP.NET MVC presentation at SLODUG.

Sounds good? If yes, read further.

Instructions

There is a single requirment: you’ll need (my preferred) template based code generator CodeSmith.

1. Create a root folder named CodeSmith in your ASP.NET MVC project.

2. Unzip the attached CodeSmith template named StrongTypedViews.cst and put it into the folder mentioned above. Include it into the project. The project should look like this:

stv1

3. Right click on CodeSmith folder and add a CodeSmith Project item next to the StrongTypedViews.cst item. Name it CodeSmith.csp.

4. Right click on CodeSmith.csp and select Manage Outputs entry. You should see a modal window like this:

stv2

5. Click Add Output button (first enabled one from the left).

6. Type in StrongTypedViews.cst in the text field of the Template group (or click […] button next to it and select StrongTypedViews.cst). Name field should be auto populated with the same name.

7. In the File group select the radio button near text box and type in ..\Controllers\ControllerBase.cs (or do the same using […] button).

8. Finally, type in the root namespace of your project, MvcApplication4 in my case, into the RootNamespace property on the right side. The complete entry should look like this:

stv3

 

 

9. Click OK and another OK. At this point Visual Studio might warn you that a file has been modified outside the source editor. That’s fine as the previous steps did actually modify CodeSmith.csp file. Click Yes to reload it.

10. Generate the strong typed views by right clicking CodeSmith.csp and selecting Generate Outputs. You should see a new file names ControllerBase.cs under Controllers folder, like this:

stv4

 

 

11. One final step remains. Derive your controllers from auto generated ControllBase class and that’s it. You are now free to use strong typed views. You’ll find them by calling Views property and the with the same structure as it appears in the solution explorer.

stv5

i.e. if you want to return Index view you would use syntax like:

return Views.Home.Index();

or you can pass it a model, through an overload of the Index() method. Note that the model argument is strong typed (or object if the type isn’t specified in the aspx file). The syntax is same for both “full” and partial views (remember, they are strong typed and they know what to return) – no need to call either View or PartialView method.

12. If your views do change and they will you’ll have to recreate ControllerBase.cs class. This is really simple. Save all the view files and re-run Generate Outputs on the CodeSmith.csp project (as in step 10.). You can also set code generation on each build if you prefer.

That’s it.

Implementation details

Read this chapter if you are interested in inner working of this approach.

There are three distinct parts of the generated code.

ControllerBase class

You are required to derive your controllers from ControllerBase class for a simple reason – it provides Views property which is a gateway to all the views. The other (internal) feature of ControllerBase is to provide access to both View() and PartialView() methods to generated code (they are protected internal).

The Views property returns a new instance of ViewFolder auto generated class. Code is pretty much self-explanatory and looks like this:

public partial class ControllerBase: Controller
{
... 
  public new ViewResult View(string path, object model)
{
    return base.View(path, model);
}
  public new PartialViewResult PartialView(string path, object model)
{
    return base.PartialView(path, model);
}
  protected ViewsFolder Views
{ get { return new ViewsFolder(this); } }
...
}

 

ViewFolderBase class

This class is implemented within ControllerBase class and servers as a base class to the classes generated for each folder

public class ViewFolderBase
{
  protected readonly ControllerBase controllerBase;
  public ViewFolderBase(ControllerBase controllerBase)
{ this.controllerBase = controllerBase; }
}

Its main purpose is to “transport” the ControllerBase reference from one class to the another. This transportation is required because the reference to ControllerBase is required by the final class in the chain to call either View or PartialView method.

Classes generated for each folder under Views folder

Each such class (ViewFolderBase derived) is named by the folder it represents and a “Folder” suffix. It represents a single folder in the Views folder structure in the solution. These classes are declared within ControllerBase to reduce their scope. They have a two set of properties:

  • Views properties where there are two overloaded methods for each view in that folder. One overload is without arguments and the other contains a single strong typed model argument (which might be an object when the model is not typed in the view).

i.e. Error view from Shared folder. Note the strong typed model argument in the second overload. Note also the full path to the aspx file.

	public ViewResult Error()
	{ return controllerBase.View("~/Views/Shared/Error.aspx"); 
	
	public ViewResult Error(System.Web.Mvc.HandleErrorInfo model)
	{ return controllerBase.View("~/Views/Shared/Error.aspx", model); }	
	

  • Subfolder properties which represents subfolders of that particular folder. The purpose is to allow chaining.

i.e. ViewFolder’s (representing Views folder) subfolders section:

	public AccountFolder Account
	{ get { return new AccountFolder(controllerBase); } }
	
	public HomeFolder Home
	{ get { return new HomeFolder(controllerBase); } }
	
	public SharedFolder Shared
	{ get { return new SharedFolder(controllerBase); } }	
	

 

 

Template properties

Besides the RootNamespace property mentioned in the step 8 of the instructions there are other properties.

stv6

  • ViewsFolder: relative path to the Views folder in the solution
  • AdditionalNamespace: namespaces you want to place (using statement) in the generated ControllerBase.cs class. This is useful because model type is just copied from the declaration in the view files. So if you don’t use fully qualified types then you should put those namespaces in this property.
  • IncludeControllersNamespace: includes the namespace (a shortcut to the putting this namespace into AdditionalNamespaces)
  • IncludeModelsNamespace: includes the namespace (a shortcut to the putting this namespace into AdditionalNamespaces)
  • RootNamespace: the root namespace of your project. It will be used to compose a namespace where ControllerBase will reside (.Controllers suffix is automatically added).

Performance considerations

The generated code should perform very well. On the positive side it forces full path to the view files so the file is found at runtime in first attempt. On the negative side there is some overhead because the chaining creates few classes and passes a reference to ControllerBase instance to them. It shouldn’t be a huge overhead, or better, I didn’t even notice it in my simple performance tests (10.000 times calling each page using single core).

  • using View(“About”): ~658 requests/s
  • using View(“AboutX”): ~580requests/s   (it searches the Home folder first and Shared folder next where it finds AboutX.aspx)
  • using Views.Home.About(): ~658 requests/s

Tests were simple but I am pretty much confident in the results. Note the performance drop in the second case when MVC does file searching. All in all the overhead is pretty non existing. And it is non existing if you take into considerations all the benefits.

But if you want full performance without overhead there is a way. Instead of chaining used one might opt for method name composition which would result in:

return Views_Home_About();

 

for the About view. This way there wouldn’t be any overhead at all. I prefer my approach but that’s subjective.

Final words

Being a “strong typed” developer I find this approach a good one from every perspective. It should reduce errors if not improve performances as well. And all this for free – no manual typing required.

In the open source spirit of the ASP.NET MVC I am giving the template for free. Use it as you wish. Let me know if you have made improvements though – I’d be interested in seeing them and perhaps incorporating them.

Comming next: strong typed routes.

StrongTypedViews.zip (2.02 kb)

Partial Output Caching in ASP.NET MVC updated

Thanks to Miha Valenčič I’ve found this great article about Partial Output Caching in ASP.NET MVC. It actually explains how to do ActionResult caching in ASP.NET MVC. It was exactly what I was looking for. I had the same idea as in this blog post but didn’t know about SwitchWriter method nor I was aware of ActionFilterAttribute. IOW I had the idea but I didn’t know how to implement it. Which the mentioned blog post does.

The encoding

Problem

While this approach works I immediately stumbled on an encoding issue. The cached output is always encoded with UTF-16 encoding which might not be always a good thing. In fact IE7 protested immediately that one can’t switch between encodings (my original encoding is UTF-8) just like that. Hm.

So I went looking who is the culprit and soon found out that it is the temporary StringWriter created in the method below:

public override void OnActionExecuting(ActionExecutingContext filterContext) { _cacheKey = ComputeCacheKey(filterContext); string cachedOutput = (string)filterContext.HttpContext.Cache[_cacheKey]; if (cachedOutput != null) filterContext.Result = new ContentResult { Content = cachedOutput }; else { _originalWriter = (TextWriter)_switchWriterMethod.Invoke( HttpContext.Current.Response, new object[] { new HtmlTextWriter( new StringWriter()) }); } }


See, StringWriter uses UTF-16 and won’t allow any other encoding to be set. Easily that is.

Solution

Luckily the solution is an easy one. It involves a StringWriter derived class that accepts any encoding, such as this one (from Jon Skeet’s message):

public class StringWriterWithEncoding : StringWriter
{
Encoding encoding;
public StringWriterWithEncoding(Encoding encoding)
{
this.encoding = encoding;
}
public override Encoding Encoding
{
get { return encoding; }
}
}

This new class should be used in creating that temporary writer, like this:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_cacheKey = ComputeCacheKey(filterContext);
string cachedOutput = (string)filterContext.HttpContext.Cache[_cacheKey];
if (cachedOutput != null)
filterContext.Result = new ContentResult { Content = cachedOutput };
else
{
StringWriter stringWriter = new StringWriterWithEncoding(filterContext.HttpContext.Response.ContentEncoding);
        HtmlTextWriter newWriter = new HtmlTextWriter(stringWriter);
_originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { newWriter });
}
}

And that’s it. New version of temporary writer will automatically use whatever encoding is set by HttpResponse.ContentEncoding.

The ContentType

Problem

The other problem involves ContentType not being cached. In my case I am testing with SyndicationFeed and ContentType has to be “application/rss+xml”. However it is ignored by the original caching mechanism where only response content is cached but not ContentType.

Solution

I’ll declare a new class that will store both content and content type.

class CacheContainer
{
public string Output;
public string ContentType;
public CacheContainer(string data, string contentType)
{
Output = data;
ContentType = contentType;
}
}

I’ll use this class to store cached content, like this:

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (_originalWriter != null) // Must complete the caching
{
HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(
HttpContext.Current.Response, new object[] { _originalWriter });
string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString();
filterContext.HttpContext.Response.Write(textWritten);
CacheContainer container = new CacheContainer(textWritten, filterContext.HttpContext.Response.ContentType);
        filterContext.HttpContext.Cache.Add(
_cacheKey, container, null, DateTime.Now.AddSeconds(_cacheDuration), 
Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}

See, I am caching both Response.ContentType and its content now.

The last step is to use the cached ContentType, like this:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_cacheKey = ComputeCacheKey(filterContext);
CacheContainer cachedOutput = (CacheContainer)filterContext.HttpContext.Cache[_cacheKey];
    if (cachedOutput != null)
{
filterContext.HttpContext.Response.ContentType = cachedOutput.ContentType;
filterContext.Result = new ContentResult { Content = cachedOutput.Output };
}
else
{
StringWriter stringWriter = new StringWriterWithEncoding(filterContext.HttpContext.Response.ContentEncoding);
HtmlTextWriter newWriter = new HtmlTextWriter(stringWriter);
_originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { newWriter });
}
}

There you go.

ActionOutputCacheAttribute.cs (3.51 kb)

My first ASP.NET MVC pet project – Walls Talking

This week I’ve been extremely busy. Besides my usual work that pays for my living I worked on my first ASP.NET MVC project: Walls talking. I followed ASP.NET MVC development for long time but never had time to do something on my own.

And just a few days before ASP.NET MVC RTM release I’ve downloaded RC2 and in a couple of days (in spare time, mind you) I’ve put together Walls Talking, a graffiti collection site. Actually the idea for this project comes from my Saša and I merely coded the site that implements the idea. It features a form for uploading graffiti photos and its description, about page, a home page listing last 10 entries and a syndication feed. So far. The site is under construction of course and I’ll add features as long as my time permits me.

I can say that ASP.NET MVC is a truly a great platform for building web sites. It feels so slick and clean and much easier than regular ASP.NET bloated wizardry. I am missing a few features (i.e. construction of URL links outside of the Views, StreamActionResult that can be cached, etc.) so far, but hey, it is only at version 1.0. And there are relatively simple workarounds for those missing features that I created.