May 232011
 

At the recently concluded I/O developer conference, Google made an much awaited announcement – The Google Places API has been opened to everyone (was in beta testing for some time). For the uninitiated, Google Places is a Google application for searching local businesses like hotels, ATMs etc. Places fits in beautifully with Google maps both on the web and the Android.

Now with the API being opened up, Its also possible for any location aware tools and websites to make uses of Places search option to add the functionality to your own sites. Check out the documentation here. I integrated the Places API search in my previous geolocation example explained in a blog post here. The application returns 20 places near the user’s current location and adds markers to the map for each.

Web Workers

Web workers are one of the most interesting concepts of HTML5. They are a standard based on running JS scripts on a background thread rather than the main UI thread. This is extremely important since the more time consuming scripts (like complex mathematical calculations) can be offloaded to a secondary thread rather than freezing up your application, having huge applications in graphics intensive work. I used Web Workers in the current application to work call a server side method which in turn calls the Google places API to search for a list of places near the user’s application.

Here are some limitations of Web Workers in their current implementation:-

  • Not supported on all browsers (Most notably Internet Explorer).
  • We cannot access any DOM object in the Web Worker script. All communication needs to be to the main thread using the postMessage function.
  • Because we cannot access DOM objects, it also doesn’t allow any script to be loaded which refers to DOM, which renders most JS libraries like JQuery and Prototype unusable.

In this example, I following components.

  • An ASP.NET MVC server side in order to call the Google places API. Its a controller method which calls the API url and a method which holds the data. On client side we have a similar method in JSON with same properties. The ASP.NET MVC Model binder converts the JSON object to a CLR object and passes it to the Action method. Client Side Javascript cannot call the Google places API directly because it would be a cross site request and not allowed by Google. Hence the Server’s broker method becomes necessary here
  • Client side main script which uses Geolocation to determine the user’s location. It then passes this location to the MVC Action. Before calling the action, it checks whether the browser supports Web Workers – If so they are offloaded to secondary thread. Else called on the main thread itself.
  • Worker script which makes the AJAX call to the Server using xmlHttpRequest object (Jquery cannot be used here) 🙁

Here is the code.

ASP.NET Server Side

        public ActionResult GoogleSearchAPI(SearchQuery query)
        {
            //Base URL for calling the Google Places API
            string BaseAPIURL = String.Format("https://maps.googleapis.com/maps/api/place/search/xml?location={0},{1}&radius={2}", query.Latitude, query.Longitude, query.Radius);
            if (!string.IsNullOrEmpty(query.Name))
                //Append the name parameter only if data is sent from Client side.
                BaseAPIURL = String.Concat(BaseAPIURL, String.Format("&name={0}", query.Name));
            //Include the API Key which is necessary
            BaseAPIURL = String.Concat(BaseAPIURL, String.Format("&sensor=false&key={0}", GetAPIKey()));
            //Get the XML result data from Google Places using a helper method whichc makes the call.
            string _response = MakeHttpRequestAndGetResponse(BaseAPIURL);
            //Wrap XML in a ContentResult and pass it back the Javascript
            return Content(_response);
        }

        //Helper method to call the URL and send the response back.
        private string MakeHttpRequestAndGetResponse(string BaseAPIURL)
        {
            var request = (HttpWebRequest)WebRequest.Create(BaseAPIURL);
            request.Method = WebRequestMethods.Http.Get;
            request.Accept = "application/json";
            string text;
            var response = (HttpWebResponse)request.GetResponse();

            using (var sr = new StreamReader(response.GetResponseStream()))
            {
                text = sr.ReadToEnd();
            }

            return text;
        }

    ///
    /// Our data object  which sends the object with data from client side to
    /// server. The Model binder takes care of conversion between JSON and
    /// CLR objects.
    ///
    public class SearchQuery
    {
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string Radius { get; set; }
        public string Type { get; set; }
        public string Name { get; set; }
    }

Client Side Main script

Most of the Geolocation code is the same as my previous example. This is the additional code written after the geolocation data is found and the coordinates is passed on to another method which uses it to retrieive places data and mark it on map

var spawnWorkerThread = function (position) {
    //This is executed if the getPosition is successfull. Moving the map to the user's location
    map.panTo(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
    var coordinates = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    //Create a JSON object with the details of search query.
    var placesQuery = { Latitude: position.coords.latitude, Longitude: position.coords.longitude, Type: "establishment", Radius: "500" };
    //Check if the browser supports WEbWorkers
    if (Modernizr.webworkers) {
        printMsg("Web Workers are supported on your browser. Searching for places nearby your location");
        //Load the Worker Script
        var myWorker = new Worker("/files/webworkersmvc/Scripts/worker.js");
        //Send the JSON object to the Worker thread after serializing it to string
        myWorker.postMessage(JSON.stringify(placesQuery));
        // receive a message from the worker
        myWorker.onmessage = function (event) {
            //Send the returned data to the processPlacesData method
            processPlacesData(event.data);
        };
    }
    else {
    //Make the call in a standard way and not using Web Workers
        printMsg("Web Workers isnt supported on your browser.Calling Places API the conventional way");
        var xhr = new XMLHttpRequest();
        //Calling the controller method.
        xhr.open("POST", "http://www.ganeshran.com/Files/webworkersmvc/Home/GoogleSearchAPI");
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
                processPlacesData(xhr.responseText);
            }
        }
        xhr.send(JSON.stringify(placesQuery));
    }

};

var processPlacesData = function (data) {
    //Parse the XML result into an xml obect
    var places = $.parseXML($.trim(data));
    var xmldoc = $(places);
    var resultstring = "";
    //Iterate through each result object
    $("result", places).each(function () {
        var typestring = "";
        $("type", this).each(function () {
            typestring += $(this).text() + " , ";
        });
        //Create a MapResult object for each result.
        var resObj = new mapResult($("name", this).text(),
                                      $('vicinity', this).text(),
                                      typestring,
                                      $('lat', this).text(),
                                      $('lng', this).text(),
                                      $('icon', this).text());
        //Create a Google Maps Marker and use the result object's latitude and
        //longitude.
        var marker = new google.maps.Marker({
            position: new google.maps.LatLng(resObj.latitude, resObj.longitude),
            title: resObj.name,
            animation: google.maps.Animation.DROP
        });
        //If the screen is smaller then zoom lesser else zoom more closer.
        //This is to make the markers visible
        if (screen.width < 1000) {
            map.setZoom(12);
        }
        else {
            map.setZoom(15);
        }
        //Set each marker on the map
        marker.setMap(map);
        //this is for the window to show information when the marker is clicked.
        //A single infowindow is reused in order to display only one
        google.maps.event.addListener(marker, 'click', function () {
            infowindow.setContent(resObj.getMarkerHTML());
            infowindow.open(map, marker);
        });
    });

};

//Javascript object to hold the map data and the get the HTML required for the marker.
function mapResult (name, vicinty, types, latitude, longitude, icon) {
    this.name = name;
    this.vicinity= vicinty;
    this.types = types;
    this.latitude = latitude;
    this.longitude = longitude;
    this.iconpath = icon;
}
//Prototype method to avoid creating seperate copies of the method
//for each object
mapResult.prototype.getMarkerHTML = function () {
var htmlstring = "
<div style="color: blue; font-weight: bold;">";
    htmlstring += "Name: " + this.name + "";
    htmlstring += "Types: " + this.types + "";
    htmlstring += "Location: " + this.latitude + "," + this.longitude + "";
    htmlstring += "Vicinity: " + this.vicinity +"</div>";
    return htmlstring;
};

Worker Side Script

The worker side script is pretty straightforward. Just calls the controller and passes on the data to the main thread using the PostMessage function

// receive a message from the main JavaScript thread
onmessage = function (event) {
    // do something in this worker
    var info = event.data;
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://www.ganeshran.com/Files/webworkersmvc/Home/GoogleSearchAPI");
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
            //Send message back to Main thread
            postMessage(xhr.responseText);
        }
    }
    xhr.send(info);
};

Demo Page

http://www.ganeshran.com/files/webworkersmvc/

Demo Pics

On PC

On Android

Jan 312010
 

Recently someone asked me to write a TicTacToe game. So I worked out a rough logic for it. Heres the code. The idea is that the computer first scans through the tic-tac-toe board for any winning positions available. If none are available, then it looks for the manual user’s winning positions. If its able to find them, it blocks it. If not the next available position is filled. The game can be played by clicking any of the squares below.

Here is the code for the game as a silverlight application. The complete project can be downloaded here.

The XAML is quite simple. Just a grid with three rows and three columns

    <grid x:Name="gridGame" Background="White" Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center">
        </grid><grid .RowDefinitions>
            <rowdefinition Height="100" />
            <rowdefinition Height="100" />
            <rowdefinition Height="100" />
        </grid>
        <grid .ColumnDefinitions>
            <columndefinition Width="100" />
            <columndefinition Width="100" />
            <columndefinition Width="100" />
        </grid>

It is then filled with the buttons in the Code Behind. I have hardcoded the grid size of three but this will work with any size.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 public MainPage()
        {
            InitializeComponent();
            InitializeGameLogic(true);
        }
 
        private void InitializeGameLogic(bool _fromConstructor)
        {
            if (!_fromConstructor)
            {
                gridGame.Children.Clear();
            }
            _logic = new TicTacLogic();
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                {
                    Button _btnGrid = new Button();
                    _btnGrid.Name = "btn" + i.ToString() + j.ToString();
                    gridGame.Children.Add(_btnGrid);
                    Grid.SetRow(_btnGrid, j);
                    Grid.SetColumn(_btnGrid, i);
                    _btnGrid.Height = 80;
                    _btnGrid.Width = 80;
                    _btnGrid.Click += new RoutedEventHandler(_btnGrid_Click);
                }
            _logic.StartGame();
        }

All the dynamically generated buttons have a single event handler. The idea is to identify the button which sent the event from the name. Once we have the position of the button, the content has to be changed with an X. After the user marks, the computer does its stuff and makes its move. After every marking, a check is done if someone has won or the board is full.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void _btnGrid_Click(object sender, RoutedEventArgs e)
        {
            Button _userClicked = sender as Button;
            int _xPos = Convert.ToInt32(_userClicked.Name.Substring(3,1));
            int _yPos = Convert.ToInt32(_userClicked.Name.Substring(4,1));
            _userClicked.Content = GetMarking("X");
            _logic.ClickUser(_xPos, _yPos);
            if (!(_logic.CheckIfSomeoneWon() || _logic.CheckIfBoardFull()))
            {
                int _xCom = 0;
                int _yCom = 0;
                _logic.MoveComputer(ref _xCom, ref _yCom);
                foreach (UIElement _tempElem in gridGame.Children)
                {
                    Button _comBtn = _tempElem as Button;
                    if (_comBtn != null && _comBtn.Name == "btn" + _xCom + _yCom)
                    {
                        _comBtn.Content = GetMarking("O");
                        break;
                    }
                }
                if (_logic.CheckIfSomeoneWon() || _logic.CheckIfBoardFull())
                    InitializeGameLogic(false);
            }
            else
                InitializeGameLogic(false);
 
        }
 
        private TextBlock GetMarking(string _mark)
        {
            TextBlock _t = new TextBlock();
            _t.Text = _mark;
            _t.FontSize = 25;
            _t.FontWeight = FontWeights.Bold;
            return _t;
        }

The Board class maintains two important things. The position of all the squares and which player has it marked. The Board doesnt depend on the Player objects. It has an object of the WinningPositions object which calculates the winning positions which can be compared with later. This object makes it possible to play with larger grids.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class Board
{
    readonly int MAXSIZE;
    List<positionmarking> _boardState = new List</positionmarking><positionmarking>();
    List<position []> _allWinningPositions;
 
    public List<positionmarking> BoardState { get { return _boardState; } }
    public List<position []> AllWinningPositions { get { return _allWinningPositions; } }
    public int MaxSize { get { return MAXSIZE; } }
 
    public Board()
    {
        this.MAXSIZE = 3;
        Initialize();
    }
 
    public Board(int _maxSize)
    {
        MAXSIZE = _maxSize;
        Initialize();
    }
 
    private void Initialize()
    {
        _allWinningPositions = (new WinningPositions(MAXSIZE)).AllWinningPositions;
        for(int _xPos=0;_xPos<maxsize ;_xPos++)
            for (int _yPos = 0; _yPos < MAXSIZE; _yPos++)
            {
                _boardState.Add(new PositionMarking(_xPos,_yPos));
            }
    }
 
    internal void MarkPosition(int _xUser, int _yUser, bool _userMarked)
    {
        foreach (PositionMarking _tempPos in _boardState)
        {
            if (_tempPos.BoardPosition.X == _xUser && _tempPos.BoardPosition.Y == _yUser)
            {
                _tempPos.MarkPosition(_userMarked);
                break;
            }
        }
#if DEBUG
        PrintBoardState();
#endif
    }
 
    internal bool? MarkedByUser(Position _tempPos)
    {
        foreach (PositionMarking _tempMark in _boardState)
        {
            if (_tempPos.X == _tempMark.BoardPosition.X && _tempPos.Y == _tempMark.BoardPosition.Y)
                return _tempMark.IsUserMarked;
 
        }
        throw new Exception("Position Not found on Board");
    }
 
    #if DEBUG
    internal void PrintBoardState()
    {
        string _userMarked = "";
        string _compMarked = "";
        foreach (PositionMarking _tempMark in _boardState)
        {
 
            if (_tempMark.IsUserMarked == true)
                _userMarked += "("+ _tempMark.BoardPosition.X + "," + _tempMark.BoardPosition.Y + ") ";
            else if (_tempMark.IsUserMarked == false)
                _compMarked += "(" + _tempMark.BoardPosition.X + "," + _tempMark.BoardPosition.Y + ") ";
        }
        Debug.WriteLine("USER: " + _userMarked);
        Debug.WriteLine("COMP: " + _compMarked);
        Debug.WriteLine("________________________________________");
    }
#endif
    internal bool IsBoardFull()
    {
        foreach (PositionMarking _tempMark in _boardState)
        {
            if (_tempMark.IsUserMarked == null)
                return false;
 
        }
        MessageBox.Show("Board Full");
        return true;
    }
}

The Winning Position class is given below and all positions are calculated at initialization. First the horizontal ones are considered and then the vertical ones. The remaining diagonal positons are taken care later. Each winning position is stored in an array of the position object which just holds the value of the Squares. These arrays are stored in a generic List.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class WinningPositions
{
    List<position []> _allWinningPositions = new List</position><position []>();
    Position[] _winPos1;
    Position[] _winPos2;
 
    public List</position><position []> AllWinningPositions
    {
        get { return _allWinningPositions; }
    }
 
    public WinningPositions(int _maxSize)
    {
        _winPos1 = new Position[_maxSize];
        _winPos2 = new Position[_maxSize];
        PopulateWinningPositions();
    }
 
    private void PopulateWinningPositions()
    {
        int _iCounter=0;
        int _xPos = 0;
        int _yPos = 0;
        for (_xPos = 0; _xPos < _winPos2.Length; _xPos++)
        {
            for (_yPos = 0; _yPos < _winPos2.Length; _yPos++)
            {
                _winPos1[_iCounter] = new Position(_xPos, _yPos);
                _winPos2[_iCounter] = new Position(_yPos, _xPos);
                _iCounter++;
            }
            _allWinningPositions.Add(_winPos1);
            _allWinningPositions.Add(_winPos2);
            _winPos1 = new Position[_winPos1.Length];
            _winPos2 = new Position[_winPos2.Length];
            _iCounter = 0;
        }
        _iCounter = 0;
        for (_xPos = 0, _yPos = 0;_xPos < _winPos1.Length; _xPos++, _yPos++)
        {
            _winPos1[_iCounter] = new Position(_xPos, _yPos);
 
            _iCounter++;
        }
        _allWinningPositions.Add(_winPos1);
        _iCounter = 0;
        for (_xPos = 0, _yPos = _winPos1.Length - 1; _xPos < _winPos1.Length; _xPos++, _yPos--)
        {
            _winPos2[_iCounter] = new Position(_xPos, _yPos);
            _iCounter++;
        }
        _allWinningPositions.Add(_winPos2);
    }
}

Then there is the Player class of which we create two objects. The user takes care of his own moving logic, all we need to do is to change the state of the square for him. But the computer has its method called the SmartMove. Here we first move through the winning positions to identify any open squares that can be marked. If there are no squares, then we look to block the opponent’s winning. If even this opportunity is not found, we mark an available open square.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class Player
{
    Board _currentBoardPosition;
    bool _isComputer;
    List<position> _allMarkedPositions;
 
    public Player(bool _isComputer,Board _currentBoardPos)
    {
        this._isComputer = _isComputer;
        _allMarkedPositions = new List</position><position>();
        _currentBoardPosition = _currentBoardPos;
    }
 
    internal void SmartMove(ref int _xCom, ref int _yCom)
    {
        bool _markingDone = false;
        bool _blockUser = false;
        Position _userBlock = new Position();
        foreach (Position[] _winPos in _currentBoardPosition.AllWinningPositions)
        {
            int _userMarked = 0;
            int _computerMarked = 0;
            int _nullMarked=0;
 
            Position _openPos= new Position();
            foreach (Position _tempPos in _winPos)
            {
                bool? isUserMarked = _currentBoardPosition.MarkedByUser(_tempPos);
                if (isUserMarked==true)
                    _userMarked++;
                else if (isUserMarked == false)
                    _computerMarked++;
                else
                {
                    _openPos = _tempPos;
                    _nullMarked++;
                }
 
            }
            if (_computerMarked == 2 && _nullMarked == 1)
            {
                Debug.WriteLine("Wining block: " + _openPos.X + "," + _openPos.Y);
                _currentBoardPosition.MarkPosition(_openPos.X, _openPos.Y, false);
                _xCom = _openPos.X;
                _yCom = _openPos.Y;
                _markingDone = true;
                break;
            }
            else if (_userMarked == 2 && _nullMarked == 1)
            {
                _userBlock = _openPos;
                _blockUser = true;
            }
 
        }
        if (_blockUser && !_markingDone)
        {
            _currentBoardPosition.MarkPosition(_userBlock.X, _userBlock.Y, false);
            _xCom = _userBlock.X;
            _yCom = _userBlock.Y;
            _markingDone = true;
        }
 
        if (!_markingDone)
        {
 
            //No Winning Position Found or not able to block;
            foreach (PositionMarking _posMark in _currentBoardPosition.BoardState)
            {
                if (_posMark.IsUserMarked == null)
                {
                    _markingDone = true;
                    _currentBoardPosition.MarkPosition(_posMark.BoardPosition.X, _posMark.BoardPosition.Y,false);
                    _xCom = _posMark.BoardPosition.X;
                    _yCom = _posMark.BoardPosition.Y;
                    break;
                }
            }
 
        }
    }
}
</position>

Since the Board needs to maintain the state of each and every position on itself, we have another object for it, which maintains a position object and a nullable boolean. If the boolean is true, then the square is marked by the user, if false – then by the computer. If its still null – its open and can be marked. The marking is done by the object’s method which makes a check to make sure no players marking is overwritten by another.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PositionMarking
{
    Position _boardPos;
    bool? _isUserMarked;
 
    public Position BoardPosition { get { return _boardPos; } }
    public bool? IsUserMarked { get { return _isUserMarked; } }
 
    public PositionMarking(int _xPos,int _yPos)
    {
        _isUserMarked = null;
        _boardPos = new Position(_xPos, _yPos);
    }
 
    public void MarkPosition(bool _markedByUser)
    {
        if(_isUserMarked == null)
        _isUserMarked = _markedByUser;
        else
        throw new Exception("Position already taken");
    }
}

Then there is the rest of the code including our business logic layer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class TicTacLogic
{
    bool _userToMove;
    bool _resultHasCome;
    Player _user;
    Player _computer;
    Board _gameBoard;
 
    public TicTacLogic()
    {
        _userToMove = true;
        _resultHasCome = false;
    }
 
    internal void StartGame()
    {
        Initialize();
    }
 
    internal bool ClickUser(int _xUser, int _yUser)
    {
        return _gameBoard.MarkPosition(_xUser, _yUser, true);
    }
 
    internal void MoveComputer(ref int _xCom, ref int _yCom)
    {
        _computer.SmartMove(ref _xCom, ref _yCom);
    }
 
    internal void Initialize()
    {
        _gameBoard = new Board(3);
        _user = new Player(false,_gameBoard);
        _computer = new Player(true,_gameBoard);
 
    }
 
    public bool CheckIfSomeoneWon()
    {
        foreach (Position[] _tempWin in _gameBoard.AllWinningPositions)
        {
            int _userWon=0;
            int _compWon=0;
            foreach (Position _p in _tempWin)
            {
                bool? isUserMarked = _gameBoard.MarkedByUser(_p);
                if (isUserMarked == null)
                    break;
                else if (isUserMarked == true)
                    _userWon++;
                else
                    _compWon++;
 
            }
            if (_userWon == _gameBoard.MaxSize)
            {
                MessageBox.Show("User Won");
                return true;
            }
            else if (_compWon == _gameBoard.MaxSize)
            {
                MessageBox.Show("Computer Won");
                return true;
            }
 
        }
        return false;
    }
 
    internal bool CheckIfBoardFull()
    {
        return _gameBoard.IsBoardFull();
    }
}
 
public class Position
{
    int _xPos;
    int _yPos;
 
    public Position()
    {
    }
 
    public int X { get { return _xPos; } }
    public int Y { get { return _yPos; } }
 
    public Position(int xPos, int yPos)
    {
        this._xPos = xPos;
        this._yPos = yPos;
    }
}

Jan 102010
 

Here are the steps to deploy MVC applications to servers with shared hosting providers.

  • In your Hosting provider’s IIS management panel, create a sub directory and configure it as an appplication- root, so that it is recognized as an asp.net application.
  • Set your MVC dll’s Copy To Local property to true, so that they are copied to the bin directory of the project.
  • Edit your web.config file and update the debug property to false. This holds true for all web applications.
  • Update the connection strings to your hosting provider’s connection strings.

Thats it. Your MVC application should run fine. Mine does.

Jan 092010
 

Though MVC is an age old architectural style that has existed since the 70s, its a relatively new entrant in the .NET world. I had written a general introduction about MVC earlier. That post is available here. While Java, Ruby, Python all had their MVC frameworks for quite some time, ASP.NET was quite comfortable with the Webform based architecture till recently. And Webforms arent really a bad way to code at all, you work with almost a Windows style of programming, dragging dropping controls. Double clicking them to code their event handlers. In fact due to this Rapid application development, ASP.NET was amazingly simple to work with. However, like with all other simple ways, ASP.NET webforms also take a lot of control away from the developer.

MVC seeks to give developers an alternative, where they have complete control over the application including the viewstate of the controls. My first look was at ASP.NET MVC was a training session given by an architect from Microsoft, and I absolutely didn’t like the complexity it brought to a simple demo application that was showed. I found that we were writing too much code, again bringing back spaghetti code that died with ASP. But over time, after digging into MVC, I found that it made a lot of sense. If my need is just to display a set of data, why should I sacrifice performance over all of the gridview’s bulky viewstate. Rather, it makes sense to use a HTML table and apply CSS to style it.  That is exactly the flexibility that MVC gives you, instead of relying on built in abstraction done by Webform architecture, you get to control every detail of your application.

WebForms

ASP.NET Webforms are radically different from any other server side scripting technology. For one, its event driven. Just like Windows programming, you assign an event handler to an element, like a button and it calls the handler when the button is clicked. No longer, do you have to keep on submitting the form to handle click events and write bulky code to do simple stuff. ASP.NET made the server side code clean – in fact a helluva lot cleaner than ASP.

But all this ignores the age old truth about the web/HTTP – that it is stateless. The server forgets about the page the minute its sent to the client and thats the end of it. So how is context maintained in WebForms? The answer is Viewstate. All of the control states are Base64 encoded and recreated on the server again where the request is executed. There is a lot of built-in plumbing to achieve this level of abstraction.

The main problem with webforms is that though it cleans up your code and markup and seperates them neatly in your Visual studio solution, it horribly clutters the rendered markup to the browser. Every control takes up more and more viewstate and the nested control names which are really long, make the markup bulky, and since this does a round trip on every request, it starts impacting performance and eventually scalability.

One more concern is the Testability, due to the event driven approach, the GUI is tighly coupled with your business layer and there isnt any effective way to test your web page. Automating such testing could be an tedious effort and you would most probably end up doing manual testing.

Despite all of these problems, Webforms are not a bad architecture. It is extremely mature and extensible. A lot of good documentation exists for it and it still makes sense for quite a few scenarios.

MVC

With MVC, it was love at first byte. In fact I never realized how inefficient webforms were, until i worked with MVC. Instead of a page receiving the request, its the Controller which lives at the heart of MVC. Once it gets the request, it decides which view to render and may use models.

One great thing about MVC is the clean Seperation of Concerns (SoC ) which you are able to achieve out of the box. Both the Controllers and the Views can be tested separately using testing tools like NUnit or MBUnit. So that means no longer. Manual testing doesn’t really make any sense for most scenarios, because the effort is not repeatable and when you want to try with a different input, you would need to start all over again.

Also, you have complete control over the HTML that your controls generate. No more grappling with heavy Viewstate and decreased performance. Also there is no concept of a postback in MVC. Hence although, we can make use of ASP.NET server controls, we cant use either ViewState or Postback in them. Microsoft is working to create a library of MVC aware server controls that would be more suitable for this architectural style. Also, you have complete control over the HTML that your controls generate. No more grappling with heavy Viewstate and decreased performance. Also there is no concept of a postback in MVC. Hence although, we can make use of ASP.NET server controls, we cant use either ViewState or Postback in them. Microsoft is working to create a library of MVC aware server controls that would be more suitable for this architectural style.

One major point that is oft repeated is that we shouldn’t jump the gun to convert every existing webforms applications into MVC just because its the in thing. You would have to rewrite your application completely, and the benefit derived would often not be worth the effort. But I would certainly  recommend that  you use MVC for any new applications you create. Its certainly the smarter way.

Nov 172009
 

MVC or Model View Controller is an architectural style that separates the business from the presentation layer to enable independent development and testing of each. MVC is not a new concept, It has existed right from the nascent days of software development with the first implementation being in SmallTalk in 1979 done by Trygve Reenskaug.

It is relatively new to the Microsoft World though, with the ASP.NET MVC framework being shipped in 2008 compatible with Visual Studio 2008 – .NET Framework 3.5. So what is MVC? MVC divides the application into three parts – The model, View and the controller.

  • Controller:- The controller is the heart of the MVC application. It recieves each and every request that is made by the user and responds to it accordingly. It controls the flow of the application.
  • Views:- This is the actual UI of the application. The controller controls how the view should be rendered to the browser. Views contain markup, scripts just like an .aspx page
  • Model:- Apart from the View and the controller, all the other parts in the application are collective called as the Model. These components are the business and data logic of the application.

A huge advantage of the MVC architecture is that it separates all these three components and makes them completely independent of each other. Hence all of them can be tested separately rather than the case with the webform architecture where testing each component is a pain and mostly we end up doing functional testing for the whole application. This also means modifying one component is far more easy without disturbing the whole application.

Since all the requests are now handled by the controller, we cannot use the postback feature of the web controls like the webform architecture. One of the drawback (or advantage) of MVC is the level of control over HTML rendering that we have. Since we can exactly control how the page will be rendered, we have to write more code to do so. Also you will see more code mixed with Markup, something that ASP.NET eliminated with the help of code behind.

MVC and WebForms are to be seen as two distinct styles which are advantageous to use depending on the scenario, rather than being pitted against each other. If you have a small standalone application to develop with fewer programming resources, a webform based application makes more sense since there is less coding involved. However if you are looking to build an application that would be used and extended by a large number of users, MVC is certainly the way to go.

To download the ASP.NET MVC framework, use this link.

Once done, you should get the options to create a new ASP.NET MVC project in the Visual Web Developer window.

newProject

The solution explorer has an organized directory structure that must be followed while developing the application.

solutionexp

A simple application is already created for us.

sampleApplication