Tuesday, April 13, 2010

ASP.NET MVC Domain Routing and areas

Wow, it's been a while since I've posted! I've got a good one (at least I think so) for this post. I've been messing around with ASP.Net MVC over the last few weeks and really love the control and simplicity of it all. So naturally I would like to re-create some existing websites I manage using MVC. The problem is I have a shared hosting account with GoDaddy so I need to host all sites with one account and route differently depending on which domain is in the url. I thought of using the new areas support in MVC2 but did not like the idea of having the area name in the url. My ideal solution would route 'www.test.com' to the 'test' area and 'www.test2.com' to the 'test2' area without having 'test' or 'test2' show up in the url.
First I found this post that describes how to create a DomainRoute class that will allow you to include the domain in the route. Pretty cool. The next step was to create some methods that allow me to include the domain when mapping a route in the area registration. I created two extension methods for that.
   10 public static class DomainRouteExtensions
   11 {
   12     public static Route MapRoute(this RouteCollection routes, string name, string domain, string url, object defaults, object constraints, string[] namespaces)
   13     {
   14         if (routes == null)
   15         {
   16             throw new ArgumentNullException("routes");
   17         }
   18         if (url == null)
   19         {
   20             throw new ArgumentNullException("url");
   21         }
   23         DomainRoute route = new DomainRoute(domain, url, defaults, new MvcRouteHandler())
   24         {
   25             Defaults = new RouteValueDictionary(defaults),
   26             Constraints = new RouteValueDictionary(constraints),
   27             DataTokens = new RouteValueDictionary()
   28         };
   30         if ((namespaces != null) && (namespaces.Length > 0))
   31         {
   32             route.DataTokens["Namespaces"] = namespaces;
   33         }
   35         routes.Add(name, route);
   37         return route;
   38     }
   40     public static Route MapRoute(this AreaRegistrationContext context, string name, string domain, string url, object defaults, object constraints, string[] namespaces)
   41     {
   42         if (namespaces == null && context.Namespaces != null)
   43         {
   44             namespaces = context.Namespaces.ToArray();
   45         }
   47         Route route = context.Routes.MapRoute(name, domain, url, defaults, constraints, namespaces);
   48         route.DataTokens["area"] = context.AreaName;
   50         // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
   51         // controllers belonging to other areas
   52         bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);
   53         route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;
   55         return route;
   56     }
   57 }
Basically, these two extension methods are just slight modifications to the existing MVC methods that allow me to include a domain string and replace the standard Route object with a DomainRoute object. After a little debuging, this worked well with one small change to the DomainRoute.GetRouteData method by adding the DataTokens to the returned data object
    1     foreach (var item in this.DataTokens)
    2     {
    3         data.DataTokens.Add(item.Key, item.Value);
    4     }
    5 }
    7 return data;
Now that that is all done, all that is left is to add a route in the area registration.
   19 public override void RegisterArea(AreaRegistrationContext context)
   20 {
   21     context.MapRoute(
   22         "KMI_Default",
   23         "test.kmi.com",
   24         "{controller}/{action}/{id}",
   25         new { controller = "Home", action = "Index", id = UrlParameter.Optional },
   26         null,
   27         null);
   28 }
And thats it. The two nulls are needed to make sure the correct overload is called and the domain string is not mapped to the constraints since it is of type object. Do this will a couple more areas and multiple domains mapped to the same root folder in IIS can route to different areas in the MVC application. A little more work needs to be done to make it a complete solution. As it stands right now, the DomainRoute class does not support constraints.

Saturday, February 9, 2008

Why I'm a Microsoft man

Alright, so I've heard all the Microsoft bashing, and I agree with some of it. They may focus on making the OS look cool instead of actual stability (Vista), they may make lots of money. Let me tell you why I am a Microsoft fan even though I would love to be a Linux geek. Ease of use, plain and simple. Linux is trying to become more user friendly (Ubuntu for example) and I applaud them for that. If they ever want to put a dent in Microsoft's desktop OS market, that's what they need to do. Now let me get to the real motivation behind this post. I love the .net framework and Visual Studio. The object oriented programming class I am taking right now requires that we create a simple web application to reserve concert tickets. Perhaps I'm just not as familiar with JSP as I am with ASP.Net, but I would have been done with this assignment long ago if it was in ASP.Net. Create a dataset, drag a few gridview controls on some pages and viola! I'd be done. Instead I'm pulling my hair out trying to transfer the ResultSets from a servlet to the JSP page and then looping through the resultset to create the page. What a mess. I know there will be those out there that will say that JSP allows a finer degree of control, is more powerful, or is more efficient (or insert argument here). Quite frankly, I don't care about that. To me the .net framework is living up to exactly what Microsoft created it for; to reduce development time. They provide the common functions that you need (gridview), still allow you to customize it, and great tools to work with the code (Visual Studio). That's why I'm a Microsoft man.

Thursday, October 4, 2007

ClickOnce upon a time...

A long time ago, in a land far away.. Ok, so it was only a year or two ago and my desk hasn't moved, but it was a few projects ago. I had built a utility that processes the data we collect using our Faro measuring arms. The supplied software didn't have the ability to include the data we needed to filter the data, such as the operator, shift, etc. My utility program picks up the comma separated file that the Faro software saves and prompts the user for this information and appends it to the file before it sends it to the appropriate location. When I created this application we were only using it on one machine so updates weren't much of an issue. Recently we decided to start collecting data in our stamping department with a Faro arm and the request was made that we also collect attribute data along with the dimensional data (defects, number of holes, etc.). So I have been extending a copy of my utility to collect this information as well and store it in an Access database. Now that we will have an machine running this application on the opposite end of the shop floor (and I am lazy), deployment has come back to the front of my mind. I had considered using Microsoft's ClickOnce architecture with the last project, but at the time it was still new, I new nothing about it, and it wasn't really an issue. If you just mess around with ClickOnce without any reading, it seams like alot of smoke and mirrors (invisible directories, unreadable directory names, shortcuts to nowhere). But after some research, I think it will definitely be the way to go from now on. I will probably convert the original application over sometime soon as well. We have had several updates recently and it would be nice if I didn't have to do anything on my side, just click "Publish". And I lived happily ever after. THE END.

Tuesday, October 2, 2007

ScribeFire rocks

While looking for a better way to post to my new blog, I stumbled across the FireFox add in called ScribeFire. What an awesome application! I can edit my blog while surfing quickly and easily. The only downside I have found so far is lack of support. The support forums on their website is full of unanswered questions and while it provides support for plug-ins, there are no plug-ins on their site or information on how to create them. Granted, this may be due to the fact that the development recently changed hands. Either way, it is definitely worth checking out and I am looking forward to future updates.

Bindable List solved and IIF

So my investigation found that the ComponentModel namespace contains classes and such to work with components. Not very helpful in answering my question unless you know that components is another name for controls. So this namespace contains classes and such for working with controls, including binding them to data. So I guess it does make sense that the BindingList(Of T) is placed in this namespace. I'm still not sure that I like the idea that there is no reference to this type in the System.Collections.Generic namespace. I know that would have saved me some time and head scratching. Oh well, at least I know it's there now. Just 15 minutes of my life I will never get back.

On another note, after getting a null reference exception when using the IIF() function in vb, some searching led me to find that since the IIF function is a built in function and not a operator, it's arguments get evaluated before they are sent to the function. That explains my exception. Here's what I was trying to do:

Dim sb As New System.Text.StringBuilder
sb.AppendFormat("Name: {0}", IIf(object Is Nothing, String.Empty, object.name))

Basically I was trying to do an inline test to prevent a null reference. Unfortunately it didn't work. Here's what I ended up with:

sb.AppendFormat("Name: {0}", getName(object))

Private Function getName(ByVal o As Object) As String
    If o Is Nothing Then
        Return String.empty
        Return o.name
    End If
End Function

Or at least that's the dumbed down version. My actual situation was slightly more complex. Apparently, the functionality of the IIF statement is something that the VB team was looking at in VB9. I haven't been able to find any reference to any actual implementations in the new version yet, but I hope they did something. With each version VB is continually growing and becoming a stronger language. I agree with some of the detractors that earlier versions of VB were not as powerful as other languages, but I think now it is quickly gaining ground. Personally, I have been using vb in different forms (VBA, VB6, and now VB.Net 2005) for several years and love the fact that I can use most of the same syntax whether I am writing an Excel or Access macro, a full blown .Net application, or an ASP.Net web application. I know you can use C# for web apps and I believe you might even be able to use it with the newer versions of Office. I still like the readability of VB. Why do I have the tell the compiler where my lines of code end? Why would I want to add extra characters to enclose code blocks?

One last gripe. While reading a post on the IIF problem, there were several comments on the post about how C# is better or that vb is an inferior language. I just wish that every post that even comes close to pointing out a problem with VB didn't end up as an argument over which language is better. Each language has it's own features and bugs. Just because you can do one thing better in a different language doesn't make it 'better'. If you like it, use it. If you don't, use yours and let us use ours without sticking your nose in. If you hate VB or think its inferior, why are you even reading a post about VB? We all have our reasons for choosing the tools we use. If VB ever becomes more of a hindrance than a help for what I need, I'll be the first to switch. But for now it serves my purpose better than any other, and I have looked. Just like any debate on religion or the Ford vs. Chevy battle, its all a matter of opinion.

EDIT: After posting I found out that they did add to the language to fix the IIF problem in VB9.