Distributed Version Control

by breeve 9. June 2010 15:59

It’s not that centralized version control is bad but rather it has a habit of getting in your way. Take creating branches. In Perforce, for example, creating a branch makes you feel more like being interrogated by the FBI than a developer wanting to set up a sandbox. Integrating and reverse integrating branches takes more work than necessary. It got so bad we abandoned branches.

A strategy of not branching—with one big trunk—may make merging a non issue but brings limitations. Complicated bug fixes that should be fixed in isolation are not an option. Instead, checkins like “bug fix part 1” and “bug fix part 2” are common leaving the trunk in a precarious state. Even worse, experiments are impossible to do in isolation discouraging innovative thinking.

Distributed version control got its start in the open source community where experimentation is essential. In distributed version control (DVCS) branching is a first class citizen. In fact, when the code is brought down locally from the server—called cloning in Mercurial—you have a copy of the entire repository. It is a self contained unit that doesn’t need to contact the server ever again. Checkins, diffs, reverts, and updating to earlier revisions all happen locally. If the server goes down, who cares?

While visions of developers being productive on airplanes, parks, or the local coffee shop brings a smile to every software manager’s face it isn't the real reason DVCS are becoming popular. Instead, it is the ease with which every developer gets their own sandbox. If the experiment goes bad simply delete the repository from the file system and start over. The server doesn’t have to know about your copy and no one is the wiser.

Not only can sandboxes be cleaned up easily but they can also be shared. Mercurial has a built in web server that allows changes to be pulled from your repository by others. This can be very handy when two or more developers are working on a feature and want to share unstable changes. No one outside this circle has to be bothered by changes going into the trunk just so they can reach a targeted subset.

If sharing repositories is easy then merging them must be a nightmare. But it turns out the most impressive feature of DVCS is the speed at which merges are done. Mercurial, for example, has a rename feature that automatically moves changes to the correct renamed files.

I started to experiment with Mercurial for my home projects and was impressed with its easy to learn command set and light weight feel. I was so impressed, I recommended we move to it at my new job. Other like minded developers I work with agree.

Tags:

Software

LINQ to SQL Awesomeness

by breeve 11. March 2010 13:37

It is often said that Microsoft doesn’t innovate but simply copies existing ideas. To argue in opposition would be no small feat considering it doesn’t take long for anyone familiar with the tech industry to rattle off examples from Windows to the browser. However, LINQ is not one of those. LINQ was wrestled forth from the keen mind of Anders Hejlsberg—the author of Turbo Pascal and chief architect of Delphi—who joined Microsoft in 1996 after being lured out of Borland.

What bothered Anders the most when he joined Microsoft was not his reported 3 million dollar signing bonus but how disconnected the existing programming models were for different domains. To communicate with databases, for example, you must know SQL and how to write stored procedures. The more they thought about the problem the more they realized different programming models were most prevalent around data query. The solution was LINQ—which stands for Language INtegrated Query. For an organization better known for its late game heroics rather than innovative ideas, LINQ sheds the stereotype by delivering a solid piece of software engineering which can query everything from arrays of object to SQL databases all from C#.

I have been using LINQ to SQL for some home projects and love it. Before you think it must be a slow library abstraction consider that Joel Spolsky and the gang at StackOverflow, which gets 6 million unique visitors per month, have been running it from the beginning with no performance issues to date.

To get started with LINQ goodness let’s look at a recent project I have been working on that screen scrapes grocery ads from the web and stores them in a database. Below is my simple database schema shown from within SQL Server Management Studio 2005.

From the schema each store can have many ads and each ad can have many items. Now that the database is defined, we can use the LINQ to SQL designer in Visual Studio 2008 to generate our data access classes that LINQ runs against. To do so, right click the C# project in Visual Studio and select Add>>New Item. Select the Data Category and then the LINQ to SQL classes template in the right pane as shown below.

A new .dbml file will be add to the project. Double clicking on it will bring up the a designer surface. The next step is to connect to the database using Server Explorer and then drag the database tables onto the LINQ to SQL design surface. To bring up Server Explorer, click View>>Server Explorer in Visual Studio. When the Server Explorer window appears right click Data Connections and select Add Connection. Follow the wizard to connect to a database. Once this is done the Server Explorer window should look something like the screen shot below.

Now multi select all the tables and drag them onto the .dbml designer surface. You will get a warning about the connection string being stored in clear text. Click yes because the generated classes have connection string overloads that can be used later. Now the designer should look something like:

The generated code lives in a partial class underneath the .dbml file in Solution Explorer ending in designer.cs. On closer inspection of the generated code, notice there is a class for each table. LINQ to SQL doesn’t have the ability to generate model classes that are different from the actual tables. This functionality is reserved for the ADO.NET Entity Framework which Microsoft has decided is their future data access technology. As such, LINQ to SQL’s priority has been changed to maintenance only. Even so, it’s as powerful as ADO.NET Entity framework just less flexible.

Now with the SQL model created I can use LINQ to query and add values to the database. To add values, I implemented the visitor pattern to insert scraped data. The HebDataContext class, below, is the data context that was code generated by Visual Studio and is the key class which LINQ queries are run on. I created an instance of it and then do a LINQ query to see if the store exists in the database. If not, it is added and then the children of the store—the ads—are visited. The code looks like:

void IElementVisitor.VisitStore(ScrapeEngine.Modeling.Store storeModel)
{
    _currentStoreID = storeModel.HebID;
    using (HebDataContext context = new HebDataContext())
    {
        DataAccess.Store currentStore = context.Stores.Where(x => x.ID == storeModel.HebID).SingleOrDefault();
        if (currentStore == null)
        {
            DataAccess.Store store = new DataAccess.Store()
            {
                ID = storeModel.HebID,
                Name = storeModel.Name,
                Address = storeModel.Address,
                City = storeModel.City,
                State = storeModel.State,
                Zipcode = storeModel.Zipcode,
                Phone = storeModel.Phone
            };

            context.Stores.InsertOnSubmit(store);
            context.SubmitChanges();
        }
    }

    foreach (ScrapeEngine.Modeling.Ad ad in storeModel.Children)
        ad.AcceptVisitor(this);
}

Where LINQ to SQL really excels is the queries. Below is an example that gets all the items in a particular store's ads that are missing a price—this happens when the item’s price is dependent on buying another item—all from C#.

using (HebDataContext context = new HebDataContext())
{
    string[] items = context.Stores
        .Where(x => x.ID == 580)
        .SelectMany(x => x.Ads)
        .SelectMany(x => x.Items)
        .Where(x => x.Price == String.Empty)
        .OrderBy(x => x.Name)
        .Select(x => x.Name)
        .ToArray();

}

This query above gives the following when I poked in the debugger.

[0] "any four (4) Lean Cuisine Entrées"
[1] "Beneful Dry Dog Food"
[2] "Bumble Bee Solid White Tuna"
[3] "Cheetos"
[4] "Deviled Eggs Party Trays"
[5] "Eckrich Meat Smoked Sausage Links or Rope"
[6] "Eckrich Premium Beef Franks"
[7] "Fritos Corn Chips"
[8] "H-E-B Cafe Olé® Coffee"
[9] "H-E-B Chef Prepared Rotisserie Chicken"
[10] "H-E-B Chocolate Milk"
[11] "H-E-B Fresher Lasting® Chunky Guacamole Kit"
[12] "H-E-B Fully Cooked® Burgers"
[13] "H-E-B Fully Cooked® Shredded Chicken or Beef"
[14] "H-E-B inControl(TM) No Coding Test Strips"
[15] "H-E-B Ridged Potato Chips"
[16] "Hebrew National Beef Franks or Knockwurst"
[17] "Kiolbassa Value Pack Smoked Sausage"
[18] "Live Louisiana Crawfish"
[19] "Maxwell House Coffee"
[20] "Oscar Mayer Lunchmeat"
[21] "Planters Peanuts"
[22] "Sunshine Krispy Saltine Crackers"

Tags:

Software

Windbg and SOS for Visual Studio .NET Weenies

by breeve 10. February 2010 16:07

I first realized I was a Visual Studio weenie when I showed up to a Mono conference in Spain with a Windows laptop in a sea of Linux ones. I was so outnumbered I entertained the idea of not booting the Windows laptop at all because having the familiar Windows boot tone echoing off the conference walls would undoubtedly gather unwanted attention. Eventually, I calmed down and even managed to boot my Windows machine without too many stares.

One of the talks at the Mono conference—Mono is an open source implementation of .NET for Linux based machines—was about the progress on the Mono .NET debugger. Mono had released without a debugger initially and the debugger quickly become the #1 requested feature, no doubt mostly from .NET Windows users looking to port their .NET apps to Linux. The debugger being worked on, the presenter informed us, would not just be a command line debugger like GDB but would be fully integrated into MonoDevelop—the Linux equivalent of a Visual Studio—so that breakpoints could be set next to the source code. He even had a cute demo that worked. This is nice, I thought. After all, who needs a complicated command line debugger that takes years to learn. I needed to get things done.

During the many breaks we had between presentations, I was able to sit next to a few of the Mono developers and watch them code. They were very efficient with GDB and VI—the command line debugger and editor of choice for any self respecting Linux hacker. Still I figured, I was just as productive in Visual Studio.

And for the most part I am, except for nasty programming realities like memory leaks. For those, Visual Studio was at a loss and I was too until I discovered Windbg, the command line debugger for Windows, and its extension SOS. After learning a handful of commands, I was impressed in how quickly I was able to track down the source of leaks that had eluded fellow Visual Studio zealots. Maybe those Mono guys were on to something. After all, nothing says I am a debugger extraordinaire quicker than opening up Windbg and pounding out a few commands in front of your fellow programmers.

Below is a few commands and a tutorial on tracking down a leak in an example program.

!dumpheap -stat            Displays every managed type on the heap

!dumpheap -type typename   Displays every managed type that matches the specified type

!dumpheap -mt methodtable  Displays every instance of the specified method table

!gcroot address            Shows reference chain

!dumpobj address           Shows fields and addresses of references

!dumparray address         Shows objects in an array

!dumpmd                    Dumps the method description

!u                         Show assembly code

g                          Continue, let the program run

Ctrl+Break                 Break into the Windbg debugger

!help [command]            Show help

The .NET Windows Forms example program I wrote draws circles on a form. The circles are placed in random locations and move from top to bottom until they vanish. There are 500 circles on the form at any time. The program works well except it leaks memory like the Titanic. It went from 10MB to 20MB in 30 seconds and climbs into the hundreds of megs if left to run.

The code is below. See if you can spot the issue before we run Windbg.

public partial class Form1 : Form
{
    private const int NumberOfCircles = 500;
    private Timer _timer;

    public event EventHandler<DrawCircleEventArgs> Draw;

    public Form1()
    {
        InitializeComponent();

        _timer = new Timer();
        _timer.Interval = 100;
        _timer.Start();
        _timer.Tick += new EventHandler(OnTick);
        InitCircles();
    }

    private void InitCircles()
    {
        for (int x = 0; x < NumberOfCircles; x++)
            AddCircle();
    }

    private void AddCircle()
    {
        Circle circle = new Circle(new Rectangle(0, 0, Width, Height));
        circle.Done += new EventHandler(OnDone);
        Draw += new EventHandler<DrawCircleEventArgs>(circle.Draw);
    }

    private void OnTick(object sender, EventArgs e)
    {
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (Draw != null)
            Draw(this, new DrawCircleEventArgs(e.Graphics));
    }

    private void OnDone(object sender, EventArgs e)
    {
        AddCircle();
    }
}

public class Circle
{
    private static Random Rand;

    private Rectangle _parentBounds;
    private Point _position;
    private Size _circleSize;
    private byte[] _bytes;
    private bool _done;
    public event EventHandler Done;

    static Circle()
    {
        Rand = new Random((int)DateTime.Now.Ticks);
    }

    public Circle(Rectangle parentBounds)
    {
        _bytes = new byte[10240]; //10K for no reason but to make object bigger
        _done = false;
        _parentBounds = parentBounds;
        int xPosition = Rand.Next(parentBounds.Left, parentBounds.Right);
        int yPosition = Rand.Next(parentBounds.Top, parentBounds.Bottom);
        _position = new Point(xPosition, yPosition);
        _circleSize = new Size(10, 10);
    }

    private void OnDone()
    {
        _done = true;
        if (Done != null)
            Done(this, EventArgs.Empty);
    }

    public void Draw(object sender, DrawCircleEventArgs args)
    {
        _position = new Point(_position.X, _position.Y + 1);
        Rectangle rect = new Rectangle(_position, _circleSize);

        if (!_parentBounds.Contains(rect) && !_done)
            OnDone();
        else
            args.Graphics.DrawEllipse(Pens.Green, rect);
    }
}

To find the leak, I will open Windbg and attach to the program using File>>Attach to Process. If you don’t have Windbg, download the debugging tools for windows here. Next, load SOS like:

.loadby sos mscorwks

With SOS loaded, I can run commands from above like:

!dumpheap -stat

658551a8      163         3912 System.Windows.Forms.InvalidateEventArgs
641b8c4c       95         5320 System.Configuration.FactoryRecord
6585394c       78         5616 System.Windows.Forms.Internal.DeviceContext
6a72a930      190         6840 System.Collections.Hashtable+HashtableEnumerator
6a73303c       28         7608 System.Collections.Hashtable+bucket[]
6a7308ec      812        61060 System.String
6a7040bc      153        63636 System.Object[]
6a729b58     4804       153728 System.EventHandler
00126ba8     4847       155104 System.EventHandler`1[[Leaky.DrawCircleEventArgs, Leaky]]
00126ac4     4725       245700 Leaky.Circle
0032c6b0     1754       279984      Free
6a73335c     4729     48451204 System.Byte[]

This shows all types on the heap, number of instances, and the sizes with the largest on the bottom. I can see the byte arrays are taking 48 Megs and Leaky.Circle is 245k. The large byte arrays are a field of the Circle class designed to show the leak easier. What is more interesting is there are 4725 instances of the circle class. I was expecting only around 500 because as the circles go outside the form they should be garbage collected. So what is causing these Circle instances to stay in memory? I dump the method table listed for the Circle like:

!dumpheap -mt 00126ac4

071deb54 00126ac4       52    
071e13f4 00126ac4       52    
071e40fc 00126ac4       52    
071e699c 00126ac4       52    
071e923c 00126ac4       52    
071ebadc 00126ac4       52    
071ee37c 00126ac4       52

This shows all the instances of that method table on the heap. I can now use !gcroot on one of the addresses like:

!gcroot 071e13f4

ebx:Root:01c842b4(System.Windows.Forms.Application+ThreadContext)->
01c83878(Leaky.Form1)->
071f0bfc(System.EventHandler`1[[Leaky.DrawCircleEventArgs, Leaky]])->
064c5b38(System.Object[])->
071e3c54(System.EventHandler`1[[Leaky.DrawCircleEventArgs, Leaky]])->
071e13f4(Leaky.Circle)

This Circle instance is being held by an event handler which is being tracked back to the form itself. Let’s examine why the event handler above is holding onto the Circle instance by using the dump object command.

!dumpobj 071e3c54

Name: System.EventHandler`1[[Leaky.DrawCircleEventArgs, Leaky]]
MethodTable: 00126ba8
EEClass: 6a4c3518
Size: 32(0x20) bytes
 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
6a730508  40000ff        4        System.Object  0 instance 071e13f4 _target
6a72fd60  4000100        8 ...ection.MethodBase  0 instance 00000000 _methodBase
6a7331b4  4000101        c        System.IntPtr  1 instance   12c118 _methodPtr
6a7331b4  4000102       10        System.IntPtr  1 instance        0 _methodPtrAux
6a730508  400010c       14        System.Object  0 instance 00000000 _invocationList
6a7331b4  400010d       18        System.IntPtr  1 instance        0 _invocationCount

Finding the method this event handler is pointing to is a bit tricky. First, we dump the raw memory for the _methodPtr field

dd 12c118

0012c118  1c4653e9 00005f00 00126aac 00000000
0012c128  00000000 00000000 00000000 00000000
0012c138  00000000 00000000 00000000 00000000
0012c148  00000000 00000000 00000000 00000000
0012c158  00000000 00000000 00000000 00000000
0012c168  00000000 00000000 00000000 00000000
0012c178  00000000 00000000 00000000 00000000
0012c188  00000000 00000000 00000000 00000000

Then we dump the method description who’s address is located on the first line fourth column

!dumpmd 00126aac

Method Name: Leaky.Circle.Draw(System.Object, Leaky.DrawCircleEventArgs)
Class: 008f0b28
MethodTable: 00126ac4
mdToken: 06000014
Module: 00122c5c
IsJitted: yes
CodeAddr: 002f0770

The event handler is pointing to the Draw method on the Circle and keeping it in memory. I must not be unhooking the event. Looking at the source code, it becomes apparent that I am not unhooking that method anywhere in the code. The proper place to do it would be in the OnDone method of the Form1 class.

private void OnDone(object sender, EventArgs e)
{
    Circle circle = sender as Circle;
    Draw -= new EventHandler<DrawCircleEventArgs>(circle.Draw);
    AddCircle();
}

Now if I run the same test as before and dump the heap I get:

!dumpheap -stat

6a72b16c      245         5880 System.Collections.Stack
6a73303c       28         7608 System.Collections.Hashtable+bucket[]
6585394c      245        17640 System.Windows.Forms.Internal.DeviceContext
6a729b58     1193        38176 System.EventHandler
00146ba8     1230        39360 System.EventHandler`1[[Leaky.DrawCircleEventArgs, Leaky]]
6a7308ec      755        59232 System.String
00146ac4     1176        61152 Leaky.Circle
6a7040bc      315        95188 System.Object[]
0035c6b0      918      1738620      Free
6a73335c     1180     12066856 System.Byte[]

Notice there is a smaller number of circles and byte arrays and when I run for a long time the memory doesn’t go over 30 megs. There are 1176 circles but most of those are waiting to be collected. This may seem like a contrived example but from my experience it is all too real. Most memory leaks I have found in code bases are because of event handlers not being unhooked.

Tags:

Software

The Dreaded Switch Statement

by breeve 27. January 2010 15:21

One of the quickest ways to bring a delightful code review to a halt is to slip in a big switch statement somewhere in the code. Upon encountering it, some will feel uncomfortable and not know why while others will give you a long soul piercing stare.

Switch statements get a bad rap for a number of reasons. The chief among them being when a value is added to an enum you have to track down every switch statement that is using that enum and add your new code. This may seem simple but most of the time there is more than one switch statement to contend with. Often the changes needed are in unfamiliar code which leads to a long and error prone process.

There are alternatives thanks to object oriented programming and polymorphism. To understand such an approach, the C# code below shows a scenario with a switch statement. The class Rect takes an enum FillStyle as a property. In the Draw method it creates different brushes—depending on the FillStyle property—using a switch statement. Code like this is very common.

public enum FillStyle
{
    Solid,
    Gradient
}

public class Rect
{
    private Point _location;
    private Size _size;

    public Rect(Point location, Size size)
    {
        _location = location;
        _size = size;
        FillStyle = FillStyle.Solid;
        FillColor = Color.Green;
    }

    public FillStyle FillStyle { get; set; }
    public Color FillColor { get; set; }

    public void Draw(Graphics graphics)
    {
        Brush brush = null;
        Rectangle bounds = new Rectangle(_location, _size);
        switch (FillStyle)
        {
            case FillStyle.Solid:
                brush = new SolidBrush(FillColor);
                break;
            case FillStyle.Gradient:
                brush = new LinearGradientBrush(bounds, FillColor, Color.Red, LinearGradientMode.Horizontal);
                break;
            default:
                Debug.Fail("Not a valid FillStyle");
                break;
        }

        using (brush)
        {
            graphics.FillRectangle(brush, bounds);
        }
    }
}

An alternate and better approach is to use an abstract class with public static concrete instances. The primary motivation for this is the abstract class can masquerade as an easy to use enum. This way the code the API user has to write is exactly the same as before and most importantly just as simple. In fact, most users, will never notice they are working with an abstract class with singleton instances; to them it just looks like an enum.

In our case, an abstract FillStyle class is created with two public singleton instances that have the same names as our original enum values above. The FillStyle class has one abstract method called CreateBrush. Reworking the above example looks like the code below.

public abstract class FillStyle
{
    public static readonly FillStyle Solid = new SolidImpl();
    public static readonly FillStyle Gradient = new GradientImpl();

    protected FillStyle()
    {
    }

    public abstract Brush CreateBrush(Color color, Rectangle bounds);

    private class SolidImpl : FillStyle
    {
        public override Brush CreateBrush(Color color, Rectangle bounds)
        {
            return new SolidBrush(color);
        }
    }

    private class GradientImpl : FillStyle
    {
        public override Brush CreateBrush(Color color, Rectangle bounds)
        {
            return new LinearGradientBrush(bounds, color, Color.Red, LinearGradientMode.Horizontal);
        }
    }
}

public class Rect
{
    private Point _location;
    private Size _size;

    public Rect(Point location, Size size)
    {
        _location = location;
        _size = size;
        FillStyle = FillStyle.Solid;
        FillColor = Color.Green;
    }

    public FillStyle FillStyle { get; set; }
    public Color FillColor { get; set; }

    public void Draw(Graphics graphics)
    {
        Rectangle bounds = new Rectangle(_location, _size);

        using (Brush brush = FillStyle.CreateBrush(FillColor, bounds))
        {
            graphics.FillRectangle(brush, bounds);
        }
    }
}

The first thing you will notice is the Draw method on the Rect class is simple and adding new values to the FillStyle class has become substantially easier than the former traditional enum example because the code can be added in one central place. Gone is the frustrating experience of updating every switch statement in the entire code base. The introduced abstraction adds to the extensibility of the design. If a user wants to add a custom FillStyle—not originally anticipated by the API authors—they can by inheriting from FillStyle and overriding CreateBrush in the same way.

Tags:

Software

HOA Forum Software Developed

by breeve 15. January 2010 16:25

Over the last months in my spare time, I developed a web based HOA (Home Owners Association) forum tailored to HOA organizations. I have posted all the code—above the screenshot—with instructions on how to get the software up and running should you care to run or modify it. You can do whatever you want with the software—it is now open source software.

The project started as a desire to play with ASP.NET MVC, LINQ to SQL, and jQuery. As the functionality increased, I tried my hand at pitching it to different neighborhoods. No interest. Not even for free. Most already had forums running like phpBB. I would be lying if I didn’t admit that part of my motivation was to develop a better looking forum than phpBB and hopefully banish it from all existence in the HOA space. Nathan Philpot helped with design work to make it look great but even that didn’t help as HOA organizations seemed just fine running their existing forums. The switching costs were just too much.

In reality, switching costs where just one reason it wasn’t adopted. HOA organizations, I found out, have very different ideas of what they want forcing the market into fragmentation. In fact, many build their own solutions in house.

Features Include:

  • Posted topics can be voted on which allows popular issues to rise to the top.
  • Each topic can show a voting history graph
  • Search topics
  • Each user has a page that lists:
    • Their posted topics and replies
    • Their reputation (gained by others voting on their topics or replies)
  • Topics can be flagged as spam or inappropriate by anyone, allowing an administrator to delete off color topics.
  • Posted topics are sorted by 4 main tabs
    • Recent - topics listed by date
    • Popular - topics listed by votes
    • Hot - topics listed by recent activity
    • No Replies - topics with no replies
  • When HOA members post, a special HOA icon is displayed notifying readers of a more official response
  • The HOA corner area on the right allows HOA members to post ideas, status, or announcements
  • RSS / Email updates

Hoa.zip

Tags:

Software

Programmatic Publish for an ASP.NET Project

by breeve 19. November 2009 16:07

I recently needed to automate an ASP.NET project build so that it copies itself to a directory. This is accomplished in Visual Studio 2008 by right clicking the ASP.NET project and selecting Publish. For ASP.NET projects, the Publish command copies all files needed for the site.

After searching some, I found this can be done by using msbuild. Below is my solution that copies all files needed to C:\Data\Projects\TestDeploy. It invokes msbuild to build the web application then uses an existing task called _CopyWebApplication to copy all the files over.

msbuild /t:Build;_CopyWebApplication /p:Configuration=Release
/p:OutDir=C:\Data\Projects\TestDeploy\bin\
/p:WebProjectOutputDir=C:\Data\Projects\TestDeploy
C:\src\Branches\HOA\HomeOwners.Mvc\HomeOwners.Mvc.csproj

Note that this solution does not precompile the site. To precompile, use aspnet_compiler.

Tags:

Software

JQuery AJAX and ASP.NET MVC Texas Hold’em.

by breeve 1. November 2009 12:13

I wrote this little app to show how easy it is to do Ajax calls with jQuery. It can be downloaded here. Below is the HTML that defines the tables the cards are a part of. There are three main div sections with id attributes of actions, dealer, and players. These divs define the main regions of the game above.

<div id="main">
     <input type="hidden" />
     <div id="actions">
        <table>
            <tr>
                <th colspan="4">Actions</th>
            </tr>
            <tr>
                <td><button>Deal</button></td>
                <td><button disabled="disabled">Flop</button></td>
                <td><button disabled="disabled">Turn</button></td>
                <td><button disabled="disabled">River</button></td>
            </tr>
        </table>
     </div>
     <div id="dealer">
        <table>
            <tr>
                <th colspan="3">Flop</th>
                <th>Turn</th>
                <th>River</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
    <div id="players">
        <table>
            <tr>
                <th colspan="2">Player 1</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 2</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 3</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
</div>

Server Side

My shuffling algorithm is very simple but let's pretend it is a highly secretive new shuffling technique that we want to keep a secret; so I do it server side. Listed below are the three services and a nested helper class called Game that keeps the state of the game. They are all members of the default HomeController ASP.NET MVC creates when you do a new MVC project.

The first service, the method called Deal, takes the number of players and returns the unique gameID—that the client will use to call the other two services—and the players initial dealt cards. The cards are integers from 1 to 52 that match the corresponding png card images. In the last line of all three services, I take advantage of the ability of ASP.NET MVC to take .NET objects and turn them into Json.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Deal(int playerCount)
{
    Game game = new Game();

    IDictionary<string, int[]> playerCards = game.Deal(playerCount);
    HttpContext.Cache[game.ID.ToString()] = game;

    return Json(new {gameID = game.ID, cards = playerCards});
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Flop(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.Flop());
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DrawCard(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.DrawCard());
}

private class Game
{
    private Guid _guid;
    private List<int> _cards;
    private Random _rand;

    public Game()
    {
        _rand = new Random((int)DateTime.Now.Ticks);
        _guid = Guid.NewGuid();
        _cards = new List<int>();
        for (int i = 1; i <= 52; i++)
            _cards.Add(i);
    }

    public Guid ID
    {
        get
        {
            return _guid;
        }
    }

    public int DrawCard()
    {
        int index = _rand.Next(0, _cards.Count - 1);
        int value = _cards[index];
        _cards.Remove(value);
        return value;
    }

    public IDictionary<string, int[]> Deal(int playerCount)
    {
        Dictionary<string, int[]> playerCards = new Dictionary<string, int[]>();
        for (int i = 0; i < playerCount; i++)
        {
            int[] cards = new int[] { DrawCard(), DrawCard() };
            playerCards.Add(i.ToString(), cards);
        }

        return playerCards;
    }

    public int[] Flop()
    {
        return new int[] { DrawCard(), DrawCard(), DrawCard() };
    }
}

Client Side

Listed below is the jQuery and JavaScript. The $().ready function is jQuery’s way of calling me to run code once the page is loaded. In that function I use jQuery's selectors to subscribe to the Deal, Flop, Turn, and River button click events.

The Deal button selector $("#actions button:eq(0)"), for example, says pick the first button under the element with an id=action. This will give me the button labeled “Deal”. I then hook up to the click event of the Deal button. Once the Deal button is clicked, I set all img elements in the page to the blank card to reset the game. Then I disable/enable other action buttons and set up the AJAX call. I set up the AJAX call by telling jQuery to do a HTTP post to the URL '/Home/Deal' and pass it the playerCount.

If there is no error on the server, jQuery will call the function set to the success property. In my case, if there is no error on the server, I set the gameID to a hidden element in the page so other service calls can use it and then go through the returned cards setting the images.

function SetPlayersCards(selector, cards){
    for (i = 0; i < cards.length; i++) {
        var imgPath = "/Images/" + cards[i] + ".png";
        selector.eq(i).attr("src", imgPath);
    }
}

$().ready(function(){

    //deal button
    $("#actions button:eq(0)").click(function(){
        $("img").attr("src", "/Images/b1fv.png");
        $("#actions button:gt(1)").attr("disabled", "disabled");
        $("#actions button:eq(1)").removeAttr("disabled");
        $.ajax({
            type: "Post",
            url: '/Home/Deal',
            data: {playerCount:3},
            dataType: "json",
            success: function(data) {
                $("#main :hidden").val(data.gameID);
                //player 1
                SetPlayersCards($("#players img:lt(2)"), data.cards[0]);
                //player 2
                SetPlayersCards($("#players img:gt(1):lt(2)"), data.cards[1]);
                //player 3
                SetPlayersCards($("#players img:gt(3)"), data.cards[2]);
            }
        });
    });
   
    //flop button
    $("#actions button:eq(1)").click(function(){
        $("#actions button:eq(1)").attr("disabled", "disabled");
        $("#actions button:eq(2)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/Flop',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var flopCards = $("#dealer img:lt(3)");
                for(i = 0; i < data.length; i++){
                     var imgPath = "/Images/" + data[i] + ".png";
                     flopCards.eq(i).attr("src", imgPath);
                }
            }
        });
    });
   
    //turn button
    $("#actions button:eq(2)").click(function(){
        $("#actions button:eq(2)").attr("disabled", "disabled");
        $("#actions button:eq(3)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(3)").attr("src", imgPath);
            }
        });
    });
   
    //river button
    $("#actions button:eq(3)").click(function(){
        $("#actions button:eq(3)").attr("disabled", "disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(4)").hide().attr("src", imgPath).fadeIn(1000);
            }
        });
    });

});

Using fiddler, I can see what is sent on the wire when the Deal button is pressed:

/****Request****/

POST /Home/Deal HTTP/1.1
Host: localhost:49220
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7 (.NET CLR 3.5.30729)
Accept: application/json, text/javascript, */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:49220/
Content-Length: 13
Cookie: filterTags=
Pragma: no-cache
Cache-Control: no-cache

playerCount=3

/****Response****/

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sun, 01 Nov 2009 16:58:39 GMT
X-AspNet-Version: 2.0.50727
X-AspNetMvc-Version: 1.0
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 94
Connection: Close

{"gameID":"2ce44c42-20b8-4819-b796-3f07c305fc9e","cards":{"0":[47,32],"1":[18,9],"2":[16,41]}}

By looking at the Json in the response, I can verify that the gameID is a GUID and the cards for all three players are there. The other two services send the data in different Json formats but the same pattern is there.

Tags:

Software

Symbol Server Setup

by breeve 23. October 2009 15:57

I went through the process of setting up a symbol server for our team at work. We develop code in .NET. Different teams were exporting .NET dlls for my team and others to use. We wanted the ability to debug the source. Without the symbols server we would have to sync the source to the correct version of the exported dll and build just to debug. This went on for some time and after much complaining, I looked for other options.

A symbol server ensures that you are able to get pdbs and source code for your dlls or executables from any machine.

Let’s get started

To get started, you will need to have your source stored in source code control. The source control products supported by symbol server out of the box are: Perforce, Visual SourceSafe, Team Foundation Server, Concurrent Versions System, and Subversion. Next, download the Debugging tools for windows which installs to the Program Files directory by default.

Now that you have the debugging tools installed, let’s get familiar with some of the tools you will need to use:

  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\ssindex.cmd - This is a batch file wrapper around perl scripts that support the souce code control products listed above. This tool is responsible for going through the pdbs and changing the source code file paths inside the pdbs to paths that can be found in source code control. This process is called indexing the pdb.
  • C:\Program Files\Debugging Tools for Windows (x86)\symstore.exe - This tool adds the indexed pdbs and dlls to a directory of your choosing. The tool creates version specific directories where the pdbs and dlls are stored. This directory is commonly set up as a file share. This location is where others will point their hungry Visual Studio to download the much needed symbols.
  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srctool.exe - This is used mostly to find out if your pdbs were indexed correctly.
  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srcsrv.doc - Documentation. Yes, we all read documentation.

Index the pdb

I am going to create a simple windows forms app that everyone on my team is itching to debug. I have checked it into perforce (the source code control we use) and it is ready to be indexed and added to the symbols store.

Before I run ssindex.cmd I must edit C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srcsrv.ini to tell it where my source code control server lives. It should be straight forward because it is a well documented file.

Now I am ready to run ssindex.cmd like this:

ssindex.cmd -system=p4 -symbols=C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug -source=C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1 /debug

The -symbols switch is the directory where my dll and pdb are. The -source switch is the root directory of my source. The /debug just tells it to be verbose (print out stuff). I can tell this worked correctly because it says it wrote the pdb file. For the curious, the way this works is ssindex.cmd calls the command “p4 have” for each source file on the local disk. This returns the path in perforce and version of that file. This is then written into the pdb. We can see these paths if we run srctool.exe on the pdb like:

srctool c:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\WindowsFormsApplication1.pdb

The first thing to notice is the bottom line which reports that 5 source files where indexed. Above that it lists each file that was indexed along with the “p4 print” command and the perforce path. When you are debugging, it will pull the source from perforce using the print command.

Now that I am confident my source files were indexed properly it is time to add them to a shared folder where my greedy debuggers can get started.

Add to Symbols Store

The tool to use here is symstore.exe. I use it like this:

symstore add /r /f C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug /s C:\Symbols /t app

The symbol store is nothing but a directory structure. The /s switch specifies the root folder to put symbols under. Symstore reports that I have successfully added 3 files to the symbol store. In this case, I am storing the symbols in the directory c:\Symbols as described above by the /s switch. If I look there I will see three directories for each symbol and an admin directory used by the tool for bookkeeping.

If I look inside the pdb dir (the directory highlighted above) I see a guid looking directory. Inside the .exe there is a matching GUID which is how the .exe finds the correct pdb. For the devoted who want to see that value inside the exe or dll, you can run dumpbin /headers on the exe or dll and look for something like:

Debug from another Machine

I want to debug this exe on another box. Let’s fire up Visual Studio 2008 and add a few things. Check enable source server support in the options dialog found in Tools>>Options as shown below.

Go into the symbols option under debugging and add the path to the share where the symbols are located. Also add the directory on your machine where the symbols will be downloaded to. Hit OK.

Now, I will run the exe on my machine. Remember, all I have is the exe and nothing else.

I want to debug what happens when I press the button on my buggy application above. I attach to the exe using Visual Studio. Make sure you have manged code selected in the attach to box.

After I click attach, I can see the symbols have been loaded for the exe using the output window. There is also a modules window accessible from Debug>>Windows in Visual Studio. This will list every dll and whether symbols were loaded. If you right click on a loaded module there are some options to see where symbols were loaded from. This is good for debugging when something goes wrong.

I have successfully attached at this point and now it is time to set a breakpoint using the breakpoint window for the button click event handler. The breakpoint window is under Debug>>Windows>>Breakpoints. Once you have the breakpoint window up, click New then break at function.

When I click on the button in the exe, I get a prompt telling me that VS wants to download the source. I click run.

The source is downloaded and I am debugging just as if the source was built on my machine.

Silverlight Gotcha

If you are wanting to do this with silverlight you will have to add the registry key below for every machine that wants to pull symbols from the symbol store.

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\AD7Metrics\Engine{00000000-0000-0000-0000-000000000000} 
Set RequireFullTrustForSourceServer to 0 (REG_DWORD)

I spent a week with Microsoft support on this one.

Tags:

Software

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About Me

I am a Software Engineer with 8 years experience developing and releasing software products. I started developing in C/C++ then moved into .NET and C# for the last 6 years and have tech lead multiple projects. I have developed products in Windows Forms, ASP.NET, Silverlight, and WPF. I currently reside in Austin, Texas.

Currently Reading