Saturday, November 29, 2008

Using MSTest With F#

Before creating this blog entry, I had always written tests against my F# projects in C#.  I prefer this approach because it helps ensure optimal interoperability between the F# libraries and other .NET languages.  However, it was always assumed that the tests could just as easily have been written in F#.  While this assumption is accurate for some of the available unit testing frameworks, it is not correct for the MSTest framework.

This post will focus on my attempt to identify a convenient method of creating F# test fixtures using the MSTest unit testing framework.  The following links provide examples using other unit testing frameworks (NUnit, FsTest  library for xUnit).

Creating My First F# MSTest Test Fixture:

To create the MSTest test fixture, a new F# library project was created and a reference was added to Microsoft.VisualStudio.QualityTools.UnitTestFramework.  Figure 1 shows the very simple MSTest test fixture. 

Figure 1

#light
namespace FSharpTests
open Microsoft.VisualStudio.TestTools.UnitTesting

[<TestClass>]
type FSharpTestsInFSharp = class
    [<testmethod>]
    member this.FSharpTests_CanGetCustomerByIdFromDB =
       Assert.IsTrue(1=1)
end 

This code compiled without issue and all seemed well until Ctrl+R,A was used to run all of the tests in the solution.  The Test Results screen appeared and all of the C# TestFixtures were represented (the new F# project was added to an existing solution, which contained a C# test project); however, my new fixture was no where to be seen.

Off to the Command Line:


This was very puzzling, so I decided to dig a little deeper by launching the test from the command line.  Figure 2 displays the basic syntax used (note: The actual location of the test project has been replaced with <project location>).  This test resulted in a message stating "No tests to execute".

Figure 2:

MSTest.exe /testcontainer:"<project location="">\bin\Debug\FSharpSpecifications.dll"

Now to MSIL:

To get a better idea of what was going on, I typed ildasm, opened FSharpSpecifications.dll, and dumped all of the IL code to an ANSI file named FSharpSpecifications.il.  After several minutes of review, it was discovered that though the TestMethodAttribute is defined in the property instance, it is not defined in the method instance.  Figure 3 displays the original IL along with the new line that allows MSTest to locate the test (note: The actual location of the test project has been replaced with <project location>).

Figure 3

//  Microsoft (R) .NET Framework IL Disassembler.  Version 3.5.30729.1
//  Copyright (c) Microsoft Corporation.  All rights reserved.
// Metadata version: v2.0.50727
.assembly extern /*23000001*/ mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly extern /*23000002*/ FSharp.Core
{
  .publickeytoken = (A1 90 89 B1 C7 4D 08 09 )                         // .....M..
  .ver 1:9:6:2
}
.assembly extern /*23000003*/ Microsoft.VisualStudio.QualityTools.UnitTestFramework
{
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
  .ver 9:0:0:0
}
.assembly /*20000001*/ FSharpSpecifications
{
  .custom /*0C000003:0A000001*/ instance void [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute/*01000002*/::.ctor(int32,
                                                                                                                                                    int32,
                                                                                                                                                    int32) /* 0A000001 */ = ( 01 00 01 00 00 00 09 00 00 00 06 00 00 00 00 00 ) 

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom /*0C000004:0A000002*/ instance void [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*01000003*/::.ctor(valuetype [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*01000003*//DebuggingModes/*01000004*/) /* 0A000002 */ = ( 01 00 01 01 00 00 00 00 ) 

  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.mresource /*28000001*/ public FSharpSignatureData.FSharpSpecifications
{
  // Offset: 0x00000000 Length: 0x000003E2
  // WARNING: managed resource file FSharpSignatureData.FSharpSpecifications created
}
.mresource /*28000002*/ public FSharpOptimizationData.FSharpSpecifications
{
  // Offset: 0x000003E8 Length: 0x000000CE
  // WARNING: managed resource file FSharpOptimizationData.FSharpSpecifications created
}
.module 'F#-Module-FSharpSpecifications'
// MVID: {492AF409-0E8B-9CF0-A745-038309F42A49}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x01140000

// =============== CLASS MEMBERS DECLARATION ===================

.class /*02000002*/ public auto ansi serializable beforefieldinit FSharpTests.FSharpTestsInFSharp
       extends [mscorlib/*23000001*/]System.Object/*01000001*/
{
  .custom /*0C000005:0A000006*/ instance void [Microsoft.VisualStudio.QualityTools.UnitTestFramework/*23000003*/]Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute/*01000008*/::.ctor() /* 0A000006 */ = ( 01 00 00 00 ) 
  .custom /*0C000006:0A000007*/ instance void [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.CompilationMappingAttribute/*01000009*/::.ctor(valuetype [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.SourceConstructFlags/*0100000A*/) /* 0A000007 */ = ( 01 00 03 00 00 00 00 00 ) 
  .method /*06000001*/ public specialname rtspecialname 
          instance void  .ctor() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x2050
    // Code size       10 (0xa)
    .maxstack  3
    .language '{AF046CD3-D0E1-11D2-977C-00A0C9B4D50C}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
// Source File '<project location>\CustomerDaoTests.fs' 
    .line 31,31 : 13,15 '<project location>\CustomerDaoTests.fs'
//000031:     new() = {}
    IL_0000:  /* 02   |                  */ ldarg.0
    IL_0001:  /* 28   | (0A)000004       */ call       instance void [mscorlib/*23000001*/]System.Object/*01000001*/::.ctor() /* 0A000004 */
    IL_0006:  /* 02   |                  */ ldarg.0
    IL_0007:  /* 26   |                  */ pop
    IL_0008:  /* 00   |                  */ nop
    IL_0009:  /* 2A   |                  */ ret
  } // end of method FSharpTestsInFSharp::.ctor

  .method /*06000002*/ public instance void 
          get_FSharpTests_CanGetCustomerByIdFromDB() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x2068
    // Code size       13 (0xd)
    // This is the new line
    .custom /*0C000002:0A000003*/ instance void [Microsoft.VisualStudio.QualityTools.UnitTestFramework/*23000003*/]Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute/*01000006*/::.ctor() /* 0A000003 */ = ( 01 00 00 00 ) 
    .maxstack  4
    .line 39,39 : 9,27 ''
//000032:     [<TestMethod>]
//000033:     member this.FSharpTests_CanGetCustomerByIdFromDB = 
//000034: //        let customerDao = new CustomerDao() :> ICustomerDao
//000035: //        let customer = customerDao.GetByIdFromDB(2)
//000036: //        Assert.IsTrue(customer.Id = int(2))
//000037: //        Assert.IsTrue(customer.Name = "AABB, Inc")
//000038: //        Assert.IsTrue(customer.Balance = 29M)
//000039:         Assert.IsTrue(1=1)
    IL_0000:  /* 00   |                  */ nop
    IL_0001:  /* 17   |                  */ ldc.i4.1
    IL_0002:  /* 17   |                  */ ldc.i4.1
    IL_0003:  /* FE01 |                  */ ceq
    IL_0005:  /* FE14 |                  */ tail.
    IL_0007:  /* 28   | (0A)000005       */ call       void [Microsoft.VisualStudio.QualityTools.UnitTestFramework/*23000003*/]Microsoft.VisualStudio.TestTools.UnitTesting.Assert/*01000007*/::IsTrue(bool) /* 0A000005 */
    IL_000c:  /* 2A   |                  */ ret
  } // end of method FSharpTestsInFSharp::get_FSharpTests_CanGetCustomerByIdFromDB

  .property /*17000001*/ instance class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.Unit/*01000005*/
          FSharpTests_CanGetCustomerByIdFromDB()
  {
    .custom /*0C000002:0A000003*/ instance void [Microsoft.VisualStudio.QualityTools.UnitTestFramework/*23000003*/]Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute/*01000006*/::.ctor() /* 0A000003 */ = ( 01 00 00 00 ) 
    .get instance void FSharpTests.FSharpTestsInFSharp/*02000002*/::get_FSharpTests_CanGetCustomerByIdFromDB() /* 06000002 */
  } // end of property FSharpTestsInFSharp::FSharpTests_CanGetCustomerByIdFromDB
} // end of class FSharpTests.FSharpTestsInFSharp

.class /*02000003*/ private abstract auto ansi sealed beforefieldinit '<StartupCode$FSharpSpecifications>'.$CustomerDaoTests
       extends [mscorlib/*23000001*/]System.Object/*01000001*/
{
  .field /*04000001*/ static assembly native int _init
  .custom /*0C000001:0A000008*/ instance void [mscorlib/*23000001*/]System.Runtime.CompilerServices.CompilerGeneratedAttribute/*0100000B*/::.ctor() /* 0A000008 */ = ( 01 00 00 00 ) 
} // end of class '<StartupCode$FSharpSpecifications>'.$CustomerDaoTests

.class /*02000004*/ private abstract auto ansi sealed beforefieldinit '<PrivateImplementationDetails$FSharpSpecifications>'
       extends [mscorlib/*23000001*/]System.Object/*01000001*/
{
} // end of class '<PrivateImplementationDetails$FSharpSpecifications>'


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************


Running the Tests From the Altered DLL:

With the new line added to the IL file, I created a new dll using the command specificed in Figure 4 (note: The actual location of the test project has been replaced with <project location>), then ran the test using the command specified in Figure 2.  This time the test was identified and successfully run.

Figure 4

ilasm "<project location>\FSharpSpecifications.il" /DLL

Conclusion:

The point of this little experiment was to find a convenient way to implement test fixtures in F# using the MSTest unit testing framework.  Unfortunately, this goal was not reached, though a certain level of success was achieved.  It is likely that full support will be provided when F# is officially shipped with VS, but until that time we can always continue to create F# test fixtures in NUnit/xUnit or use my prefered approach and write the tests in C#.

Friday, November 21, 2008

F# Tools and Libraries

Since F# is still in infancy, the number of associated tools and libraries is minimal when compared to more mature languages. However, the list is growing and will continue to increase as more and more developers get introduced to the language.

The following, is a list of tools and libraries compiled through various Google searches.  Since the line is sometimes blurred between a tool/library, I have generally relied upon the author's description to determine the correct category.

Tools:

fsunit - This is a testing (a.k.a. specifications) framework written in F# for F#.
F# SyntaxHighlighter Brush - This is an add on to SyntaxHighlighter, which allows F# code to be posted on a web page and displayed as it would in Visual Studio.
FS Web Tools - This is a set of tools to "author homogeneous client/server/database web applications".
AbsIL SDK  - Allows manipulation of .NET files and binaries.
F# MPI Tools - This is a message passing interface (MPI) tool that can be used to develop distributed memory applications.
FsCheck  - This is "a tool for testing F# code automatically".
PDF Toolkit  - This is a command line tool that allows manipulation of PDF files.
fsc.exe - This is the compiler that comes with the F# CTP.
fsi.exe - This is the interactive tool that comes with the F# CTP.
fslex.exe - This is a tool used to create lexical analyzers that comes with the F# CTP.
fsyacc.exe - This is a tool used to create parsers that comes with the F# CTP.

Libraries:

STM for F# - This is a software transactional memory (STM) library for F#.
FParsec  - This is an F# parser combinator library ported from Haskell.
Parallel Extensions Library - This library is not specific to F#, but lends itself nicely to the "immutable by default" approach.  It provides "a managed programming model for data parallelism, task parallelism, and coordination on parallel hardware unified by a common work scheduler".

Conclusion:


One of the major advantages that F# has over other functional programming languages is the .NET framework.  This allows almost any .NET tool/library to be compatible with F#.  In addition, a number of tools/libraries have been created specifically with F# in mind and that number will continue to increase.  Feel free to leave a comment linking to those that I have missed.




Saturday, November 15, 2008

Linq to SQL in F#

In the entry titled Linq to XML in F#, I walked through an example of how to use Linq to XML in F# to retrieve customer information from an XML file. In this post, I will show how this same functionality can be achieved with a database using the "experimental" Linq to SQL (a.k.a. FLinq) support found in FSharp.Powerpack.FLinq.dll that was released in the Sept. 2008 CTP. Don Syme states that this "is labelled experimental because some advanced SQL concepts are not yet handled (e.g. left-outer-joins)" (post by Don - Note: This post also contains several FLinq examples).

Path to Completion:
Since basic Separation of Concern (SoC) concepts were originally applied, only the data access layer will need to be updated to support the change from an XML repository to a SQL repository.

The following steps will need to be completed to accomplish this goal:
1. Create the test.
2. Create the database.
3. Generate the object model.
4. Implement the functionality.

Creating the Test:

This test is going to be similar to that which was written for the Linq to XML example. It will simply retrieve a customer object based on a specified Customer id. For example purposes only, I will be adding these methods/functions into the same projects that were created in the Linq to XML example.
        
[TestMethod]
public void CanGetCustomerByIdFromDB()
{   
    FSharpMockExample.Data.ICustomerDao customerDao = new FSharpMockExample.Data.CustomerDao();   
    FSharpMockExample.Entities.ICustomer customer = customerDao.GetByIdFromDB(2);      
    Assert.AreEqual(customer.Id, 2);   
    Assert.AreEqual(customer.Name, "AABB, Inc");        
    Assert.AreEqual(customer.Balance, 30);
} 
Creating the Database:

The database for this example is very simple with a single table that contains three fields.

Here are the steps that were followed to create the database using the SQL Server 2005 Express Edition:

1. Create a database called FSharpSample.
2. Run the following script to create the table:

USE [FSharpSample]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Customers](
    [CustomerId] [int] NOT NULL,
    [Name] [varchar](50) NOT NULL,
    [Balance] [decimal](18, 2) NOT NULL, CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED (    [CustomerId] ASC)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) 
ON [PRIMARY]) ON [PRIMARY]
GO
SET ANSI_PADDING OFF 
3. Populate the table with the following script:

USE [FSharpSample]
GO
INSERT INTO dbo.Customers (CustomerId, Name, Balance)VALUES (1, 'ABC Company', 20.00)
INSERT INTO dbo.Customers (CustomerId, Name, Balance)VALUES (2, 'AABB, Inc', 30.00)
GO

Generating the Object Model:

The object model is generated using the code generation tool called SqlMetal.exe, which is often found in c:\program files\Microsoft SDKS\Windows\v6.0A\bin\. Since SqlMetal.exe does not yet support F#, the object model is generated in C#.

There are a lot of ways to generate the object model; however, for this specific example I did the following:

1. Open a command prompt and navigate to c:\program files\Microsoft SDKS\Windows\v6.0A\bin\.
2. Execute the following command: sqlmetal /server:".\SQLEXPRESS" /database:FSharpSample /namespace:FSharpSample /code:FSharpSampleDB.cs /language:csharp
3. This generates a file called FSharpSampleDB.cs and places it in the c:\program files\Microsoft SDKS\Windows\v6.0A\bin\ directory.
4. Create a new CSharp Class Library project within the solution and add FSharpSampleDB.cs to it.
5. Build the new project.

On to the F# Code:

The first step that is needed to support FLinq in a project is to add references to System.Data, System.Data.Linq, and FSharp.PowerPack.Linq. After that, it is simply a matter of adding a new member to the CustomerDao class.

Signature File:

The only change to the CustomerDao signature file is the addition of one new abstract member.

#light
namespace FSharpMockExample.Data
open FSharpMockExample.Entities
open System.Xml.Linq

type ICustomerDao = interface
    abstract GetById: int -> ICustomer
    abstract GetById: XElement * int -> ICustomer
    abstract GetByIdFromDB: int -> ICustomer
end

type CustomerDao = class
    new: unit -> CustomerDao
    interface ICustomerDao
end


Source File:

The first thing that should be noticed is the #nowarn "57" compiler directive. Since FLinq is still in the "experimental" phase, this directive is necessary to suppress the related warning. Next, you'll notice that the Microsoft.FSharp.Linq and FSharpSampleDB namespaces have been opened. The rest of the work is in the GetByIdFromDB member.

Here's a description of the GetByIdFromDB member:

1. The first identifier is set to the database connection string (note: This is hard coded for example purposes only. Normally the value would be stored in a config file.)
2. The second identifier is set to a new instance of the DataContext.
3. Finally we build and execute a Linq Query Expression. There are a few interesting things that should be noticed. First, the quoted expression identified by "<@" and "@>". These quotations cause the compiler to treat the syntax within them in a special way. Second, the Seq.hd function causes only the first element to be returned. Finally, the resulting object is cast to the ICustomerDao interface.

#light
#nowarn "57"
namespace FSharpMockExample.Data
open FSharpMockExample.Entities
open System.Xml.Linq
open Microsoft.FSharp.Linq
open FSharpSampleDB

module XLinqHelper = 
    let GetXName xname = XName.op_Implicit(xname)

type ICustomerDao = interface
    abstract GetById: int -> ICustomer
    abstract GetById: XElement * int -> ICustomer
    abstract GetByIdFromDB: int -> ICustomer
end

type CustomerDao = class
    new()={}
    interface ICustomerDao with
        member this.GetById id =
            let rawXml = "removed from this sample"
            let thisInterface = (this :> ICustomerDao)
            let xml = XElement.Parse rawXml
            thisInterface.GetById (xml, id)

        member this.GetById (xml, id) =
            let GetCustomer (customerElement:XElement) = 
                new Customer(int(customerElement.Attribute(XLinqHelper.GetXName "Id").Value), 
                    customerElement.Attribute(XLinqHelper.GetXName "Name").Value, 
                    decimal(customerElement.Attribute(XLinqHelper.GetXName "Balance").Value)) :> ICustomer

            xml.Elements() |> Seq.find(fun customer -> (int(customer.Attribute(XLinqHelper.GetXName "Id").Value) = id)) 
                           |> GetCustomer
        member this.GetByIdFromDB id =
            let connString = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=FSharpSample;Data Source=.\SQLEXPRESS"

            let db = new FSharpSampleDB(connString)

            Query.query <@ seq { for c in db.Customers do
                                 if id = c.CustomerId then
                                     yield (new Customer(c.CustomerId, c.Name, c.Balance))} 
                   |> Seq.hd @> :> ICustomer

end
Conclusion:

With the help of Flinq and some basic SoC principles, switching our data access layer from an XML repository to a SQL repository is trivial. While Flinq is not yet ready for production, full support is likely in the not too distant future. With Flinq, the future looks bright!

Thursday, November 13, 2008

Great F# Resources

For those of you interested in F#, here are some great resources worth checking out.


- HubFS - Here you will find forums, announcements, and many other resources.  Don Syme is often spotted here answering questions and posting examples.
- Don Syme's Blog - This is the blog of the creator of F# and one of the authors of the book Expert F#.
- Robert Pickering's Blog - Author of the book Foundations of F# and recent guest on Hansel Minutes show #76
- Ted Neward's Blog - Ted was recently on DNR show #377 discussing F# with Amanda Laucher.  They are currently working on a book.
- Amanda Laucher's Blog
- Real World Functional Programming by Tomas Petricek
- F# MSDN Developer Center - This site has several great resources to get you started including links to the latest release, free chapters of Expert F#, announcements, and specification documentation.
- Examples on CodePlex - Contains links to additional resources including several listed on this blog entry.
- Matthew Podwysocki's blog - Contains great content and examples.


If you know of other great resources, let me know!

Saturday, November 8, 2008

Linq to XML in F#

Last week in the entry titled "Mocking F#", we walked through the creation of a customer service implementation in F# and explored how a dynamic mock object framework can be used to help quickly drive development with tests. In that entry, we implemented a DAO (Data Access Object) that simply creates a dummy customer class . This blog entry will focus on adding functionality to that CustomerDao class so that it retrieves the data from an XML file using Linq to XML.

First, Add References:

The first step that we must do is add a reference to System.Xml and System.Xml.Linq in both the test project and the F# project.

The C# Tests:

The goal of these tests is to verify that a specific customer can be retrieved from the XML source.  Method "CanGetCustomerById" retrieves the customer from the actual XML source (which in this case is hard coded into the GetById member, but could have easily been retrieved from a file or database).  Method "CanGetCustomerByIdWithXmlProvided" creates a fake XElement and passes it to the function for test purposes.

        
[TestMethod]
public void CanGetCustomerById()
{
    FSharpMockExample.Data.ICustomerDao customerDao = new  FSharpMockExample.Data.CustomerDao();
    FSharpMockExample.Entities.ICustomer customer = customerDao.GetById(1);
    Assert.AreEqual(customer.Id, 1);
    Assert.AreEqual(customer.Name, "ABC Company");
    Assert.AreEqual(customer.Balance, 20);
}

[TestMethod]
public void CanGetCustomerByIdWithXmlProvided()
{
    XElement xElement =
        new XElement("c",
            new XElement("Customer",
                new XAttribute("Id", "1"),
                new XAttribute("Name", "ABC Company"),
                new XAttribute("Balance", "20.00")),
            new XElement("Customer",
                new XAttribute("Id", "2"),
                new XAttribute("Name", "AABB, Inc"),
                new XAttribute("Balance", "30.00")));
    FSharpMockExample.Data.ICustomerDao customerDao = new FSharpMockExample.Data.CustomerDao();
    FSharpMockExample.Entities.ICustomer customer = customerDao.GetById(xElement, 2);
    Assert.AreEqual(customer.Id, 2);
    Assert.AreEqual(customer.Name, "AABB, Inc");
    Assert.AreEqual(customer.Balance, 30);
}

The F# Signature File:

The signature file looks very similar to that which was created during the last entry. The main difference is a new overloaded GetById function, which contains a tuple (a tuple is common F# data structure used to group data types) of type "XElement * int". Since XElement is used, we must also open System.Xml.Linq.

#light
namespace FSharpMockExample.Data
open FSharpMockExample.Entities
open System.Xml.Linq

type ICustomerDao = interface
    abstract GetById: int -> ICustomer
    abstract GetById: XElement * int -> ICustomer
end

type CustomerDao = class
    new: unit -> CustomerDao
    interface ICustomerDao
end

The F# Source File:

Most of the changes are found in this file.  The first thing that an astute reader might notice is the XLinqHelper module.  Modules provide a way to group and reuse identifiers and functions.  The CustomerDao class encapsulates the majority of the changes.  Member GetById xml retrieves, and in this sample actually creates, the source xml.  It then casts the current class to the ICustomerDao interface, parses the raw xml into an XElement and calls the overloaded GetById XElement*int member.  The overloaded GetById XElement*int member uses the Seq library to find the customer by the specified CustomerId, news up a customer object, casts it to an ICustomer, and finally returns the result.

#light
namespace FSharpMockExample.Data
open FSharpMockExample.Entities
open System.Xml.Linq

module XLinqHelper = 
    let GetXName xname = XName.op_Implicit(xname)

type ICustomerDao = interface
    abstract GetById: int -> ICustomer
    abstract GetById: XElement * int -> ICustomer
end

type CustomerDao = class
    new()={}
    interface ICustomerDao with
        member this.GetById id =
            let rawXml = "<Customers>
                              <Customer Id=\"1\" Name=\"ABC Company\" Balance=\"20.00\"/>
                              <Customer Id=\"2\" Name=\"AABB, Inc\" Balance=\"30.00\"/>
                          </Customers>"
            let thisInterface = (this :> ICustomerDao)
            let xml = XElement.Parse rawXml
            thisInterface.GetById (xml, id)

        member this.GetById (xml, id) =
            let GetCustomer (customerElement:XElement) = 
                new Customer(int(customerElement.Attribute(XLinqHelper.GetXName "Id").Value), 
                    customerElement.Attribute(XLinqHelper.GetXName "Name").Value, 
                    decimal(customerElement.Attribute(XLinqHelper.GetXName "Balance").Value)) :> ICustomer

            xml.Elements() |> Seq.find(fun customer -> (int(customer.Attribute(XLinqHelper.GetXName "Id").Value) = id)) 
                           |> GetCustomer
end

Conclusion:

As you can see from the example, adding basic data access functionality using Linq to XML is pretty easy.  With the help of the Seq library and System.Xml.Linq, the possibilities of XML data manipulation are endless. 

A special thanks goes out to Elijah Manor for adding F# support to Syntax Higherlighter.  Very cool Elijah!!