Posts in category .net - page 16

Observations on Microsoft MVC for ASP.NET

Anyone who’s tried to develop large complex web sites with ASP.NET has likely run into many problems covering the page and control life cycle, view state and post backs and subsequent maintainability including the difficulty in creating automated unit tests.

Microsoft, taking a cue from the popularity of Ruby on Rails and subsequent .NET related efforts such as MonoRail, are embracing the model-view-controller (MVC) pattern and developing something more suited to web development than WebForms which aimed to make web development as similar to Windows development as possible (despite the major underlying differences in architecture).

The prototype, currently named System.Web.Mvc or Microsoft.Web.Scalene depending on where you look, is headed by Scott Guthrie with both Phil Haack and Scott Hanselman involved (sounds like a dream project and team to be involved with) and a preview release (“drop”) is due within the coming weeks.

Guthrie and Hanselman presented Microsoft MVC at the ALT.NET conference which revealed some interesting details buried in the video, my rough observations and notes based on the prototype they showed follows:

Philosophy

  • Don’t repeat yourself
  • Highly extensible & pluggable
  • Use generics to achieve strong-typing without code generation
  • Good performance, fast enough for large-scale sites
  • Separation of concern for maintainability
  • Clean URLs in and out
  • Clean HTML

Extensible

  • Interfaces used extensively
  • No sealed classes
  • Plug-in points for view engines (e.g. MonoRail’s NVelocity, Brail)
  • Support for Inversion of Control (IoC) engines (e.g. Windsor, StructureMap, Spring.NET)

Compatibility

  • Runs on the .NET 2.0 CLR
  • Some helper classes require .NET 3.5 (extension methods)
  • Normal Request, Response objects (via interfaces for mocking)
  • Does not support postback form runat="server"
  • Supports MasterPages, DataBinding, CodeBehind
  • Existing .aspx’s are blocked using web.config

Visual Studio

  • Solution templates for web project and unit testing
  • Full designer & IntelliSense integration

Flow

  • Route -> ControllerFactory -> Controller -> Action -> ViewEngine -> View

Routing

  • Routes can be defined dynamically and support RegEx URL matching
  • IControllerFactory pops out the required IController
  • Routing is case insensitive by default
  • Support REST, blog engine, Jango style mappings etc. default is /controller/action/parameters

Controllers

  • FrontController style
  • IController exposes:
    • Execute(IHttpContext context, RouteData routeData)
    • IViewEngine ViewEngine property
  • Some implementations available, all with virtual methods:
    • ControllerBase (adds dispatching)
    • Controller (Populate parameters into ViewData["key"])
    • Controller (Populates parameters by making ViewData type T)
  • Attributes
    • [ControllerAction] attribute to expose methods as actions (secure default behavior by not exposing helper methods)
    • Attribute for output caching
    • [ControllerAction(DefaultAction=true)] to override default method of Index

Parameters (to the controller)

  • Automatically parsed where a TypeConverter exists
  • Future versions will support more complex serialization of types
  • Can be nullable – use null coalesce operator for defaults

View Engine

  • IViewEngine
    • IView LoadView(string viewName)
  • Implementations include:
    • WebFormViewEngine

Views

  • IView
    • virtual void RenderView(object data)
  • Implementations include:
    • ViewPage – pick up parameters from ViewData[""] in conjunction with Controller
    • ViewPage pick up parameters from ViewData as type T in conjunction with Controller

HTML

  • Clean HTML generation (have they sorted out the id mangling by MasterPages/INamingContainer?)
  • Static HTML class supports Link, PagingLinks and Url methods
  • Map to action names using Lambda expressions to ensure follows refactoring, e.g. string url – Html.Link<ProductController>(controller =>controller.Edit(4);
  • Is there a way to follow the default action?

Model/data

  • Pagination extension methods extend IQueryable for getting pages of data (skip, limit)
  • Pattern for view-update-view cycle
  • Object property to form field id mapping available and pluggable, allows
    • product.UpdateFrom(Request.Form)

Testing

  • Easy to write tests by using mock objects (request, response)
  • Unit testing framework project (NUnit, MBUnit, xUnit.NET)

Finally

  • What’s with ScottGu’s nametag, can the show’s organizers not afford anything more than a Post It note?
  • What cool software is Scott Hanselman using to do the screen-cast video/zoom/overlay/highlighting?

[)amien

Refactoring shared libraries and public APIs

Refactoring is an essential process to keep code clean and elegant while it evolves. IDE’s offer common refactorings (although somewhat short of those prescribed in Fowler’s excellent Refactoring book and way short of the overall goals explained in Kerievsky’s Refactoring Patterns).

One limitation of existing tools is that they can only update references within your solution. When you are refactoring a shared library this is a problem, especially if it is your public API to the outside world.

We need to introduce metadata to document how the API has evolved and extend the tools to generate and understand this metadata.

Let’s take a look at a few of the refactoring in Visual Studio and see how they could be documented using the .NET metadata mechanism of choice, attributes.

Rename

Starting simple lets we had a property named Reference:

public string Reference {
  get { return id; }
}

We are going to rename Reference to StockCode for the 1.1.0.0 release. The tool could introduce a stub for backward compatibility whilst also marking it with metadata giving us:

[DeprecatedRefactorRename("StockCode", "1.1.0.0")]
public string Reference {
  get { return StockCode; }
}

public string StockCode {
  get { return id; }
}

The library is both binary and source compatible but with a little IDE work they could get a warning that Reference is now StockCode and given the choice of updating all the references in their project.

Nice. Let’s try a few more:

Remove Parameters

public bool AddStock(int quantity, DateTime arrival, StorageBin location) {
   ...
}

We are switching to a managed warehouse and so we no longer need to know where items are stored so we refactor and remove the StorageBin.

[DeprecatedParameterRemoved("location", "1.1.0.0.")]
public bool AddStock(int quantity, DateTime arrival, StorageBin location) {
  return AddStock(quantity, arrival);
}

public bool AddStock(int quantity, DateTime arrival) {
  ...
}

Reorder Parameters

[DeprecatedParametersReordered("arrival, quantity", "1.1.0.0.")]
public bool AddStock(int quantity, DateTime arrival) {
  return AddStock(arrival, quantity);
}

public bool AddStock(DateTime arrival, int quantity) {
  ...
}

Move Method

Existing tools offer little support for MoveMethod because they haven’t considered how to refactor the references. It is difficult to retain binary compatibility unless the class has a reference to class that now has the method we are interested in.

[DeprecatedMethodMoved("StockController", "Add", "1.1.0.0")]
public bool AddStock(DateTime arrival, int quantity) {
  return stockController.Add(this, arrival, quantity);
}

Let’s say the current calling code looks something like:

stockController.DoSomething();
selectedProduct.AddStock(DateTime.Now, input.Value);

However with a little ingenuity the IDE could examine the new method and map existing parameters based on name and type. If it still doesn’t have enough information consider local variables and properties of the objects it does have to present choices. This works especially well if your parameters are not primitives. Our code becomes:

stockController.DoSomething();
stockController.Add(selectedProduct, DateTime.Now, input.Value);

Keeping it clean

We don’t want our classes being cluttered with deprecated code indefinitely so the solution should contain two extra revision numbers, one detailing the oldest revision of attributes to keep in the source, the other for the oldest revision to compile into the binary. All the [Deprecated] marked methods and properties can slip into another file, perhaps Product.deprecated.cs so they stay out of sight until needed.

For .NET it would need somebody at Microsoft to take this on board and move us forward from ObsoleteAttribute as the facility should be cross-tool and so adding it solely to SharpDevelop would be of limited gain.

[)amien

LINQ in 60 seconds

Microsoft’s Language INtegrated Query (LINQ) aims to provide a way of selecting objects with a common syntax independent of the data source.

By integrating query into the language instead strings parsed by an external provider at runtime we gain IntelliSense prompting for fields, members and table names and full compile-time syntax checking and a unified syntax.

They will be included in the .NET Framework 3.5 and delivered as part of the Visual Studio 2008 ‘Orcas’ release which is currently available in beta and comprises of:

LINQ syntax

The language extensions themselves. An example in C# might look like:

GridView1.DataSource = from country in countries
                       where country.Continent == 'Europe'
                       orderby population descending
                       select country;

LINQ to Objects

A core part of the .NET Framework 3.5 and allows you to query against any IEnumerable collection and test or sort against any of T’s properties.

In the above example imagine Country is a business class and Countries is a List. Continent is a string property and population a numeric one.

LINQ to SQL (formerly known as DLinq)

LINQ to SQL works by mapping classes and properties to tables and fields as any normal Object-Relational Mapping (ORM) tool would.

It achieves this by marking the classes and properties with attributes to indicate how they map to the underlying database. A visual modeling tool is provided that can generate and manipulate such classes for you from an existing SQL database.

The ORM functionality includes tracking of changed objects and change persistence. It also ensures that you will not obtain multiple objects for the same underlying row in the database (Fowler’s Identity Map).

Other components

There are many other components in the LINQ family including:

LINQ to XML (formerly known as XLinq)

Similar to LINQ to SQL but along the idea of querying XML documents using LINQ syntax rather than the XPath/XQuery syntax.

LINQ to Entities (ADO.NET Entities)

A more advanced ORM solution that allows more extensive mapping and manipulation between how the object appears and the underlying data source.

Third-party support

Mono is actively implementing LINQ as are the people behind the NHibernate ORM.

[)amien

Investigating MonoRail

Fighting WinForms

I hate fighting with a technology to get it to do what I want because it means I either have the wrong expectation or wrong technology.

With web development I expect strict web standard support and clean code that is easy to maintain.

I am, therefore, tired of fighting with WebForms and seeing as I’m not prepared to change my expectation then the technology must change.

Looking at MonoRail

Ruby on Rails is very fast, elegant and powerful but comes with a bunch of unknowns. The IDE’s I’ve tried have been so-so, there is no support for IntelliSense so I’m forced to remember exact property and method names. There are concerns about performance and scalability and I find the Ruby language itself cryptic.

My current .NET environment has all these things, so what I’m really looking for is an alternative to the WebForms element itself. It also has a powerful framework, tons of samples, and C# is not only enjoyable but very in-demand :)

MonoRail seems to be just what I am looking for but there are a number of things keeping me away. I decided to spend an hour watching a screen cast on WinForms and MonoRail from Ayende @ Rahien’s blog. It calmed some concerns but raised a few others…

NHibernate mapping files

NHibernate provides the core ORM system within MonoRail and normally requires XML mapping files to do so.

I really don’t want or need another abstraction layer here – my tables are freshly modeled and represent my domain classes very well. Rails, Subsonic and LINQ to SQL are all happy to just do it/

Thankfully a project called ActiveWriter gives you a very LINQ to SQL-like experience in dragging tables off, changing names and properties if you want and doing the magic for you.

ActiveRecord template

I still don’t like this mix of static and instance methods providing some sort of split between what should really be two classes but I can live with it.

There is also a Repository option mentioned which perhaps solves this, I shall have to investigate it further.

View engines

There are a number of view engines available for MonoRail but the primary ones are NVelocity and Brail.

As I already have C# and JavaScript in my project and I have no desire to add another language unless there is a good reason to do so. If they want to stop people writing too much view code then what is wrong with a subset of C#?

The template engines also mean giving up strong typing (everything is passed to the view in a type-less property bag accessed with a string key!) and a complete lack of IntelliSense (the demo stalls as fields are mistyped on occasion proving just how useful this is).

HTML injection

Yes, in this day and age HTML injection should be a long-dead concern and yet even the built in SmartGridComponent will happily squirt out data without encoding it and thus allowing data from anywhere to contain HTML ready to be injected into an unsuspecting page.

Ayende has investigated the issue now and is working on getting a fix into the tree.

[)amien