Merge Replication – The Ultimate Guide

Merge Replication – The Ultimate Guide

Enterprise Data Synchronization with Microsoft SQL Server 2008 and SQL Server Compact 3.5 Mobile Merge Replication

Enterprise Data Synchronization with Microsoft SQL Server 2008 and SQL Server Compact 3.5 Mobile Merge Replication by Rob Tiffany would have to be the ultimate guide when it comes to setting up and tuning merge replication between SQL Server and SQL Compact.  Starting with the basics of what and how merge replication works, Rob then drops into a great level of detail that will be sure to help you if you get stuck.  I would definitely have this book by my side when working with merge replication.

Cloud Costing

Cloud Costing

Following a discussion on the OzAzure mailing list I went off hunting for what others had put together on calculating the ROI for building or migrating to Windows Azure.  I’m not sure how I didn’t come across the Azure ROI Calculator earlier in my hunt for information on Windows Azure as it is the top 10 or so links when you search for Azure ROI.  Anyhow, it provides a neat little Silverlight application for doing a basic ROI for cloud applications. 

Of course where this gets interesting is when you compare it to the other offerings/alternatives in cloud space. I’m hoping that others out there will share their thoughts around costing the cloud.

Windows Azure, .NET Services from a Windows Mobile Widget

Windows Azure, .NET Services from a Windows Mobile Widget

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]


That’s right, if you hadn’t guessed what I was working on with my previous posts on .NET Services (Tokens, Queues I, II, III, Routers I & II) I’ve been working towards accessing .NET Services from within a widget. I must apologise for the following as it’s just a code dump of a sample widget that shows how to create a queue and subscribe to an existing router.  Note that if you are going to use this on a number of devices you need to create a unique queue on each device, otherwise you’ll end up subscribing to the router multiple times – this will cause duplicate messages in your queue and contention between the devices dequeuing the messages.


To get this widget to work, simply create a widget (Developing Widgets for Windows Mobile 6.5) and then copy the following code into your html file.



<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html http://www.w3.org/1999/xhtml%22″ mce_href=’http://www.w3.org/1999/xhtml”‘>http://www.w3.org/1999/xhtml” >
<head>
    <title>Widget Chat</title>


      <script type=”text/javascript”>
          var theToken;
          var theRouter = “myrouter”
          var widgetQueue = “widget”;


          function connect() {
              var solutionName = document.getElementById(“SolutionNameText”);
              var solutionPassword = document.getElementById(“SolutionPasswordText”);


              alert(‘Solution Name: ‘ + solutionName.value);
              alert(‘Solution Password: ‘ + solutionPassword.value);
              httpGetAuthenticationToken(solutionName.value, solutionPassword.value, createQueue);
          }


          function createQueue() {
              alert(‘Creating Queue’);
              httpCreateQueue(theToken, widgetQueue, subscribeToRouter);
          }


          function subscribeToRouter() {
              alert(‘Subscribing to Router’);
              httpSubscribeQueueToRouter(theToken, theRouter, widgetQueue, pollForMessage);
          }


          function pollForMessage() {
              httpDequeueMessage(theToken, widgetQueue, messageReceived, pollForMessage);
          }


          function messageReceived(message) {
              alert(‘Message Received: ‘ + message);
              pollForMessage();
          }


          function send() {
              var messageBox = document.getElementById(“Message”);
              httpRouteMessage(theToken, theRouter, messageBox.value);
          }


          function httpGetAuthenticationToken(username, password, callback) {
              try {
                  http = new XMLHttpRequest();
                  http.open(“GET”, “
https://accesscontrol.windows.net/IssueToken.aspx?u=” + username + “&p=” + password);
                  http.onreadystatechange = function() {
                      if (http.readyState == 4) {
                          if (http.status == 200) {
                              theToken = http.responseText;
                              alert(‘Authenticated:’ + theToken);
                              callback();
                          }
                          else {
                              alert(‘Authentication failed’);
                          }
                      }
                  }
                  http.send();
              }
              catch (e) {
                  alert(e.message);
              }
          }


          function httpCreateQueue(token, queue, callback) {
              var policy = “<entry mce_href=’http://www.w3.org/2005/Atom”‘>http://www.w3.org/2005/Atom”>” +
  “<QueuePolicy mce_href=’http://schemas.microsoft.com/netservices/2009/05/servicebus/connect”‘>http://schemas.microsoft.com/netservices/2009/05/servicebus/connect”>” +
    “<ExpirationInstant>2009-09-03T01:00:00.0000000Z</ExpirationInstant>” +
  “</QueuePolicy>” +
“</entry>”;


              http = new XMLHttpRequest();
              http.open(“POST”, “
https://blogsample.servicebus.windows.net/” + queue, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.setRequestHeader(“Content-type”, “application/atom+xml;type=entry;charset=utf-8”);
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 201) {
                          var created = http.responseText;
                          alert(‘queue created: ‘ + created);


                      }
                      else {
                          alert(‘queue not created – ‘ + http.status);
                      }
                      callback();
                  }
              }
              http.send(policy);
          }


          function httpGetServiceList(token) {
              http = new XMLHttpRequest();
              http.open(“GET”, “
https://blogsample.servicebus.windows.net/”, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      output = http.responseText;
                      alert(output);
                      // Do something with the service list
                      httpRenewQueue(token, “mobilequeue”, output);
                  }
              }
              http.send();
          }


          function httpRenewQueue(token, queue, getResponse) {
              xmlDoc = new ActiveXObject(“Microsoft.XMLDOM”);
              xmlDoc.async = “false”;
              xmlDoc.loadXML(getResponse);


              var entry;
              var entries = xmlDoc.getElementsByTagName(“title”);
              var i;
              for (i = 0; i < entries.length; i++) {
                  if (entries[ i ].childNodes[0].nodeValue == queue) {
                      entry = entries[ i ].parentNode;
                  }
              }
              entry.getElementsByTagName(“ExpirationInstant”)[0].childNodes[0].nodeValue = “2009-08-31T09:56:25.2951184Z”;


              http = new XMLHttpRequest();
              http.open(“PUT”, “
https://blogsample.servicebus.windows.net/” + queue + “/!(queue)”, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.setRequestHeader(“Content-type”, “application/atom+xml;type=entry;charset=utf-8”);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      var output = http.responseText;
                      alert(‘renew: ‘ + output);
                      httpEnqueueMessage(token, queue, “Hi Everyone”);
                  }
              }
              http.send(entry.xml);
          }


          function httpDeleteQueue(token, queue) {
              http = new XMLHttpRequest();
              http.open(“DELETE”, “
https://blogsample.servicebus.windows.net/” + queue + “/!(queue)”, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      var deleted = http.responseText;
                      alert(‘deleted’);
                  }
              }
              http.send();
          }


          function httpEnqueueMessage(token, queue, message) {
              http = new XMLHttpRequest();
              http.open(“POST”, “
https://blogsample.servicebus.windows.net/” + queue, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.setRequestHeader(“Content-type”, “text/plain;charset=utf-8”);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 202) {
                      var output = http.responseText;
                      alert(‘enqueue: ‘ + output)
                      httpDequeueMessage(token, queue);
                  }
              }
              http.send(message);
          }


          function httpDequeueMessage(token, queue, callback, noMessageCallback) {
              http = new XMLHttpRequest();
              http.open(“DELETE”, “
https://blogsample.servicebus.windows.net/” + queue + “/!(queue/head)?encoding=asreply&maxmessages=1&timeout=30”, true);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 200) {
                          var output = http.responseText;
                          callback(output);
                      }
                      else {
                          noMessageCallback();
                      }
                  }
              }
              http.send();
          }


          function httpRouteMessage(token, router, message) {
              http = new XMLHttpRequest();
              http.open(“POST”, “
https://blogsample.servicebus.windows.net/” + router, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.setRequestHeader(“Content-type”, “text/plain;charset=utf-8”);
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 202) {
                          var output = http.responseText;
                          alert(‘Send ok!’);


                      }
                      else {
                          alert(‘Unable to send’);
                      }
                  }
              }
              http.send(message);
          }


          function httpSubscribeQueueToRouter(token, router, queue, callback) {
              var policy = “<entry mce_href=’http://www.w3.org/2005/Atom”‘>http://www.w3.org/2005/Atom”>” +
              “<link rel=’alternate’ href=’
https://blogsample.servicebus.windows.net/” + queue + “‘ />” +
  “<HttpHeaders mce_href=’http://schemas.microsoft.com/netservices/2009/05/servicebus/connect”‘>http://schemas.microsoft.com/netservices/2009/05/servicebus/connect”>” +
    “<HttpHeader name=’X-MS-Identity-Token’ value='” + token + “‘ />” +
  “</HttpHeaders>” +
“</entry>”;


              http = new XMLHttpRequest();
              http.open(“POST”, “
https://blogsample.servicebus.windows.net/” + router + “/!(router/subscriptions)”, false);
              http.setRequestHeader(“X-MS-Identity-Token”, token);
              http.setRequestHeader(“Content-type”, “application/atom+xml;type=entry;charset=utf-8”);
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 200) {
                          var created = http.responseText;
                          alert(‘subscribed ok:’ + created);
                          callback();
                      }
                      else {
                          alert(‘not subscribed ok’ + http.status);
                      }
                  }
              }
              http.send(policy);
          }


      </script>
</head>
<body>
    <p>   
    Solution Name:<br />
    <input id=”SolutionNameText” type=”text” />
        <br />
    Solution Password:<br />
    <input id=”SolutionPasswordText” type=”password” /><br />
    <Button onclick=”connect()”>Connect</Button>
    </p>


    <p>   
    Message:
        <br />
    <input id=”Message” type=”text” /><br />
    <Button onclick=”send()”>Send</Button>
    </p>


</body>
</html>