Monday, July 24, 2006

Its a Boy !!!

The due date is December 29th.

Tuesday, July 18, 2006

Virtual PC is Free!

Virtual PC is a cool product and I'm glad to see that Microsoft is giving it away for free. They're giving away the 2004 version but apparently they are going to give away the 2007 version (which supports windows vista) when it becomes available.

http://www.microsoft.com/windows/virtualpc/default.mspx

Sunday, July 09, 2006

NMock Constraints

NMock constraints allow you to run a validation against a parameter that is passed to your NMock object's method during an NUnit test. I want to share a custom NMock constraint I've written. With this constraint you can write an expectation that compares an expected XML document and the actual XML document. The constraint is called IsEquivalentXml. The constraint class uses a class library written by Microsoft called XmlDiffPatch to do the actual xml comparison.

Some tests that use the new constraint are shown below:



   1:  [Test]
   2:  public void MyTest1()
   3:  {
   4:      string expectedXml = "<Order></Order>";
   5:      string actualXml = "<Order></Order>";
   6:   
   7:      DynamicMock mockObject = new DynamicMock(typeof(IOrderManager));
   8:      mockObject.Expect("SubmitOrder", new IsEquivalentXml(expectedXml));
   9:      ClassUnderTest classUnderTest = 
  10:          new ClassUnderTest((IOrderManager)mockObject.MockInstance);
  11:      classUnderTest.DoSometing(actualXml);
  12:      mockObject.Verify();
  13:  }
 


   1:  [Test]
   2:  public void MyTest2()
   3:  {
   4:      string expectedXml = "<Order/>";
   5:      string actualXml = "<Order></Order>";
   6:   
   7:      DynamicMock mockObject = new DynamicMock(typeof(IOrderManager));
   8:      mockObject.Expect("SubmitOrder", new IsEquivalentXml(expectedXml));
   9:      ClassUnderTest classUnderTest = 
  10:          new ClassUnderTest((IOrderManager)mockObject.MockInstance);
  11:      classUnderTest.DoSometing(actualXml);
  12:      mockObject.Verify();
  13:  }



   1:  [Test]
   2:  public void MyTest3()
   3:  {
   4:      string expectedXml = @"<Order id=""123""></Order>";
   5:      string actualXml = "<Order id='123'></Order>";
   6:   
   7:      DynamicMock mockObject = new DynamicMock(typeof(IOrderManager));
   8:      mockObject.Expect("SubmitOrder", new IsEquivalentXml(expectedXml));
   9:      ClassUnderTest classUnderTest = 
  10:          new ClassUnderTest((IOrderManager)mockObject.MockInstance);
  11:      classUnderTest.DoSometing(actualXml);
  12:      mockObject.Verify();
  13:  }



   1:  [Test]
   2:  public void MyTest10()
   3:  {
   4:      string expectedXml = @"<Order>abc123</Order>";
   5:      string actualXml = "<Order>    abc123    </Order>";
   6:   
   7:      DynamicMock mockObject = new DynamicMock(typeof(IOrderManager));
   8:      mockObject.Expect("SubmitOrder", 
   9:          new IsEquivalentXml(expectedXml, XmlDiffOptions.IgnoreWhitespace));
  10:      ClassUnderTest classUnderTest = 
  11:          new ClassUnderTest((IOrderManager)mockObject.MockInstance);
  12:      classUnderTest.DoSometing(actualXml);
  13:      mockObject.Verify();
  14:  }


The ClassUnderTest code is shown below:



   1:  using System;
   2:   
   3:  namespace CustomNMockConstraintsClassLibrary
   4:  {
   5:      public class ClassUnderTest
   6:      {
   7:          IOrderManager _orderManager;
   8:   
   9:          public ClassUnderTest(IOrderManager orderManager)
  10:          {
  11:              this._orderManager = orderManager;
  12:          }
  13:   
  14:          public void DoSometing(string orderXml)
  15:          {
  16:              this._orderManager.SubmitOrder(orderXml);
  17:          }
  18:      }
  19:  }


The actual Constraint class is shown below. Notice that I inherit from the NMock BaseConstraint class instead of the IConstraint interface. BaseConstraint is an abstract class that already implements the IConstraint interface.



   1:  using System;
   2:  using System.IO;
   3:  using System.Xml;
   4:   
   5:  using NMock;
   6:  using NMock.Constraints;
   7:   
   8:  using Microsoft.XmlDiffPatch;
   9:   
  10:  namespace CustomNMockConstraintsClassLibrary
  11:  {
  12:      public class IsEquivalentXml : BaseConstraint
  13:      {
  14:          private string _expectedXml = string.Empty;
  15:          private XmlDiff _xmlDiff = new XmlDiff();
  16:   
  17:          public IsEquivalentXml(string xml) : this(xml, XmlDiffOptions.None)
  18:          {            
  19:          }
  20:   
  21:          public IsEquivalentXml(string expectedXml, XmlDiffOptions options)
  22:          {
  23:              _expectedXml = expectedXml;
  24:              _xmlDiff.Options = options;
  25:          }
  26:   
  27:          public override bool Eval(object val)
  28:          {            
  29:              string actualXml = (string)ExtractActualValue(val);
  30:              return AreEqual(_expectedXml, actualXml);
  31:          }
  32:      
  33:          public override string Message
  34:          {
  35:              get    { return "<" + _expectedXml + ">";    }
  36:          }
  37:   
  38:          private bool AreEqual(string expectedXml, string actualXml)
  39:          {
  40:              bool areEqual = false;
  41:   
  42:              try
  43:              {
  44:                  areEqual = _xmlDiff.Compare(
  45:                      new XmlTextReader(new StringReader(expectedXml)), 
  46:                      new XmlTextReader(new StringReader(actualXml)));
  47:              }
  48:              catch(Exception e)
  49:              {
  50:                  _expectedXml += "\n" + e.ToString();
  51:              }
  52:   
  53:              return areEqual;
  54:          }
  55:      }
  56:  }

Thursday, July 06, 2006

My First Soap Extension

I've finally got a chance to write a Soap Extension. This one allows you to have caching for a web service method but the cache duration can be specified in the web.config file. The .NET "WebMethod" attribute supports caching but requires a constant value for the cache duration.

Here's the code for the extension:


   1:  using System;
   2:  using System.Configuration;
   3:  using System.Diagnostics;
   4:  using System.IO;
   5:  using System.Web;
   6:  using System.Web.Services.Protocols;
   7:   
   8:  namespace CacheExtensionProj
   9:  {
  10:      [AttributeUsage(AttributeTargets.Method)]
  11:      public class CacheExtensionAttribute : SoapExtensionAttribute 
  12:      {
  13:          private string cacheDurationConfigKey;
  14:          private int priority;
  15:   
  16:          public override Type ExtensionType 
  17:          {
  18:              get { return typeof(CacheExtension); }
  19:          }
  20:   
  21:          public override int Priority 
  22:          {
  23:              get { return priority; }
  24:              set { priority = value; }
  25:          }    
  26:   
  27:          public string CacheDurationConfigKey
  28:          {
  29:              get { return cacheDurationConfigKey;}
  30:              set { cacheDurationConfigKey = value;}
  31:          }
  32:      }
  33:   
  34:      public class CacheExtension : SoapExtension 
  35:      {
  36:          Stream originalStream;
  37:          Stream newStream;
  38:          string cacheKey = null;
  39:          string cacheDurationConfigKey;
  40:          int cacheDuration = 0;
  41:   
  42:          public override object GetInitializer(LogicalMethodInfo methodInfo,
  43:              SoapExtensionAttribute attribute) 
  44:          {
  45:              //Cast attribute and return attribute value
  46:              CacheExtensionAttribute cea = (CacheExtensionAttribute)attribute;
  47:              return cea.CacheDurationConfigKey;
  48:          }
  49:   
  50:          public override object GetInitializer(Type serviceType)
  51:          {
  52:              return null;
  53:          }
  54:   
  55:          public override void Initialize(object initializer) 
  56:          {
  57:              cacheDurationConfigKey = (string)initializer;            
  58:              if (cacheDurationConfigKey == null 
  59:                   cacheDurationConfigKey == string.Empty)
  60:              {
  61:                  cacheDurationConfigKey = "DefaultCacheDuration";
  62:              }
  63:   
  64:              cacheDuration = GetCacheDuration(cacheDurationConfigKey);        
  65:          }
  66:   
  67:          public override void ProcessMessage(SoapMessage message) 
  68:          {
  69:              switch (message.Stage) 
  70:              {
  71:                  case SoapMessageStage.BeforeSerialize:
  72:                      break;
  73:   
  74:                  case SoapMessageStage.AfterSerialize:                
  75:                      newStream.Position = 0;  //need to reset stream 
  76:                      CopyStream(newStream, originalStream);
  77:                      
  78:                      SaveStreamToCache(cacheKey, newStream);                    
  79:                      break;
  80:   
  81:                  case SoapMessageStage.BeforeDeserialize:
  82:                      //Cache key is the entire request xml string
  83:                      cacheKey = GenerateCacheKeyFromStream(originalStream);
  84:                                          
  85:                      Stream stream = GetStreamFromCache(cacheKey);
  86:                      if (stream != null)
  87:                      {
  88:                          Trace.WriteLine("Returning Cached Version");
  89:                          HttpContext.Current.Response.ContentType = 
  90:                                  "text/xml";
  91:                          CopyStream(stream, 
  92:                              HttpContext.Current.Response.OutputStream);
  93:                          HttpContext.Current.Response.Flush();
  94:                          HttpContext.Current.Response.End(); 
  95:                      }
  96:                      else
  97:                      {
  98:                          CopyStream(originalStream, newStream);
  99:                          newStream.Position = 0;  //need to reset stream
 100:                      }
 101:                      break;
 102:   
 103:                  case SoapMessageStage.AfterDeserialize:
 104:                      break;
 105:   
 106:                  default:
 107:                      throw new Exception("invalid stage");
 108:              }            
 109:          }
 110:   
 111:          public override Stream ChainStream( Stream stream )
 112:          {
 113:              originalStream = stream;
 114:              newStream = new MemoryStream();
 115:              return newStream;
 116:          }
 117:   
 118:          private void CopyStream(Stream from, Stream to) 
 119:          {
 120:              TextReader reader = new StreamReader(from);
 121:              TextWriter writer = new StreamWriter(to);
 122:              writer.WriteLine(reader.ReadToEnd());
 123:              writer.Flush();
 124:          }
 125:   
 126:          private string GenerateCacheKeyFromStream(Stream stream)
 127:          {
 128:              string cacheKey = new StreamReader(stream).ReadToEnd();
 129:              stream.Position = 0;  //need to reset stream
 130:              return cacheKey;
 131:          }
 132:   
 133:          private void SaveStreamToCache(string cacheKey, Stream stream)
 134:          {            
 135:              if (cacheDuration > 0) 
 136:              {
 137:                  Trace.WriteLine("Saving result in Cache");
 138:                  Trace.WriteLine("cacheKey = " + cacheKey);
 139:   
 140:                  MemoryStream ms = new MemoryStream();
 141:                  stream.Position = 0;
 142:                  CopyStream(stream, ms);
 143:                  ms.Position = 0;
 144:   
 145:                  HttpContext.Current.Cache.Insert(cacheKey, ms, null, 
 146:                      DateTime.Now.AddHours((double)cacheDuration), 
 147:                      TimeSpan.Zero);
 148:              }
 149:          }        
 150:   
 151:          private Stream GetStreamFromCache(string cacheKey)
 152:          {
 153:              Stream stream = HttpContext.Current.Cache[cacheKey] as Stream;
 154:              if (stream != null)
 155:              {
 156:                  stream.Position = 0;
 157:              }
 158:              return stream;
 159:          }
 160:   
 161:          private int GetCacheDuration(string cacheDurationConfigKey)
 162:          {
 163:              int cacheDurationValue = -1;
 164:              string cacheDurationStringValue = 
 165:                  ConfigurationSettings.AppSettings[cacheDurationConfigKey];
 166:              if (cacheDurationStringValue != null && 
 167:                  cacheDurationStringValue != string.Empty) 
 168:              {
 169:                  try
 170:                  {
 171:                      cacheDurationValue = int.Parse(cacheDurationStringValue);
 172:                      if (cacheDurationValue <=0)
 173:                      {
 174:                          Trace.WriteLine("Caching disabled. " +
 175:                              cacheDurationConfigKey + 
 176:                              " must be positive and greater than zero.");
 177:                      }
 178:                  }
 179:                  catch(Exception e)
 180:                  {
 181:                      Trace.WriteLine("Caching disabled. Unable to convert " +
 182:                          cacheDurationConfigKey + 
 183:                  " value from configuration file into int type. \nException: "
 184:                          + e.ToString());
 185:                  }
 186:              }
 187:              else
 188:              {
 189:                  Trace.WriteLine("Caching disabled. " + 
 190:                      cacheDurationConfigKey + 
 191:                      " value is null or empty string.");
 192:              }
 193:   
 194:              return cacheDurationValue;
 195:          }
 196:      }
 197:  }



You use the extension by adding the CacheExtension attribute to your
web service method:

   1:  [WebMethod]
   2:  [CacheExtension(CacheDurationConfigKey="GetDateTime.CacheDuration")]        
   3:  public string GetDateTime(string name)
   4:  {
   5:    return "Hello " + name + " the time is " + DateTime.Now.ToString();
   6:  }



Your web.config file should look something like this:
   1:    <appSettings>
   2:      <add key="DefaultCacheDuration" value="8"/>
   3:      <add key="GetDateTime.CacheDuration" value="2"/> 
   4:    </appSettings>



Some good links about Soap Extensions:

Tuesday, July 04, 2006

We're having a baby!



The baby was putting on quite a show for us during the ultrasound, it was kicking and moving its arms around, it was amazing! Its' too early to know the baby's sex, so stay tuned!

Monday, July 03, 2006

Mexico Vacation April 2006