Dot Net Thoughts

January 19, 2008

Serializing with WCF

Filed under: csharp,Uncategorized — dotnetthoughts @ 1:23 pm
Tags: , , ,

First of all, I want to apologize for the amount of time it has taken me to put up a new blog post. I typically try to write once a week, but my life has been crazy as of late. The outcome of all of the madness is that I will be packing up my family and moving from Portland, Oregon to Denver, Colorado at the end of this month. (My wife has been accepted into a Master’s degree program there!)Because of this, posts for the next couple of months may be sporadic, as well. (I’m looking for work in the Denver area. If anybody has any leads, let me know… )

In celebration of my move, I thought that I would put together an example that demonstrates two different ways that WCF serializes objects. (This posting will simply show different ways the engine can work. I hope to actually wire it into the WCF pipeline in a future post.) Since I spent last night packing all of my books into boxes, I thought that maybe I’d find a way to organize them. The goal of this application is to create a manifest list allows me quickly find any book that I’ve packed away in a moving box.

As always, the complete code is at the end of the blog post. The amount of code with this post is a little bit greater than usual. I’ll try and write code directly related to the topic within the text, but I would recommend copying the code to studio if you want to follow along.

Since my main objective is to manage books and boxes, I’m going to begin by simply creating a very basic object for each. My Book object will contain two different properties (Title and Author), and my Box object will simply contain an Id. Since we will be using the WCF engine to do our serialization we will need to mark the classes and properties we wish to expose to the serialization engine with the [DataContract] and the [DataMember] attributes.

 
    #region Book Class            

    [DataContract] 
    public class Book 
    { 
        [DataMember] 
        public string Title { get; set; }            

        [DataMember] 
        public string Author { get; set; } 
    } 
    #endregion            

    #region Box Class 
    [DataContract] 
    public class Box 
    { 
        [DataMember] 
        public int Id { get; set; } 
    } 
   #endregion 

I now need a method to associate books with boxes. In order to keep my Book and Box object as loosely coupled as possible, I will add a Manifest object to the project which will be responsible for maintaining the relationship between the two. The Manifest object will expose three properties. The Books property will contain a collection of all of the Books on my bookshelf. The Boxes property will contain all of the Boxes I’m using to move. The LineItems property will contain information as to which book is in which box.

 
        //Property inside of the Manifest Class. 
        private List<Box> _boxes = new List<Box>(); 
        [DataMember] 
        public List<Box> Boxes 
        { 
            get 
            { 
                return _boxes; 
            } 
            private set 
            { 
                _boxes = value; 
            } 
        } 
        //LineItems and Books here… 

Since we are using WCF, the serializer will require both a getter and a setter for every object it serializes. I don’t really want to give consumers of my class the ability to blow away my boxes collection, so I have explicitly created a private setter. This allows WCF the access it needs, while denying direct access to other consumers.

LineItems are added to the collection by calling the AddLineItem method. This method adds the book to the book collection, adds a box to the box collection, and creates a relationship between the two by adding a new LineItem to the LineItems collection. It is important to note that the association created in the LineItems collection does not contain new instances of books and boxes, rather it contains a reference to the master copy in the Books and Boxes collection. (In other words, the association is by reference.)

 
        public void AddLineItem(Book book, Box box) 
        { 
            Books.Add(book); 
            Boxes.Add(box); 
            LineItems.Add(new LineItem(book, box)); 
        } 

I’ve overridden the ToString on the manifest object to display the complete list of books and boxes that it contains. There is also a CreateShippingManifest method which will be used to create a generic manifest of three books contained in two boxes. (See code below.)

To test our code, we’ll create our sample manifest and view the results.

 
      Manifest manifest = CreateShippingManifest(); 
      Console.WriteLine(manifest.ToString()); 

The results which come back are as follows:

 
        Box 1   A Brief History of Time 
        Box 1   Guards Guards 
        Box 2   The Reptile Room 

Now, we’ll make an assumption that I made a mistake in entering a box label into the program. Instead of box 1, I meant the Id to be box 100. We’ll make the change to the box id, and the manifest will automatically pick up the results.

 
        Manifest manifest = CreateShippingManifest(); 
        manifest.Boxes[0].Id = 100; 
        Console.WriteLine(manifest.ToString()); 

Results:

 
        Box 100 A Brief History of Time 
        Box 100 Guards Guards 
        Box 2   The Reptile Room

Now, let’s add some code to serialize and deserialize our manifest. This is actually a fairly simple process. The first method will serialize the data to disc (as Xml), the second method will read the file and deserialize it into a new object.

 
      public void Serialize(string fileName) 
      { 
        DataContractSerializer ds = new DataContractSerializer(this.GetType());            

         using (Stream stream = File.Create(fileName)) 
         { 
             ds.WriteObject(stream, this); 
         } 
      }   

      public void Deserialize(string fileName) 
      { 
          using (Stream stream = File.OpenRead(fileName)) 
          { 
              DataContractSerializer ds = new DataContractSerializer(this.GetType()); 
              Manifest manifest = (Manifest)ds.ReadObject(stream); 
              this.Books = manifest.Books; 
              this.Boxes = manifest.Boxes; 
              this.LineItems = manifest.LineItems; 
          } 
      } 

We can now run code similar to that we ran above. Between steps, let’s serialize and deserialize our objects, though.

 
      static void Main(string[] args) 
      { 
          string serializationFile = @"c:\tempmw\Manifest.xml";            

          Manifest manifest = CreateShippingManifest(); 
          manifest.Serialize(serializationFile);            

          Manifest manifest2 = new Manifest(); 
          manifest2.Deserialize(serializationFile);            

          Console.WriteLine("Object after deserialization"); 
          Console.WriteLine(manifest2.ToString());            

          manifest2.Boxes[0].Id = 100; 
          Console.WriteLine(manifest2.ToString());            

          Console.ReadLine(); 
      } 

This yields the results:

 
      Object after deserialization 
      Box 1   A Brief History of Time 
      Box 1   Guards Guards 
      Box 2   The Reptile Room            

      Box 1   A Brief History of Time 
      Box 1   Guards Guards 
      Box 2   The Reptile Room 

But wait a minute! What happened here? We clearly changed the Id of the first box to be 100, yet the results still state that our box is Box 1! The code looks very similar to what we used in the non-serialized objects. The reason that this happens is because of the way the default instance of the DataContractSerializer writes out the results. If you take a look at the Xml file that is created from our serializer, you’ll see results similar to the following:

 
<Books> 
  <Book> 
    <Author>Stephen Hawking</Author> 
    <Title>A Brief History of Time</Title> 
  </Book> 
  … 
</Books> 
<Boxes> 
  <Box> 
    <Id>1</Id> 
  </Box> 
  … 
</Boxes> 
<LineItems> 
  <Manifest.LineItem> 
    <Book> 
      <Author>Stephen Hawking</Author> 
      <Title>A Brief History of Time</Title> 
    </Book> 
    <Box> 
      <Id>1</Id> 
    </Box> 
  </Manifest.LineItem> 
  … 
</LineItems>  

The Xml does not contain the associations between the manifest items and the books and boxes that we set up so carefully in our code. Changing the box Id in the master collection no longer changes the box id in all of the children.

We can very easily fix this by updating the DataContractSerializer instantiation in our Serialize method. The important parameters in the new constructor are the third and fifth. The third parameter (maxItemsInObjectGraph) indicates the total number of objects that the Xml can contain. If the number of objects in the Xml is exceeded, an error will be raised. The fifth parameter (preserveObjectReferences) indicates that the associations between objects should be preserved.

 
        public void Serialize(string fileName) 
        { 
            //DataContractSerializer ds = new DataContractSerializer(this.GetType());            

            DataContractSerializer ds = new DataContractSerializer(this.GetType(), null, 100, false, true, null);            

            using (Stream stream = File.Create(fileName)) 
            { 
                ds.WriteObject(stream, this); 
            } 
        } 

The important parameters in the new constructor are the third and fifth. The third parameter (maxItemsInObjectGraph) indicates the total number of objects that the Xml can contain. If the number of objects in the Xml is exceeded, an error will be raised. The fifth parameter (preserveObjectReferences) indicates that the associations between objects should be preserved. Now, when we rerun our demo, we see that the Id of the box is correctly updated.

 
      Object after deserialization 
      Box 1   A Brief History of Time 
      Box 1   Guards Guards 
      Box 2   The Reptile Room            

      Box 100 A Brief History of Time 
      Box 100 Guards Guards 
      Box 2   The Reptile Room 

Examining the Xml now shows the Id and IdRef structures in place to reassociate the data.

 
<Manifest z:Id="1">        

<Books z:Id="2" z:Size="3"> 
   <Book z:Id="3"> 
     <Author z:Id="4">Stephen Hawking</Author> 
     <Title z:Id="5">A Brief History of Time</Title> 
   </Book> 
   ... 
</Books> 
<Boxes z:Id="12" z:Size="3"> 
   <Box z:Id="13"> 
      <Id>1</Id> 
   </Box> 
   ... 
</Boxes> 
<LineItems z:Id="15" z:Size="3"> 
   <Manifest.LineItem z:Id="16"> 
      <Book z:Ref="3" i:nil="true"/> 
      <Box z:Ref="13" i:nil="true"/> 
   </Manifest.LineItem> 
   ... 
   </LineItems> 
</Manifest> 

So, there you have it. With WCF you can serialize by value or by reference. Pretty neat stuff. This has kind of been a marathon post, I hope you could follow along. Let me know if you find anything that isn’t clear!
Code Safe!
MW

 
---------------------Sample Code-------------------------------------- 
//Console Application 
using System; 
using WcfSample;        

namespace PersistApp 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            //You will need to change the filepath to a file on your local machine. 
            string serializationFile = @"c:\tempmw\Manifest.xml"; 
            //*********************************************************************        

            Manifest manifest = CreateShippingManifest();        

            Console.WriteLine("Manifest before serializing...\n"); 
            Console.WriteLine(manifest.ToString());        

            manifest.Serialize(serializationFile);        

            Manifest manifest2 = new Manifest(); 
            manifest2.Deserialize(serializationFile);        

            Console.WriteLine("Object after deserialization"); 
            Console.WriteLine(manifest2.ToString());        

            manifest2.Boxes[0].Id = 100; 
            Console.WriteLine(manifest2.ToString());        

            Console.ReadLine(); 
        }        

        private static Manifest CreateShippingManifest() 
        { 
            Manifest manifest = new Manifest();        

            Book book1 = new Book { Title = "A Brief History of Time", Author = "Stephen Hawking" }; 
            Book book2 = new Book { Title = "Guards Guards", Author = "Terry Pratchett" }; 
            Book book3 = new Book { Title = "The Reptile Room", Author = "Lemony Snicket" };        

            Box box1 = new Box { Id = 1 }; 
            Box box2 = new Box { Id = 2 };        

            manifest.AddLineItem(book1, box1); 
            manifest.AddLineItem(book2, box1); 
            manifest.AddLineItem(book3, box2);        

            return manifest; 
        }        

    } 
}        

//*************************************************************        

//Classes        

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Runtime.Serialization; 
using System.Text;        

namespace WcfSample 
{        

    #region Manifest 
    [DataContract] 
    public class Manifest 
    { 
        private List<Box> _boxes = new List<Box>(); 
        private List<Book> _books = new List<Book>(); 
        private List<LineItem> _lineItems = new List<LineItem>();        

        [DataMember] 
        public List<Box> Boxes 
        { 
            get 
            { 
                return _boxes; 
            } 
            private set 
            { 
                _boxes = value; 
            } 
        }        

        [DataMember] 
        public List<Book> Books 
        { 
            get 
            { 
                return _books; 
            } 
            private set 
            { 
                _books = value; 
            } 
        }        

        [DataMember] 
        public List<LineItem> LineItems 
        { 
            get 
            { 
                return _lineItems; 
            } 
            private set 
            { 
                _lineItems = value; 
            } 
        }        

        public void AddLineItem(Book book, Box box) 
        { 
            Books.Add(book); 
            Boxes.Add(box); 
            LineItems.Add(new LineItem(book, box)); 
        }        

        public void Serialize(string fileName) 
        { 
            //DataContractSerializer ds = new DataContractSerializer(this.GetType()); 
            DataContractSerializer ds = new DataContractSerializer(this.GetType(), null, 100, false, true, null);        

            using (Stream stream = File.Create(fileName)) 
            { 
                ds.WriteObject(stream, this); 
            } 
        }        

        public void Deserialize(string fileName) 
        { 
            using (Stream stream = File.OpenRead(fileName)) 
            { 
                DataContractSerializer ds = new DataContractSerializer(this.GetType()); 
                Manifest manifest = (Manifest)ds.ReadObject(stream); 
                this.Books = manifest.Books; 
                this.Boxes = manifest.Boxes; 
                this.LineItems = manifest.LineItems; 
            } 
        }        

        public override string ToString() 
        { 
            StringBuilder sb = new StringBuilder();        

            foreach (LineItem li in this.LineItems) 
            { 
                sb.AppendLine(String.Format("Box {0}\t{1}", li.Box.Id, li.Book.Title)); 
            }        

            return sb.ToString(); 
        } 
    #endregion        

        #region LineItemClass 
        [DataContract] 
        public class LineItem 
        { 
            private Book _book; 
            private Box _box;        

            public LineItem(Book book, Box box) 
            { 
                _box = box; 
                _book = book; 
            }        

            [DataMember] 
            public Box Box 
            { 
                get 
                { 
                    return _box; 
                } 
                private set 
                { 
                    _box = value; 
                } 
            }        

            [DataMember] 
            public Book Book 
            { 
                get 
                { 
                    return _book; 
                } 
                private set 
                { 
                    _book = value; 
                } 
            } 
        } 
        #endregion 
    }         

    #region Book Class        

    [DataContract] 
    public class Book 
    { 
        [DataMember] 
        public string Title { get; set; }        

        [DataMember] 
        public string Author { get; set; } 
    } 
    #endregion        

    #region Box Class 
    [DataContract] 
    public class Box 
    { 
        [DataMember] 
        public int Id { get; set; }        

    } 
    #endregion 
}        

Advertisements

November 29, 2007

Debug and Release Builds

Filed under: csharp,Debugging,Uncategorized — dotnetthoughts @ 9:41 pm
Tags: , ,

Occassionally, I find old code concepts rattling around in my head that just don’t apply in today’s world. My most recent one was the difference between release and debug builds.

Back in VB6 days, one of the big reasons for creating a debug build was the generation of the pdb file. The pdb (portable database) file contained information about the names of items such as variables, classes and methods. Furthermore, it contained information about where these values were located in the code. Without the pdb file, all of this information was unavailable.

Imagine my surprise when I ran some code without a pdb file the other day, and I received a stack trace containing method names. With a little further thought, though, I realized it wasn’t that surprising. When disassembling a dll with ILDASM, I can see all of the method and variable names with or without a pdb file present. It turns out that the inclusion of a pdb file will allow you to trace a bug to a module and line number, but it is no longer needed for artifact names.

Release builds now contain pdb files by default. So what is the difference between the two build types? To help figure this out, I generated a very simple HelloWorld class and compiled it as both a release and a debug build. This class contained two methods.

       

        static void Main(string[] args) 
        { 
            Console.WriteLine(SayHello("Bob")); 
            Console.ReadLine(); 
        }               

        static string SayHello(string name) 
        { 
            return String.Format("Hello {0}!", name); 
        }       

Opening the Main method of the release dll in ILDasm yields a pretty straight forward implementation of the code. In the decompilation, you can see the creation of the string, the loading of the parameter, the string formatting, and the return of the value.

       

  .method private hidebysig static string 
          SayHello(string name) cil managed 
  { 
    // Code size       12 (0xc) 
    .maxstack  8 
    IL_0000:  ldstr      "Hello {0}!" 
    IL_0005:  ldarg.0 
    IL_0006:  call       string [mscorlib]System.String::Format(string, 
                                                                object) 
    IL_000b:  ret 
  } // end of method Program::SayHello       

The debug code looks almost exactly the same, but it includes a few differences to help assist in debugging. A local variable is initialized to hold the value of parameter so that it is available to a debugger. Also added are several nop (no operation) and a br_s (branch) method. These set points within the application on which a breakpoint can be set.

       

  .method private hidebysig static string 
          SayHello(string name) cil managed 
  { 
    // Code size       17 (0x11) 
    .maxstack  2 
    .locals init ([0] string CS$1$0000) 
    IL_0000:  nop 
    IL_0001:  ldstr      "Hello {0}!" 
    IL_0006:  ldarg.0 
    IL_0007:  call       string [mscorlib]System.String::Format(string, 
                                                                object) 
    IL_000c:  stloc.0 
    IL_000d:  br.s       IL_000f    IL_000f:  ldloc.0 
    IL_0010:  ret 
  } // end of method Program::SayHello       

I expected that tweaking the code to call a few subroutines would at least lead to some inlining optimizations within the IL. The differences between the Debug and Release versions of the IL were similar to those above. So where were the optimizations?

The only other major difference between the IL versions are the values stored in the DebuggingModes attribute. This attribute links back to the debug compilation flags that can be set when compiling a project. Since these are the only differences in the IL, the optimizations must occur within the JIT compiler itself. Scott Hanselman has an excellent post on compiled release and debug that is well worth reading.

That would be it for today! Good luck and code safe!

MW

November 10, 2007

TransactionScope and Unit Tests

Filed under: Uncategorized — dotnetthoughts @ 6:29 am
Tags: , , , , ,

Writing unit tests to validate a data access layer (DAL) can be a time consuming (but life saving) task. One of the biggest challanges of DAL unit tests is assuring that you have consistent data to pull from the database. Dedicating a database with static data for unit tests doesn’t always work. As unit tests are added to the project, data may need to be added to the database. This can cause previously created tests to fail, and a lot of time can be lost trying to resync everything.

One technique for getting around the unit test database consistency issue is to write data to the database you expect to find. The steps for doing this would be:

  • Begin a transaction in your unit test.
  • Write the data to the database that your data access layer will need.
  • Test the data access layer functionality against the inserted data.
  • Roll back the transaction.

Using a transaction has a couple of distinct advantages. Since you are running in the scope of an uncommitted transaction, your fellow developers running unit tests will not see your added data (isolation). Also, rolling back the transaction places the database back to the original state in which you found it.

Managing transactions by explicitly attaching them to the connection object doesn’t always work well when testing a data access layer. Since the DAL often contains code to retrieve its own connection, the following sequence often occurs:

  • Begin a transaction in your unit test.
  • Write the data to the database that your data access layer will need.
  • Call the Data Access Layer. The Data Access layer creates its own connection.
  • The DAL attempts to read the new data from the database. It is blocked, though, because it is in a different transaction than the unit test transaction, and will not be able to complete until the DAL commits.

The TransactionScope object helps alleviate this problem. It sits on top of the Distributed Transaction Coordinator, and will assign any code running within its context to the same transaction. In other words, a TransactionScope context within your unit tests will force your data access layer code to run within the same context. Isolation is maintained from other developers, but your DAL can access and manipulate the data as needed. (This does require that you have the Distributed Transaction Coordinator Service running on the box that handles the transaction.)

To demonstrate, let’s assume that I have a DAL method that I want to test that returns all users from a database. This method gets its own connection, retrieves the users, and returns them as a DataSet.

public DataSet GetUsers() 
{ 
    DataSet dataSet = new DataSet(); 
    string connectionString = GetConnectionString();      

    using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
        connection.Open(); 
        string sql = "SELECT * FROM [User]"; 
        SqlCommand command = new SqlCommand(sql, connection); 
        SqlDataAdapter adapter = new SqlDataAdapter(command); 
        adapter.Fill(dataSet); 
    }      

    return dataSet; 
}

Here is the unit test to test this code. It doesn’t test nearly all of the functionality you would want to check in a real unit test, but it does demonstrate the TransactionScope. Note that the TransactionScope object doesn’t have an explicit RollBack() method. The Rollback occurs if the TransactionScope object is disposed without Complete() being called on it. This occurs at the end of the using block.

[TestMethod()] 
public void GetUsersTest() 
{ 
    string connectionString = GetConnectionString();     

    using (TransactionScope ts = new TransactionScope()) 
    { 
        using (SqlConnection connection = 
            new SqlConnection(connectionString)) 
        { 
            connection.Open(); 
            DataLayer dataAccessLayer = new DataLayer();     

            DataSet dataSet = dataAccessLayer.GetUsers(); 
            AddNewUser("Fred", connection);     

            dataSet = dataAccessLayer.GetUsers(); 
            DataRow[] dr = dataSet.Tables[0].Select("[UserName] = 'Fred'"); 
            Assert.AreEqual(1, dr.Length); 
        } 
    } 
}

Hope this is helpful. Good luck and code safe!

MW

Create a free website or blog at WordPress.com.