ASP.NET MVC Robust HyperLinks

Sunday, September 05, 2010 / Posted by Luke Puplett / comments (0)

Motivated by ActionLink failing to produce proper MVC-style / / / links and, although RouteLink does work, I think route names and possibly all route logic should be kept out of the view, I decided to bake my own link maker, and make the links in the controller and put them in the model. Here’s how I did that.

I have a BaseSiteNameController in which helper methods go. To this, I added the following method:

public static string BuildLink(
    RequestContext requestContext,
    string routeName,
    string action,
    string controller,
    object routeValues)
{
    UrlHelper u = new UrlHelper(requestContext);

    var rvd = new RouteValueDictionary(routeValues);
    rvd.Add("controller", controller);
    rvd.Add("action", action);

    var httpContext = System.Web.HttpContext.Current;

    return UrlHelper.GenerateUrl(
        routeName, null, null,
        httpContext.Request.Url.Scheme, 
        httpContext.Request.Url.DnsSafeHost, 
        null, rvd, u.RouteCollection, 
        requestContext, true);
}

Note that I specify controller and action in the RouteValueDictionary even though GenerateUrl has parameters for controller and action. When these are used, it kicks out old-skool URLs – I think these params were designed to be used when a route name is not supplied. The GenerateUrl method smells like it wasn’t designed for general direct use.

GenerateUrl is the thing that does the magic; it looks up the route and works out how the URL should be structured.

It’s a static method because I also want to be able to call it from my models. Some of my models have sub-models and some of those have links. I figured that a View is for layout, and links kind of straddle both sides but the logic required to make them, tips them into the non-view side, in my opinion.

My models aren’t always created and prepared by my controllers. Some of my models contain a small amount of logic to populate themselves, so I want them to be able to call this method* when they populate their links (e.g. FavouriteBooksModel.AddBookLink)

Note also that the method takes a RequestContext. I now have to have this context available in my models, so I pass it down using a CustomerWebProfile class that I already use to flow important data into my models, such as TimeZone data. Each model has a CustomerWebProfile property (inherited via a base model).

*Except that my models don’t directly call BuildLink because I wrap these calls helper methods.

Under each action method, I add a method to build a link to the method. I do this because I can prevent the caller from having to know the action name, and so I can refactor it easily.

public static string BuildLinkToCustomerAccount(
    System.Web.Routing.RequestContext requestContext, string customerId)
{
    return BuildLink(requestContext, "Default", "CustomerAccount", "Account",
        new { data = customerId });
}

Now my models use these helper methods to make their links (code in model).

public string CustomerAccountLink
{
    get
    {
        return Controllers.PlannerController.BuildLinkToRecordOneTime(
            this.WebProfile.RequestContext, this.TransmissionKeys);
    }
}

If I change my controllers or actions, I can change the helper method, and renames can be done without worrying about strings in aspx pages!

My views now make links in the normal way, like this:

<a href="<%: Model.CustomerAccountLink %>" ><%: Html.GetLocalString("Your Account") %></a>


And if I change the model, my page won’t compile and I can sort them out before runtime.

Labels: , , ,

Finding a MemoryLeak in 30 sec [WinDbg]

Monday, August 30, 2010 / Posted by Luke Puplett / comments (0)

Supplementary to my last post on WinDbg, here’s how I found a memory leak (600Mb working set for a background app anyone?) in under a minute.

The only instructions are: create a dump of your process using Task Manager, then drag and drop it into WinDbg. Load SOS and then issue the commands in bold in the following screen dump.

WinDbg

So, –dumpheap stat gives all objects and the memory they’re hogging. Strings and Ints are always the most so ignore them and focus on the next largest ‘real’ object.

For me, a generic Dictionary is hoarding over 100Mb. I then use !dumpheap –mt {address} to dump out the method table for this dodgy type.

This spits out addresses for each object. Then I just pick a random one and use !gcroot to see which object is holding the reference open.

The entire chain of references is shown, and RecorderScheduler is right at the bottom. I look at this in my code and find I have a Dictionary of “bad items” which is holding references to the Microsoft Windows Media Center API, which has a memory leak – i.e. my code is fine but the MS API is apparently not designed to have objects held open for days.

Luke

P.S. Another thing I noticed was how easy it is to see the value of a string, and thus how bad it is to store passwords in plaintext.

Labels: ,

ASP.NET MVC 2 RedirectToSignin

Thursday, August 19, 2010 / Posted by Luke Puplett / comments (1)

If, for whatever reason, you cannot use the [Authorize] attribute on an action method, or as in my case, you have an unusual architecture, then this helpful method directs a visitor back to the sign-in or login screen and then re-runs the original action. It’s designed to work exactly as the AuthorizeAttribute works, but with the difference that you can do your own IsAuthorised logic within the method.

An application I’m working on has a WCF service which is where customers are logged-in. The MVC application simply packages up the web front end and ships all requests, CRUD ops, everything into service calls and then paints the results out via ASP.NET/HTML.

When a customer logs in to my MVC 2 app, it’s really just calling Login on the WCF Authentication Service – the MVC app doesn’t keep track of sessions and is truly stateless. The MVC app does, however, use forms authentication and so the cookie can say “yep, customer is signed-in” while the WCF service says, “Uh uh. This customer’s session expired.”

This means that my action methods with [Authorize] run, but then fail. I wrote this method to redirect the customer to the sign-in box, and then continue to execute the original action, using the returnUrl.

Code:


protected ActionResult RedirectToSignin(
    string returnAction, 
    string returnController, 
    object returnRouteValues, 
    RequestContext requestContext)
{
    UrlHelper u = new UrlHelper(requestContext);
            
    string returnUrl = UrlHelper.GenerateUrl(
        null, returnAction, returnController, new RouteValueDictionary(returnRouteValues),
        u.RouteCollection, requestContext, true);

    string baseAddress = String.Format("{0}://{1}"
        HttpContext.Request.Url.Scheme, HttpContext.Request.Url.Authority);

    return Redirect(String.Format("{0}/Customer/Signin?returnUrl={1}"
        baseAddress, returnUrl));
}

Now within my action method, if I get a null or a fault from my service, I return RedirectToSignin(xyz) instead of returning an error. After sign-in, the action is called again and all is good in the hood.

Labels: , , ,

The Shame of CSS (or HTML)

Tuesday, August 17, 2010 / Posted by Luke Puplett / comments (0)

Is the web built on the heroic patience of thousands of creative geniuses, or by a bunch of morons? This is what I'm left wondering after a second go at HTML and CSS.

As a programmer working in programming languages, I'm used to being a dictator of determinism.

That is to say that, what I instruct the computer to do, happens exactly as I dictate. I run the whole show. This affords me as much power as it does responsibility. If I get those instructions wrong, my programme will not compile. If my code runs, it is computationally perfect even if there are logical bugs. My world is mathematically beautiful.

Due to a problem with my web designer deciding to ignore me, I thought I could knock up a very simple two-column, header, content, footer type web-page in no time. I was wrong and the memories of why I hired a web dude in the first place, came flooding back.

For years now I've been reading the UK’s most popular web designer/developer rag .NET Magazine (nothing to do with that .NET, the irony being that the editor almost completely eschews .NET), and not once has the magazine cared to tell me that HTML is utter garbage.

Born out of SGML and XML, HTML differs in that its tags don't need to be closed. From its inception, HTML immediately repudiated it's first opportunity to work properly.

Although, to be honest, I'm not entirely sure whether it's not CSS that's to blame, since I don't know where HTML ends and CSS begins.

If CSS does the layout then CSS is to blame. I would take back my previous comment about HTML but like a couple of naughty children, they're both at fault even if the other one did it.

In retrospect, I see now that there were some clunking great CSS warning clues:

  • After 15 years of mainstream web content creation, no one has made a decent WYSIWYG designer.
  • CSS is often referred to as being "hand crafted" (in the way that chiselling wood is semi-random).
  • "Hacks" are the everyday vernacular of a web designer.

The simplest tasks are nigh on impossible. Like centering. Particularly centering some content in relation to some other thing to the side of it.

Moving something without nudging something else somewhere else you didn't want it or bringing some other thing along for the ride.

Grids. You used to be able to use Tables which worked really nicely but Health & Safety came along and told everyone that they must use a combination of DIVs and strong painkillers.

And fonts are out.

Please stare in amazement at this very simple attempt to put a logo in the corner of a page with a band of grey going along to top.

Designer

Looks alright doesn't it? I mean, the logo doesn’t have it’s alpha background but that’ll be alright in the real browser.

Here’s how to make this amazing page. You'd think to just put a logo image in the corner and make it a link. You can’t just make something a link, of course, so instead the HTML and CSS instructions say: stick a hyperlink in the corner and then set its background picture so it looks like a logo but have no link text and then fuck around a bit to make the link the same size as the background image and then add some margin. Also use someone else's 960 Grid System thing to shortcut the almost impossible process of aligning stuff.

As you go about "crafting" the page, the designer will show you what you want to see because it doesn't like it when you're mad, but really all your hard work looks like this:

IE8

Quite how Internet Explorer 8.0 - the same people that wrote the designer and Microsoft's best attempt to make a working web browser - manages to screw this one up so spectacularly is a mystery. Overlapping images? WTF?!

Okay, Firefox is the web developer's favourite PC browser, let's see what it looks like properly rendered.

Firefox

Yeah, erm. Almost. Firefox very nearly nailed this complex grey box and logo. Good attempt, gold star for effort. I have no idea how adding margin to my logo-cum-hyperlink managed to add a margin to the DIV that is the hyperlink's parent's parent but it's the equivalent to me doing a vasectomy on my granddad before my dad was born.

Of course stuff in the mark-up is never born. Not unless you're using a different language: XAML. I am used to XAML. I like XAML. Extensible Application Markup Language was written by the people that gave us the world's worst browser, however, XAML works perfectly.

In XAML, each tag is actually an instance of an object in memory. Each thing is created (born) when the XAML is processed, and things inside things, really do become the children of their parents. The system is tied to the underlying programming language which is compiled and so must be perfect.

I can create XAML elements in code, and I can create code elements in XAML. It’s a mark-up language for creating object graphs.

The XAML designer is perfect, stuff can be moved on the page without it writing the movements to the wrong place, and it shows reality.

By comparison, designing in HTML and CSS is like writing upside down with your wrong hand while blindfolded with only 4 lying bastards to assist you.

HTML 5 and CSS 3 aren't going to help much. They offer only a few extra commands to allow such extravagances as round corners and Flashless video.

To truly take the web into the future, the whole system needs to be bulldozed. As HTML 5 and CSS 3 have taken years and years to get this far (nowhere) then a new great system will never be the product of the committee. It would have to be a disruptive innovation from a team of just a few.

N.B. If there are unusual breaks throughout this document, it's because Windows Live Writer and Blogger can't decide how to format the HTML. Or maybe its the CSS.

Labels: , , ,

Email to Phil Haack on MVC Routing

Wednesday, August 11, 2010 / Posted by Luke Puplett / comments (0)

Hi Phil

Congratulations on MVC, I can say that it's the first major MS framework I've enjoyed in a while, due to its simplicity.

Can I suggest a non-matching style routing system for MVC 4?

The route-matching logic is prone to problems, see link, and although there may be an answer, I don't care; as a 'user' of your technology, I want to go home happy and make progress on my project. I just want it to work.

My idea is to have an attribute on each and every action method that sets the route, optional default values for the params and IsDefaultAction for the controller.

A full list of all controllers and routes can then be made and the need to 'match' is eliminated.

The only problem might be that attributes are fussy about using constants, so maybe this would suffice if the attrib won't take an anonymous type:

[Route(Format = "{controller}/{id}/{action}")]
[DefaultAction]
[DefaultParamValue(Param = "id", Value = "")]
public ActionResult ... (string id) { ... }

And for, controller-less and action-less URLs:

[Route(Format = "{year}/{month}/{day}")]
public ActionResult ... (int year, int month) { ... }

Which would throw because the day parameter is missing, making route misconfiguration easier to discover. The Controller and Action method to call is inferred from what the attribute decorates. If you wanted to allow it, the controller no longer needs to be named XyzController.

Furthermore, if someone defines two controller-less and action-less routes, both with 3 params, then this can be caught and thrown when the route table is built instead of only being discoverable when a request comes in.

For example, if I also add to a different method:

[Route(Format = "{country}/{case}/{agent}")]

Then it should detect that this conflicts with the one above. There's no controller name or action name in the URL to assist routing and both take 3 params.

I'm sure I've missed out some key things that the current system permits, but as I said, I don't want it to be smart and enigmatic. I want it to work, or clearly direct me to the problem when I mess up.

Thanks for listening.

Luke

Labels: , ,

Note to Self: Using WinDbg to See Memory Usage

Thursday, July 29, 2010 / Posted by Luke Puplett / comments (0)

How to list the memory being used in a .NET application, by type. This can be useful in finding memory usage problems (not always leaks in the strict sense). It's really pretty awful going back to commands and seeing pages of shitty numbers without comma digit grouping, but Microsoft seem not to care to produce a visual debugger.

Download the SDK for the version of Windows and the .NET Framework you have and install it.

Run your app.

Run WinDbg from the Start menu (search for WinDbg).

Start Task Manager and right-click the process and create a dump file. You can also attach to the process from WinDbg.

Drag and drop the .dmp file into the WinDbg shell.

Load the SOS.dll helper thingy using either one of the following commands in WinDbg. The first one instructs the debugger to load the DLL from the same folder as the mscorwks DLL was loaded from. The second is specific, check which version of the Framework your app runs under (I assume 4.0 for my app and forgot I hadn't upgraded it from 3.5).


.loadby sos mscorwks
.load c:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll

Successful commands don't always give any feedback - no news is good news.

All going well, you should now be able to issue some commands to WinDbg and start analysing the app. Enter the following command:



!dumpheap –stat

If you get "Failed to find runtime DLL" (clr.dll), 0x80004005 then it can be because the wrong SOS.dll was loaded, run the command .unload on its own and it should unload it and you can try loading the right version again.

Otherwise a whole heap of stuff will spew out. Heap. See what I did there? Ordered by total bytes consumed, column 3, the biggest hoarder of memory should be last in the list. System.String usually.

Here are a bunch of links useful to this subject.

Windows SDK Home

!dumpheap -stat explained

Memory Leak Hunting

Tracking Memory Leaks - Rico Mariani

Memory Usage Auditing for .NET Applications

Investigating Memory Issues

WinDbg Cheat Sheet

Labels: ,

Note to Self: Don't Let WCF (SvcUtil) Reuse All Libraries

Friday, July 23, 2010 / Posted by Luke Puplett / comments (0)

Another short post to remind myself for when I inevitably forget some of the nuances of my nemesis, WCF. This one regards the error below:

Warning 1 Custom tool warning: Cannot import wsdl:portType Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter Error: List of referenced types contains more than one type with data contract name 'Recorder' in namespace 'http://schemas.vuplan.tv/2008/11/vuserv/entities/core/user'. Need to exclude all but one of the following types. Only matching types can be valid references: "S26.Vuplan.Core.User.ClientRecorder, S26.Vuplan.Client.MediaCenter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" (not matching) "S26.Vuplan.Core.User.Recorder, S26.Vuplan.Core, Version=1.0.2.0, Culture=neutral, PublicKeyToken=80726da9f797f65e" (matching) XPath to Error Source: //wsdl:definitions[@targetNamespace='http://schemas.vuplan.tv/2008/11/vuserv/user']/wsdl:portType[@name='IUserService']etc. yeah yeah.

I don't know why the WCF team didn't add a proper UI to this. To see and configure the mappings between a service and your client app, as well as a more wizardy interface and perhaps a more verbose "here's what I'm doing now" style feedback system would save hours.

I have a core library in which a Recorder object lives. This core library is used on server and client and defines many entities and other pan-application helpers and types. The problem comes when I define a derived type of Recorder for the client to use, which has more client specific logic in it. I want to new up a ClientRecorder and then pass the thing back up to the cloud, so I add its Recorder DataContract, and that's when it all falls apart.

The SvcUtil has been instructed to reuse types in all referenced assemblies, so when it tries to map a Recorder contract to one of my types, it can't tell if I want to use the one in the core lib or the new client one.

I could rem out the DataContract line and reimport, this'd work, but the fix is to make sure that I don't try and reuse types in my client assembly.

The config page for the Service Reference doesn't allow excludes, so I have to tick almost all assemblies except the ones I definately know don't contain serializable classes. See pic:

Service Reference Reuse

By doing it this way, I should be able to hit Update at any point in the future and not suddenly be thrown out by the error and the massive knock on effect it has to compilation.

Labels: , ,