Scott Hanselman

Plug-In Hybrids: ASP.NET WebForms and ASP.MVC and ASP.NET Dynamic Data Side By Side

October 02, 2008 Comment on this post [32] Posted in ASP.NET | ASP.NET Dynamic Data | ASP.NET MVC | Web Services
Sponsored By

I wanted to write this post because there seems to be a lot of confusion about how ASP.NET WebForms, MVC and Dynamic Data all fit together. Folks may have WebForms applications and want to start looking at ASP.NET MVC but aren't sure if they need to start from scratch or not. Folks might look at Dynamic Data but mistakenly assume it's just for scaffolding administrative sites.

When you're in Visual Studio and you hit "File | New Project" you're given a number of choices. This is a starting point, but it easy to assume that this point is a fork in the road and you can mix them up.

File New Project Dialog

You can (and should) feel free to have Hybrid applications. You can have single Web applications that have all of these inside them (if it makes you happy):

  • ASP.NET MVC
  • ASP.NET WebForms
  • ASP.NET Web Services (ASMX)
  • WCF Services
  • ASP.NET Dynamic Data
  • ASP.NET AJAX

Here's an extreme example. I'll pick ASP.NET MVC Application to start with, but it doesn't really matter. If you're confused as to what web.config entries are required, just make one of each kind of project and compare the files with your favorite diff tool (mine is Beyond Compare).

Ok, so first I've got a Hello World ASP.NET MVC application:

Visual Studio Solution with just ONE MVC application

I'll add a quick LINQ to SQL database connection to the AdventureWorksLT database so I have something to query.

Next, I'll throw a quick ASMX Web Service into this ASP.NET MVC application that returns some data. I create it via File | New Item and select Web Service. I make a quick LINQ expression and a smaller class SmallProduct (a LINQ projection) that is returned. I also make it ScriptService, so I could call it via AJAX if I liked.

namespace Overkill
{
public class SmallProduct
{
public string Name { set; get; }
public string Color { set; get; }
public decimal Price { set; get; }
}

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Products : System.Web.Services.WebService
{
[WebMethod]
public List<SmallProduct> GetProductsByColor(string color)
{
AdventureWorksDataContext d = new AdventureWorksDataContext();
return (from p in d.Products
where p.Color == color
select new SmallProduct
{
Name = p.Name, Color = p.Color, Price = p.ListPrice
}).ToList<SmallProduct>();
}
}
}

What does this have to do with ASP.NET MVC? Nothing. That's the point. This is an ASP.NET 2.0 style ASMX Web Service with an ASP.NET AJAX ScriptService attribute using a .NET 3.x LINQ Query to return the data, all living in an ASP.NET MVC application.

Why doesn't ASP.NET MVC grab the request? Two reasons. First, there's an option on RouteCollection called RouteExistingFiles. It's set to false by default which causes ASP.NET MVC to automatically skip routing when a file exists on disk.

if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}

Because the default Route in Global.asax isn't greedy enough to care even if we were routing all requests:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

}

However, in my personal experience, File.Exists is a very expensive operation (very is relative…it's expensive because it hits the disk at all). A best (and example of attention to detail) practice would be to put in an IgnoreRoute call for those pages, directories, and/or HttpHanders. For example:

routes.IgnoreRoute("{myWebForms}.aspx/{*pathInfo}");
routes.IgnoreRoute("{myWebServices}.asmx/{*pathInfo}");
routes.IgnoreRoute("myCustomHttpHandler.foo/{*pathInfo}");
routes.IgnoreRoute("Contents/{*pathInfo}");

Here I'm ignoring .aspx, .asmx, a custom HttpHandler with a custom extension and the whole of the Contents folder. I might even want to set routes.RouteExistingFiles = true which would turn off the File.Exists check and put ALL the pressure for routing on my routes. I'll need to be more careful and explicit, but that's rarely a bad thing. You could also just structure your site such that all your non-MVC things live in their own folder. It's up to you.

Now, let me add a WebForm and *gasp* drag a GridView into it. Wow, I'm a bad person, I've just used the Designer. I was productive so, but what price my immortal soul? ;)

WebForm with a GridView

Seriously, though, use what makes you happy. This grid is kind of lame, so I'll add some ASP.NET DynamicData. However, while you usually see Dynamic Data from a File | New Application point of view, I'm going to just bring a DynamicDataManager control onto the page. You'll also want to confirm that you have DynamicData controls listed in your web.config:

<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.DynamicData" assembly="System.Web.DynamicData, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</controls>
</pages>

I'll add a few things to my WebForms page:

<div>
<asp:DynamicDataManager ID="DynamicDataManager1" runat="server" />
<asp:GridView ID="GridView1" runat="server" DataSourceID="GridDataSource"
AutoGenerateColumns="false" DataKeyNames="ProductID" AllowPaging="true" AllowSorting="true">
<Columns>
<asp:DynamicField DataField="Name" />
<asp:DynamicField DataField="ProductNumber" />
<asp:DynamicField DataField="Color" />
<asp:DynamicField DataField="ListPrice" />
</Columns>
</asp:GridView>
<asp:LinqDataSource ID="GridDataSource" runat="server" EnableDelete="true"
ContextTypeName="Overkill.AdventureWorksDataContext" TableName="Products"/>
</div>

In the Global.asax.cs, I'll add these two lines to let the DynamicData system know that we're working on this DataContext:

MetaModel model = new MetaModel();
model.RegisterContext(typeof(AdventureWorksDataContext), new ContextConfiguration() { ScaffoldAllTables = false });

Then, the most important part, I'll want to bring in the ~\DynamicData folder, since that's where DynamicData finds all of its templates. For this example, I really only need ~\DynamicData\FieldTemplates as I'm only using the smallest bit of Dynamic Data functionality.

To do this easily and quickly, I usually make a throwaway new DynamicData Web Application in another instance of Visual Studio, making sure to give it the same name (and hence, namespace) as the one I'm working on. Then I just drag that project's DynamicData folder over into my original application, and ensure that all the designer files and code-behinds are included in the project (Show All files, then right click each one, Include in Project). The rumor is that there will be a quick way in the future to bring a fresh DynamicData folder into an existing app.

Now, I'll hit my page again and then I get shiny Dynamic Data goodness.

DynamicData

At this point I've got a WebForm, Dynamic Data, and a totally random unused WebService living inside an ASP.NET MVC application. Of course, now this begs the question "Is this an ASP.NET MVC application."

Oh, you wanted MVC used also? ;) I'll add a quick Products method to the HomeController:

public ActionResult Products(string color)
{
AdventureWorksDataContext d = new AdventureWorksDataContext();
var smallProducts = (from p in d.Products
where p.Color == color
select new SmallProduct
{
Name = p.Name,
Color = p.Color,
Price = p.ListPrice
}).ToList<SmallProduct>();
return View("Products", smallProducts);
}

Then a quick view, making sure it's derived from ViewPage<List<SmallProduct>>:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Products.aspx.cs" Inherits="Overkill.Views.Home.Products" %>
<%@ Import Namespace="Overkill" %>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<p>
<ul>
<% foreach (SmallProduct p in ViewData.Model)
{ %>
<li><%=p.Name %> <%=p.Price.ToString("C")%></li>

<% } %>
</ul>
</p>
</asp:Content>

And that works also, visiting /Home/Products, ensuring there's a route that matches. I'll make this Route overly specific:

routes.MapRoute(
"ProductsWithColor",
"Home/Products/{color}",
new { controller = "Home", action = "Products", color = "Red" }
);

And it renders like this:

MVCList

I hope this helps and it's more clear now that it's just "an ASP.NET application."

You can feel free to mix and match. Not everyone can (or should) rewrite an existing ASP.NET application, so it is nice that everyone can use some new features in the same ASP.NET application as their existing functionality.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

October 6th - Seattle/Redmond/Bellevue Nerd Dinner

September 30, 2008 Comment on this post [10] Posted in Musings
Sponsored By

Are you in King County/Seattle/Redmond/Bellevue Washington and surrounding areas? Are you a huge nerd? Perhaps a geek? No? Maybe a dork, dweeb or wonk. Maybe you're in town for an SDR (Software Design Review) or a meeting with Steve Ballmer. Quite possibly you're just a normal person.

Regardless, why not join us for some Mall Food at the Crossroads Bellevue Mall Food Court on Monday, October 6th around 6:30pm?

Here's some links to help you remember and add this to your calendar. There's photos of previous Nerd Dinners up on Flickr thanks to Orcmid.

Feel free to invite your friends, bring your nerdy spouses and children - just tell them the details are at http://www.nerddinner.com.* Spread the word!

Add to your calendar

    I hope to see you there!

    * Which very soon will be more than just redirect to this post! ;)

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    jQuery to ship with ASP.NET MVC and Visual Studio

    September 28, 2008 Comment on this post [60] Posted in ASP.NET | ASP.NET MVC | Javascript | Microsoft
    Sponsored By

    I've done a series of four podcasts dedicated to JavaScript over the last month. Why? Because of this rockin' sweet announcement:

    Microsoft is going to make jQuery part of the official dev platform. JQuery will come with Visual Studio in the long term, and in the short term it'll ship with ASP.NET MVC. We'll also ship a version includes Intellisense in Visual Studio.

    The Announcement Blog Posts

    This is cool because we're using jQuery just as it is. It's Open Source, and we'll use it and ship it via its MIT license, unchanged. If there's changes we want, we'll submit a patch just like anyone else. JQuery will also have full support from PSS (Product Support Services) like any other Microsoft product, starting later this year. Folks have said Microsoft would never include Open Source in the platform, I'm hoping this move is representative of a bright future.

    jQuery Intellisense

    Visual Studio 2008 has very nice JavaScript intellisense support that can be made richer by the inclusion of comments for methods in 3rd party libraries. Today you can search the web and find intellisense-enabled jQuery files hacked together by the community, but we intend to offer official support for intellisense in jQuery soon.

     image

    JQuery is really complimentary to MS-Ajax. For example, we had been talking about writing CSS-Selector support, but figured, jQuery does it really well, why not just use it?

    JavaScript Libraries Play Well With Others

    I wanted to put together a little demo application that used jQuery to spice up a talk I've given on ADO.NET Data Services. The app would retrieve some data from a Bikes database, and would have some radio buttons to change the color queried.

    The whole application is a single static page. There's no code-behind and the only server-side work is the data retrieval from SQL. However, the concepts could be applied to ASP.NET WebForms or ASP.NE T MVC.

    Here's a one page app using:

    • ADO.NET Data Services and it's JavaScript Client Library
    • ASP.NET AJAX
    • ASP.NET AJAX Client Templating (Preview)
    • jQuery 1.2.6

    It looks like this:

    image

    Here's what's going on underneath. First, I'm retrieving data from SQL Server and I need it in JSON format. I'm  using the AJAX Client Library for ADO.NET Data Services to make a REST (GET) query to the back-end. To start with I'll just get the data...I include "datatable.js" as a client-side script and use Sys.Data.DataService() to make an async query. In JavaScript you can tell it's a Microsoft type if it's got "Sys." front of it.  All the client support for ADO.NET Data Services is in datatable.js.

    I'll be getting back dynamically created JSON objects that look just like my server-side data. In the query I'm asking for the top 5 results given a color.

    BTW, the first line of LoadBikes() is a little JavaScript syntax that says "if q isn't there, then make a q that equals "Red."

    <script type="text/javascript" src="Scripts/DataService.js"></script>    
    <script type="text/javascript">
    var bikes;

    Sys.Application.add_init(function() {
    LoadBikes();
    });

    function LoadBikes(q) {
    q = q || "Red";
    var svc = new Sys.Data.DataService("bikes.svc");
    svc.query("/Products?$filter=Color eq '" + q + " '&$top=5", OnProductsLoaded);
    }
    ...

    When I get back the results from the asynchronous query call, I could use string concatenation with the ASP.NET AJAX Sys.StringBuilder type to build HTML on the fly like this:

    var html = new Sys.StringBuilder("<ul>");
    for (var i = 0; i < result.length; i++){
    html.append("<li><div class=\"bikerow\">");
    html.append("<img src=\"bikes.svc/Products(" + result[i].ProductID + ")/Photo/$value\">" + result[i].Name + " " + result[i].ListPrice);
    html.append("</div></li>");
    }
    html.append("</ul>");
    $get("tbl").innerHTML = html.toString();

    There's MANY ways to get the exact same results in JavaScript when you introduce different libraries. There's tradeoff's between size, speed, maintainability, and your happiness. It's nice to pick and choose.

    Rather than using StringBuilder, I'll use the new (preview) ASP.NET AJAX 4.0 Client Template stuff from BLR, Dave Reed, Boris Rivers-Moore and Nghi Nguyen. This is more declarative and easy to maintain way to accomplish the same thing.

    <div id="bikes" class="sys-template">
    <ul>
    <li>
    <div class="bikerow">
    <img sys:src="{{'bikes.svc/Products(' + ProductID + ')/Photo/$value'}}"/>
    {{Name + ' ' + ListPrice}}
    </div>
    </li>
    </ul>
    </div>

    This is a declaration of what I want the table to look like with {{ binding expressions }} in curly braces. The img src= is an ADO.NET Data Services HREF to a product image in the database like "/bikes.svc/Products(740)/Photo/$value" that returns an image directly.

    I'll add ASP.NET AJAX's JavaScript library along with the Preview Templates script from CodePlex:

    <script type="text/javascript" src="Scripts/MicrosoftAjax.debug.js"></script>
    <script type="text/javascript" src="Scripts/MicrosoftAjaxTemplates.debug.js"></script>

    Then, when things are initialized, I'll $get() that template and make a Sys.UI.DataView and store it in a variable called "bikes" and when asynchronous call returns, I'll take the array of data from result and apply it to the DataView.

    <script type="text/javascript">
    var bikes;
    Sys.Application.add_init(function() {
    bikes = $create(Sys.UI.DataView, {}, {}, {}, $get("bikes"));
    LoadBikes();
    });
    ...
    function OnUsersLoaded(result){
    bikes.set_data(result); ...

    Now, I'll start leaning heavily on the jQuery library to change the background colors of just the even-numbered items in my list. I'll also add 100ms animation that draws a border and increases the font size of the item the mouse is over. Notice the "chaining" of the functions as I modify the div. Each method returns the jQuery object so you can increase fluency with chaining as much as you like. I'll also use jQuery to easily setup a group of click events on the radio buttons.

    The complete hacked-together code is this:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Friggin' Sweet</title>
    <link href="styles.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="Scripts/MicrosoftAjax.debug.js"></script>
    <script type="text/javascript" src="Scripts/MicrosoftAjaxTemplates.debug.js"></script>
    <script type="text/javascript" src="Scripts/DataService.js"></script>
    <script type="text/javascript" src="Scripts/jquery-1.2.6-intellisense.js"></script>
    <script type="text/javascript" src="Scripts/DataService.js"></script>
    <script type="text/javascript">
    var bikes;
    Sys.Application.add_init(function() {
    bikes = $create(Sys.UI.DataView, {}, {}, {}, $get("bikes"));
    $(".colorfilter").click(function(e) {
    LoadBikes($(this).val());
    });
    LoadBikes();
    });

    function LoadBikes(q) {
    q = q || "Red";
    var svc = new Sys.Data.DataService("bikes.svc");
    svc.query("/Products?$filter=Color eq '" + q + " '&$top=5", OnProductsLoaded);
    }

    function OnProductsLoaded(result) {
    bikes.set_data(result);

    $("ul li:even").css("background-color", "lightyellow");
    $("ul li").css("width", "450px").css("font-size", "12px");

    $("div.bikerow").mouseover(function(e) {
    $(this).animate({
    fontSize: "18px",
    border: "2px solid black"
    }, 100);
    }).mouseout(function(e) {
    $(this).animate({
    fontSize: "12px",
    border: "0px"
    }, 100);
    });

    }
    Sys.Application.initialize();
    </script>
    </head>
    <body xmlns:sys="javascript:Sys">
    <form id="form1" runat="server">
    <input type="radio" class="colorfilter" name="color" value="Red" />Red
    <input type="radio" class="colorfilter" name="color" value="Silver" />Silver
    <input type="radio" class="colorfilter" name="color" value="White" />White
    <input type="radio" class="colorfilter" name="color" value="Multi" />Multi
    <input type="radio" class="colorfilter" name="color" value="Black" />Black
    <div>
    <div id="tbl">
    </div>
    <div id="bikes" class="sys-template">
    <ul>
    <li>
    <div class="bikerow">
    <img sys:src="{{'bikes.svc/Products(' + ProductID + ')/Photo/$value'}}" />
    {{Name + ' ' + ListPrice}}
    </div>
    </li>
    </ul>
    </div>
    </div>
    </form>
    </body>
    </html>

    And it looks like this. Notice that I've got FireBug open and you can see three AJAX calls via ADO.NET Data Services with different queries. I'm hovering the mouse over the second bike, so its font is larger it has a border.

     image

    All of the scripts getting along happily. My code clearly sloppy, but this is a good example of how jQuery provides functionality that the Microsoft libraries don't. Things are better when the libraries are used together. JQuery complements ASP.NET, ASP.NET AJAX and ASP.NET MVC nicely and jQuery already has a large following within the .NET community. That's why we're going to ship, support and promote jQuery in ASP.NET, ASP.NET MVC and Visual Studio going forward.

    This was a simplistic example and I'm sure you've got better ideas, so I'd encourage you to go around the Net and get involved in the dynamic jQuery community. If you've used jQuery on an ASP.NET site, sound off in the comments.

    JQuery Resources

    Hanselminutes JavaScript Podcast Series

    * Thanks to Pablo Castro for his Bike database and ongoing help. Big thanks to Scott Koon for helping me debug my demo at 2am this morning using CrossLoop while kindly not asking what I was working on. Also thanks indirectly to Rick Strahl for his excellent .NET (and often jQuery) blog.

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    The Weekly Source Code 34 - The Rise of F#

    September 25, 2008 Comment on this post [14] Posted in Source Code
    Sponsored By

    First, let me remind you that in my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-fourth in a infinite number of posts of "The Weekly Source Code."

    F#, everyone is agog over F# and getting all functional. Again. ;) The F# September 2008 CTP came out a few weeks back and folks I'm talking to are digging it. Here's the goal:

    "F# developed as a research programming language to provide the much sought-after combination of type safety, succinctness, performance, expressivity and scripting, with all the advantages of running on a high-quality, well-supported modern runtime system."

    Looks like it's more than a research language as it's inside Visual Studio and looking pretty with syntax highlighting and intellisense and everything. This might be old news to you, Dear Reader, but F# is reaching a very significant level of polish. This release is moving F# to the level of being a peer of C# and VB. It's got its own F# DevCenter at MSDN. How's that for legit?

    Here's a great one paragraph explanation from Vertigo's Rick Taylor about what/why/how F#:

    "Unless you specify otherwise, everything in F# is immutable, much like the string construct in C#. This extends to areas that you might not expect. For instance, once you set an int to some specific value, that's it – you can't change it, unless you have marked it as mutable. The reason for this is because the language is primarily a functional one. Programs in functional languages contain functions which return values, which are then further used, etc – but each value or set of values is its own entity, and to change it while within a function is to produce a side effect, something undesirable in a functional language. In the strictest sense, functions return values but do not alter their parameters, or the outside world (which, incidentally, gives rise to the monad pattern mentioned earlier). F# follows these rules of functional programming, but also allows you to break those rules with the mutable keyword."

    What's the best way to jump into F#? Well there's a few great ways. First, a little blatant self-promotion. I've done two F# podcasts with smart F# fanboys and there's great .NET Rocks and Herding Code episodes too.

    Books

    • I enjoyed Robert's "Foundations of F#" book very much.
    • Also, if you're a freaking ninja rocket scientist, you can always read "F# for Scientists." Seriously. I dare you to buy that, in person, at Borders. Freaking scientists. (Seriously, though, it's a fabulous book and you can get some free excerpts here)
    • Don Syme, the creator of F# has "Expert F#" available and it's very highly rated.
    F# Blogs

    Code

    F# Eye for the C# Guy

    However, as a F# newbie with a few years of Haskell in the back of my head, the presentation that has clicked with me the most was Leon's F# eye for the C# guy PowerPoint. Scandalous, I know, as you may know Leon as the cruel bully who called me Hanselgirl in public recently at TechEd Australia. I promptly boxed his ears (it was more of a slap-fight actually) and there was also a public arm-wrestling. Between the two of us I'm sure there was the potential to splice together one normal-sized male arm, but I digress. You can download his deck here, but I've also taken the liberty to put it on SlideShare and embedded it here.

    F# Eye for the C# Guy
    View SlideShare presentation or Upload your own. (tags: f# c#)

    I had intellectualized slides 42 through 45, but the way he described it clicked. He did what I would do:

    "I worked this out using Reflector. I did not work this out from reading books or papers or listening to podcasts or quizzing intelligent people. A simple let statement is a static function, under the hood. When you see let, think function."

    Check out those few slides,. It's a great deck, and I'm sure Leon wouldn't mind YOU, Dear Reader, showing his talk at your local User Group or having a Programmer's Lunch at work and taking complete credit for it. That's what I fully plan on doing. ;) Suck it Bambrick!*

    Let's see three bits of code.

    First, something basic.

    Remember there are no variables. No side-effects.

    #light

    let sqr x = x * x
    let multiply x y = x * y

    print_int (sqr 3)
    print_int (multiply 3 4)

    // recursive function fibonacci series using pattern matching
    let rec fib x =
    match x with
    | x when x <= 0 -> failwith "An integer greater than 0 is required."
    | 1 -> 1
    | 2 -> 1
    | x -> fib (x - 1) + fib (x - 2)

    print_int (fib 15)

    // functions as values
    let add x y = x + y
    let a1 = add 3
    let a2 = a1 4

    print_int a2

    Second, something shiny.

    Units of Measure in F#. What? Andrew Kennedy's PhD thesis from 13 years ago just happens to be a feature in F#. He's done a three part series:

    Basically they've added static checking and inferences for units-of-measure. Not any specific ones, but the concept itself.

    "As far as F# is concerned, ft and m have nothing to do with each other. It's up to you, the programmer, to define appropriate conversion factors."

    Here's a screenshot from his blog that says a thousand words and showcases the FSharp.Math.PhysicalConstants namespace, along with the International System of Units (SI) namespace.

    image

    Also from Andrew's blog:

    "You can define mutually recursive measures using "and" to connect them and placing the Measure attribute immediately before the name of the measure:"

    type [<Measure>] km = 
    static member toM = 1.0/1000.0<m/km>
    and [<Measure>] m =
    static member toKm = 1000.0<km/m>

    Third, something silly.

    A fun little 2D Tron-Clone game from last year, written in only 182 lines of F# by Phil Trelford. You can download it from hubFS and read how he wrote it on the Applied Games Group Blog. The game even supports Xbox 360 controllers! Sweet.

    image

    Here's all 182 lines, not counting the first 4 lines of comments.

    //-----------------------------------------------------------------------------
    // LightCycles.fs Mini game using windows forms
    // 2007 written by Phillip Trelford
    //-----------------------------------------------------------------------------

    #light

    #if DIRECTX
    #R @"C:\WINDOWS\assembly\GAC_32\Microsoft.DirectX\2.0.0.0__31bf3856ad364e35\Microsoft.DirectX.dll" // Feb 206
    open Microsoft.DirectX.XInput // Required to read XBox 360 controllers
    #endif

    open System
    open System.Drawing
    open System.Windows.Forms

    /// Game states
    type GameState = | Start | Play | Over

    /// Form key handler type
    type KeyHandler (form:Form) =
    do form.KeyPreview <- true
    let keys = Enum.GetValues (type Keys) :?> (Keys [])
    let keysDown = Array.create keys.Length false
    let FindKeyIndex code = keys |> Array.find_index (fun x -> code = x)
    do form.KeyDown.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- true)
    do form.KeyUp.Add (fun e -> keysDown.[FindKeyIndex e.KeyCode] <- false)
    member this.IsKeyDown (keyCode:Keys) = keysDown.[FindKeyIndex keyCode]
    member this.AnyKeyDown () = keysDown |> Array.exists (fun x -> x)

    /// Player direction type
    type Direction = | Left | Right | Up | Down

    /// Player type
    type Player (color,startX,startY,direction,keys,keyHandler:KeyHandler) =
    let mutable x = startX
    let mutable y = startY
    let mutable d = direction

    member this.Color = color
    member this.X = x
    member this.Y = y
    member this.Keys = keys

    /// Reset player to start values
    member this.Reset () = x <- startX; y <- startY; d <- direction

    /// Updates player position
    member this.Update i =
    // Read keyborad
    let mutable newD = d
    let up, down, left, right = keys
    if keyHandler.IsKeyDown(up) then newD <- Up
    if keyHandler.IsKeyDown(down) then newD <- Down
    if keyHandler.IsKeyDown(left) then newD <- Left
    if keyHandler.IsKeyDown(right) then newD <- Right
    #if DIRECTX
    // Read XBox 360 controller
    let state = Controller.GetState(i)
    if state.IsConnected then
    let pad = state.GamePad
    if pad.UpButton then newD <- Up
    if pad.DownButton then newD <- Down
    if pad.LeftButton then newD <- Left
    if pad.RightButton then newD <- Right
    #endif
    /// Don't allow suicide move
    match (d,newD) with
    | (Left, Right) | (Right, Left) | (Up, Down) | (Down, Up) -> ()
    | _ -> d <- newD
    /// Update position with direction
    match d with
    | Up -> y <- y - 1
    | Down -> y <- y + 1
    | Left -> x <- x - 1
    | Right -> x <- x + 1

    /// Main form
    let form = new Form (Text="Light Cycles", Width=680, Height=544)

    do /// Layout for game window and status panel
    let layout = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount = 2)
    layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Percent, Width = 100.0f ) ) |> ignore
    layout.ColumnStyles.Add( ColumnStyle(SizeType = SizeType.Absolute, Width = 128.0f) ) |> ignore
    /// Play area in pixels
    let playArea = 500
    /// Game play area bitmap
    let bm = new Bitmap(playArea, playArea)
    /// Clears screen
    let ClearScreen () =
    using (Graphics.FromImage(bm)) (fun graphics -> graphics.Clear(Color.Black))
    /// Draws text to screen
    let DrawText s =
    using (Graphics.FromImage(bm)) (fun graphics ->
    let rect = new RectangleF(0.0f,0.0f,float32 playArea,float32 playArea)
    let align = new StringFormat(Alignment=StringAlignment.Center, LineAlignment=StringAlignment.Center)
    graphics.DrawString(s, form.Font, Brushes.White, rect, align)
    )
    // Initialise screen
    ClearScreen ()
    DrawText "Press any key to start"
    /// PictureBox to contain game bitmap
    let pictureBox = new PictureBox(Dock=DockStyle.Fill)
    pictureBox.Image <- bm
    layout.Controls.Add(pictureBox)

    let keyHandler = KeyHandler (form)

    /// Players array
    let players =
    [| Player (Color.Red,playArea/2+20,playArea/2,Down,(Keys.Q,Keys.A,Keys.Z,Keys.X),keyHandler);
    Player (Color.LightBlue,playArea/2-20,playArea/2,Up,(Keys.P,Keys.L,Keys.N,Keys.M),keyHandler) |]
    players |> Array.iter (fun player -> bm.SetPixel(player.X,player.Y,player.Color))

    /// Display player controls
    let statusPanel = new TableLayoutPanel(Dock=DockStyle.Fill, ColumnCount=1, BackColor=Color.DarkGray)
    players |> Array.iteri (fun i player ->
    let name =
    [| ((new Label (Text=sprintf "Player %d" i, ForeColor=player.Color)) :> Control) |]
    let up, down, left, right = player.Keys
    let controls =
    Array.combine [|"Up";"Down";"Left";"Right"|] [|up;down;left;right|]
    |> Array.map (fun (name,key) -> (new Label (Text=sprintf "%s '%O'" name key)) :> Control )
    Array.append name controls
    |> statusPanel.Controls.AddRange
    )
    layout.Controls.Add(statusPanel)
    form.Controls.Add(layout)

    /// Game play - returns true if there has been a collision otherwise false
    let PlayGame () =
    let collisions = players |> Array.mapi (fun i player ->
    player.Update i
    let x, y = (player.X, player.Y)
    let wall = x < 0 || x >= playArea || y < 0 || y >= playArea
    if wall then
    true
    else
    let bgColor = bm.GetPixel(x, y)
    bm.SetPixel (x, y, player.Color)
    players |> Array.exists (fun player -> let c = player.Color in c.R = bgColor.R && c.G = bgColor.G && c.B = bgColor.B )
    )
    pictureBox.Refresh ()

    match collisions |> Array.tryfind_index (fun x -> x = true) with
    | Some(i) -> i
    | None -> (-1)

    /// Current game state
    let gameState = ref GameState.Start
    let gameOverWaitCount = ref 200
    let r = new Random()

    /// Timer instance
    let timer = new Timer()
    timer.Interval <- 1000/50
    // Timer event
    timer.Tick.Add (fun _ ->
    match !gameState with
    | Start ->
    if keyHandler.AnyKeyDown () then
    ClearScreen ()
    gameState := GameState.Play

    | Play ->
    let i = PlayGame ()
    if i>=0 then
    gameState := GameState.Over
    gameOverWaitCount := 200
    DrawText (sprintf "Game Over - Play %d Lost" i)
    pictureBox.Refresh ()
    | Over ->
    // Shake screen
    form.Left <- form.Left + if !gameOverWaitCount > 150 then r.Next(5) - 2 else 0
    // Decrement Game Over wait
    decr gameOverWaitCount
    if !gameOverWaitCount <= 0 then
    gameState := GameState.Start
    players |> Array.iter (fun player -> player.Reset ())
    ClearScreen ()
    DrawText "Press any key to start"
    pictureBox.Refresh ()
    )
    timer.Start ()

    [<STAThread>]
    do Application.Run(form)

    Try to learn ONE new language each year!

    Related Posts

    * Oh, relax, Dear Reader. Leon and I are, like, totally, BFF. We're peas and carrots. Truly. The bastard.

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    Update on .NET Framework 3.5 SP1 and Windows Update

    September 23, 2008 Comment on this post [53] Posted in ASP.NET | ASP.NET Dynamic Data | Bugs | Programming | Windows Client
    Sponsored By

    The .NET Framework 3.5 SP1 included a bunch of new features, but as a Service Pack it also included a number of bug fixes and many improvements. These fixes included all aspects of the .NET Framework from ASP.NET to WPF and the CLR.

    Will the .NET Framework 3.5 SP1 break my 2.0 apps?

    Almost certainly not.

    Why Not?

    Remember that 3.5 (and 3.0 before it) all have the 2.0 CLR at their core. If you want excessive detail on this, I've got it. Because the 2.0 CLR is the engine underneath and 3.0 and 3.5 are primarily additive*, there's inherently high application compatibility between these releases.

    Realize that 2.0, 3.0 and 3.5 are not different products, no matter what anyone says. They are not different "Side By Side" releases, like 1.x and 2.0 were. They are evolutionary; if anything, our naming could have been better (you think?), but rather each adds functionality to the one before it. They are really additive releases to the same core product.

    But, I've hit an edge case…

    We’re committed to application compatibility. However you may have heard or reported issues or bugs around 3.5 SP1 and I’ll go into how we’re dealing with those below. Most of these are corner-case/edge-case situations.

    It may be cold comfort when it’s your bug and your company and it sounds like a marketing line, but it’s true. There are a lot of resources working to minimize impact to you.

    I’ve now been on both sides, when working in a large ISV and trying to get a hotfix, and now on the inside trying to keep compatibility while keeping things secure and correct. There is a massive amount of unit and integration testing that goes into the .NET Framework (that includes all technologies under that umbrella).  That means that every effort is made not to break stuff. That’s why Visicalc still runs nicely on my Vista x86 machine (although I can’t run OS 9 apps on OS X anymore, interestingly ;) ) 

    Software testing is a combinatorial problem, and as such all software has bugs, but sometimes when a bug comes back it’s called a “regression.” That means it was fixed before, and now it doesn’t. Sometimes folks call a new bug a regression their software worked before and it doesn’t now. This might be because they relied on an incorrect behavior that was later corrected, or that it was just a bug.

    It IS possible that something could break, so as with all SP’s you should do compatibility testing to make sure you’re not hitting an edge case.  If you are affected by a bug at some point, we’re trying to get you a very fast response. Notice in the table below there’s a “How Found” column. You can report on Forums, contact PSS or use Connect to report bugs.

    When will the .NET Framework 3.5 SP1 be pushed to Windows Update?

    Later this year, probably November-ish, the .NET Framework 3.5 SP1 will begin show up on Windows Update in a rolling and throttled fashion so that all machines that have .NET 2.0 or higher will be automatically upgraded to 3.5 SP1.

    If you’re an ISV or Hoster, you might be concerned that you’d wake up one day and find machines updated to 3.5 SP1 via Windows Update before these bugs are fixed.

    There will be an update/patch made to .NET 3.5 SP1 before it goes live on Windows Update. We are holding SP1 on Windows Update (Microsofties call it “WU” or “Woo”) until this patch is finalized. 

    That patch is called a GDR, or General Distribution Release, coming for .NET 3.5 SP1. A GDR is a Microsoft TLA (Three Letter Acronym) for an update that is for everyone. This update’s goal is to fix bugs that have been found in .NET 3.5 SP1. Many of these bugs were found by the community and reported on the Connect site.

    We won’t push .NET 3.5 SP1 to WU until everyone feels confident it’s solid.

    I know if you have a particular bug on Connect that you’re watching, you might be a little frustrated and be wondering what its status is. We’re working on getting the Connect Bugs updated and lots of folks (myself included) are trying at every turn to increase transparency. This blog post is an example. If they stop abruptly, I’ve finally been fired for them. ;)

    Transparency

    Sometimes your app might break and the issue isn’t “fixed,” but closed with “By Design” or “Workaround.” This can be frustrating (believe me, I know) but some fixes can break other things, and there’s always security to consider. In the near future I’m going to try to dig into some really icky details of a few of the more interesting bugs and get some color commentary on them. I’m going to encourage the other teams to do the same. I know the BCL team had a few interesting issues and have expressed an interested in digging in and blogging some wonky technical details.

    If you don’t want a bunch of details, you can stop reading now.

    Wonky Technical Details

    In the interested on transparency, here’s some of the bugs I’m tracking for this GDR. Note you can find most, if not all, of these bugs/issues on Connect and each team will be updating those with as much details as they have. Watch the issues there for the most up-to-date information we've got. If you feel something isn't getting attention, let me know and I'll poke the right manager.

    Title

    Product Unit

    Details

    How Found

    .NET 3.5 issue - Dynamic Data Issue

    ASP.NET

    Dynamic Data fails on Entity Framework data models that contain 1->0..1 and *->1 database relations with an error like "'System.Web.UI.WebControls.EntityDataSourceWrapper' does not contain a property with the name 'Orders.OrderID'". These types of relationships occur in many databases including Northwind and AdventureWorks.

    The error is caused by a naming mismatch that Dynamic Data has with the wrapper objects being returned by the EntityDataSource. We have a temporary fix available at: http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16367 which replaces the data model provider with one that names the properties correctly.

    3rd party Forum

    Hidden files/folders inside App_Browsers are not ignored

    ASP.NET

    This customer applied FrontPage Server Extensions (FPSE) to the site.  Normal behavior is to add metadata files inside _vti_cnf folders for each file in the site.  Therefore, inside App_Browsers folder, after applying FPSE, we get a hidden folder called _vti_cnf that contains the file called BrowserFile.browser
    Trying to parse that file will result in this error, since this is not a real .browser file, but instead just a metadata file.

    The workaround for now is to delete _vti_cnf folder, but we'll fix this.

    PSS DTS Issue

    After installing .NET 3.5 SP1, a web site using a derived version of the UpdateProgress control may encounter the following exception: “A ProgressTemplate must be specified on UpdateProgress control with ID ‘id’.”

    ASP.NET

    In the .NET Framework 3.5, the UpdateProgress control enforced the requirement of a ProgressTemplate from its PreRender routine. A derived UpdateProgress control could subvert that requirement by overriding OnPreRender in the derived control, and avoiding calling base.OnPreRender. In the .NET Framework 3.5 SP1, the UpdateProgress control now uses CreateChildControls to instantiate the ProgressTemplate, causing the requirement to be enforced at a different point in the page life cycle, and preventing the OnPreRender technique from subverting the check.

    Other

    SGEN and Obsolete attribute

    WCF

    ASMX web methods do not return serialized results. What the customer does is to SGEN an assembly that contains some types with [Obsolete(IsError = true)].  What he sees is SGEN throwing an error and  refusing to generate a serialization assembly. 

    Here is the message you get from SGEN:
    Microsoft (R) Xml Serialization support utility
    [Microsoft (R) .NET Framework, Version 2.0.50727.1432]
    Copyright (C) Microsoft Corporation. All rights reserved.
    Error: Unable to generate a temporary class (result=1).
    error CS0619: 'SGenTest.Program' is obsolete: ‘Testing.'
    error CS0619: 'SGenTest.Program' is obsolete: 'Testing.'

    Other

    .NET 3.5 SP1: JIT generates incorrect code in managed C++ edge case

    CLR

    This is caused by JIT optimization changes we made in 3.5SP1. We promote some fields to registers when we shouldn't. Limited to structs or classes with four or fewer scalar fields, none of which are managed object references.The scope is additionally reduced in that this bug only manifests when using the cpblk or initblk instructions, which are only emitted by the managed C++ compiler.The issue does apply to both JITted and NGEN'd code.

    MSConnect

    Obfuscated 1.1 assemblies may fail if they override certain methods in the Framework

    CLR

    1.1 code that used to run successfully on 2.0 will no longer run on 3.5 SP1 (throws a MissingMethodException).

    The underlying problem is as follows. Let’s say you have a 1.1 Framework type that overrode a virtual method, then stopped overriding it in 2.0. This should not be a breaking change, because an implementation of the method still exists (somewhere earlier in the inheritance hierarchy). However, if a customer overrode that method, built against 1.1, then obfuscated the code, the obfuscated code no longer works when run against 2.0 SP2/3.5 SP1.

    Obfuscators that are using undocumented techniques to accomplish their task tend to get broken when we optimize things. Workaround is to not obfuscate these few methods, usually by marking them with an attribute. Long term workaround is for the obfuscator to play nice.

    3rd party Forum

    How .Net 3.5 SP1 broke Rhino Mocks (ExecutionEngineException...)

    CLR

    This bug has been reported to break Rhino Mocks, an open-source, mock testing framework.The specific impact to Rhino Mocks is that it breaks its support for F#, C++ and Spec# It has 71 validations and 119 ratings (avg. 4.9), which is high for a Connect bug
    http://www.ayende.com/Blog/archive/2008/08/13/How-.Net-3.5-SP1-broke-Rhino-Mocks.aspx is the blog which discusses the issue with many community comments This is likely the source of much of the validations.

    In 3.5 SP1 we removed a null check as a side-effect of changes we made to support ASLR. As a result, a failure case we used to handle now results in an AV in the runtime which manifests as an ExecutionEngineException and process termination

    MSConnect

    Serialization hangs or throws an OutOfMemoryException

    CLR

    This issue is also mentioned on the Rhino Mocks web site, where another breaking change in 3.5SP1 was reported. It is unclear as to whether or not this issue also breaks Rhino Mocks test software.

    Due to changes in the type system, types with the following criteria:Generic type instantiated with a reference typeImplements ISerializableContains a static field.

    MSConnect

    AutoCommit behavior change in Oracle Transactions in .Net Framework 2.0 SP2

    DPR

    Existing applications which rely on transaction behavior to work correctly will break causing data corruption.

    MSConnect

    EntityDataSource runtime: Not able to display Dynamic Data's FK Ids in a 1:0..1 relationship

    DPR

    This breaks web sites/applications created with ASP.NET Dynamic Data because Dynamic Data assumes the property descriptors exist and uses them to obtain labels for their links. The only known workaround requires manually editing Dynamic Data's templates (for wich each site/app has private copies) to capture the exception. The exception generally of the form:
    [HttpException (0x80004005): DataBinding: 'System.Web.UI.WebControls.EntityDataSourceWrapper' does not contain a property with the name 'Manager'.]

    3rd party Forum

    SaveChanges doesn't support inserting an entity and binding as a single operation

    DPR

    If the resource takes part in a relationship (e.g. 1:1), then while inserting a new instance of the resource, we need to send the link also, since the link is required at the database level. The client does not send the links while inserting such resources

    3rd party Forum

    DataServiceContext: DeleteObject on an entity with a link fails

    DPR

    This impacts deletion of any resource which has reference properties.

    A link for a reference property should never be in Added or Deleted state. Instead, it should always be modified with reference target to null in case of delete/non-null in case of add/update.

    3rd party Forum

    .NET 3.5 SP1 breaks use of WPF under IIS

    WPF

    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=361469

    MSConnect

    Relative Hyperlinks inside XPS documents broken and causes app to Crash

    WPF

    Fixed.

    MSConnect

    Regression: Geometry.Combine creates more points/segments than before in 3.5 sp1

    WPF

    Performance regression in scenarios with Geometry (drawn shapes).

    Other

    Related Posts

    * Remember, if you're running around edge cases and are concerned, you can happily target 2.0 and 3.0 from VS2008 and use a CodeAnalysis Rule to make sure you're only calling methods for your targeted framework.

    About Scott

    Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

    facebook bluesky subscribe
    About   Newsletter
    Hosting By
    Hosted on Linux using .NET in an Azure App Service

    Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.