Visual Studio 2008 and .NET 3.5 SP1 Beta

13 05 2008

The blogs are abuzz this morning after the first beta release of the VS2008 and .NET 3.5 SP1.   Download it here.

In my opinion, this isn’t a service pack—this is a new version!

There are quite a few bug fixes (what you normally associate with a service pack), but also a huge list of new additions and improvements.

From Somasegar:

Traditionally our service packs address a range of issues found both through customer and partner feedback as well as our own internal testing.  While this service pack holds true to that theme and delivers updates for these types of issues, it also builds on the tremendous value that Visual Studio 2008 and .NET Framework 3.5 deliver today and enables an improved developer experience by adding a number of additional components that cover a range of highly requested customer features. For example, the service pack is the first release for Visual Studio 2008 that delivers full support for SQL Server 2008 and the ADO.NET Entity Framework.

I’ve posted a few links at the end of the post to the more extensive sources right now, take a look and get ready for the plunge.

So, what am I most excited about?

  • ADO.NET Entity Framework – I’m hoping that the “real” release motivates Oracle to develop provides for the entity framework and my dream of LINQ-esque connections to Oracle will be realized.
  • ASP.NET Routing Engine – As the MVC framework gets closer to a production reality, it’s very motivating to see the underpinnings already in place.
  • VS2008 Performance Improvements – Anything has to be an improvement. :(
  • JavaScript Code Formatting – Sweet, now if I can only get JavaScript intellisense to work. :(
  • LINQ Debug Support – Very nice, love seeing the generated SQL right there at debug time.

There are also lots of updates to WCF and WPF.  Hopefully this summer I’ll have more time to use these .NET 3.0 technologies and maybe be a bit more excited. ;)

Visual Studio 2008 GUI/Tools

The Web Developer Tools team has released a comprehensive list of designer bug fixes, IIS templates and modules, formatting changes, intellisense upgrades, and more on their blog.

MVC and URL Routing

Phil Haack details the effects of the URL routing changes on the MVC Preview releases as well as how it affects the upcoming Preview 3.

Everything

ScottGu, as always does an excellent job tying everything up together—designer, framework, and tooling.

Now, if ReSharper 4.0 would EVER get to RTW before we’re ready to VS2009, it’d be super!





Adding Parameters to the Generic BindListControl Method

25 03 2008

Yesterday, I blogged about a generic method I created that could take a series of parameters and bind most any type of IEnumerable data source to most any type of DataBoundControl control.  I needed a simple and portable method (so that I could eventually toss it into the base helper library I use).

Unfortunately, the post didn’t cover something quite common—what happens when your data methods have parameters?  Integers, strings, even objects (passing a User object, etc).

For this, I’ll be modifying our “third attempt,” which, for now is, is the final version of the code.  Here’s the change. 

protected void BindDataControl

<TDataControlType, TEnumerableType, TDataSourceClass>

       (bool hasBeenModified, string sessionVariable,

              TDataControlType dataControl,

string dataSourceMethod,

              TDataSourceClass dataSourceClass,

object[] methodArguments)

where TDataControlType : DataBoundControl,

              new() where TEnumerableType : IEnumerable,

             new() where TDataSourceClass : class

{

// If session is null or has been modified

       // (thus invalidated), update the session state.

       if (Session[sessionVariable] == null || hasBeenModified)

       {

              // Invoke the specified method that

//creates our data source.

             var data = dataSourceClass.GetType().InvokeMember(

                     dataSourceMethod,

                    BindingFlags.InvokeMethod |

                    BindingFlags.NonPublic |

                    BindingFlags.Public |

                    BindingFlags.Instance,

                    null,

dataSourceClass, methodArguments);

// Add it to session.

             Session.Add(sessionVariable, data);

}

 

// Read the data from session and bind the data control.

dataControl.DataSource =

(TEnumerableType)Session[sessionVariable];

 

dataControl.DataBind();

}

Pay close attention to the new methodArguments object array.  I’ve bolded them in the code above.  The InvokeMember reflection method allows for arguments to be passed, so I simply opened that up to the calling method.

To use this, here’s an example that takes the Page.User property and passes it to a LINQ data context method:

BindDataControl<ListView, List<Gallery>, WebGalleryDataContext>

(true,

       “GalleryList”,

       GalleriesList,

       “GetGalleriesByRole”,

       db,

       new [] { Page.User });

or, pulling from the QueryString:

BindDataControl<ListView, List<WebFile>, WebGalleryDataContext>

(true,

       “CurrentGallery”,

       lv,

       “GetWebFilesByGalleryName”,

       db,

new [] {Request.QueryString["id"]});

and passing multiple arguments with different base types:

BindDataControl

<ListView, List<IGrouping<Gallery, WebFile>>,

WebGalleryDataContext>

       (true,

       “ChangesSinceLastVisit”,

       listViewTest,

       “GetAllSinceLastVisit”,

       db,

       new object[]

{Convert.ToDateTime(Session["LastLoginDate"]),

Page.User});

Cool.





Using Generics to Update DataBoundControls - A Prototype

24 03 2008

NOTE: This is a prototype, an idea, a random thought expressed aloud (well, in type).  The code explains a concept and isn’t “tested” or production worthy (in my opinion). 

Feedback is always appreciated. :)  This is also what happens when I have a week off work and come back with ‘ideas.’

I find that a few of my projects, like the WebGallery2, all have a similar functionality.  On pages with GridViews, ListViews (which, I’m slowly replacing all my GridViews with), or other DataBoundControls, I follow a common theme for data binding:

If the data set will be cached or in session, is that session/cache null OR has the data set been explicitly modified?

  1. true – regenerate the data set and repopulate session/cache.
  2. false – read the current data set from session/cache to the control.

In a single instance, the code to do this might look like:

private void BindList(bool hasBeenModified, string sessionVariable)

{

       // If our session variable is null or the data has

// been explicitly modified, then rebuild the session variable.

if (Session[sessionVariable] == null || hasBeenModified)

              Session[sessionVariable] =

db.GetWebFilesByGalleryName(this.Id);

 

resultsListView.DataSource =

Session[sessionVariable] as List<WebFile>;

 

resultsListView.DataBind();

}

This would then be called with:

BindList(true, “CurrentGallery”);

However, this code really bothers me. 

The data source (the db.GetWebFilesByGalleryName method), data bound control (the resultsListView), and the type of the data source (List<WebFile&gt ;) are all hard coded.

How could this helper method use generics to add a bit of resuability?  What about when I want to use a GridView instead of a ListView, or have a List<Gallery>, List<String>, string[] of information?

First Attempt

The first attempt works.  It takes the generics as anticipated and is rather easy to use.

protected void BindDataControl<TDataControlType, TEnumerableType>

(bool hasBeenModified, string sessionVariable,

              TDataControlType dataControl, TEnumerableType dataSource)

where TDataControlType : DataBoundControl,

              new() where TEnumerableType : IEnumerable

{

// Add the dataSource to session.

       if (Session[sessionVariable] == null || hasBeenModified)

              Session.Add(sessionVariable, dataSource);

 

// Read the data from session and bind the data control.

       dataControl.DataSource =

(TEnumerableType)Session[sessionVariable];

       dataControl.DataBind();

}

The constructor here has both generic parameters and standard parameters.

  • TDataControlType has a generic constraint that requires it to be part of or a subclass of DataBoundControl (GridView, ListView, etc).
  • TEnumerableType requires the source to inherit from IEnumerable (List, Array, etc).

Here are a few examples of using it this bit of code:

var gv = new GridView();

this.Page.Controls.Add(gv);

BindDataControl<GridView, Array>(

true,   // this is new, so build a session variable.

       “test”, // the session variable

       gv,     // the Id of our GridView

       new[]   // The data source, an array.

              { “hello”, “world” });

When rendered, we have a simple GridView with our two data items.

GridView and Array data source.

What about a more complicated example using collections and a ListView? On the current build of the WebStorage2 project, the galleries are built in a similar method (see this post for more details).  I could just as easily replace the logic in Show.aspx’s Page_PreRender with:

BindDataControl<ListView, List<WebFile>>(

true,

       “CurrentGallery”,

       lv,

       db.GetWebFilesByGalleryName(Request.QueryString["id"]));

So what’s the downfall to this method? 

Downfall #1: It’s a performance nightmare. AFAIK, when passing a method (which GetWebFilesByGalleryName is a method from my LINQ DataContext), it is evaluated immediately.  So, with that in mind, the hasBeenModified is irrelevant—it may not NEED to update the session, but the method will still go out, search the database, and return the results.  That’s a bad deal.

What I don’t know and am not sure how to check is whether or not the lazy/delayed loading in LINQ would balance out this at all.  Ideas?

Second Attempt

The second attempt adds in a bit more “generic” and a lot more reflective.  By adding a reference to System.Reflection, we can simply pass a string reference to a “builder” method rather than the method it self—thus saving the prefabrication of the data source when it’s not really needed.

protected void BindDataControl<TDataControlType, TEnumerableType>

(bool hasBeenModified,

              string sessionVariable,

             TDataControlType dataControl,

             string dataSourceMethod)

where TDataControlType : DataBoundControl,

              new() where TEnumerableType : IEnumerable

{

// If session is null or has been modified (thus invalidated),

       // update the session state.

       if (Session[sessionVariable] == null || hasBeenModified)

       {

              // Invoke the specified method that

// creates our data source.

             var data = Page.GetType().InvokeMember(

dataSourceMethod,.

BindingFlags.InvokeMethod |

                    BindingFlags.NonPublic |

                    BindingFlags.Instance,

                    null, this, null);

 

// Add it to session.

             Session.Add(sessionVariable, data);

}

 

// Read the data from session and bind the data control.

       dataControl.DataSource =

(TEnumerableType)Session[sessionVariable];

      

dataControl.DataBind();

}

In this method, the Page.GetType().InvokeMember method iterates through the methods on the page, finds the one that matches the string name passed to it, and executes it. 

Then, with our “data” results, the rest is the same as the previous method.

Unfortunately, I cannot pass the LINQ direct lookup anymore because the scope of InvokeMember is limited to the calling page.  I’ll need to create another little method, called GetResults in this case, to do the query for me.

protected List<WebFile> GetResults()

{

return db.GetWebFilesByGalleryName(Request.QueryString["id"]);

}

And the updated BindDataControl method:

BindDataControl<ListView, List<WebFile>>(
  false,
  “CurrentGallery”,
  lv,
  “GetResults”);

Now, since our constructor does not contain the method to fetch our data, simply a string, the results are not refetched each time the method is called—only when the requirements are met further in the code.

Downfall #1: This method requires an additional “helper” method on every page to fetch the data.  You can’t access methods outside of the page—or can you?

Downfall #2: What happens if you need to pass parameters to your InvokeMember?  You CAN, but the syntax is nasty and becomes even more difficult if the parameters are not always in the same order (which doubtfully they would be if you’re using generics).

Third Attempt

The third attempt looks more like the signature from Hell than a real method.  There had to be a way around the “stuck on this page” snafu with the second attempt… and there was, by specifying the class too using a generic.

protected void BindDataControl

<TDataControlType, TEnumerableType, TDataSourceClass>

(bool hasBeenModified, string sessionVariable,

              TDataControlType dataControl,

string dataSourceMethod,

TDataSourceClass dataSourceClass)

where TDataControlType : DataBoundControl,

              new() where TEnumerableType : IEnumerable,

             new() where TDataSourceClass : class

{

// If session is null or has been modified

// (thus invalidated), update the session state.

       if (Session[sessionVariable] == null || hasBeenModified)

       {

              // Invoke the specified method that

// creates our data source.

             var data = dataSourceClass.GetType().InvokeMember(

dataSourceMethod,

                     BindingFlags.InvokeMethod |

                    BindingFlags.NonPublic |

                    BindingFlags.Public |

                    BindingFlags.Instance,

                    null, dataSourceClass, null);

 

// Add it to session.

Session.Add(sessionVariable, data);

}

 

// Read the data from session and bind the data control.

dataControl.DataSource =  

(TEnumerableType)Session[sessionVariable];

 

dataControl.DataBind();

}

Good grief. 

This method adds a third generic to the constructor—TDataSourceClass—as well as the additional constraint requirement.  I’ve also added an additional BindingFlag—Public—since most of the methods in LINQ DataContext classes are decorated public.

Rather than pulling from this.Page, we’re now calling InvokeMember from the parameter class and returning the results to the calling page.  There’s one other change—rather than looking at “this” as the Binder parameter, we’re referencing the class passed along in the constructor—the dataSourceClass.

BindDataControl<ListView, List<WebFile>, WebGalleryDataContext>(
    true,
    “CurrentGallery”,
    lv,
    “GetWebFiles”,
    db);

Here we have two additional parameters, the generic parameter, TDataSourceClass, that I’ve passed the LINQ Data Context into to define the Type of the dataSourceClass parameter passed later in the constructor. 

“Verbalized”, the method’s generics read: BindDataControl to a ListView with the expected data format of a List<WebFile> using the WebGalleryDataContext class. 

The parameters (which could be reordered to make better sense) read: the data is new or has changed, so store the results in “CurrentGallery” and return them to ‘lv’ (the ListView object on the web form).  Fetch the data with GetWebFiles from the instance of db (the WebGalleryDataContext object instanciated previously in the page).

Downfall #1: Methods are still required—you cannot pass a simple data source to the BindDataControl method.

Downfall #2: Additional coding on the BindDataControl methods required to handle parameters.

This third attempt handles our most complex request—but what about the original “hello”/”world” array request?  It can be done, but, as previously mentioned, requires extracting the array list outside of the method constructor.

 

protected void Page_Load(object sender, EventArgs e)

{

var gv = new GridView();

       this.Page.Controls.Add(gv);

 

BindDataControl<GridView, ArrayList, Page>(

true, “junk”, gv, “Get”, this.Page);    

}

 

protected ArrayList Get()

{

return new ArrayList {“hello”, “world”};

}

To reference methods that exist in the same code-behind page, this.Page and the Page class offer the correct class access.

So, is this the best way to do it?  Probably not!  How would you tidy this up or rewrite it?  I’m interested!





ASP.NET MVC Source Code Now on CodePlex

21 03 2008

As promised, Microsoft has released the source for the MVC framework (and potentially others if the naming of the site is an indicator) on CodePlex.

http://www.codeplex.com/aspnet

Awesome!  Even if you have no plans to contribute or build up your own, custom libraries, reading through the source and understanding how the controllers are called, ViewPage is rendered, and such—it’s just better than Object Explorer. ;)





Exploring the ASP.NET MVC Preview 2 - Tests and Mocks

21 03 2008

The changes made around testing and mocking are a big step in the right direction from Preview 1. 

Mocking Controller Tests

In Preview 1, it was more common to create subclass testers for each Controller object—a very time consuming tasks.  Mocks “worked”—but, honest, just acted a bit odd.  Our ViewEngine still requires faking out, but Moq helps us along with the rest.

To start off, we’ll “Moq” up a Contact view and get the test to fail, then fix it (aka: implement it) to pass the test.

[TestMethod]

public void Contact_ReturnsContactView()

{

// Create a Moq instance of our HomeController.

       var controller = new Mock<HomeController>();

 

       // Create an instance of our FakeViewEngine.

       // There has got to be a way to do this without

       // ‘faking’ it. :(

var fakeViewEngine = new ViewHelper.FakeViewEngine();

 

       // Set the ViewEngine of our mock object to the fakeViewEngine.

       controller.Object.ViewEngine = fakeViewEngine;

 

       // Using the extension method from the MvcMockHelpers class,

       // set the controller context.

       controller.Object.SetFakeControllerContext();

 

       // Invoke the “Index” method.

       controller.Object.Contact();

 

       // Assert that Index() actually returned a view named Index.

       Assert.AreEqual(

              “Contact”,

             fakeViewEngine.ViewContext.ViewName);

}

This is part of the unit testing I still don’t like—that FakeViewEngine class.  The class consists of a ViewContext and get/set properties because, by default, IViewEngine can only RenderData()—it doesn’t allow direct access to the ViewContext. 

I suppose you could override IViewEngine and implement your own—but, for testability, is there harm in exposing those as read-only?

At this point, our Contact() method call is invalid (as it doesn’t exist in the HomeController class.

For now, so our test will run, we’ll add in an empty method to the HomeController class.

public void Contact()

{

           

}

Now, we can run the test.  As expected, it fails because the object (the ViewPage) doesn’t exist yet.

Test method Contact_ReturnsContactView threw exception:  System.NullReferenceException: Object reference not set to an instance of an object..

Now, to set off to resolve the error. 

Note: The templates are very precise.  This is a content page (linked to a master page) and a view page rendering the view of a MVC controller.  Be sure to pick the right item template for your task.

In our Views > Home directory, we need to add a new MVC View Content Page item named Contact.

After the View page itself has been added, implement the RenderView method in the HomeController class by modifying the empty method added earlier.

public void Contact()

{

RenderView(“Contact”);      

}

Now, rerun our test!

Testing Routes

There isn’t any mocking (yet) in our Route tests; however, we do use some of the helper methods Scott Hanselman wrote about and that I prepackaged up (see First Glance post for downloads).  I’ve created two quick and simple tests:

  • Does RegisterRoutes work successfully register routes to the RouteTable?
  • Does the Default.aspx mapping work at the root?

Before we get started, the tests require a simple TestInitialize (or SetUp, depending on your testing tool) to initialize the RouteCollection object.

private RouteCollection routes;

 

[TestInitialize()]

public void TestInitialize()

{

routes = new RouteCollection();

}

RegisterRoutes()

Our RegisterRoutes test method is extremely easy:

[TestMethod]

public void RoutesRegistered()

{

Assert.IsTrue(routes.Count == 0);

RouteManager.RegisterRoutes(routes);

Assert.IsTrue(routes.Count > 0);

}

Simply initialized, our RouteCollection should be empty (Count == 0) and, after RegisterRoutes is called, should be populated with routes.  Easy enough and ensures that our RegisterRoutes method is doing it’s job.

ContactRoot_MapsToHomeView()

To test our routes, we must do two things: register our route and then fake the navigation to a specific URL—in this example, “~/Contact.aspx”.   This is really useful during transitions from other frameworks to MVC—especially if you have preprinted letterhead, business cards, etc. with a specific URL on it. :)

To bypass the default “controller/action/id” logic of the MVC framework, we can specify exact names, paths, etc. 

First, let’s build our test.

[TestMethod]

public void ContactRoot_MapsToHomeView()

{

RouteManager.RegisterRoutes(routes);

RouteData routeData =

              routes.GetRouteData(

MockHelper.FakeHttpContext(“~/Contact.aspx”));

           

// Check to see if a route exists for the specified URL.

Assert.IsNotNull(routeData);

      

// Check to see that the controller matches our expectation.

Assert.AreEqual(“Home”, routeData.Values["Controller"]);

 

// Check to see that the action matches our expectation.

Assert.AreEqual(“Contact”, routeData.Values["Action"]);

}

This test checks three things to pass:

  • does the URL specified exist in the route data (is there a valid route?),
  • does it call the anticipated controller,
  • and does it call the anticipated action?

All three are required to pass as a “valid route”.

When we try to run the test, the first to fail is our Assert.IsNotNull—a route does not exist for our Default.aspx page.  So let’s add a route to get this test to pass.

routes.Add(new Route(“Contact.aspx”, defaultRouteHandler)

{

Defaults =

              new RouteValueDictionary(

                     new

                    {

controller = “Home”,

action = “Contact”,

id = “”

}),

});

Success!

Working with tests for both Controllers and Routes is quite a bit easier now—both due to continued improvement to the MVC framework and the work in the community with helper and extension methods.

For more details on MVC Testing, check out Scott Hanselman’s great webcast on asp.net.

Edit: Don’t bother with the asp.net web site with IE 8–-it’s totally borked up and, even worse, Silverlight won’t load properly in FireFox 2 (it’s an invalid browser).  Break out IE7 for this venture.





Exploring the ASP.NET MVC Preview 2 - First Glance

21 03 2008

Download(s): MvcMockHelpers rev. 3

Better late than never, I suppose.  Between my home computer going on the fritz and the BEAUTIFUL weather making me want to just sit outside, this article has taken me nearly three days. 

A few months ago, I wrote a series of articles evaluating Preview 1 of the MVC Framework with rave results.  After working with Java for a while, I was thrilled to see MVC patterns hit .NET WITHOUT the sometimes painful manual implementations that I had been using to-date. 

Preview 2 builds on a few of those expectations, fixes some bugs, addresses a bit of testability, and, generally, just works. 

Starting Off

After installing Preview 2, you’ll notice that the project templates have changed around a bit.  Thankfully, they’ve separated out the MVC Web Application and MVC Test templates into their correct project types classifications.  What’s even better?  When adding a MVC Web Application, it now PROMPTS you to build in your tests.

MVC Preview 2 - Unit Test Selector

ImportantImportant: I’d like to point out that the “Create Test Project” dialog only shows up when you are initially adding the ASP.NET MVC Web Application.  If you choose “No, do not generate a unit test project” and later try to add a new MVC Test Project, you will not have the option to select the testing framework.  I truly hope this changes in future builds. :)

Another great feature is that the ‘Test Framework’ is fully configurable.  The folks at MbUnit have already built a preinstaller package to integrate with the MVC Framework.  You can read about it here.  If you use NUnit, XUnit, or another testing framework or you wish to build your own, customized testing framework, Joe Cartano has a great walkthrough on creating the project template packages.

MVCPreview2 SolutionFor these examples, I’ll use the Microsoft VSTS Testing Framework (built into VS2008, easy, simple, and integrated) and Moq (Scott Hanselman has a great post with some Moq Helpers on it—the Moq helpers will need a bit of tweaking due to casing, but work very well!).  I’ve taken the helpers that Scott (and community) have put together and tied them into a MvcMockHelpers library for my own usage.  If you’d like the source code and the libraries, you can download them here.  The current library includes Moq and RhinoMocks.

I’d like to point out that I’m not an expert with Moq by any stretch of the imagination—I’m sure there are better, faster, more Moq’able ways of doing the Unit Testing… :)  Feel free to comment and let me know. ;)  I like the framework and would appreciate any tips and tricks.

Out of the box, we have a nice framework of our MVC web and testing projects.  I’ll use good, old reliable Northwind for this example (recent examples have used my Photo Gallery as a data prototype, this is a nice change).

“First Glance”

At first glance, the MVC package and solution templates look much the same.  Index and About are still prefab’d and based on a single Site.Master page.  Routes are still, by default, managed in Global.asax.

So, what’s different at 10,000’?  Here’s a brief list and some descriptive information from the gurus.

  • Separation of Routing from the MVC Framework itself.  Phil Haack explains the separation details here.
  • Controller actions no longer require the redundant ControllerAction attribute. Brilliant.
  • The ‘MVCToolkit’ that Rob Conery had developed is now integrated into System.Web.Mvc as HtmlHelper.
  • Views are generated with code-behind pages now (no longer requiring you to right-click > Convert to Web Application on each that has code-behind coding).

There are a few things that still, in my opinion, should be worked on.

  • Extracting the routing from Global.asax as a practice and placing them in an external “RouteManager” class that is either prefab’d from the template.  I’m not sure why, but having a few hundred lines in Global.asax bugs me.
  • Mocking, as we’ll see in the next posts, still requires faking out some objects, like the Views.

In upcoming posts, I’ll dig into changes in testing, routing, and a bit into using the new HtmlHelpers for generating clean, XHTML compliant code.





Spring Break, oh how I love thee.

14 03 2008

The beach.Spring Break is great—especially when you work somewhere where it’s mandatory and paid

This is the first year in… well, ever, that I won’t have to attend college classes during my spring break holiday and can take the time to relax, catch up on projects I’ve been putting off, and sleep later than “almost going to be late” 6:00 AM.

Over the next week, I’m going to attempt to:

  • Catch up on all the new MVC bits from Release 2.  I really missed out being unconscious for a week with the flu and haven’t had time this week to dig in.
  • Now that Silverlight 2 is out, actually CARE about it.  Silverlight 1.0 was “neat,” but encapsulating videos into little boxes didn’t really appeal to me.  Full UI development does.
  • Build a desk, or a table, or something.  I’ve (well, my dad and I) had plans to build a new computer desk, recessed lighting, and shelving into my office area.  He’s been busy, I’ve been busy—it just hasn’t gotten done started.  There will be power tool usage, I swear it.  *Tim the Toolman laugh goes here*
  • WebGallery2 updates. I have made a few cosmetic and code changes to WebGallery2 to address performance issues and I need to get those changes tidied up and posted.

All in all, I honestly plan to spend as little time doing “work” as I can next week and just relax.  Of course, as fate may have it, it’s been sunny and in the 70’s all this week and is supposed to rain and be in the 40’s and 50’s all next week.  *sigh*

(Oh, and no, no beach for me—Kansas is about as landlocked as you could possibly get.)

 





The .NET MVC Framework - Part 4 - Conclusions and Code

20 12 2007

Over the past few days, I’ve written about what the MVC framework is and provided a prototype example using the existing photo gallery for this blog.

MVC Framework

Download the Project Code: Here

Is the MVC Framework a replacement for web forms?

No.  I don’t think so and haven’t seen any evidence from Microsoft to indicate such.  I think it’s setup to be an alternative—just as it is with frameworks like Java.  Right off the top, for things even like the photo gallery example, I can see this being overkill.  On the other side of the coin, I can think of a few applications I’ve rolled out in the past year that emulated MVC to a degree (as far as separation), but were somewhat of a pain to test—and would greatly benefit from this framework.

Is the MVC Framework easier?

Ehh, honestly?  No, but you end up getting more in the end for your work.  Properly implemented, your output code is slimmer (lacking ViewState data, etc), cleaner, and you have gained full testability of your code.  The theory is that you could take the Controllers and Models, drop them into another environment (or another UI) and they’d work—no fuss needed.  Unfortunately, since MVC is web-only right now, I don’t have a good way to test that right now.


For now, the best bet is familiarization—seeing how each of these new technologies work, their pros and cons, and using that knowledge to make better design decisions down the road.





The .NET MVC Framework - Part 3 - Linking Models to Views and More!

19 12 2007

This is the third part of the series looking at rolling out a new Microsoft .NET MVC application.  I’ve taken an existing application, the prototype photo gallery I use for this blog (http://photos.tiredstudent.com) and am recreating it (with some extras) inside of the MVC framework—it’s a bit more fun than dinking with Northwind. :)

MVC Framework

With that, let’s take a look at creating a data model and linking data to our controllers and then to our views.  This part should fill out our Galleries controller quite nicely.

Adding a Data Model

For these examples, I’m going to use LINQ to SQL.  You could just as easily use the new Entity Framework/Astoria, NHibernate, Castle, or any other you wish.  I like LINQ. :)

 I’ve pulled down a copy of the existing Photos database, so the examples you see here match what’s in production on the blog’s photo gallery.  The first step is to create a LINQ-to-SQL file for the Web Gallery.   It’s best, in my opinion, to place these files under the ~/Models directory since, in essence, that’s what they will become.

The two primary tables for the gallery are Galleries and WebFiles.  Dragging those two onto the DBML file results in:

Data Model

I’ll create a quick unit test example—I’ll assume the rest is up and going as these are no different in the MVC framework as they are in any other project.  :)

For the example, I’ll check that my GetGalleries method returns a non-null result:

[TestClass]

public class GalleriesModelTest

{

WebGalleryDataContext db = new WebGalleryDataContext();

       

       [TestMethod]

       public void GetGalleries_ReturnsAllGalleries()

       {

              List<Gallery> galleries = db.GetGalleries();

              Assert.IsNotNull(galleries);

       }

}

 

Okay, that fails; which is what it is supposed to do—since my GetGalleries method doesn’t exist yet.  I want the methods to show up in side of my existing model and, thankfully, the LINQ-to-SQL classes are partial classes.

If I add a new class called WebGalleryDataContext (the same as the existing DataContext) under my model and mark it as partial, the methods will merge across classes to create a single class.  It is a brilliant and extremely useful concept for extending existing classes like those created by the DBML generator.

Our GetGalleries method looks like the code snippet below.  We’ll use that to build up on our Index action on the galleries controller.

public partial class WebGalleryDataContext

{

       public List<Gallery> GetGalleries()

       {

            return Galleries.ToList();

       }

}

With that done, we’re ready to tackle creating a view and attaching some data to it—or creating an actual web page! :)

Creating a Gallery List with ViewData

On the first post, I created the new views for the Galleries controller; however, I didn’t do anything with them at that time—simply rendering was enough.  Now, however, we want to see REAL data on those pages.