| It’s finally here – I’ve just received my copy of “Windows Phone 7 Developer Guide – Building connected mobile applications with Microsoft Silverlight,” a book that I worked on last year. The book describes a scenario that uses Windows Phone 7 as a client platform for the Tailspin Surveys application that runs in Windows Azure (this cloud-based application is described in the previous book in the series “Developing Applications for the Cloud”), and focuses on building a Windows Phone 7 app that integrates with a range of cloud-hosted services. The book will help you get started with Windows Phone 7 development, providing practical, real-world examples, and detailed discussions of the trade-offs and design decisions you may have to make. The sample application uses the MVVM design pattern, so if you want to learn more about that, then this book is a great place to start. To download the source code and read more, look here on MSDN. We’ve now started work on the next project – extending the the “Guide to Claims-based Identity and Access Control” to include Windows Azure AppFabric Access Control Services and SharePoint in some of the scenarios. |
Thursday, 10 February 2011
WP7 Guide
Wednesday, 9 February 2011
Reactive Extensions
Rx requires a slightly different way of thinking about asynchronous behaviour and events. The fundamental concept of Rx is based around the idea of there being something that is observable that generates a sequence of things for an observer to observe. The other things to be aware of about the way that Rx works are:
- It’s a push model — the observable instance pushes information to the observer instance.
- The observer can filter the information that it receives by using LINQ queries.
int[] numbers = { 1, 2, 3, 5, 8, 13, 21 };foreach (var number in numbers) { Console.WriteLine("Number: {0}", number); }
IObservable<int> observableNumbers = numbers.ToObservable();observableNumbers.Subscribe( number => Console.WriteLine("Number: {0}", number), ex => Console.WriteLine("Something went wrong: {0}", ex.Message), () => Console.WriteLine("At the end of the sequence") );
- Handle each item in the sequence as it arrives.
- Handle any exceptions raised.
- Handle the end of the sequence.
However, to start understanding the Rx approach I found it useful to work out how to do things in Rx that I already new how to do with other approaches. The following examples show some of these scenarios:
Handling Events
Writing code to handle events is a very common task. The following code shows how you might prevent a user from entering non-numeric characters into a text box:<TextBox Height="23" Name="textBox1" Width="120" KeyDown="textBox1_KeyDown" />
private void textBox1_KeyDown(object sender, KeyEventArgs e) { if (e.Key < Key.NumPad0 || e.Key > Key.NumPad9) { e.Handled = true; } }
<TextBox Height="23" Name="textBox2" Width="120" />
var keys = from evt in Observable.FromEvent<KeyEventArgs>(textBox2, "KeyDown") where (evt.EventArgs.Key < Key.NumPad0 || evt.EventArgs.Key > Key.NumPad9) select evt.EventArgs;
// Ideally you should dispose of keySubscription when the Window is disposed. var keysSubscription = keys.Subscribe(evt => { evt.Handled = true; label1.Content = "Invalid character: " + evt.Key.ToString(); });
Running a Background Task
The following example shows the outline to a standard approach to running a background task in a WPF application using a the Task class. See John Sharp’s post here for more background in the Task class and the Task Parallel Library.private void button1_Click(object sender, RoutedEventArgs e) { Task task = new Task(doWork); task.Start(); } delegate void ContentSetterDelegate(Label label, string text); private void doWork() { // Work really hard at something. Thread.Sleep(2000); this.Dispatcher.Invoke(new ContentSetterDelegate(setContentProperty), label1, "Finished the work at last!"); } private void setContentProperty(Label label, string text) { label.Content = text; }
To achieve the same results using Rx, you could use the following code:
var clicks = from evt in Observable.FromEvent<RoutedEventArgs>(button2, "Click") select evt; var clicksSubscription = clicks.Subscribe(evt => { var backgroundTask = Observable.Start(() => { // Work really hard at something. Thread.Sleep(3000); }).ObserveOnDispatcher(); backgroundTask.Subscribe( _ => { }, () => label1.Content = "It's all done now!" ); });
Synchronizing Tasks
John Sharp’s post here also described how you could synchronize several background tasks using the WaitAll and WaitAny methods in the Task Parallel Library. You can do a similar thing using Rx like this:var task1 = Observable.Start(() =>
{
// Work really hard and return a result
Thread.Sleep(4000);
return 10;
});
var task2 = Observable.Start(() =>
{
// Work really hard and return a result
Thread.Sleep(3000);
return 30;
});
var tasks = Observable.ForkJoin(
task1, task2
).Finally(() => Console.WriteLine("Done!"));
// Wait for all the tasks to finish
var results = tasks.First();
// Process all the results
int sum = 0;
foreach (int r in results)
sum += r;
Console.WriteLine("The sum of all the tasks was: {0}", sum);There’s an interesting discussion about the differences between running multiple background tasks with Rx and using the Task Parallel Library here: http://social.msdn.microsoft.com/Forums/en-US/rx/thread/12d3f79a-0a53-4854-976e-5fa0d86f01ee/.
Friday, 4 February 2011
Step by Step Guide to Setting up HADR in SQL Server Denali
Following on from my previous post, I wanted to try out the HADR functionality in SQL Server “Denali” CTP1.
To test the solution, I wanted to run SQL Server HADR in a virtual machine environment, with all the VMs running on one physical host.
This seemed like a pretty straightforward task at first. There were a number of blogs with step by step instructions, however none of them actually showed HADR running and I was soon to find out why. After following the steps and finding that it did not work, I did some digging around online and, thanks to the help of Justin Erickson at Microsoft, found that Books Online is unclear on the pre-requisites (and soon to be corrected). Essentially, it sounds like you can run HADR on a single node cluster and this is what the other blog posters, and myself initially, had done. This is not the case. HADR must run on a multi-node cluster. So here are the steps to set this up in a virtual machine environment:
Pre-requisites
Hardware (physical)
One machine running Windows Server 2008 R2 with enough memory to run three virtual machines (I had 8GB and this was sufficient)
Hardware (virtual)
One domain controller running Windows Server 2008 R2 which I have name DenaliDC.
Two domain joined Windows Server 2008 R2 VMs with SQL Server Denali installed. Note: These cannot be a copy of the same VHD or clustering will fail (and it will take you ages to work out why). I have named these DenaliOne and DenaliTwo.
Note: I had some problems connecting to the default instance in Management Studio and so I have used named instances DenaliOne\DenaliOne and DenaliTwo\DenaliTwo.
Virtual Network
I created five private virtual networks as follows:
This is a bit over the top, but virtual network cards are free, so why not?
Domain Controller Networking
I added two network cards on the DataOne and DataTwo networks. DenaliOne has a fixed IP address of 192.168.10.1 and DenaliTwo has a fixed IP address of 192.168.20.1.
I added DHCP with a scope of 192.168.10.10 – 192.168.10.20.
I added DNS and created a default forward lookup zone.
DenaliOne Networking
I added five network cards as follows:
Name: DataOne
IP Address: Obtain automatically
Name: DataTwo
IP Address: 192.168.20.11
Default gateway and preferred DNS Server: 192.168.10.1.
Name: iSCSIOne
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 6
IP Address: 192.168.2.1
Register this connection’s address in DNS: Unchecked
Name: iSCSITwo
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 6
IP Address: 192.168.3.1
Register this connection’s address in DNS: Unchecked
Name: Heartbeat
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 4
IP Address: Obtain an IPv6 address automatically
Register this connection’s address in DNS: Unchecked
DenaliTwo Networking
I added five network cards as follows:
Name: DataOne
IP Address: Obtain automatically
Name: DataTwo
IP Address: 192.168.20.12
Default gateway and preferred DNS Server: 192.168.10.1.
Name: iSCSIOne
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 6
IP Address: 192.168.2.2
Register this connection’s address in DNS: Unchecked
Name: iSCSITwo
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 6
IP Address: 192.168.3.2
Register this connection’s address in DNS: Unchecked
Name: Heartbeat
Remove Client for Microsoft Networks, QoS Packet Scheduler, File and Printer Sharing for Microsoft Networks, IP Version 4
IP Address: Obtain an IPv6 address automatically
Register this connection’s address in DNS: Unchecked
Additional Steps
There is a slight problem with clustering in a virtual machine which is the inability to share a VHD. The way around this is to use a virtual iSCSI device. This involves either using Windows Storage Server or a product called Starwind. I hadn’t used either before and opted for a free trial of Starwind as there was less to download.
Starwind Setup
1. Download and install Starwind on the DC from here.
2. Add a host with an IP address of 127.0.0.1 and Port of 3261.
3. Double click the host and logon with a username of root and password of starwind.
4. Add a target with a suitable name (I used Quorum) and the following settings:
a. Storage type of Hard Disk
b. Device type of Basic Virtual and Image File device
c. Device creation method: Create new virtual disk (place it wherever you want with a .img extension)
d. Image File device parameters: Allow multiple concurrent iSCSI connections
e. Accept defaults for all other parameters.
5. You should end up with something like this:
6. Click on the host and then click Network in Server Settings. You should then see the IP Address and port that the host is using. In this case I will use 192.168.10.1 and 3260. Note down these values.
Install iSCSI Inititor
On both SQL Server VMs:
1. Install the iSCSI Initiator from here. There is also a Starwind version, but I used the Microsoft one.
2. Run iSCSI Initiator and click the Discovery tab.
3. Click Discover Portal and enter the values from the Starwind software that you noted earlier.
4. Now click the Targets tab and click Refresh.
5. Connect to the Starwind target.
6. Run the Disk Management console (Diskmgmt.msc) and you should see a new disk.
7. Bring this disk online in both virtual machines, but only format it from one VM (DenaliOne)
Add Windows Clustering
On both SQL Server boxes run Server Manager and add the Failover Clustering feature. After any reboots check that you can still see the iSCSI disk and if not run iSCSI Initiator and check that you are connected to the target. It is quicker to disconnect and connect again than wait for the automatic reconnection.
Follow these steps to build the cluster:
1. On DenaliOne run Failover Cluster Manager and in the Actions box click Create a Cluster.
2. Skip the introduction screen and add DenaliOne and DenaliTwo. They should appear with their FQDNs.
3. Click Next through all of the warnings until the Cluster Validation runs.
4. Resolve and Cluster Validation errors. Don’t worry about warnings for this example.
Install SQL Server HADR
1. On DenaliOne:
a. Create a folder for transferring logs. I created C:\HADRShare\Logs.
b. Share this folder.
2. On both SQL Server VMs:
a. Star SQL Server Configuration Manager.
b. Right click the SQL Server service and click Properties.
c. Click the SQL HADR tab.
d. Click Enable SQL HADR service and click OK.
e. Stop and Restart SQL Server.
3. On DenaliOne run SQL Server Management Studio.
a. Create a database (I called this SQLHADR). For simplicity with backup and restore, place the files in a directory that exists on both servers. I created C:\Databases\Data on both virtual machines and used this.
b. Create a table in this database and add a row to the table.
c. Backup this database and restore it on DenaliTwo with the RESTORE WITH NORECOVERY option.
d. Expand Management and right click Availability Groups.
e. Click New Availability Group and click Next.
f. Enter a suitable name (I used SQLHADR) and click Next.
g. On the Select Databases page select SQLHADR and click Next.
h. Click Add and connect to DenaliTwo.
i. I want to use the secondary, or mirror, database to offload querying from the primary server. It is no longer necessary to create a snapshot and the querying happens with live data. To configure this change the read mode in Secondary Role to Allow Read Intent Connections and click Next:
j. Click Finish.
Start Data Synchronization
1. Click Start Data Synchronization.
2. Specify the share that you created previously and click Test.
3. Click OK.
4. Click Close.
Querying the Secondary Instance
With SQL HADR you can now query the secondary database. Previously, you could not query the mirror. Now, as long as we allowed connections when we set up the availability group, we can offload queries from the primary database:
Now, with SQL Server HADR, we have a high availability solution providing a failover database, but we can also offload querying to this database and have a reduced workload on our primary server. We’ve come a long way since log-shipping!
Performing a Failover
1. In Management Studio on the DenaliTwo\DenaliTwo instance expand Management, expand Availability Groups, expand SQLHADR, expand Availability Replicas and right click DenaliTwo\DenaliTwo.
2. Click Force Failover.
3. Click OK.
Note: Do not try to failover the primary database. This will fail.
Notes
This wasn’t the most straightforward environment to setup, mostly due to inaccurate step by step instructions on other blogs (never trust a solution that doesn’t show it working at the end). The key things that I found were:
- You need a two node cluster. This requires a shared disk, although SQL Server doesn’t use this shared disk.
- For some reason, I had inconsistent problems with default instances. If this is the case, try a named instance.
- When you create the networks in Hyper-V, create one at a time and configure it in the VMs before creating the next network. This ensures that you know which network is which and helps with troubleshooting.
- If anything doesn’t work and you change the configuration, you must disable HADR on all instances, restart the instances, re-enable HADR and restart the instances again.
- I had HADR running on every instance on every server, although it now seems to work with only the mirrored instance having HADR enabled.
- Perform failovers on the secondary database, not the primary. This makes sense when you have multiple secondary databases because you will failover the new primary.
SQL Server “Denali” HADR
Continuing with the SQL Server Denali blogs , I thought I’d take a look at the new high availability disaster recover, or HADR, functionality.
With SQL Server 2005, Microsoft introduced Database Mirroring. Prior to this we had a system called Log Shipping which was essentially an automatic backup and recovery system. Log Shipping did supply a failover instance of SQL Server, but, by default, it replicated every 15 minutes, which could result in the loss of important data. Furthermore, Log Shipping had no automatic failover. With Database Mirroring we had a system which was easier to setup, allowed us to choose the level of availability against performance, and with SQL Native Client, had automatic failover. The only limitation was that you couldn’t query the mirror. This could be remedied by taking periodic snapshots and using these for reporting, however if you wanted a real-time reporting database to take the load off your principal instance, then database mirroring wasn’t the answer. The other limitation was that you only had one mirror for each database.
Five years on and what has changed? Well, we now have High Availability Disaster Recovery (HADR). HADR is mirroring on steroids. You can have multiple mirrors (or secondary databases) to provide improved availability and ,furthermore, we can query these mirrors. We also have availability groups. Each availability group can have multiple databases and we can now failover these databases together in their group rather than on database at time.
In the CTP version of Denali there are some limitations which will not exist in the final product. For now, we can only have one secondary database, but in the release version, we can have four. We also only have asynchronous mode which is the equivalent to high performance mode, but again this is expected to be supplemented in the release version. Failovers can only be forced, which has the potential for data loss and are not currently automatic, but again, this will change.
There are some caveats. Obviously, there is a load on the server to transfer the logs. The CTP release uses an asynchronous mode whereby the primary server does not wait until the secondary server confirms that it has written the log records to disk before the transaction is committed. This reduces load on the primary server, but does have the potential that an update could be lost if the primary server fails just after a transaction is committed, but before it has been written to the secondary server. With the final release there will be a synchronous mode, but you will need to decide between protection and performance.
Currently, there are not many tools beyond setup and Force Failover available in Management Studio, although there are the following PowerShell cmdlets allowing you to also suspend and resume availability group databases:
Add-SqlAvailabilityGroupDatabase
Join-SqlAvailabilityGroup
New-SqlAvailabilityGroup
New-SqlAvailabilityReplica
Remove-SqlAvailabilityGroup
Resume-SqlAvailabilityGroupDatabase
Suspend-SqlAvailabilityGroupDatabase
Switch-SqlAvailabilityGroup
Although this is much more complex to setup than database mirroring, it provides a genuine high availability solution that also improves performance by offloading query operations to a mirror server. It will be very interesting to see how this evolves in the final product, but from these beginnings, SQL Server HADR looks to be an excellent availability solution.
Tuesday, 1 February 2011
Using the New THROW Keyword in SQL Server "Denali"
When SQL Server 2005 introduced the TRY...CATCH blocks, SQL Server developers were finally able to use structured exception handling to their code. However, passing error information back to the application from the CATCH block using a RAISERROR statement is not as simple as it seems. To ensure that the correct information is passed back to the caller, you have to add your error information to the sys.messages table. That's easy enough to do, but if your application moves onto another server, there's a risk that you'll forget to add the error to sys.messages on the new server and your application may not function correctly.
SQL Server "Denali" introduces the THROW keyword, which extends the exception handling functionality of SQL Server 2008. It eliminates the issue of having to define errors in sys.messages and also enables you to handle an error and then rethrow that error back to the calling code.
So let's look at some examples of how you can use THROW in your Transact-SQL code. If you are implementing search functionality, you might want to customize the information returned when a SELECT statement finds no records. In this situation, you can simply use the THROW statement to return your own error message to the calling application.
SELECT * FROM Person.Person
WHERE LastName = 'Smyth'
IF @@ROWCOUNT = 0
THROW 50001, 'No results found.', 1
You can see in this example that I've used an error number greater than 50000. This is a requirement of THROW to avoid replicating any of the system error numbers. I've specified a meaningful message to be returned to the calling application that can be used directly in the calling code and the state parameter, which you can use to locate where the error originated.
The output when you run this code will be:
(0 row(s) affected)
Msg 50001, Level 16, State 1, Line 7
No results found.
You can also use THROW in a TRY ... CATCH block. This enables you to execute Transact-SQL code such as rolling back a transaction or logging information before passing the error back to the calling application.
BEGIN TRY
DECLARE @BusEntityID int;
SET @BusEntityID = (SELECT MAX(BusinessEntityID) FROM Person.BusinessEntity)
INSERT Person.Person(BusinessEntityID, PersonType, NameStyle, FirstName, LastName)
VALUES(@BusEntityID, 'EM', 0, 'Lin', 'Joyner')
END TRY
BEGIN CATCH
-- Handle the error.
-- Log the error in the SQL Server application log.
THROW
END CATCH
By using the THROW statement on its own, you will simply rethrow the existing error that has occurred. This is useful when the SQL Server error message is meaningful to the user or application. If you run this code with a non-unique value for the BusinessEntityID field, you will see the following error:
(0 row(s) affected)
Msg 2627, Level 14, State 1, Line 5
Violation of PRIMARY KEY constraint 'PK_Person_BusinessEntityID'. Cannot insert duplicate key in object 'Person.Person'. The duplicate key value is (20780).
You can use the parameters of THROW to make the error information more meaningful to the calling application or user as shown in the following example.
BEGIN TRY
DECLARE @BusEntityID int;
SET @BusEntityID = (SELECT MAX(BusinessEntityID) FROM Person.BusinessEntity)
INSERT Person.Person(BusinessEntityID, PersonType, NameStyle, FirstName, LastName)
VALUES(@BusEntityID, 'EM', 0, 'Lin', 'Joyner')
END TRY
BEGIN CATCH
-- Handle the error.
-- Log the error in the SQL Server application log.
THROW 51111, 'Invalid business entity id.', 2
END CATCH
In this case, the output when you violate the primary key will be as follows:
(0 row(s) affected)
Msg 51111, Level 16, State 2, Line 9
Invalid business entity id.
You'll notice that the severity level when you throw your own error is defaulting to 16. You cannot define a severity level for custom errors, however when you rethrow a SQL Server error, the original severity level is retained. You can only rethrow errors from inside a CATCH block, if you are outside of the CATCH block, you must specify the parameters.
So, as you can see, the new THROW statement in SQL Server "Denali" extends the structured exception handling capabilities of earlier versions of SQL Server.
