<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7592516066850632747</id><updated>2022-01-02T12:09:53.048+00:00</updated><category term="sql server"/><category term=".NET"/><category term="MongoDB"/><category term="C#"/><category term="performance"/><category term="sql"/><category term="conference"/><category term="nosql"/><category term="usergroup"/><category term="sqlsoton"/><category term="SQLBits"/><category term="Ordnance Survey"/><category term="QCon"/><category term="SQLBulkCopy"/><category term="XML"/><category term="architecture"/><category term="asp.net"/><category term="spatial"/><category term="Angular"/><category term="Azure"/><category term="MVC"/><category term="SignalR"/><category term="community"/><category term="development"/><category term="geographic"/><category term="optimisation"/><category term="personal"/><category term="scalability"/><category term="CDATA"/><category term="CSV"/><category term="DBA"/><category term="DataTable"/><category term="DevBA"/><category term="DevDays"/><category term="ElasticSearch"/><category term="LINQ"/><category term="Microsoft Community Contributor Award"/><category term="MongoUK"/><category term="Riak"/><category term="SET FMTONLY"/><category term="SET NOEXEC"/><category term="SSMS"/><category term="TSQL"/><category term="Twitter"/><category term="add-in"/><category term="best practice"/><category term="blogging"/><category term="bulk load"/><category term="cache"/><category term="career"/><category term="cassandra"/><category term="constraints"/><category term="devconnections"/><category term="developer"/><category term="geospatial"/><category term="index"/><category term="masterclass"/><category term="performance selenium browsermob .NET"/><category term="primary key"/><category term="profiler"/><category term="replication"/><category term="southampton"/><category term="sql server 2008"/><category term="sqldataadapter"/><category term="stackoverflow"/><category term="syntax"/><category term="table valued parameter"/><category term="validation"/><title type='text'>AdaTheDev</title><subtitle type='html'>Full-stack C#, Azure, database developer. Remote worker. Always learning.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>82</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-2891054234069223590</id><published>2021-12-17T13:02:00.003+00:00</published><updated>2021-12-17T13:02:59.120+00:00</updated><title type='text'>Troubleshooting Azure Function Consumption Plan Scaling</title><content type='html'>&lt;p&gt;I recently encountered an issue with an Azure Function Event Hub trigger running on a Linux Consumption Plan, whereby events stopped being consumed by the trigger once it went to sleep. I learned a lot about consumption plan scaling and diagnosing what was going on, so in this post I&#39;m going to share the key takeaways that I picked up as it was a bit of a journey. &lt;/p&gt; &lt;h4&gt;Background: Azure Function Event Hub trigger stopped processing events&lt;/h4&gt;This was in a low-throughput scenario for testing - so the rate of events coming into the Event Hub was low/sporadic.  What I was seeing was: &lt;ul&gt;&lt;li&gt;my Event Hub trigger (C#) would stop consuming events - no errors, no failures, events were still hitting the Event Hub but just weren&#39;t being consumed&lt;/li&gt;&lt;li&gt;I went into the Function App in the Azure Portal to investigate, and it would then kick back into life and process the events despite not changing anything&lt;/li&gt;&lt;li&gt;inevitably, it would then stop consuming events again after a short period of time once I came out of the Portal&lt;/li&gt;&lt;/ul&gt;Based on some Googling, I saw others raising similar issues going back to 2017. But either there was no real outcome as the raised issues went cold and closed, or they were due to issues that had now been resolved and weren&#39;t the issue in my case.  &lt;h4&gt;Background: Scale controller&lt;/h4&gt;A good intro can be found in the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-functions/event-driven-scaling&quot; target=&quot;_blank&quot;&gt;MS docs here&lt;/a&gt;.   The key points are: &lt;ul&gt;&lt;li&gt;the scale controller monitors events from whichever source is triggering your Azure Function (Event Hub in my case) to determine when instances need to scale out or in&lt;/li&gt;&lt;li&gt;this will scale instances down to 0 when no events are coming in, and your app will then go to sleep&lt;/li&gt;&lt;li&gt;this should then scale instances back up when it detects events are coming in and the number of current instances is not sufficient to process&lt;/li&gt;&lt;li&gt;in the scenario where it needs to scale back up from 0 instances, you will experience a cold start - a slight delay while it spins up an instance&lt;/li&gt;&lt;/ul&gt; In my scenario, no matter how long I waited, it was not scaling up from 0 despite new events coming in. It only processed them when I went into the Azure Function in the Portal - this is explained by the fact that when you go in there, it actually forces it to be loaded onto an instance. This is bypassing the scale controller. Once you know this behaviour, it helps focus you on to &quot;why isn&#39;t the scale controller doing its thing?&quot;  &lt;h4&gt;Diagnosing scale controller&lt;/h4&gt;The key to identifying the underlying issue, was to enable scale controller logging by adding an appsetting for &lt;b&gt;SCALE_CONTROLLER_LOGGING_ENABLED&lt;/b&gt;. I wanted logging out to Application Insights so I set it to &lt;b&gt;AppInsights:Verbose&lt;/b&gt;. Alternatively you can log out to a blob if you prefer, as per &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-functions/configure-monitoring?tabs=v2#configure-scale-controller-logs&quot; target=&quot;_blank&quot;&gt;the docs&lt;/a&gt;, but the following relates to diving into AppInsights.  There is info on how to query those logs &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-functions/analyze-telemetry-data#query-scale-controller-logs&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; - I used this query to see everything going on, which was key: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;traces &lt;br /&gt;| extend CustomDimensions = todynamic(tostring(customDimensions))&lt;br /&gt;| where CustomDimensions.Category == &quot;ScaleControllerLogs&quot;&lt;br /&gt;| order by timestamp desc&lt;br /&gt;&lt;/pre&gt; I could then see this error: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;FunctionName: &#39;TestEventHubTrigger&#39;. Trigger&#39;s Event Hub name &#39;%EventHubSettings:HubName%&#39;&lt;br /&gt;failed to resolve from AppSettings. Error: &#39;&#39;%EventHubSettings:HubName%&#39; does not resolve&lt;br /&gt;to a value.&#39;.&lt;br /&gt;&lt;/pre&gt; Here&#39;s my trigger method signature: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;[FunctionName(&quot;TestEventHubTrigger&quot;)]&lt;br /&gt;public async Task Run(&lt;br /&gt;    [EventHubTrigger(&quot;%EventHubSettings:HubName%&quot;,&lt;br /&gt;        Connection = &quot;EventHubSettings:Connection&quot;,&lt;br /&gt;        ConsumerGroup = &quot;%EventHubSettings:ConsumerGroup%&quot;)]&lt;br /&gt;    EventData[] events,&lt;br /&gt;    ILogger log)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;My Function *does* work and processes events when I nudge it in the Portal, those appsettings are configured correctly - yet the scale controller is having an issue resolving them. As we&#39;re targeting Linux, I had the appsettings configured in the portal using the &lt;b&gt;__&lt;/b&gt; form - i.e. &lt;b&gt;EventHubSettings__HubName&lt;/b&gt;. So I tried out changing that setting from &lt;b&gt;%EventHubSettings:HubName%&lt;/b&gt; to &lt;b&gt;%EventHubSettingsHubName%&lt;/b&gt; so it&#39;s not nested but root-level instead and updated the appsetting to &lt;b&gt;EventHubSettingsHubName&lt;/b&gt;. Restarted the Function App - bingo! Well, nearly - it didn&#39;t like the ConsumerGroup setting either, but showed I was on the right path. &lt;/p&gt;&lt;p&gt;Once I rolled this out, I then spotted a consistent higher level of requests on the Event Hub, even when events were not coming in. This looks like a good base indicator of a healthy scale controller as I believe it shows the scale controller monitoring the Event Hub for incoming messages. Here&#39;s a screenshot of the Requests metric from my Event Hub namespace dashboard - you can clearly see the increase in the base level of Requests as soon as I rolled out the fix: &lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjShu9Cw_OwwrxX8I0AkTTlpUMKqNIwyRPImBJu-AepGInW4cbx8Fq2OABeOPw8TyfPKNnW0X_4F1b2et5f-MpLJ1FWMDaXRhmvzgfSvQhupQJJsRPYiTjdp7ELT56Dq9ylANVROv1qnoTLO2WN3K79jHCILbwOf5RyWjf1pHzC-GxQAJBjAAaKTaEPcQ=s528&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;290&quot; data-original-width=&quot;528&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjShu9Cw_OwwrxX8I0AkTTlpUMKqNIwyRPImBJu-AepGInW4cbx8Fq2OABeOPw8TyfPKNnW0X_4F1b2et5f-MpLJ1FWMDaXRhmvzgfSvQhupQJJsRPYiTjdp7ELT56Dq9ylANVROv1qnoTLO2WN3K79jHCILbwOf5RyWjf1pHzC-GxQAJBjAAaKTaEPcQ=s320&quot;/&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Resolution&lt;/h4&gt;Changing those appsettings to avoid the multiple levels was the fix. Once I updated them all, including the Connection one (which does not required the &lt;b&gt;%&lt;/b&gt;), I then started seeing the Event Hub trigger behaving properly. Via the scale controller logging, I could see no more errors. Waiting for it to log it had changed the instance count down to 0 + a bit of extra time to pass, it then correctly scaled out when I fired an event in. The cold start resulted in about a 10-15 second delay before it picked up the event.  &lt;p&gt;This feels like a workaround and perhaps an issue within the scale controller - given the trigger works and pulls in those appsetting substitutions fine.&lt;/p&gt; &lt;h4&gt;Key Takeaways&lt;/h4&gt;&lt;ul&gt;  &lt;li&gt;Use &lt;b&gt;SCALE_CONTROLLER_LOGGING_ENABLED&lt;/b&gt; appsetting temporarily to help troubleshoot what is going on&lt;/li&gt;&lt;li&gt;Avoid any issues with appsetting substitution, by keeping them as simple top-level settings - not nested levels&lt;/li&gt;  &lt;li&gt;The Requests metrics graph in the Event Hub dashboard is a good indicator of base health&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/2891054234069223590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2021/12/troubleshooting-azure-function.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2891054234069223590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2891054234069223590'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2021/12/troubleshooting-azure-function.html' title='Troubleshooting Azure Function Consumption Plan Scaling'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEjShu9Cw_OwwrxX8I0AkTTlpUMKqNIwyRPImBJu-AepGInW4cbx8Fq2OABeOPw8TyfPKNnW0X_4F1b2et5f-MpLJ1FWMDaXRhmvzgfSvQhupQJJsRPYiTjdp7ELT56Dq9ylANVROv1qnoTLO2WN3K79jHCILbwOf5RyWjf1pHzC-GxQAJBjAAaKTaEPcQ=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-3625501685797319949</id><published>2018-04-29T11:51:00.001+01:00</published><updated>2021-01-03T19:24:28.825+00:00</updated><title type='text'>Samsung Gear Sport vs SWIMTAG - Swimathon 2018</title><content type='html'>This post is a bit different to my normal ones in that it&#39;s not my usual ramblings about something software development related. Instead, it&#39;s a cross between a write-up of my taking part in &lt;a href=&quot;https://swimathon.org/&quot; target=&quot;_new&quot;&gt;Swimathon 2018&lt;/a&gt;, and a side-by-side comparison of how my swim was tracked by 2 different bits of kit: a Samsung Gear Sport and SWIMTAG. For those that sponsored me, this is proof that I held up my side of the deal! :) If you are here for the proof, feel free to scroll down - I won&#39;t judge you....much. &lt;p&gt;Swimathon is an event to raise money for 2 very worthwhile charities: &lt;a href=&quot;https://www.cancerresearchuk.org/&quot; target=&quot;_new&quot;&gt;Cancer Research UK&lt;/a&gt; and &lt;a href=&quot;https://www.mariecurie.org.uk/&quot; target=&quot;_new&quot;&gt;Marie Curie&lt;/a&gt;. You pick a swimming challenge to complete from 400m, 1.5km, 2.5km or 5km and raise money by getting sponsors. Simple as that.  I signed up to complete 1.5km, which I did on Sunday 29th April, 2018.  &lt;/p&gt;&lt;h4&gt;Background&lt;/h4&gt;In 2017, I took up swimming after picking up a knee injury during my annual 10K road race. Being unable to run without pain left a huge gap in my exercise routine, so I naively thought I&#39;d   migrate my running fitness level over to swimming. However, having not swum properly since my school days 20-odd years ago, and never having done lane swimming, it was a wet slap round the face and a bit disheartening when I struggled to complete more than a couple of lengths without feeling like I&#39;d run a fast 5K. It turns out, running fitness doesn&#39;t necessarily translate to swimming fitness.  There were a number of problems I had: &lt;ol&gt;&lt;li&gt;unrealistic expectation of translating running performance over to swimming&lt;/li&gt;&lt;li&gt;poor technique&lt;/li&gt;&lt;li&gt;going too fast / not wanting to be &quot;that person&quot; who holds everyone up in the lane - the ego factor&lt;/li&gt;&lt;li&gt;no way to track my progress&lt;/li&gt;&lt;/ol&gt; So I researched into swimming - read articles and watched videos on technique, slowed everything down, got my breathing sorted etc. I&#39;m lucky enough to have a local leisure centre that has &lt;a href=&quot;https://www.swimtag.net&quot; target=&quot;_new&quot;&gt;SWIMTAG&lt;/a&gt;. SWIMTAG tracks your swim and gives you insights into your swim - how many lengths, split times, stroke types, DPS (Distance Per Stroke), tracks your PBs, allows you to enter competitions to compete against others locally, nationally or globally. After a swim, the data is sync&#39;d to your account and then available online or via a mobile app. As a Software Developer stat geek, this is right up my street.  &lt;p&gt;I analyse things.&lt;/p&gt;&lt;p&gt;ALL TEH THINGZ.&lt;/p&gt;I like to see my progress as that drives me on to do more and do better - I&#39;m one for continual improvement and SWIMTAG has been a big part of that.  Soon I was making progress; my times were dropping, my distances were increasing. And 9 months after picking up my knee injury, I was also able to get back to running. #Winning!  &lt;h4&gt;Samsung Gear Sport - The Chosen One&lt;/h4&gt;It&#39;s my Jose Mourinho of smartwatches. Earlier this year, I started looking into smart watches/fitness trackers. I went with a &lt;a href=&quot;http://www.samsung.com/global/galaxy/gear-sport/&quot;&gt;Samsung Gear Sport&lt;/a&gt; as it ticked the main boxes for what I was looking for: &lt;ul&gt;&lt;li&gt;GPS for tracking runs outside&lt;/li&gt;&lt;li&gt;waterproof to 5ATM (50 metres) with swimming tracking&lt;/li&gt;&lt;li&gt;offline storage for Spotify music&lt;/li&gt;&lt;li&gt;heart rate monitor&lt;/li&gt;&lt;li&gt;slick, responsive UI - TizenOS is great&lt;/li&gt;&lt;li&gt;good battery life (for a smartwatch!)&lt;/li&gt;&lt;/ul&gt; The Gear Sport is, for me, a great option if you want a watch that has a good combination of smartwatch functionality crossed with fitness tracking. SWIMTAG is dedicated to swim tracking so I&#39;d expect that to be more accurate. In previous swims where I wore one or the other, but not both at the same time, they seemed on a par in terms of accuracy of tracking lengths and stroke types. Both missed the odd length at times. But one huge advantage of the Samsung Gear Sport is the fact it has a visual display, plus can give you feedback via vibration every x lengths - if you&#39;re like me and get distracted by thinking about what&#39;s for lunch halfway down a length and lose your count, this is a very useful feature. The Samsung display, as you&#39;d expect, is awesome, though I would like a slightly stronger vibration and sometimes during a swim you might not always feel it.  There are a few apps you can install to track swims - I used the one as part of Samsung Health, as personally I liked all my fitness activity activity going into one place. I have also tried the Speedo app briefly in the past, which would be another option.  &lt;h4&gt;Swimathon 2018 result&lt;/h4&gt;I achieved my goal of completing the 1.5km swimming without rests in just under 38 minutes. Despite aspiring to glide through water like a cross between Adam Peaty and a dolphin, I knew that would not be the case - **obviously** only because of the reduced streamlining I introduced by wearing a Gear Sport on one wrist, and a SWIMTAG and locker key on the other ;) &lt;p&gt;&lt;b&gt;SWIMTAG&lt;/b&gt;&lt;br/&gt;&lt;ul&gt;&lt;li&gt;tracked all 60 lengths correctly&lt;/li&gt;&lt;li&gt;tracked the stroke type correctly&lt;/li&gt;&lt;li&gt;reported a time of 37m 43s&lt;/li&gt;&lt;/ul&gt;&lt;a href=&quot;https://1.bp.blogspot.com/-Jy4xCc940pE/WuWX6RIeoFI/AAAAAAAAMkU/WkDikuagC4g06hP0XAbSqZzWrdLLBL_aACPcBGAYYCw/s1600/SwimTag1.JPG&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://1.bp.blogspot.com/-Jy4xCc940pE/WuWX6RIeoFI/AAAAAAAAMkU/WkDikuagC4g06hP0XAbSqZzWrdLLBL_aACPcBGAYYCw/s400/SwimTag1.JPG&quot; width=&quot;400&quot; height=&quot;299&quot; data-original-width=&quot;781&quot; data-original-height=&quot;584&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://3.bp.blogspot.com/-qu7kUOahzYs/WuWjX53bSdI/AAAAAAAAMlw/_K_LfmS3Wo4xtTPgGTVF73poA7NzuCXNQCPcBGAYYCw/s1600/SwimTag2.JPG&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://3.bp.blogspot.com/-qu7kUOahzYs/WuWjX53bSdI/AAAAAAAAMlw/_K_LfmS3Wo4xtTPgGTVF73poA7NzuCXNQCPcBGAYYCw/s400/SwimTag2.JPG&quot; width=&quot;400&quot; height=&quot;137&quot; data-original-width=&quot;784&quot; data-original-height=&quot;269&quot; /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;b&gt;Samsung Gear Sport&lt;/b&gt;&lt;br/&gt;&lt;ul&gt;&lt;li&gt;did not track the lengths correctly - it went wonky twice, missing 2 lengths in total. I found this weird, as both times, I&#39;d paused at the end for 5 seconds to let someone pass so it should have been easier if anything, to detect the end of a length. This shows up in the breakdown you get in Samsung Health as 2 length times are about double the rest.&lt;/li&gt;&lt;li&gt;tracked the stroke type correctly&lt;/li&gt;&lt;li&gt;reported a time of 37m 48s&lt;/li&gt;&lt;/ul&gt;&lt;a href=&quot;https://3.bp.blogspot.com/-Xr3Bw7ehE3Y/WuWc-pJvpBI/AAAAAAAAMk4/4t_ltmDibXE9-eee99vW5SN5JlKi7F7_gCPcBGAYYCw/s1600/Screenshot_20180429-103853%257E2.jpg&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://3.bp.blogspot.com/-Xr3Bw7ehE3Y/WuWc-pJvpBI/AAAAAAAAMk4/4t_ltmDibXE9-eee99vW5SN5JlKi7F7_gCPcBGAYYCw/s320/Screenshot_20180429-103853%257E2.jpg&quot; width=&quot;320&quot; height=&quot;285&quot; data-original-width=&quot;1080&quot; data-original-height=&quot;962&quot; /&gt;&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;https://2.bp.blogspot.com/-pa2pQpLx8Q8/WuWdAGhgoQI/AAAAAAAAMlI/wKwhTX_V0jUGJcUy2r4KDaMMyLqT8cEeACPcBGAYYCw/s1600/Screenshot_20180429-103835%257E2.jpg&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://2.bp.blogspot.com/-pa2pQpLx8Q8/WuWdAGhgoQI/AAAAAAAAMlI/wKwhTX_V0jUGJcUy2r4KDaMMyLqT8cEeACPcBGAYYCw/s320/Screenshot_20180429-103835%257E2.jpg&quot; width=&quot;200&quot; height=&quot;320&quot; data-original-width=&quot;998&quot; data-original-height=&quot;1600&quot; /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;b&gt;Verdict&lt;/b&gt;&lt;br/&gt;The Samsung Gear Sport is a great watch. For those that want the best fitness tracker, I&#39;m sure there are better options that are more fitness focused. In terms of swimming, I was disappointed that it didn&#39;t track the full swim accurately. Usually, a brief pause at each end is enough for the tracking to be spot on. Perhaps the Speedo app would have performed differently - unfortunately, I couldn&#39;t track in both apps at the same time.   &lt;p&gt;For me, SWIMTAG is my primary source of tracking. As a dedicated swim tracker, I like having all my history in there with PBs tracked, competition leagues and a sense that it should be the most accurate. I like the Gear Sport as a secondary tracker - much better for feedback while you&#39;re swimming as opposed to SWIMTAG which is only helpful post-swim.&lt;/p&gt;&lt;/p&gt; </content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/3625501685797319949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2018/04/samsung-gear-sport-vs-swimtag-swimathon.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3625501685797319949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3625501685797319949'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2018/04/samsung-gear-sport-vs-swimtag-swimathon.html' title='Samsung Gear Sport vs SWIMTAG - Swimathon 2018'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://1.bp.blogspot.com/-Jy4xCc940pE/WuWX6RIeoFI/AAAAAAAAMkU/WkDikuagC4g06hP0XAbSqZzWrdLLBL_aACPcBGAYYCw/s72-c/SwimTag1.JPG" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-9166094901573630787</id><published>2016-10-11T22:56:00.002+01:00</published><updated>2021-01-03T20:07:32.609+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Angular"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="SignalR"/><title type='text'>Getting started with SignalR</title><content type='html'>&lt;p&gt;&lt;b&gt;&lt;i&gt;Follows on from previous post: &lt;a href=&quot;http://www.adathedev.co.uk/2016/10/real-time-signalr-angular-2-dashboard.html&quot; target=&quot;_new&quot;&gt;Real-time SignalR and Angular 2 dashboard&lt;/a&gt;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;I had a couple of questions from someone who forked the project on GitHub and is using it as the basis for their own custom dashboard, which reminded me that there are a few key things I picked up on my learning journey that I should share. These are primarily focused around SignalR aspects. &lt;/p&gt; &lt;h4&gt;An instance of a SignalR hub is created for each request&lt;/h4&gt;A single instance is &lt;b&gt;not&lt;/b&gt; shared across all clients connected to that hub. This is much like a controller in ASP.NET MVC, whereby a new instance of the controller is created for each request. Hence in my &lt;a href=&quot;https://github.com/AdaTheDev/SignalRDashboard&quot; target=&quot;_new&quot;&gt;SignalRDashboard project&lt;/a&gt;, the mechanism for holding a single instance of shared data for all connections, is via a singleton instance that gets passed into each hub&#39;s constructor.  &lt;h4&gt;Setup callback methods in Javascript for SignalR hubs BEFORE starting the SignalR connection&lt;/h4&gt;To open a SignalR connection, you use the following: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;    $.connection.hub.start();&lt;br /&gt;&lt;/pre&gt;To be able to receive messages from the server-side SignalR hub in client-side Javascript, you have to hookup a method to be called. You&#39;d do this using something like: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;    var hub = $.connection.myDemoHub;&lt;br /&gt;    hub.client.updateMeWhenYouHaveUpdates = function(message) {&lt;br /&gt;        // Do something useful with what we&#39;ve just received from the server&lt;br /&gt;    };&lt;br /&gt;&lt;/pre&gt;The import thing to remember is &lt;b&gt;&lt;i&gt;once you&#39;ve started the SignalR connection, you can&#39;t hook up these callbacks&lt;/i&gt;&lt;/b&gt;. So make sure they are done BEFORE you start the connection. The correct ordering would be:  &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;    var hub = $.connection.myDemoHub;&lt;br /&gt;    hub.client.updateMeWhenYouHaveUpdates = function(message) {&lt;br /&gt;        // Do something useful with what we&#39;ve just received from the server&lt;br /&gt;    };&lt;br /&gt;    $.connection.hub.start();&lt;br /&gt;&lt;/pre&gt; &lt;h4&gt;You can connect to multiple SignalR hubs over the same connection&lt;/h4&gt;This is great as it means, you can connect to multiple hubs easily - all you need to do is setup the callback methods in Javascript before you open the connection, as per the previous point. &lt;pre&gt;&lt;br /&gt;    var hub1 = $.connection.myDemoHub;&lt;br /&gt;    hub1.client.updateMeWhenYouHaveUpdates = function(message) {&lt;br /&gt;        // Do something useful with what we&#39;ve just received from the server&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    var hub2 = $.connection.myOtherHub;&lt;br /&gt;    hub2.client.callMePlease = function(message) {&lt;br /&gt;        // Do something useful with what we&#39;ve just received from the server&lt;br /&gt;    };&lt;br /&gt;    $.connection.hub.start();&lt;br /&gt;&lt;/pre&gt;In the &lt;a href=&quot;https://github.com/AdaTheDev/SignalRDashboard&quot; target=&quot;_new&quot;&gt;SignalRDashboard project&lt;/a&gt;, you&#39;ll see this being handled in &lt;a href=&quot;https://github.com/AdaTheDev/SignalRDashboard/blob/master/SignalRDashboard.Web/Scripts/app/dashboard.js&quot; target=&quot;_new&quot;&gt;dashboard.js&lt;/a&gt; as follows: &lt;ul&gt;&lt;li&gt;each Angular 2 dashboard component registers itself with dashboard.js when it is constructed&lt;/li&gt;&lt;li&gt;once the component has been initialised (ngOnInit event), it lets dashboard.js know that it&#39;s finished and registration is completed&lt;/li&gt;&lt;li&gt;once all components have completed registration, then the initialiseComponents() method is called which then calls in to each registered component (each must expose a setupHub() method) so they can set up the callbacks they&#39;re interested in from the hubs they use&lt;/li&gt;&lt;li&gt;finally, the connection is then started&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;The basics are covered well in the official &lt;a href=&quot;https://www.asp.net/signalr&quot; target=&quot;_new&quot;&gt;SignalR site&lt;/a&gt;, but the points above were some of the main things that I found myself looking a bit deeper for. &lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/9166094901573630787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2016/10/getting-started-with-signalr.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/9166094901573630787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/9166094901573630787'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2016/10/getting-started-with-signalr.html' title='Getting started with SignalR'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-5487830065071802472</id><published>2016-10-07T09:14:00.001+01:00</published><updated>2016-10-11T22:57:31.945+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="Angular"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="MVC"/><category scheme="http://www.blogger.com/atom/ns#" term="SignalR"/><title type='text'>Real-time SignalR and Angular 2 dashboard</title><content type='html'>&lt;div class=&quot;post&quot;&gt;&lt;b&gt;tl;dr&lt;/b&gt; Check out my new &lt;a href=&quot;https://github.com/AdaTheDev/SignalRDashboard&quot; target=&quot;_new&quot;&gt;SignalRDashboard GitHub repo&lt;/a&gt;&lt;br/&gt;&lt;b&gt;Followup post:&lt;/b&gt; &lt;a href=&quot;http://www.adathedev.co.uk/2016/10/getting-started-with-signalr.html&quot; target=&quot;_new&quot;&gt;Getting started with SignalR&lt;/a&gt;&lt;p&gt;For a while now, I&#39;ve had that burning curiosity to do more than just the bit of hacking around with &lt;a href=&quot;https://www.asp.net/signalr&quot; target=&quot;_new&quot;&gt;SignalR&lt;/a&gt; that I&#39;d previously done. I wanted to actually start building something with it. If you&#39;re not familiar with it, SignalR is a: &lt;blockquote&gt;...library for ASP.NET developers that makes developing real-time web functionality easy.&lt;/blockquote&gt;  &lt;/p&gt;&lt;p&gt;It provides a simple way to have two-way communication between the client and server; the server can push updates/data to any connected client. There are a number of great starting examples out there, but to give a very quick overview, a typical basic example is web-based chat functionality. When a user navigates to the chat page using their browser-of-choice, a connection is made to a SignalR &quot;hub&quot; on the server, using Javascript. This connection provides the channel for bi-directional client-server communication. When a user enters some text in the UI and presses send, a message is sent across this channel to the server, invoking a method on the hub. In the case of a basic chat example, this then broadcasts the message out to all the clients who are connected to that hub, which results in a Javascript method on the client being called to then render that message out to each user&#39;s screen.  &lt;/p&gt;&lt;p&gt;No need to make AJAX requests to send requests to the server. No need to make AJAX requests to make polling-style requests to the server for updates. SignalR handles the approach used to maintain the communication channel between the client browser and the server, depending on the functionality supported by the browser. It also handles the nuts and bolts of how to broadcast out to any connected clients.  &lt;/p&gt;&lt;h4&gt;Enter the dashboard&lt;/h4&gt;In the dev room I work in, we&#39;ve had a large screen showing a dashboard of metrics for years - evolving over time as we realise the value of new metrics. I decided that a great learning project to work on in my spare time would be to create a real-time web-based replacement (the original one was a WPF app). I&#39;m a big fan of giving visibility of relevant and timely, high-level application metrics to the business. Current CI build broken? Show it. Recent system error rates? Show it. How many stories do the test team have left to accept? Show it. That ability to take a quick glance, and get a quick feel for the current state of play across a wide area of the business, is invaluable. It&#39;s also handy to have things flash and/or play a sound, when there&#39;s something of particular importance to flag up. &lt;p&gt;From a technical perspective, I set myself a number of goals I wanted to achieve: &lt;ul&gt;&lt;li&gt;a centralised dashboard accessible across all areas of the business (it&#39;s an ASP.NET MVC 5 web app...job done!)&lt;/li&gt;&lt;li&gt;no matter how many clients are connected to the dashboard, I don&#39;t want to increase the load on the systems/services that the dashboard is drawing data from&lt;/li&gt;&lt;li&gt;try out a new (to me) Javascript framework to aid the creation of slick, dynamic views&lt;/li&gt;&lt;li&gt;create an dashboard framework that&#39;s easy to extend, with some demo dashboard components so hopefully others interested in the technology can find it useful, and better still, put it to use in their own environments&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;h4&gt;Moar learning!&lt;/h4&gt;&lt;a href=&quot;https://angularjs.org/&quot; target=&quot;_new&quot;&gt;AngularJS&lt;/a&gt; has also been on my radar for a while, and with &lt;a href=&quot;http://angularjs.blogspot.co.uk/2016/09/angular2-final.html&quot; target=&quot;_new&quot;&gt;Angular 2 coming along as the successor to Angular 1&lt;/a&gt;, it seemed like a chance to have a play around with that too. When I first started hacking about with it, an early beta version was available which I started off using. More recently, the Final Release has become available - so I have since upgraded to that, patching up where there were breaking changes.   &lt;h4&gt;GitHub repo&lt;/h4&gt;The result is a cunningly named (does-what-it-says-on-the-tin) &lt;a href=&quot;https://github.com/AdaTheDev/SignalRDashboard&quot; target=&quot;_new&quot;&gt;SignalRDashboard project GitHub repo&lt;/a&gt;. There&#39;s a few demo components as examples, and I plan to add to those as and when I have time. It is still very much work in progress and evolving!  &lt;p&gt;Here&#39;s a quick screenshot with some of the demo components currently written (all randomly generated data, refreshing every 15 seconds):&lt;br/&gt;&lt;a href=&quot;https://4.bp.blogspot.com/-9f75-Q9laJo/V_dZAt7xy5I/AAAAAAAAI4o/LKPWpstTvfoXYgc3qahjAKOor70WDi2cwCLcB/s1600/SignalRDashboardScreenshot.JPG&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://4.bp.blogspot.com/-9f75-Q9laJo/V_dZAt7xy5I/AAAAAAAAI4o/LKPWpstTvfoXYgc3qahjAKOor70WDi2cwCLcB/s320/SignalRDashboardScreenshot.JPG&quot; width=&quot;320&quot; height=&quot;254&quot; /&gt;&lt;/a&gt;&lt;/p&gt;As always, all feedback is welcome - if you do find it useful/end up using it, drop me a message as it would be great to know! &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/5487830065071802472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2016/10/real-time-signalr-angular-2-dashboard.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/5487830065071802472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/5487830065071802472'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2016/10/real-time-signalr-angular-2-dashboard.html' title='Real-time SignalR and Angular 2 dashboard'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://4.bp.blogspot.com/-9f75-Q9laJo/V_dZAt7xy5I/AAAAAAAAI4o/LKPWpstTvfoXYgc3qahjAKOor70WDi2cwCLcB/s72-c/SignalRDashboardScreenshot.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-8558685742921642318</id><published>2014-08-12T16:03:00.004+01:00</published><updated>2014-08-12T16:03:57.690+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>dm_exec_query_plan returning NULL query plan</title><content type='html'>&lt;div class=&quot;post&quot;&gt;I recently hit a scenario (SQL Server 2012 Standard, 11.0.5058) where I was trying to pull out the execution plan for a stored procedure from the plan cache, but the following query was returning a NULL query plan: &lt;/div&gt; &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, &lt;br /&gt;    qp.query_plan, tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE  text LIKE &#39;%MyStoredProcedure%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;&lt;/pre&gt; &lt;div class=&quot;post&quot;&gt;Each time I ran the stored procedure, the usecounts was incrementing, but I just could not get the query plan to be returned. Initially I thought I&#39;d found the answer on &lt;a href=&quot;http://www.patrickkeisler.com/2013/09/the-case-of-null-queryplan.html&quot; target=&quot;_new&quot;&gt;this blog post&lt;/a&gt;: &lt;/div&gt; &lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;Getting a NULL query_plan from dm_exec_query_plan, scratched head for a bit, then found this - &lt;a href=&quot;http://t.co/DGcF2qMvK4&quot;&gt;http://t.co/DGcF2qMvK4&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/sqlserver?src=hash&quot;&gt;#sqlserver&lt;/a&gt;&lt;/p&gt;&amp;mdash; Adrian Hills (@AdaTheDev) &lt;a href=&quot;https://twitter.com/AdaTheDev/statuses/498812938975981568&quot;&gt;August 11, 2014&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt; &lt;div class=&quot;post&quot;&gt;However, dm_exec_text_query_plan also returned NULL for the plan handle so it was a dead end for this scenario. So, a bit more digging around and came across &lt;a href=&quot;http://stackoverflow.com/questions/14326146/what-causes-dmv-to-show-a-null-query-plan&quot; target=&quot;_new&quot;&gt;this question on StackOverflow&lt;/a&gt;. This was pretty much the scenario I was experiencing - my stored procedure had a conditional statement that wasn&#39;t being hit based on the parameters I was supplying to the stored procedure. I temporarily removed the IF condition, ran it again and hey presto, this time an execution plan WAS returned. Re-instating the condition then, sure enough, made it no longer return the plan via `dm_exec_query_plan`. I tried to create a simplified procedure to reproduce it, with multiple conditions inside that weren&#39;t all hit, but a query plan was successfully returned when I tested it - so it wasn&#39;t as straight forward as just having multiple branches within a procedure. &lt;br/&gt;&lt;br/&gt;I was just starting to suspect it was something to do with temporary table jiggery-pokery that was being done within the conditional statement, and trying to create a very simplified repro when... &lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/AdaTheDev&quot;&gt;@AdaTheDev&lt;/a&gt; read this one? &lt;a href=&quot;http://t.co/g7XKObB24E&quot;&gt;http://t.co/g7XKObB24E&lt;/a&gt;&lt;/p&gt;&amp;mdash; Arthur Olcot (@sqlserverrocks) &lt;a href=&quot;https://twitter.com/sqlserverrocks/statuses/499183638853193728&quot;&gt;August 12, 2014&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;br/&gt;This was pretty much exactly the scenario I was hitting. I carried on with my ultra-simplified repro example which shows the full scope/impact of this issue (see below). As noted in &lt;a href=&quot;http://db-pub.com/forum-80537640/unable-to-get-the-xml-plan-with-conditional-statements-using-temp-tables.html&quot;&gt;the forum post&lt;/a&gt; provided above, it&#39;s an issue that occurs when using a temp table in this context, but table variables do NOT result in the same behaviour (i.e. testing a switch over to a table variable instead of a temp table sure enough did result in query plan being returned by dm_exec_query_plan ). N.B. It goes without saying, this is not an endorsement for just blindly switching to table variables! &lt;/div&gt; &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;-- 1) Create the simple repro sproc&lt;br /&gt;CREATE PROCEDURE ConditionalPlanTest &lt;br /&gt; @Switch INTEGER&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt; CREATE TABLE #Ids (Id INTEGER PRIMARY KEY)&lt;br /&gt; DECLARE @Count INTEGER&lt;br /&gt;&lt;br /&gt; IF (@Switch &gt; 0)&lt;br /&gt;  BEGIN  &lt;br /&gt;   INSERT INTO #Ids (Id) VALUES (1)&lt;br /&gt;  END &lt;br /&gt;&lt;br /&gt; IF (@Switch &gt; 1)&lt;br /&gt;  BEGIN&lt;br /&gt;   INSERT #Ids (Id) VALUES (2)&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt; SELECT * FROM #Ids&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 2) Run it with a value that does NOT result in all conditions being hit&lt;br /&gt;EXECUTE ConditionalPlanTest 1&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 3) Check plan cache - no query plan or text query plan will be returned, &lt;br /&gt;--    usecounts = 1&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, &lt;br /&gt;    qp.query_plan, tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE text LIKE &#39;%ConditionalPlanTest%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 4) Now run it with a different parameter that hits the 2nd condition&lt;br /&gt;EXECUTE ConditionalPlanTest 2&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 5) Check the plan cache again - query plan is now returned and &lt;br /&gt;--    usecounts is now 2.&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, &lt;br /&gt;    qp.query_plan, tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE text LIKE &#39;%ConditionalPlanTest%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 6) Recompile the sproc&lt;br /&gt;EXECUTE sp_recompile &#39;ConditionalPlanTest&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 7) Confirm nothing in the cache for this sproc&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, &lt;br /&gt;    qp.query_plan, tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE text LIKE &#39;%ConditionalPlanTest%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 8) This time, run straight away with a parameter that hits ALL conditions&lt;br /&gt;EXECUTE ConditionalPlanTest 2&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 9) Check the plan cache again - query plan is returned and usecounts=1.&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, &lt;br /&gt;    qp.query_plan, tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE text LIKE &#39;%ConditionalPlanTest%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 10) Now change the sproc to switch from temp table to table variable&lt;br /&gt;ALTER PROCEDURE ConditionalPlanTest &lt;br /&gt; @Switch INTEGER&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt; DECLARE @Ids TABLE (Id INTEGER PRIMARY KEY)&lt;br /&gt; DECLARE @Count INTEGER&lt;br /&gt;&lt;br /&gt; IF (@Switch &gt; 0)&lt;br /&gt;  BEGIN  &lt;br /&gt;   INSERT INTO @Ids (Id) VALUES (1)&lt;br /&gt;  END &lt;br /&gt;&lt;br /&gt; IF (@Switch &gt; 1)&lt;br /&gt;  BEGIN&lt;br /&gt;   INSERT @Ids (Id) VALUES (2)&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt; SELECT * FROM @Ids&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 11) Execute the sproc with the parameter that does NOT hit all the conditions&lt;br /&gt;EXECUTE ConditionalPlanTest 1&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 12) Check the plan cache - query plan is returned, usecounts=1&lt;br /&gt;SELECT plan_handle,usecounts, cacheobjtype, objtype, size_in_bytes, text, qp.query_plan, &lt;br /&gt;    tqp.query_plan AS text_query_plan&lt;br /&gt;FROM sys.dm_exec_cached_plans cp&lt;br /&gt; CROSS APPLY sys.dm_exec_sql_text(plan_handle) t&lt;br /&gt; CROSS APPLY sys.dm_exec_query_plan(plan_handle) qp&lt;br /&gt; CROSS APPLY sys.dm_exec_text_query_plan(plan_handle, NULL, NULL) tqp&lt;br /&gt;WHERE text LIKE &#39;%ConditionalPlanTest%&#39;&lt;br /&gt; AND objtype = &#39;Proc&#39;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- 13) CLEANUP&lt;br /&gt;DROP PROCEDURE ConditionalPlanTest&lt;br /&gt;GO&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/8558685742921642318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2014/08/dmexecqueryplan-returning-null-query.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/8558685742921642318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/8558685742921642318'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2014/08/dmexecqueryplan-returning-null-query.html' title='dm_exec_query_plan returning NULL query plan'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-6306750360399514247</id><published>2013-09-19T09:59:00.001+01:00</published><updated>2013-09-19T09:59:22.378+01:00</updated><title type='text'>70-486 Developing ASP.NET MVC 4 Web Applications</title><content type='html'>&lt;div class=&quot;post&quot;&gt;&lt;p&gt;Last week I passed the 70-486 Microsoft exam - &lt;a href=&quot;http://www.microsoft.com/learning/en-us/exam-70-486.aspx&quot; target=&quot;_new&quot;&gt;Developing ASP.NET MVC 4 Web Applications&lt;/a&gt;, so thought I&#39;d knock up a quick post on my experience and what materials I found useful as part of my preparation.&lt;/p&gt; &lt;p&gt;Going into this exam, I had just over a year and a half&#39;s commercial experience using ASP.NET MVC 2 &amp; 3. Before that, I had experience in ASP.NET and prior to that, when dinosaurs still roamed, classic ASP. It&#39;s no secret I&#39;m a bit of a data nerd (a lot of my blog is db related, SQL Server, MongoDB...) and I have tended to be backend focused, but I wanted to even out the balance a bit by pushing myself in this area, and in JS/HTML5/CSS3.  I took the opportunity to upgrade the web solution at my current company from MVC 3 to MVC 4, and being able to do that at the start of my preparation was really useful - this is how I like to learn, by actually &quot;getting stuff done&quot;. That is the obvious, number 1 recommendation - do not just read and swot up on the theory, actually &quot;do&quot;. My brain likes me to be stupid and make practical mistakes - when I then work out how I&#39;ve done something daft, it reinforces the learning and makes the knowledge stick. &lt;/p&gt; &lt;b&gt;Preparation&lt;/b&gt;&lt;p&gt;The first thing I was disappointed to find was that there is (as of time of writing) no Microsoft Exam prep book for this exam. There is one due out I believe at the beginning of October 2013 - Exam Ref 70-486 : Developing ASP.NET MVC 4 Web Applications by William Penberthy (ISBN-10: 0735677220 | ISBN-13: 978-0735677227). So I obviously can&#39;t comment on how good that book is. The book I went with was &lt;a href=&quot;http://www.wrox.com/WileyCDA/WroxTitle/Professional-ASP-NET-MVC-4.productCd-111834846X.html&quot; target=&quot;_new&quot;&gt;Professional ASP.NET MVC 4&lt;/a&gt; from Wrox, by Jon Galloway (&lt;a href=&quot;https://twitter.com/jongalloway&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt;), Phil Haack (&lt;a href=&quot;https://twitter.com/haacked&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt;), K. Scott Allen (&lt;a href=&quot;https://twitter.com/OdeToCode&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt;) and foreword by Scott Hanselman (&lt;a href=&quot;https://twitter.com/shanselman&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt;). While the book alone isn&#39;t enough for the exam, for me it gave a good coverage on quite a few areas I needed so it is definitely worth a read. &lt;/p&gt;&lt;p&gt;Having a &lt;a href=&quot;http://www.pluralsight.com/training/&quot; target=&quot;_new&quot;&gt;Pluralsight&lt;/a&gt; subscription was good and while not necessarily geared towards the exam, you can never go wrong with a bit of Pluralsight training. I went through a number of the MVC courses - ASP.NET MVC Fundamentals, ASP.NET MVC 2.0 Fundamentals, MVC 4 Fundamentals and Building Applications with MVC 4. One of the things I like about Pluralsight is you can control the playback speed so for most of the stuff I already knew, I glossed over at a faster speed. I also went through the &quot;&lt;a href=&quot;http://www.microsoftvirtualacademy.com/training-courses/create-web-apps-with-asp-net&quot; target=&quot;new&quot;&gt;Building Web Apps with ASP.NET Jump Start&lt;/a&gt;&quot; training on the Microsoft Virtual Academy (Scott Hanselman, Jon Galloway and Damian Edwards (&lt;a href=&quot;https://twitter.com/DamianEdwards&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt;)) - that was quite entertaining! &lt;/p&gt;&lt;p&gt;I found some great study guide blog posts that collated together a lot of links to some good material: &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.bloggedbychris.com/2012/11/06/microsoft-exam-70-486-study-guide/&quot; target=&quot;_new&quot;&gt;Blogged By Chris&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://geekswithblogs.net/WTFNext/archive/2013/05/29/exam-70-486-study-material-developing-asp.net-mvc-4-web-applications.aspx&quot; target=&quot;_new&quot;&gt;GeeksWithBlogs - WTF Next?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; These give a lot of useful links to MSDN articles, MS resources, blogs, interesting StackOverflow questions etc. &lt;/p&gt;&lt;p&gt;Last but definitely not least, there was my practical setup - Windows 8 on a VM, with Visual Studio 2012 and a Windows Azure account. To re-iterate what I said before - the best way to learn, is to do...and make daft mistakes. I started work on a new web app from scratch, with a real-world mindset on (i.e. writing production-worthy code) putting to good use the new things I was learning. I also had a scratch-pad web app where I would just dump rough code to try out short, simple snippets. &lt;/p&gt; &lt;b&gt;Bottom line&lt;/b&gt;&lt;p&gt;Overall, I put a lot of time and effort into preparation for this exam and it paid off. The biggest benefit for me is what I learned along the way and the challenge it gave me. &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/6306750360399514247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/09/70-486-developing-aspnet-mvc-4-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/6306750360399514247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/6306750360399514247'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/09/70-486-developing-aspnet-mvc-4-web.html' title='70-486 Developing ASP.NET MVC 4 Web Applications'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-1272065746964837724</id><published>2013-06-05T21:18:00.000+01:00</published><updated>2013-06-05T21:18:11.865+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>SQL Server 2008 R2 in-place upgrade error</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Today I encountered the following error during the process of performing an in-place upgrade of a SQL Server 2008 instance to 2008  R2:  &lt;blockquote&gt;The specified user &#39;someuser@somedomain.local&#39; does not exist&lt;/blockquote&gt; I wasn&#39;t initially sure what that related to - I hadn&#39;t specified that account during the upgrade process so I went looking in the Services managemement console. The SQL Server service for the instance I was upgrading was configured to &quot;Log On As&quot; that account and it had been happily running prior to the upgrade.   &lt;p&gt;This domain account did exist but to sanity check, the first thing I did was switched the service to use another domain account that also definitely existed and retried the Repair process. Same error. Then I spotted further down the list, another service had specified an account in the &quot;somedomain\someuser&quot; form. So I switched the SQL Server instance service to specify the account in that form (Down-Level Logon Name) instead of the UPN (User Principal Name) format (&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windows/desktop/aa380525%28v=vs.85%29.aspx&quot; target=&quot;_new&quot;&gt;reference&lt;/a&gt;). &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Bingo.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;While I was waiting for the Repair process to run through again, I carried on searching and found &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en-US/sqlsetupandupgrade/thread/bf97c30a-f0b6-4d6b-b5cc-4d79a2da4e8f&quot; target=&quot;_new&quot;&gt;this question&lt;/a&gt; on the MSDN Forums. The very last answer there confirmed it. The SQL Server 2008 R2 upgrade does NOT like user accounts in the UPN format.  &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/1272065746964837724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/06/sql-server-2008-r2-in-place-upgrade.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1272065746964837724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1272065746964837724'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/06/sql-server-2008-r2-in-place-upgrade.html' title='SQL Server 2008 R2 in-place upgrade error'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-4679486476138465805</id><published>2013-03-21T09:53:00.001+00:00</published><updated>2013-03-21T09:53:58.201+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><title type='text'>.NET Project File Analyser</title><content type='html'>&lt;div class=&quot;post&quot;&gt;I&#39;ve started knocking together a little app to automate the process of trawling through a folder structure and checking .NET project files (C# .csproj currently) to extract some info out of them. The &lt;a href=&quot;https://github.com/AdaTheDev/DotNetProjectFileAnalyser&quot; target=&quot;_new&quot;&gt;DotNetProjectFileAnalyser repo&lt;/a&gt; is up on GitHub. I&#39;ve been working against Visual Studio 2010 project files, but could well work for other versions assuming the project file structure is the same for the elements it currently looks at -  I just haven&#39;t tried as yet. &lt;br/&gt;Currently, it will generate an output file detailing for each .csproj file it finds: &lt;ul&gt;  &lt;li&gt;Build output directory (relative and absolute) for the configuration/platform specified (e.g. Debug AnyCpu).    Useful if you want find which projects you need to change to build to a central/common build directory.&lt;/li&gt;  &lt;li&gt;List of all Project Reference dependencies (as opposed to assembly references).     Useful if you want to find the projects that have Project References so you can switch them to assembly references&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;b&gt;Usage&lt;/b&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;DotNetProjectFileAnalyser.exe {RootDirectory} {Configuration} {Path}&lt;br /&gt;&lt;br /&gt;{RootDirectory} = start directory to trawl for .csproj files (including subdirectories)&lt;br /&gt;{Configuration} = as defined in VS, e.g. Debug, Release&lt;br /&gt;{Platform} = as defined in VS, e.g. AnyCpu&lt;br /&gt;&lt;/pre&gt; Example: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;DotNetProjectFileAnalyser.exe &quot;C:\src\&quot; &quot;Debug&quot; &quot;AnyCpu&quot;&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;More stuff will go in over time, with ability to automatically update csproj files as well to save a lot of manual effort. &lt;/div&gt; </content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/4679486476138465805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/03/net-project-file-analyser.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4679486476138465805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4679486476138465805'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/03/net-project-file-analyser.html' title='.NET Project File Analyser'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-530097632131188604</id><published>2013-03-13T23:09:00.001+00:00</published><updated>2013-03-13T23:09:48.023+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="geospatial"/><category scheme="http://www.blogger.com/atom/ns#" term="Ordnance Survey"/><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>GB Post Code Importer Conversion Accuracy Fix</title><content type='html'>&lt;div class=&quot;post&quot;&gt;In a post last year (&lt;a href=&quot;http://www.adathedev.co.uk/2012/03/ordnance-survey-data-importer.html&quot; target=&quot;_new&quot;&gt;Ordnance Survey Data Importer Coordinate Conversion Accuracy&lt;/a&gt;) I looked into an accuracy issue with the conversion process within the GeoCoordConversion DLL that I use in this &lt;a href=&quot;https://github.com/AdaTheDev/Ordnance-Survey-Code-Point-Data-Importer&quot; target=&quot;_new&quot;&gt;project&lt;/a&gt; (&lt;a href=&quot;http://www.adathedev.co.uk/2011/01/gb-post-code-geographic-data-load-to.html&quot; target=&quot;_new&quot;&gt;blog post&lt;/a&gt;). Bottom line, was that it was a minor with an average inaccuracy of around 2.5 metres and a max of ~130 metres by my reckoning. I&#39;ve since had a few requests asking if I can supply an updated GeoCoordConversion DLL with fixes to the calculations. &lt;p&gt;After getting in contact with the owner of the &lt;a href=&quot;http://code.google.com/p/geocoordconversion/&quot; target=&quot;_new&quot;&gt;GeoCoordConversion project&lt;/a&gt;, they&#39;ve kindly added me as a committer. I&#39;ve now pushed the fixes up to it, rebuilt the DLL (now v1.0.1.0) and pushed up the latest DLL to the Ordnance Survey Importer project on GitHub. &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/530097632131188604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/03/gb-post-code-importer-conversion.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/530097632131188604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/530097632131188604'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/03/gb-post-code-importer-conversion.html' title='GB Post Code Importer Conversion Accuracy Fix'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-8504014739209633179</id><published>2013-03-08T13:11:00.001+00:00</published><updated>2013-03-08T13:50:01.281+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>SQL Server Table Designer Bug With Filtered Unique Index</title><content type='html'>&lt;div class=&quot;post&quot;&gt;A colleague was getting a duplicate key error when trying to add a new column to a table via the Table Designer in SQL Server Management Studio (2008R2 - not tested on other versions), despite there being no violating data in the table. After a bit of digging around, I tracked the problem down to what appears to be a bug in Table Designer when there is a unique, filtered index in place on the table and the table is being recreated (i.e. you&#39;re adding a new column, but not at the end after all the existing columns).  &lt;br/&gt;&lt;br/&gt;&lt;b&gt;Steps to reproduce&lt;/b&gt;&lt;ol&gt;&lt;li&gt;In SSMS in Tools -&gt; Options -&gt; Designers -&gt; Table and Database Designers, uncheck the &quot;Prevent saving changes that require table re-creation&quot; option &lt;/li&gt;&lt;li&gt;Create table: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;CREATE TABLE [dbo].[Test]&lt;br /&gt;(&lt;br /&gt;Column1 INTEGER NOT NULL,&lt;br /&gt;Column2 INTEGER NOT NULL,&lt;br /&gt;Column3 INTEGER NULL&lt;br /&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Create dummy data: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;INSERT dbo.Test(Column1, Column2, Column3) VALUES (1, 1, 1);&lt;br /&gt;INSERT dbo.Test(Column1, Column2, Column3) VALUES (1, 1, NULL); -- OK as Column3 is NULL&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Now run the following, duplicate key error is correctly thrown: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;-- Errors, Duplicate Key exception as expected&lt;br /&gt;INSERT dbo.Test(Column1, Column2, Column3) VALUES (1, 1, 2); &lt;br /&gt;&lt;/pre&gt;So at this point we have 2 rows in the table, no violations of the unique filtered index. &lt;/li&gt;&lt;li&gt;Right click the table in SSMS -&gt; Design &lt;/li&gt;&lt;li&gt;Insert a new column &quot;Column4&quot; before Column3 and the press Save. &lt;/li&gt;&lt;/ol&gt;The error that occurs is: &lt;pre&gt;&lt;br /&gt;&#39;Test&#39; table&lt;br /&gt;- Unable to create index &#39;IX_Test_Column1_Column2&#39;.  &lt;br /&gt;The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name &#39;dbo.Test&#39; and the index name &#39;IX_Test_Column1_Column2&#39;. The duplicate key value is (1, 1).&lt;br /&gt;The statement has been terminated.&lt;br /&gt;&lt;/pre&gt; &lt;p&gt;So what it appears to be doing, is losing the WHERE filter on the index. This can be confirmed by clicking &quot;Generate change script&quot; in the Table Designer instead of Save - at the end of the generated script: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;CREATE UNIQUE NONCLUSTERED INDEX IX_Test_Column1_Column2 ON dbo.Test&lt;br /&gt; (&lt;br /&gt; Column1,&lt;br /&gt; Column2&lt;br /&gt; ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]&lt;br /&gt;GO&lt;br /&gt;&lt;/pre&gt;Now, if there was no data in the table, or there were no rows with the same Column1 and Column2 value combination when you go into Table Designer, then you can save the table change and be blissfully unaware that the filter has been lost from the index. i.e. repeat the repro steps again, but this time move step 3 and 4 (insert dummy data) to the end of the process. The previously OK 2nd data row will now error upon insert. &lt;/p&gt;&lt;p&gt;Personally, I almost never use the Table Designer and as a safeguard, will be recommending to the rest of the team that the &quot;Prevent saving changes that require table re-creation&quot; option is checked as a basic guard. &lt;/p&gt;&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt;The following Connect items relating to this issue:&lt;br/&gt;&lt;a href=&quot;http://connect.microsoft.com/SQLServer/feedback/details/462053/the-filter-expression-of-a-filtered-index-is-lost-when-a-table-is-modified-by-the-table-designer#details&quot; target=&quot;_new&quot;&gt;The filter expression of a filtered index is lost when a table is modified by the table designer&lt;/a&gt;&lt;br/&gt;&lt;a href=&quot;https://connect.microsoft.com/SQLServer/feedback/details/362699/2008-rtm-ssms-engine-table-designer-doesnt-script-where-clause-in-filtered-indexes&quot; target=&quot;_new&quot;&gt;2008 RTM, SSMS/Engine: Table designer doesn&#39;t script WHERE clause in filtered indexes&lt;/a&gt;&lt;br/&gt;Referring to comments in that 2nd item, my &quot;Script for server version&quot; setting was set to &quot;SQL Server 2008 R2&quot;. &lt;br/&gt;&lt;br/&gt;Sounds like this may have been addressed in SQL 2012, but still a problem in 2008/2008R2. &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/8504014739209633179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/03/sql-server-table-designer-bug-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/8504014739209633179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/8504014739209633179'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/03/sql-server-table-designer-bug-with.html' title='SQL Server Table Designer Bug With Filtered Unique Index'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-4840750543755934222</id><published>2013-03-03T22:05:00.000+00:00</published><updated>2013-03-03T22:05:36.435+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="asp.net"/><category scheme="http://www.blogger.com/atom/ns#" term="MongoDB"/><title type='text'>MongoDB ASP.NET Session Store Provider v1.1.0</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Since I created the &lt;a href=&quot;http://www.adathedev.co.uk/2011/05/mongodb-aspnet-session-state-store.html&quot; target=&quot;_new&quot;&gt;MongoDB ASP.NET Session State Store Provider&lt;/a&gt; (v1.0.0), a few things have moved on in the MongoDB C# Driver. I&#39;ve pushed a number of changes up to the project on GitHub (which I&#39;ve incremented to v1.1.0), so it now uses v1.7.0.4714 of the driver. There is no change to way it is configured in web.config, so if you are using v1.0.0 of my provider it should be painless. Of course, I&#39;d recommend thorough testing first :) &lt;p&gt;The changes relate to: &lt;ul&gt;&lt;li&gt;SafeMode is obsolete. I&#39;ve replaced with WriteConcern.&lt;br/&gt;&lt;a href=&quot;https://jira.mongodb.org/browse/CSHARP-615&quot; target=&quot;_new&quot;&gt;C# Driver Jira case&lt;/a&gt;&lt;br/&gt;&lt;a href=&quot;http://docs.mongodb.org/manual/core/write-operations/#write-concern&quot; target=&quot;_new&quot;&gt;WriteConcern C# Driver reference&lt;/a&gt;&lt;/li&gt;&lt;li&gt;MongoServer.Create  is obsolete. I&#39;ve replaced with MongoClient.GetServer.&lt;br/&gt;&lt;a href=&quot;https://wiki.10gen.com/display/DOCS/CSharp+Driver+Tutorial#CSharpDriverTutorial-MongoClientclass&quot; target=&quot;_new&quot;&gt;MongoClient C# Driver reference&lt;/a&gt;&lt;/li&gt;&lt;li&gt;QueryComplete is obsolete.&lt;/li&gt;&lt;/ul&gt; &lt;br/&gt;&lt;b&gt;web.config recap&lt;/b&gt;&lt;br/&gt;These web.config settings have not changed, so should continue working as before. &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;  &amp;lt;connectionStrings&amp;gt;&lt;br /&gt;    &amp;lt;add name=&quot;MongoSessionServices&quot; connectionString=&quot;mongodb://localhost&quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/connectionStrings&amp;gt;&lt;br /&gt;  &amp;lt;system.web&amp;gt;&lt;br /&gt;    &amp;lt;sessionState&lt;br /&gt;        mode=&quot;Custom&quot;&lt;br /&gt;        customProvider=&quot;MongoSessionStateProvider&quot;&amp;gt;&lt;br /&gt;      &amp;lt;providers&amp;gt;&lt;br /&gt;        &amp;lt;add name=&quot;MongoSessionStateProvider&quot;&lt;br /&gt;             type=&quot;MongoSessionStateStore.MongoSessionStateStore&quot;&lt;br /&gt;             connectionStringName=&quot;MongoSessionServices&quot;&lt;br /&gt;             writeExceptionsToEventLog=&quot;false&quot;&lt;br /&gt;             fsync=&quot;false&quot;&lt;br /&gt;             replicasToWrite=&quot;0&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;/providers&amp;gt;&lt;br /&gt;    &amp;lt;/sessionState&amp;gt;&lt;br /&gt;  &amp;lt;/system.web&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/pre&gt;replicasToWrite is interpreted as follows: &lt; 0 = ignored. Treated as 0.&lt;br/&gt;0 = will wait for acknowledgement from primary node.&lt;br/&gt;&gt; 0 = will wait for writes to be acknowledged from (1 + {replicasToWrite}) nodes. &lt;p&gt;&lt;b&gt;&lt;u&gt;Please Note&lt;/u&gt;&lt;/b&gt;, per the MongoDB Driver docs, if (1 + {replicasToWrite}) equates to a number greater than the number of replica set members that hold data, then MongoDB waits for the non-existent members to become available (so blocks indefinitely). &lt;/p&gt;&lt;p&gt;It still treats write concerns as important, ensuring it waits for acknowledgement back from at least one MongoDB node. &lt;/p&gt;As always, all feedback welcome. &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/4840750543755934222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/03/mongodb-aspnet-session-store-provider.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4840750543755934222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4840750543755934222'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/03/mongodb-aspnet-session-store-provider.html' title='MongoDB ASP.NET Session Store Provider v1.1.0'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-1614318095347051001</id><published>2013-02-10T19:53:00.000+00:00</published><updated>2013-02-10T20:04:16.061+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="ElasticSearch"/><category scheme="http://www.blogger.com/atom/ns#" term="Twitter"/><title type='text'>Importing twitter archive into ElasticSearch</title><content type='html'>&lt;div class=&quot;post&quot;&gt;I recently downloaded my full history of tweets from Twitter (via the &quot;Request your archive&quot; button on the Settings page), which gives the data in CSV and JSON formats. The download comes with a simple web UI so you can browse locally, all your historical tweets, as well as perform basic searches. When I tried performing a wildcard search, it resulted in an unresponsive script warning and nobbled the browser tab.  &lt;h4&gt;Cue ElasticSearch&lt;/h4&gt;I&#39;ve kept meaning to have another hack around with &lt;a href=&quot;http://www.elasticsearch.org/&quot; target=&quot;_new&quot;&gt;ElasticSearch&lt;/a&gt; for a while now, as I started looking into it in 2011 and was very impressed with it. So I decided to knock up a little .NET app to take the .js files provided by Twitter in the export, extract the tweet data and push into &lt;a href=&quot;http://www.elasticsearch.org/&quot; target=&quot;_new&quot;&gt;ElasticSearch&lt;/a&gt;. It seems like a good little starter project to learn more about &lt;a href=&quot;http://www.elasticsearch.org/&quot; target=&quot;_new&quot;&gt;ElasticSearch&lt;/a&gt; and actually could give me some useful insights into all the tweets I&#39;ve made. I&#39;m sure I&#39;m going to find some really good links I tweeted and then couldn&#39;t find again! &lt;p&gt;At the moment there&#39;s a very basic GUI (and I mean basic!) exposing the functionality to import the tweet data into ElasticSearch. I plan to stick a basic search GUI together too and then...well, I&#39;m not quite sure yet, but I&#39;m sure as time goes on I&#39;ll see more ways to extract interesting information from my tweet history. &lt;/p&gt;&lt;p&gt;The ElasticTweets project as I&#39;ve called it, can be found on &lt;a href=&quot;https://github.com/AdaTheDev/ElasticTweets&quot; target=&quot;_new&quot;&gt;GitHub here&lt;/a&gt; - feel free to use and abuse it, and let me know how you get on!  &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/1614318095347051001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2013/02/import-twitter-archive-elasticsearch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1614318095347051001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1614318095347051001'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2013/02/import-twitter-archive-elasticsearch.html' title='Importing twitter archive into ElasticSearch'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-7069128716949079055</id><published>2012-05-28T10:20:00.002+01:00</published><updated>2012-05-28T10:59:05.119+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><title type='text'>Configuring Azure Storage Emulator SQL Server Instance</title><content type='html'>&lt;div class=&quot;post&quot;&gt;If you&#39;re using Windows Azure Storage, you are almost certainly going to be running the storage emulator during development, instead of working directly against your storage account up in the cloud. This emulator (which comes in the &lt;a href=&quot;https://www.windowsazure.com/en-us/develop/downloads/&quot; target=&quot;_new&quot;&gt;Windows Azure SDK&lt;/a&gt; - see the &quot;other&quot; category), allows you to test locally against local instances of Blob, Queue and Table services.   &lt;p&gt;As per the &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windowsazure/gg432983.aspx&quot; target=&quot;_new&quot;&gt;Overview of Running a Windows Azure Application with Storage Emulator&lt;/a&gt; reference, the emulator needs a local SQL Server instance. By default, it&#39;s configured to run against a SQL Server Express 2005 or 2008 database. &lt;/p&gt;&lt;p&gt;If you want to point it at a different instance of SQL Server, for example a shared development database server, you can do this using the DSInit command line tool. I originally came across this MSDN on &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windowsazure/gg433134.aspx&quot; target=&quot;_new&quot;&gt;How to Configure SQL Server for the Storage Emulator&lt;/a&gt;, which led me to try the following in the Windows Azure Command Prompt:  &lt;pre class=&quot;code&quot;&gt;DSInit /sqlInstance:MyDevServerName\MyInstanceName&lt;/pre&gt; This tried to create the storage database, but failed with the following:  &lt;pre&gt;Creating database DevelopmentStorageDb20110816...&lt;br /&gt;Cannot create database &#39;DevelopmentStorageDb20110816&#39; : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)&lt;br /&gt;&lt;br /&gt;One or more initialization actions have failed. Resolve these errors before attempting to run the storage emulator again. These errors can occur if SQL Server was installed by someone other than the current user. Please refer to http://go.microsoft.com/fwlink/?LinkID=205140 for more details.&lt;br /&gt;&lt;/pre&gt; The correct way when trying to use a different database server instead of the local machine, is to use the SERVER switch instead: &lt;pre class=&quot;code&quot;&gt;DSInit /SERVER:MyDevServerName\MyInstanceName&lt;/pre&gt; See the full &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windowsazure/gg433005.aspx&quot; target=&quot;_new&quot;&gt;DSInit Command-Line Tool&lt;/a&gt; reference. &lt;/p&gt;&lt;p&gt;When you then run the Storage Emulator, it will target that database server/instance. You can easily cleardown/reset that database by right clicking the Windows Azure Emulator icon in the taskbar, select &quot;Show Storage Emulator UI&quot; and the click &quot;Reset&quot;. &lt;b&gt;NB. Just to be clear, this will delete everything in your local storage emulator database&lt;/b&gt;. &lt;/p&gt;&lt;p&gt;An added &quot;gotcha&quot; to watch out for, if you have the storage account connection stored in a web/app.config and want to specify to use the local emulated instance, you need to use &lt;pre class=&quot;code&quot;&gt;UseDevelopmentStorage=true&lt;/pre&gt; &lt;b&gt;&lt;u&gt;exactly&lt;/u&gt;&lt;/b&gt; as it appears here. If like me, you initially give an ending semi-colon, you will get a FormatException stating with the error: &quot;Invalid account string&quot;. &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/7069128716949079055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/05/configuring-azure-storage-emulator-sql.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/7069128716949079055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/7069128716949079055'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/05/configuring-azure-storage-emulator-sql.html' title='Configuring Azure Storage Emulator SQL Server Instance'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-1409146766043917980</id><published>2012-05-24T09:19:00.001+01:00</published><updated>2012-05-24T09:19:27.331+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><title type='text'>Error Creating Azure Blob Storage Container</title><content type='html'>&lt;div class=&quot;post&quot;&gt;I received a rather...vague...error when trying out a bit of .NET code to connect to a Windows Azure Blob Storage account, and create a new container in which to store some blobs.  &lt;h3&gt;The code&lt;/h3&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;CloudStorageAccount storageAccount = CloudStorageAccount.Parse(&lt;br /&gt;    &quot;DefaultEndpointsProtocol=https;AccountName=REMOVED;AccountKey=REMOVED&quot;);&lt;br /&gt;&lt;br /&gt;CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();&lt;br /&gt;            &lt;br /&gt;CloudBlobContainer container = &lt;br /&gt;    blobClient.GetContainerReference(&quot;TestContainer1&quot;);&lt;br /&gt;&lt;br /&gt;container.CreateIfNotExist(); // This error&#39;d&lt;br /&gt;&lt;/pre&gt; &lt;h3&gt;The error&lt;/h3&gt;A StorageClientException was thrown saying &quot;One of the request inputs is out of range.&quot;. An inner WebException showed that &quot;The remote server returned an error: (400) Bad Request.&quot;  &lt;h3&gt;The cause&lt;/h3&gt;I&#39;d assumed (incorrectly) that a container name could be pretty much any string. But that&#39;s not the case. As per &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windowsazure/dd135715.aspx&quot; target=&quot;_new&quot;&gt;this MSDN reference&lt;/a&gt;, a container name must: &lt;ul&gt;&lt;li&gt;be in lowercase (this was the cause in my case)&lt;/li&gt;&lt;li&gt;start with a letter or a number and only contain letters, numbers and hyphens (multiple consecutive hyphens are not allowed)&lt;/li&gt;&lt;li&gt;be between 3 and 63 characters long&lt;/li&gt;&lt;/ul&gt; The &quot;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/windowsazure/dd135715.aspx&quot; target=&quot;_new&quot;&gt;Naming and Referencing Containers, Blobs and Metadata&lt;/a&gt;&quot; reference is worth a bookmark. &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/1409146766043917980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/05/error-creating-azure-blob-storage.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1409146766043917980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1409146766043917980'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/05/error-creating-azure-blob-storage.html' title='Error Creating Azure Blob Storage Container'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-2172022797124095569</id><published>2012-05-23T09:33:00.000+01:00</published><updated>2012-05-23T09:33:06.491+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="asp.net"/><category scheme="http://www.blogger.com/atom/ns#" term="MVC"/><category scheme="http://www.blogger.com/atom/ns#" term="performance"/><title type='text'>ASP.NET MVC Performance Profiling</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Building up a profile of how a web application functions, all the database interactions that take place and where the server-side time is spent during a request can be a challenging task when you are new to an existing codebase. If you&#39;re trying to address the generic/non-specific &quot;we need to improve performance / it&#39;s slow&quot; issue, you need to get a good picture of what is going on and where to prioritise effort. &lt;p&gt;There are a number of ways to identify specific problems depending on the technologies. For example, if your application is backed by SQL Server, you can query a set of DMVs to identify the top n worst performing queries and then focus your effort on tuning those. Identifying a badly performing query and tuning it can obviously yield huge benefits in terms of the end user&#39;s experience. But this doesn&#39;t necessarily flag up all the problems. If you are looking at a particular page/view within the application, then you could start a SQL Profiler trace to monitor what&#39;s going on during the lifecycle of the request - this is another common and valuable tool to use. Personally, I usually have SQL Profiler open most of the time during development. If you&#39;re developing against a shared dev database with others, you can filter out other people&#39;s events from the trace - injecting your machine name into the connection string as the ApplicationName, and then filtering on this is one of a number of ways to achieve this which works nicely. &lt;/p&gt;&lt;h3&gt;MiniProfiler For The Win&lt;/h3&gt;Recently, I&#39;ve started using another extremely valuable tool within an ASP.NET MVC solution - &lt;a href=&quot;http://miniprofiler.com/&quot; target=&quot;_new&quot;&gt;MiniProfiler&lt;/a&gt; which is (quote): &lt;blockquote&gt;A simple but effective mini-profiler for ASP.NET MVC and ASP.NET&lt;/blockquote&gt; It was developed by the team over at &lt;a href=&quot;http://www.stackoverflow.com&quot; target=&quot;_new&quot;&gt;StackOverflow&lt;/a&gt;. Simply put, it can render performance statistics on the page you are viewing that detail where the time was spent server-side, fulfilling that request. But the key thing for me, is it provides an ADO.NET profiler. Say you&#39;re using LINQ-to-SQL - by wrapping the SqlConnection in a ProfiledDbConnection before then passing it to the constructor of a DataContext, info on the SQL queries executed within the lifetime of a request are then also included in the statistics displayed. (It can also profile calls via raw ADO.NET / Entity Framework etc, minimal effort required). &lt;/p&gt;&lt;h3&gt;Make the invisible, visible&lt;/h3&gt;Since integrating this into an MVC application, the benefits have been priceless. The key thing for me is: &lt;b&gt;&lt;u&gt;VISIBILITY&lt;/u&gt;&lt;/b&gt;. It provides extremely value visibility of what is happening under the covers. Going back to the start of this post, if you&#39;re new to a codebase, then having this information provided to you as you browse is invaluable. It enables you to identify problems at a glance, and increases visibility of problems to other developers so the &quot;life expectancy&quot; of those problems is lower - they&#39;re a lot less likely to hover undetected just under the radar if the information is being pushed right in front of the developer on screen. It also helps you build up a picture of how things hang together. &lt;p&gt;MiniProfiler includes functionality to flag up N+1 and duplicate queries, a common potential problem you could encounter with  ORMs if you&#39;re not careful. If a view were performing 100 low hitting queries, these may not show themselves as queries to be tuned. But the fact that 100 database roundtrips are being made, could scream out that perhaps they could be replaced with a single roundtrip and a performance improvement gained there. &lt;/p&gt;&lt;p&gt;I&#39;m now a big fan of MiniProfiler, especially due to it&#39;s simplicity to integrate into a codebase. Working on ASP.NET MVC/ASP.NET applications? You might want to give it a try! &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/2172022797124095569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/05/aspnet-mvc-performance-profiling.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2172022797124095569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2172022797124095569'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/05/aspnet-mvc-performance-profiling.html' title='ASP.NET MVC Performance Profiling'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-7306475222022390311</id><published>2012-04-13T11:59:00.000+01:00</published><updated>2012-04-13T11:59:50.611+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="development"/><title type='text'>Trust your developer instinct and experience</title><content type='html'>&lt;div class=&quot;post&quot;&gt;&lt;p&gt;That feature you&#39;re implementing, that bug you&#39;re fixing, that performance improvement you&#39;re trying to make...it&#39;s using up a fair few brain CPU cycles trying to analyse how best to implement it. Scalability, performance, ease of maintenance, clean code...just some of the things you are mentally scoring your potential approaches against to determine which route to take. And that&#39;s without any mention of business domain logic. We&#39;ve all been in the situation where you start heading down one path only to find a hurdle. It&#39;s OK though, you&#39;re an athlete (very debatable in my case) - you can jump over that hurdle. So you do. But then you find a 7 foot high wall with barbed wire on top. No problem - you have a ladder and wire cutters; it&#39;s a bit unwieldy carrying them around with you everywhere, but hey - over you go. All good. Until you come across Chuck Norris, who is angry about that &quot;Yo&#39; mamma..&quot; joke you made about his mother. That wall you climbed...that was protecting his private property. Your ladder? Oh, that&#39;s still on the outside of the wall - you couldn&#39;t pull it over with you so you jumped down without it. Brace for pain. &lt;/p&gt;&lt;h4&gt;Those voices in your head&lt;/h4&gt;There&#39;s always unexpected problems and hurdles that crop up. It&#39;s all part of being a developer after all. But maybe at the point you got your ladder and wire cutters out to climb over that wall, you heard a little voice inside say &quot;Hmm, this doesn&#39;t quite feel right&quot; or perhaps you just felt something niggling away with you about the approach. That voice, your instinct and experience as a developer built up over your career, is worth listening to. When it pipes up, sit back and take 5 to just take a bird&#39;s eye view.  &lt;p&gt;Trust that instinct and experience - it&#39;s usually right. At the very least, it&#39;s highlighting the need to at least re-justify to yourself that you are heading down the right path. &lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/7306475222022390311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/04/trust-your-developer-instinct-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/7306475222022390311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/7306475222022390311'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/04/trust-your-developer-instinct-and.html' title='Trust your developer instinct and experience'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-2254629423609347758</id><published>2012-03-14T22:11:00.000+00:00</published><updated>2013-03-13T23:17:53.284+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Ordnance Survey"/><category scheme="http://www.blogger.com/atom/ns#" term="spatial"/><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>Ordnance Survey Data Importer Coordinate Conversion Accuracy</title><content type='html'>&lt;div style=&quot;background-color:#FFE97F; border: 1px solid #FFD800&quot;&gt;&lt;b&gt;Update 13 March 2013:&lt;/b&gt; Please see latest blog post (fix) on this &lt;a href=&quot;http://www.adathedev.co.uk/2013/03/gb-post-code-importer-conversion.html&quot;&gt;here&lt;/a&gt;. &lt;/div&gt;&lt;div class=&quot;post&quot;&gt;&lt;p&gt;Thanks to a &lt;a href=&quot;http://www.adathedev.co.uk/2011/01/gb-post-code-geographic-data-load-to.html?showComment=1331734250799#c7081796813185142076&quot; target=&quot;_new&quot;&gt;comment on my original post&lt;/a&gt; around my project that imports Ordnance Survey CodePoint data into SQL Server, I was made aware of a potential issue with the (awesome) third party &lt;a href=&quot;http://code.google.com/p/geocoordconversion/&quot; target=&quot;_new&quot;&gt;GeoCoordConversion&lt;/a&gt; DLL I use to convert the Eastings/Northings coordinates supplied in the Ordnance Survey data files, into Latitude/Longitude coordinates. The &lt;a href=&quot;http://code.google.com/p/geocoordconversion/issues/detail?id=2&quot; target=&quot;_new&quot;&gt;issue&lt;/a&gt; relates to an inaccuracy in the conversion process, specifically to do with integer divisions instead of double. &lt;/p&gt;&lt;p&gt;So, this blog post is to document my analysis of that and try to quantify what, if any, impact there is on the accuracy of the conversion of Eastings/Northings to Latitude/Longitude. So what I&#39;ve done is:  &lt;ol&gt;&lt;li&gt;Import the Ordnance Survey data set using the released version (1.0.0.0) of the &lt;a href=&quot;http://code.google.com/p/geocoordconversion/downloads/list&quot; target=&quot;_new&quot;&gt;GeoCoordConversion library&lt;/a&gt; (i.e. the version my project uses)&lt;/li&gt;&lt;li&gt;Downloaded the source code for GeoCoordConversion and corrected the parts of the calculations where the &lt;a href=&quot;http://code.google.com/p/geocoordconversion/issues/detail?id=2&quot; target=&quot;_new&quot;&gt;raised issue&lt;/a&gt; noted an inaccuracy&lt;/li&gt;&lt;li&gt;Reran the data import into a separate table using the &quot;corrected&quot; conversion code&lt;/li&gt;&lt;li&gt;Ran the following query to try and quantify how much of a difference it actually makes: &lt;/ol&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;;WITH CTEDiff AS &lt;br /&gt;(&lt;br /&gt;    SELECT orig.OutwardCode, orig.InwardCode,&lt;br /&gt;        orig.GeoLocation.STDistance(new.GeoLocation) Dist&lt;br /&gt;    FROM PostCodeData orig&lt;br /&gt; JOIN PostCodeDataWithFix new ON orig.InwardCode = new.InwardCode &lt;br /&gt;            AND orig.OutwardCode = new.OutwardCode&lt;br /&gt;)&lt;br /&gt;SELECT MIN(Dist) AS MinDifferenceInMetres, &lt;br /&gt;    MAX(Dist) AS MaxDifferenceInMetres, &lt;br /&gt;    AVG(Dist) AS AverageDifferenceInMetres, &lt;br /&gt;    SUM(CASE WHEN Dist = 0 THEN 0 ELSE 1 END) AS NumberOfDifferences&lt;br /&gt;FROM CTEDiff&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt; &lt;h4&gt;Results&lt;/h4&gt;The &lt;b&gt;&lt;u&gt;MAXIMUM&lt;/u&gt;&lt;/b&gt; distance between the 2 conversions for a given postcode is: &lt;b&gt;&lt;u&gt;124.9 metres&lt;/u&gt;&lt;/b&gt;.&lt;br/&gt;The &lt;b&gt;&lt;u&gt;MINIMUM&lt;/u&gt;&lt;/b&gt; distance between the 2 conversions for a given postcode is: &lt;b&gt;&lt;u&gt;0 metres&lt;/u&gt;&lt;/b&gt;.&lt;br/&gt;The &lt;b&gt;&lt;u&gt;AVERAGE&lt;/u&gt;&lt;/b&gt; distance between the 2 conversions for a given postcode is: &lt;b&gt;&lt;u&gt;2.5 metres&lt;/u&gt;&lt;/b&gt;.&lt;br/&gt;The &lt;b&gt;&lt;u&gt;TOTAL NUMBER&lt;/u&gt;&lt;/b&gt; of differences was 1,652,458 (out of 1,705,177 conversions).  &lt;p&gt;This map shows the case that had the biggest difference (postcode: NR34 8JW). Plotted points are as follows:&lt;br/&gt;&lt;b&gt;(A)&lt;/b&gt; = position from conversion process before fix&lt;br/&gt;&lt;b&gt;(B)&lt;/b&gt; = position from conversion process with fix&lt;br/&gt;&lt;b&gt;(C)&lt;/b&gt; = position Google Maps gives for location&lt;br/&gt; Note &lt;b&gt;(B)&lt;/b&gt; and &lt;b&gt;(C)&lt;/b&gt; are in the same spot, so that&#39;s why you can&#39;t see both! &lt;/p&gt;&lt;iframe width=&quot;425&quot; height=&quot;350&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://www.google.com/maps?f=d&amp;amp;source=s_d&amp;amp;saddr=52.44527%091.53&amp;amp;daddr=52.44528%091.528163+to:NR34%098JW&amp;amp;hl=en&amp;amp;geocode=FVZAIAMdkFgXAA%3BFWBAIAMdY1EXAA%3BFWFAIAMdY1EXAClRsJ1xffbZRzGcjYe25cwC0g&amp;amp;aq=&amp;amp;sll=52.445281,1.528163&amp;amp;sspn=0.014831,0.042272&amp;amp;g=NR34%098JW&amp;amp;mra=ls&amp;amp;ie=UTF8&amp;amp;t=m&amp;amp;ll=52.445312,1.528924&amp;amp;spn=0.001144,0.00228&amp;amp;z=18&amp;amp;output=embed&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href=&quot;http://www.google.com/maps?f=d&amp;amp;source=embed&amp;amp;saddr=52.44527%091.53&amp;amp;daddr=52.44528%091.528163+to:NR34%098JW&amp;amp;hl=en&amp;amp;geocode=FVZAIAMdkFgXAA%3BFWBAIAMdY1EXAA%3BFWFAIAMdY1EXAClRsJ1xffbZRzGcjYe25cwC0g&amp;amp;aq=&amp;amp;sll=52.445281,1.528163&amp;amp;sspn=0.014831,0.042272&amp;amp;g=NR34%098JW&amp;amp;mra=ls&amp;amp;ie=UTF8&amp;amp;t=m&amp;amp;ll=52.445312,1.528924&amp;amp;spn=0.001144,0.00228&amp;amp;z=18&quot; style=&quot;color:#0000FF;text-align:left&quot;&gt;View Larger Map&lt;/a&gt;&lt;/small&gt; &lt;h4&gt;Summary&lt;/h4&gt;For me, and what I see as potential uses for this data, I personally don&#39;t see this as much of an issue especially with an average difference of 2.5 metres.  I will add a comment on to the GeoCoordConversion project issue page, referencing this post. Though there doesn&#39;t look to be any activity on the project, so whether a fix can be applied is yet to be seen. That&#39;s my preferred route at present, to see if the author of that project is around to apply a fix as I currently don&#39;t have full knowledge of what I changes I can make to the source code and what would be acceptable in order to distribute the amended source code as part of my project (my project is released under a different, but compatible license...but things might get murkier when it comes to amending/distributing those amendments). In a nutshell, I just don&#39;t have time to try to make head and tail of what I can legitimately do (and I don&#39;t want to change the license for my project)! &lt;p&gt;All feedback welcomed&lt;/p&gt; &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/2254629423609347758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/03/ordnance-survey-data-importer.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2254629423609347758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2254629423609347758'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/03/ordnance-survey-data-importer.html' title='Ordnance Survey Data Importer Coordinate Conversion Accuracy'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-2246119748705521439</id><published>2012-03-05T09:30:00.000+00:00</published><updated>2012-03-05T09:30:22.765+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="performance"/><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>Quick win - check your table variable use</title><content type='html'>&lt;div class=&quot;post&quot;&gt;&lt;p&gt;Quick wins are awesome. Making a change that takes minimal effort and yields a significant performance improvement is very satisfying. &lt;/p&gt;&lt;p&gt;This particular potential quick win relates to the use of table variables vs. temporary tables. Have a non-trivial stored procedure that produces some intermediary results and stores in a table variable which then goes on to be used further in the stored procedure? Consider evaluating a switch to a temporary table instead. This very quick change, can give a great performance boost for such little effort due to the points outlined on &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms175010.aspx&quot; target=&quot;_new&quot;&gt;MSDN&lt;/a&gt; (quote):  &lt;blockquote&gt;table variables are not supported in the SQL Server optimizer&#39;s cost-based reasoning model. Therefore, they should not be used when cost-based choices are required to achieve an efficient query plan. Temporary tables are preferred when cost-based choices are required. This typically includes queries with joins, parallelism decisions, and index selection choices.&lt;/blockquote&gt;&lt;blockquote&gt;Queries that modify table variables do not generate parallel query execution plans. Performance can be affected when very large table variables, or table variables in complex queries, are modified. In these situations, consider using temporary tables instead......Queries that read table variables without modifying them can still be parallelized. &lt;/blockquote&gt;&lt;blockquote&gt;Indexes cannot be created explicitly on table variables, and no statistics are kept on table variables. In some cases, performance may improve by using temporary tables instead, which support indexes and statistics.&lt;/blockquote&gt; I&#39;ve seen cases where &lt;b&gt;&lt;i&gt;this alone&lt;/i&gt;&lt;/b&gt;, with no other modifications, results in noticeable better performance. This could then be built on further by adding supporting indices. Of course, you should be considering whether you need temporary tables/table variables at all in your stored procedure. If you can remove the need entirely, then look to do that. &lt;/p&gt;Further reading: &lt;br/&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://blogs.msdn.com/b/sqlserverstorageengine/archive/2008/03/30/sql-server-table-variable-vs-local-temporary-table.aspx&quot; target=&quot;_new&quot;&gt;TempDB : Table variable vs local temporary table&lt;/a&gt; by Sunil Agarwal over on the SQL Server Storage Engine MSDN blog.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms175010.aspx&quot; target=&quot;_new&quot;&gt;TABLE reference on MSDN&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/2246119748705521439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/03/quick-win-check-your-table-variable-use.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2246119748705521439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2246119748705521439'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/03/quick-win-check-your-table-variable-use.html' title='Quick win - check your table variable use'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-2813185862524157987</id><published>2012-02-20T21:36:00.000+00:00</published><updated>2012-02-20T21:36:38.624+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="performance selenium browsermob .NET"/><title type='text'>Automating Web Performance Stats Collection in .NET</title><content type='html'>&lt;div class=&quot;post&quot;&gt;You have a web application. You&#39;re a .NET developer. Maybe you already have some automated UI testing in place via &lt;a href=&quot;http://seleniumhq.org/&quot; target=&quot;_new&quot;&gt;Selenium&lt;/a&gt;, or maybe you don&#39;t. What you want to do is automate the collection of some performance metrics about your application.&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Q.&lt;/b&gt; How would you go about doing that in .NET?&lt;br/&gt;&lt;b&gt;A.&lt;/b&gt; Use the following recipe for success. &lt;br/&gt;&lt;h4&gt;Ingredients&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://opensource.webmetrics.com/browsermob-proxy/&quot; target=&quot;_new&quot;&gt;BrowserMob Proxy&lt;/a&gt; by Webmetrics, which (quote) is: &lt;blockquote&gt;A free utility to help web developers watch and manipulate network traffic from their web applications&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://seleniumhq.org/&quot; target=&quot;_new&quot;&gt;Selenium&lt;/a&gt;, which (quote): &lt;blockquote&gt;automates browsers. That&#39;s it.&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/AutomatedTester/AutomatedTester.BrowserMob&quot; target=&quot;_new&quot;&gt;BrowserMob Proxy .NET Library&lt;/a&gt;, a .NET library to provide a simple way to work with BrowserMob Proxy and Selenium, written by David Burns (&lt;a href=&quot;http://www.theautomatedtester.co.uk&quot; target=&quot;_new&quot;&gt;blog&lt;/a&gt; | &lt;a href=&quot;http://twitter.com/automatedtester&quot; target=&quot;_new&quot;&gt;twitter&lt;/a&gt;) and myself (you&#39;re already on my blog | &lt;a href=&quot;http://twitter.com/adathedev&quot; target=&quot;_new&quot;&gt;twitter&lt;/a&gt;). &lt;/ul&gt; &lt;h4&gt;Preparation&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Download the BrowserMob Proxy from &lt;a href=&quot;https://github.com/webmetrics/browsermob-proxy/downloads&quot; target=&quot;_new&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Download the BrowserMob Proxy .NET Library from GitHub (&lt;a href=&quot;https://github.com/AutomatedTester/AutomatedTester.BrowserMob/downloads&quot; target=&quot;_new&quot;&gt;binaries&lt;/a&gt;, or get the source and build yourself)&lt;/li&gt;&lt;li&gt;Reference Selenium in your .NET project (available via nuget, or from &lt;a href=&quot;http://seleniumhq.org/download/&quot;&gt;seleniumhq&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Reference the BrowserMob Proxy .NET Library in your .NET project&lt;/li&gt;&lt;/ol&gt; &lt;h4&gt;Make and Bake&lt;/h4&gt;Example: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;using AutomatedTester.BrowserMob.HAR;&lt;br /&gt;using OpenQA.Selenium;&lt;br /&gt;using OpenQA.Selenium.Firefox;&lt;br /&gt;&lt;br /&gt;namespace AutomatedTester.BrowserMob.Example&lt;br /&gt;{&lt;br /&gt;    public class ExampleClass&lt;br /&gt;    {&lt;br /&gt;        public void ExampleUse()&lt;br /&gt;        {&lt;br /&gt;            // Supply the path to the Browsermob Proxy batch file&lt;br /&gt;            Server server = new Server(@&quot;C:\BrowserMobProxy\bin\browsermob-proxy.bat&quot;);&lt;br /&gt;            server.Start();&lt;br /&gt;&lt;br /&gt;            Client client = server.CreateProxy();&lt;br /&gt;            client.NewHar(&quot;google&quot;);&lt;br /&gt;&lt;br /&gt;            var seleniumProxy = new Proxy { HttpProxy = client.SeleniumProxy };&lt;br /&gt;            var profile = new FirefoxProfile();&lt;br /&gt;            &lt;br /&gt;            profile.SetProxyPreferences(seleniumProxy);&lt;br /&gt;            // Navigate to the page to retrieve performance stats for&lt;br /&gt;            IWebDriver driver = new FirefoxDriver(profile);&lt;br /&gt;            driver.Navigate().GoToUrl(&quot;http://www.google.co.uk&quot;);&lt;br /&gt;&lt;br /&gt;            // Get the performance stats&lt;br /&gt;            HarResult harData = client.GetHar();           &lt;br /&gt; &lt;br /&gt;            // Do whatever you want with the metrics here. Easy to persist &lt;br /&gt;            // out to a data store for ongoing metrics over time.&lt;br /&gt;            &lt;br /&gt;            driver.Quit();&lt;br /&gt;            client.Close();&lt;br /&gt;            server.Stop();&lt;br /&gt;        }   &lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; What&#39;s great is that if you already have some Selenium tests in place, you can add in the collection of performance metrics quickly and easily. This gives you the ability to collate performance metrics over time - a perfect way to identify problem areas to investigate and to quantify performance improvements you make.  To learn more about what is in the performance data, check out these links which go into more detail about the HAR format (HTTP Archive) - this is what Webmetric&#39;s BrowserMob Proxy returns, which we expand out into a POCO structure (HarResult type). &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://groups.google.com/group/http-archive-specification&quot; target=&quot;_new&quot;&gt;HTTP Archive specification google group&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.softwareishard.com/blog/har-12-spec/&quot; target=&quot;_new&quot;&gt;HAR 1.2 Spec&lt;/a&gt;&lt;/ul&gt; &lt;a href=&quot;http://opensource.webmetrics.com/browsermob-proxy/&quot; target=&quot;_new&quot;&gt;BrowserMob Proxy&lt;/a&gt; allows you to do some pretty funky stuff, such as: &lt;ul&gt;&lt;li&gt;blacklisting / whitelisting content&lt;/li&gt;&lt;li&gt;simulate network traffic / latency&lt;/li&gt;&lt;/ul&gt;The .NET library wrapper we&#39;ve made available supports this functionality.  Hopefully, this will come in useful for those in the .NET world! &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/2813185862524157987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/02/automating-web-performance-stats.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2813185862524157987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/2813185862524157987'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/02/automating-web-performance-stats.html' title='Automating Web Performance Stats Collection in .NET'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-1678938059227823700</id><published>2012-01-16T10:36:00.000+00:00</published><updated>2012-01-16T10:36:01.832+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><category scheme="http://www.blogger.com/atom/ns#" term="TSQL"/><category scheme="http://www.blogger.com/atom/ns#" term="XML"/><title type='text'>Excluding nodes from XML data before returning from SQL Server</title><content type='html'>&lt;div class=&quot;post&quot;&gt;This post follows on from a question I recently replied to, for how to exclude a specific node from an XML column value before returning it, using TSQL. &lt;h4&gt;Example setup&lt;/h4&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;CREATE TABLE Example&lt;br /&gt;(&lt;br /&gt;ID INTEGER IDENTITY(1,1) PRIMARY KEY,&lt;br /&gt;XmlField XML&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;INSERT Example (XmlField) VALUES (&#39;&amp;lt;Root&amp;gt;&amp;lt;ChildA&amp;gt;Value I want to see&amp;lt;/ChildA&amp;gt;&amp;lt;ChildB&amp;gt;Value I do not want to see&amp;lt;/ChildB&amp;gt;&amp;lt;/Root&amp;gt;&#39;)&lt;br /&gt;&lt;/pre&gt; So if you want to return the XML minus the &lt;b&gt;ChildB&lt;/b&gt; node, how do you do it? &lt;h4&gt;Modify it&lt;/h4&gt;Literally, using the &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms187093.aspx&quot;&gt;modify&lt;/a&gt; method that is supported on the XML data type. To quote MSDN, modify:  &lt;blockquote&gt;Modifies the contents of an XML document. Use this method to modify the content of an xml type variable or column. This method takes an XML DML statement to insert, update, or delete nodes from XML data. The modify() method of the xml data type can only be used in the SET clause of an UPDATE statement.&lt;/blockquote&gt; That last statement, to me at least, seems slightly open for misinterpretation as strictly speaking it isn&#39;t only valid in an UPDATE statement - you can use it as part of a standalone SET statement on an XML variable (though yes, to UPDATE that variable). &lt;br/&gt;&lt;br/&gt;So, while you &lt;u&gt;&lt;b&gt;can&#39;t&lt;/b&gt;&lt;/u&gt; do something like this: &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;SELECT XmlField.modify(&#39;delete /Root/ChildB&#39;)&lt;br /&gt;FROM Example&lt;br /&gt;WHERE ID = 1&lt;br /&gt;&lt;/pre&gt;as it results in the following error: &lt;pre class=&quot;error&quot;&gt;&lt;br /&gt;Msg 8137, Level 16, State 1, Line 1&lt;br /&gt;Incorrect use of the XML data type method &#39;modify&#39;. A non-mutator method is expected in this context.&lt;br /&gt;&lt;/pre&gt; you can instead do this:  &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;DECLARE @xml XML&lt;br /&gt;SELECT @xml = XmlField&lt;br /&gt;FROM Example&lt;br /&gt;WHERE ID = 1&lt;br /&gt;&lt;br /&gt;-- Delete the node from the XML in the variable&lt;br /&gt;SET @xml.modify(&#39;delete /Root/ChildB&#39;)&lt;br /&gt;&lt;br /&gt;SELECT @xml&lt;br /&gt;&lt;/pre&gt; That does the trick. &lt;h4&gt;Dealing with multiple rows&lt;/h4&gt;If you wanted to do something like this from the database side when returning multiple rows then you&#39;d need to either: &lt;ol&gt;&lt;li&gt;create a scalar UDF to perform the removal. e.g. &lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;CREATE FUNCTION dbo.RemoveNode(@xml XML)&lt;br /&gt; RETURNS XML&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt; SET @xml.modify(&#39;delete /Root/ChildB&#39;)&lt;br /&gt; RETURN @xml&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;SELECT dbo.RemoveNode(XmlField) AS XmlField&lt;br /&gt;FROM Example&lt;br /&gt;&lt;/pre&gt;Note the parameter to the modify method must be a literal string, so you can&#39;t parameterise the UDF to make it a bit more generic by using that parameter in the call to modify(). &lt;br/&gt;&lt;br/&gt;&lt;/li&gt;&lt;li&gt;SELECT the xml values into a temp table/table variable, do an UPDATE like this:&lt;br/&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;UPDATE #Temp&lt;br /&gt;SET XmlField.modify(&#39;delete /Root/ChildB&#39;)&lt;br /&gt;&lt;/pre&gt;and then SELECT from the temp table/table variable.&lt;br/&gt;&lt;br/&gt;&lt;/li&gt;&lt;li&gt;Use SQLCLR&lt;/li&gt;&lt;/ol&gt; Of course, the typical question should be asked - do you need to do this on the database side, or can you return the XML as-is from SQL Server and modify it accordingly in your calling code? In the original case acting on a single XML value, it&#39;s OK but when you start looking into scalar UDF&#39;s etc for multi-row requirements then you should be careful to check performance. &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/1678938059227823700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/01/modifying-xml-data-before-returning.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1678938059227823700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/1678938059227823700'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/01/modifying-xml-data-before-returning.html' title='Excluding nodes from XML data before returning from SQL Server'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-3821401087892466110</id><published>2012-01-02T22:32:00.000+00:00</published><updated>2012-01-03T10:31:23.197+00:00</updated><title type='text'>Round up of 2011</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Another year is done and dusted so it&#39;s that time again - time to reflect on the past year. &lt;br/&gt;&lt;br/&gt;&lt;b&gt;Quick Timeline&lt;/b&gt;&lt;br/&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;March: &lt;a href=&quot;http://skillsmatter.com/event-details/nosql/mongouk-2011&quot; target=&quot;_new&quot;&gt;Mongo UK&lt;/a&gt; - London&lt;/b&gt;&lt;br/&gt;A chance to attend my first NOSQL conference, having recently entered in the world of MongoDB for some projects. Picked up some good pointers and food-for-thought.&lt;br/&gt;&lt;i&gt;&lt;b&gt;Just some words: &lt;/b&gt;NOSQL, hard seats, gonna-need-a-bigger-venue, enlightening.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;April: &lt;a href=&quot;http://sqlbits.com/events/event8/SQLBitsVIII.aspx&quot; target=&quot;_new&quot;&gt;SQLBits 8 - Beside The Seaside&lt;/a&gt; - Brighton&lt;/b&gt;&lt;br/&gt;  Highlight of the year for me, conference-wise (was unable to make SQLBits 9 - Liverpool). Fantastic venue, truly awesome speakers/technical content and a really great crowd of people - a true community. Out of all conferences I&#39;ve gone to over the years, SQLBits never fails to stand out and this one took things to a whole new level.&lt;br/&gt;&lt;i&gt;&lt;b&gt;Just some words: &lt;/b&gt;Crappy code, community, awesome, invaluable, bean bags.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;April: Microsoft Community Contributor Award&lt;/b&gt;&lt;br/&gt;I really enjoy chipping in on sites like StackOverflow and the MSDN forums, and this was a nice (humbling) surprise when I got the email to say I&#39;d received this acknowledgement (&lt;a href=&quot;http://www.adathedev.co.uk/2011/04/microsoft-community-contributor-award.html&quot; target=&quot;_new&quot;&gt;blog&lt;/a&gt;).&lt;br/&gt;&lt;i&gt;&lt;b&gt;Just some words: &lt;/b&gt;Surprise&lt;/i&gt;&lt;li&gt;&lt;b&gt;July: Job Change&lt;/b&gt;&lt;br/&gt;After a great 4 and a half years, it was time to move on to a new role. Took the opportunity to work with fellow SQLSoton attendee Matt Whitfield (&lt;a href=&quot;http://twitter.com/atlantis_uk&quot; target=&quot;_new&quot;&gt;twitter&lt;/a&gt;) - who by the way, has some great (&lt;u&gt;FREE&lt;/u&gt;) SQL Server tools which can be found &lt;a href=&quot;http://www.atlantis-interactive.co.uk/&quot; target=&quot;_new&quot;&gt;here&lt;/a&gt;.&lt;br/&gt;&lt;b&gt;&lt;i&gt;Just some words: &lt;/b&gt;New challenge, acquisition, end of an era.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;September: &lt;a href=&quot;http://www.10gen.com/events/mongouk-sept-2011&quot; target=&quot;_new&quot;&gt;Mongo UK&lt;/a&gt; - London&lt;/b&gt;&lt;br/&gt;A chance to brush up on the latest in the world of MongoDB after a few months away from it.&lt;br/&gt;&lt;i&gt;&lt;b&gt;Just some words: &lt;/b&gt;NOSQL, comfy seats, information gems, lego man USB stick.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Monthly: SQLSoton User Group&lt;/b&gt;&lt;br/&gt;A really good year of UserGroup meets down here in Southampton every month. Thanks to Mark Pryce-Maher (&lt;a href=&quot;http://twitter.com/tsqltidy&quot; target=&quot;_new&quot;&gt;twitter&lt;/a&gt;) for his time and effort, and all the speakers who have taken the time to give a talk. From the first meeting just over a year a go - a small group, in a tiny side room, with no projector - to now - a larger crowd, in a bigger hall, with a projector - it&#39;s obvious SQLSoton is providing a valuable SQL Server community service to the Southampton/South Coast area. Check out all the UK SQL Server UserGroups over at &lt;a href=&quot;http://sqlserverfaq.com/&quot; target=&quot;_new&quot;&gt;SQLServerFAQ&lt;/a&gt;.&lt;br/&gt;&lt;i&gt;&lt;b&gt;Just some words: &lt;/b&gt;Community, pizza, swag, network.&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br/&gt;&lt;b&gt;Top 3 Blog Posts By Views&lt;/b&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2010/02/sqlbulkcopy-bulk-load-to-sql-server.html&quot; target=&quot;_new&quot;&gt;High Performance Bulk Loading to SQL Server Using SqlBulkCopy&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2010/02/sql-server-2008-table-valued-parameters.html&quot; target=&quot;_new&quot;&gt;SQL Server 2008 - Table Valued Parameters vs XML vs CSV&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2010/07/getting-started-with-cassandra-and-net.html&quot; target=&quot;_new&quot;&gt;Getting Started With Cassandra and .NET&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2011/01/gb-post-code-geographic-data-load-to.html&quot; target=&quot;_new&quot;&gt;Ordnance Survey Post Code Geographic Data Load to SQL Server Using .NET&lt;/a&gt;*&lt;/li&gt;&lt;/ol&gt;* OK, so that&#39;s the top 4, not the top 3. But that&#39;s because I suspect #3 is actually skewed by people looking for &quot;an entirely different kind of site&quot;...I know I had to be careful which links to click when googling for Cassandra (as in the NOSQL database, obviously) and .NET! &lt;br/&gt;&lt;br/&gt;&lt;b&gt;Open Source Projects&lt;/b&gt;&lt;br/&gt;I started on a few little side projects, which I stuck up on to &lt;a href=&quot;https://github.com/AdaTheDev&quot; target=&quot;_new&quot;&gt;GitHub&lt;/a&gt; for the world to see and (hopefully) use. First came a .NET app to load some of the freely available geographical datasets from Ordnance Survey into SQL Server (&lt;a href=&quot;https://github.com/AdaTheDev/Ordnance-Survey-Code-Point-Data-Importer&quot; target=&quot;_new&quot;&gt;GitHub&lt;/a&gt;) which relates to blog post #4 above. Next came a MongoDB-backed ASP.NET session state store (&lt;a href=&quot;https://github.com/AdaTheDev/MongoDB-ASP.NET-Session-State-Store&quot; target=&quot;_new&quot;&gt;GitHub&lt;/a&gt;). It was a lot of fun working on these, even though they are pretty trivial things, and was great to actually get some nice feedback from people who found and started using them. &lt;br/&gt;&lt;br/&gt;&lt;b&gt;Bottom Line&lt;/b&gt;&lt;br/&gt;2011 was a good year. A lot of learning and new challenges meant I was kept on my toes. Looking back though, there are definitely things I&#39;m not pleased about. I dropped the blogging ball in the second half of the year - activity pretty much flatlined for various reasons. So I&#39;m disappointed about that. To &lt;a href=&quot;https://twitter.com/#!/JohnSansom/status/150141809920704512&quot; target=&quot;_new&quot;&gt;quote a tweet&lt;/a&gt; by John Sansom (&lt;a href=&quot;http://twitter.com/johnsansom&quot; target=&quot;_new&quot;&gt;twitter&lt;/a&gt;)... &lt;blockquote&gt;blogging is like going to the gym. Once you fall out of a routine it&#39;s tougher to get back into your rhythm&lt;/blockquote&gt;True that.  Looking forward to 2012! &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/3821401087892466110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2012/01/round-up-of-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3821401087892466110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3821401087892466110'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2012/01/round-up-of-2011.html' title='Round up of 2011'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-4549262424275753860</id><published>2011-09-15T21:44:00.000+01:00</published><updated>2011-09-15T21:45:00.879+01:00</updated><title type='text'>OS CodePoint Data Geography Update Sept 2011</title><content type='html'>&lt;div class=&quot;post&quot;&gt;&lt;b&gt;History:&lt;/b&gt;&lt;br/&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2011/01/gb-post-code-geographic-data-load-to.html&quot; target=&quot;_new&quot;&gt;GB Post Code Geographic Data Load to SQL Server using .NET&lt;/a&gt;&lt;br/&gt;&lt;a href=&quot;http://www.adathedev.co.uk/2011/02/os-codepoint-data-geography-load-update.html&quot; target=&quot;_new&quot;&gt;OS CodePoint Data Geography Load Update&lt;/a&gt;&lt;p&gt;Following a comment on my original post listed above, it appears the structure of the CodePoint data file has changed. This means that the importer will fail when attempting to import the latest data files supplied by Ordnance Survey as the columns in the files have changed. &lt;/p&gt;&lt;p&gt;I&#39;ve pushed an update to the &lt;a href=&quot;http://github.com/AdaTheDev/Ordnance-Survey-Code-Point-Data-Importer&quot; target=&quot;_new&quot;&gt;project on GitHub&lt;/a&gt; to address this - it now takes a sensible approach so the index positions are no longer hard coded. Instead, the CodePoint importer now takes an extra argument - the path to the CSV file that contains the column header listing as supplied in the supporting docs in the download. The column indices we are interested in (for Easting, Northing and PostCode) are worked out from this. Obviously makes it a bit more future proof, so if the structure changes again in the future, it should pick up on that automatically. &lt;/p&gt;&lt;p&gt;This is an update to the CodePoint import only at the moment as I haven&#39;t had time to check the Scale Gazetteer import as yet. See the project/readme on GitHub for more info.&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/4549262424275753860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2011/09/os-codepoint-data-geography-update-sept.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4549262424275753860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/4549262424275753860'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2011/09/os-codepoint-data-geography-update-sept.html' title='OS CodePoint Data Geography Update Sept 2011'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-6228557977473291416</id><published>2011-08-18T21:44:00.001+01:00</published><updated>2011-08-18T21:45:58.938+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="nosql"/><category scheme="http://www.blogger.com/atom/ns#" term="Riak"/><title type='text'>Getting Started with Riak and .NET</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Earlier in the year, I started playing around with MongoDB using .NET and wrote up a &lt;a href=&quot;http://www.adathedev.co.uk/2011/01/getting-started-with-mongodb-and-net.html&quot; target=&quot;_new&quot;&gt;Getting Started&lt;/a&gt; guide. Now it&#39;s Riak&#39;s turn.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Nutshell&lt;/b&gt;&lt;br /&gt;Riak is a distributed, schemaless, data-type agnostic key-value &quot;NoSQL&quot; database written primarily in Erlang.&lt;br /&gt;&lt;br /&gt;Check out the &quot;&lt;a href=&quot;http://wiki.basho.com/What-is-Riak%3F.html&quot; target=&quot;_new&quot;&gt;What is Riak?&lt;/a&gt;&quot; page on Basho&#39;s wiki.&lt;br /&gt;&lt;br /&gt;Riak does not run on Windows so you&#39;ll need to choose your supported OS of choice to install Riak on. Having used Ubuntu before for MongoDB, I went down that route so my notes here are oriented that way. So, after getting a Ubuntu VM set up...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 1&lt;/b&gt;&lt;br /&gt;Install the latest custom .deb package, as per the &lt;a href=&quot;http://wiki.basho.com/Installing-on-Debian-and-Ubuntu.html&quot; target=&quot;_new&quot;&gt;Basho guide&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 2&lt;/b&gt;&lt;br /&gt;Before you start Riak up, you need to tweak the configuration in Riak&#39;s app.config file. Follow the &lt;a href=&quot;http://wiki.basho.com/Basic-Cluster-Setup.html&quot; target=&quot;_new&quot;&gt;Basic Cluster Setup&lt;/a&gt; guide, but  note that when you update the IP address for the http interface per those instructions, also make the same change to the pb_ip config value - this is for the Protocol Buffers interface. For performance, I wanted to use the Protocol Buffers interface instead of the HTTP REST API and had some initial problems as the pb_ip configuration was still set to 127.0.0.1.&lt;br /&gt;&lt;br /&gt;You then need to edit vm.args, per the setup guide.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 3&lt;/b&gt;&lt;br /&gt;Now you&#39;re ready to fire up Riak. Run:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;$ riak start&lt;br /&gt;&lt;/pre&gt;Then make sure all is well using:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;$ riak ping&lt;br /&gt;&lt;/pre&gt;If all is well, it will respond with &quot;pong&quot;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 4&lt;/b&gt;&lt;br /&gt;Now, back in the comfort of Windows (my Linux-fu is seriously lacking) we&#39;re ready to get going. There is no official .NET client library at time of writing, but Basho list a few &lt;a href=&quot;http://wiki.basho.com/Client-Libraries.html&quot;&gt;community contributed projects&lt;/a&gt;. When I first looked, only 2 were listed and these appeared to be inactive with no updates since last year. It was a bit disappointing to see the lack of support for those of us in the .NET world. I then discovered &lt;a href=&quot;http://corrugatediron.org/&quot; target=&quot;_new&quot;&gt;CorrugatedIron&lt;/a&gt;, very hot-of-the-press, under development by OJ Reeves (&lt;a href=&quot;http://twitter.com/thecolonial&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt; | &lt;a href=&quot;http://buffered.io/&quot; target=&quot;_new&quot;&gt;Blog&lt;/a&gt;) and Jeremiah Peschka (&lt;a href=&quot;http://twitter.com/peschkaj&quot; target=&quot;_new&quot;&gt;Twitter&lt;/a&gt; | &lt;a href=&quot;http://facility9.com/&quot;&gt;Blog&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The simplest way to get everything you need to get going is via NuGet.&lt;br /&gt;&lt;br /&gt;Nu-what?&lt;br /&gt;&lt;br /&gt;NuGet. It&#39;s a Visual Studio extension that you can download from &lt;a href=&quot;http://nuget.org/&quot;&gt;here&lt;/a&gt;. Once installed, fire up VS and create a new .NET 4.0 Framework project. Then in the Solution Explorer, right click the project and select &quot;Manage NuGet Packages...&quot;.&lt;br /&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/-43q2u5SeTS8/Tk1tuz2PKPI/AAAAAAAAAV0/ODm7OvIWBWs/s800/ManageNuGet.png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;In the dialog, search online packages for: CorrugatedIron.&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/-KcUWXidnH6k/Tk1ujytAvRI/AAAAAAAAAV8/SvpHGgAuuNM/s800/CorrugatedIron.png&quot;/&gt;&lt;br /&gt;&lt;br /&gt;Click Install, and it will download all the assemblies/dependencies you need. When it&#39;s finished, you&#39;ll see you have references to CorrugatedIron, Newtonsoft.Json and protobuf-net.&lt;br /&gt;&lt;br /&gt;Open up app.config and you&#39;ll see some initial example config for CorrugatedIron. Configure that accordingly to point at your running Riak node. For example, my Riak node is running on a VM called &quot;Riak1&quot;, with the REST interface listening on port 8098 and the protocol buffers interface listening on port 8087:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;  &amp;lt;configSections&amp;gt;&lt;br /&gt;    &amp;lt;section name=&quot;riakConfig&quot; type=&quot;CorrugatedIron.Config.RiakClusterConfiguration, CorrugatedIron&quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/configSections&amp;gt;&lt;br /&gt;  &amp;lt;riakConfig nodePollTime=&quot;5000&quot; defaultRetryWaitTime=&quot;200&quot; defaultRetryCount=&quot;3&quot;&amp;gt;&lt;br /&gt;    &amp;lt;nodes&amp;gt;&lt;br /&gt;      &amp;lt;node name=&quot;Riak1&quot; hostAddress=&quot;Riak1&quot; pbcPort=&quot;8087&quot; restScheme=&quot;http&quot; restPort=&quot;8098&quot; poolSize=&quot;20&quot; /&amp;gt;      &lt;br /&gt;    &amp;lt;/nodes&amp;gt;&lt;br /&gt;  &amp;lt;/riakConfig&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Step 5&lt;/b&gt;&lt;br /&gt;A quick check to make sure you can communicate with your Riak node:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;    IRiakCluster cluster = RiakCluster.FromConfig(&quot;riakConfig&quot;);&lt;br /&gt;    IRiakClient client = cluster.CreateClient();&lt;br /&gt;    RiakResult result = client.Ping();&lt;br /&gt;    Console.WriteLine(result.IsSuccess);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Fingers crossed, and you will get a successful response! From here, you can start playing around with Riak from .NET and find your way round the client.&lt;br /&gt;&lt;br /&gt;CorrugatedIron is still very new, so keep an eye on the &lt;a href=&quot;http://corrugatediron.org/&quot; target=&quot;_new&quot;&gt;site&lt;/a&gt; and/or on the &lt;a href=&quot;https://github.com/DistributedNonsense/CorrugatedIron&quot; target=&quot;_new&quot;&gt;GitHub repository&lt;/a&gt;. Give it a whirl, and be sure to let the guys know how you get on - I&#39;m sure they&#39;d be keen to hear from those using it and the project deserves support from those of us in the .NET world.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Useful tool&lt;/b&gt;&lt;br /&gt;It&#39;s useful to have some form of GUI over Riak so you can actually &quot;see stuff&quot;. I&#39;ve been using Rekon which I found useful to get up and running with Riak. See the download/install instructions on it&#39;s &lt;a href=&quot;https://github.com/basho/rekon&quot; target=&quot;_new&quot;&gt;GitHub repository page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Hopefully, this post will help anyone else wanting to give Riak a try from a .NET perspective!&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/6228557977473291416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2011/08/getting-started-with-riak-and-net.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/6228557977473291416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/6228557977473291416'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2011/08/getting-started-with-riak-and-net.html' title='Getting Started with Riak and .NET'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh4.googleusercontent.com/-43q2u5SeTS8/Tk1tuz2PKPI/AAAAAAAAAV0/ODm7OvIWBWs/s72-c/ManageNuGet.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-3457010554494897869</id><published>2011-06-27T20:28:00.000+01:00</published><updated>2011-06-27T20:28:12.605+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>sp_executesql change between 2005 and 2008</title><content type='html'>&lt;div class=&quot;post&quot;&gt;Today I tripped over what turned out to be a difference in the way sp_executesql behaves between SQL Server 2005 and 2008 when executing a string containing a parameterised stored procedure call.&lt;br /&gt;&lt;br /&gt;Take this simplified example:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;DECLARE @SQL NVARCHAR(256)&lt;br /&gt;SET @SQL = &#39;sp_help @obj&#39;&lt;br /&gt;EXECUTE sp_executesql @SQL, N&#39;@obj NVARCHAR(100)&#39;, &#39;sp_help&#39;&lt;/pre&gt;In SQL Server 2008 (10.0.4000.0), the above executes successfully.&lt;br /&gt;In SQL Server 2005 (9.00.1399.06), it throws the following exception:&lt;br /&gt;&lt;pre class=&quot;error&quot;&gt;Msg 102, Level 15, State 1, Line 1&lt;br /&gt;Incorrect syntax near &#39;sp_help&#39;.&lt;/pre&gt;Adding the &quot;EXEC(UTE)&quot; before the stored procedure name in @SQL resolves the issue in 2005. As standard, I (usually) standardise on ensuring all stored procedure calls are made with &quot;EXEC(UTE)&quot; even when it is the only statement in the batch. Obviously, in this case it was overlooked and tripped me up!&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre class=&quot;code&quot;&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/3457010554494897869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2011/06/spexecutesql-change-between-2005-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3457010554494897869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/3457010554494897869'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2011/06/spexecutesql-change-between-2005-and.html' title='sp_executesql change between 2005 and 2008'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7592516066850632747.post-5718442392417962451</id><published>2011-06-24T20:55:00.000+01:00</published><updated>2011-06-24T20:55:29.617+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MongoDB"/><category scheme="http://www.blogger.com/atom/ns#" term="performance"/><category scheme="http://www.blogger.com/atom/ns#" term="scalability"/><category scheme="http://www.blogger.com/atom/ns#" term="sql server"/><title type='text'>The importance of &quot;Working Set&quot;</title><content type='html'>&lt;div class=&quot;post&quot;&gt;One of the things that I see cropping up pretty often is this thing called &quot;working set&quot;. After recently chipping in on another StackOverflow question on the subject of &quot;&lt;a href=&quot;http://http://stackoverflow.com/questions/6453584/what-does-it-mean-to-fit-working-set-into-ram-for-mongodb&quot; target=&quot;_new&quot;&gt;What does it meant to fit &#39;working set&#39; in RAM?&lt;/a&gt;&quot;, I thought it was a good subject for a blog post. This is really just a copy and extension of my input on that question and focused in certain parts on MongoDB, but is also as relevant to other databases.&lt;br /&gt;&lt;br /&gt;&quot;Working set&quot; is basically the amount of data and indexes that will be active/in use by your system at any given time.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Why is it important to keep your working set in RAM?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Accessing RAM is quick. Accessing disk is slow. When querying your data store, if all the data and indexes typically accessed are in RAM, then performance is blisteringly quick. If it&#39;s not in RAM, then disk access is required and that is when performance suffers. Hence it is important to ensure you have enough to hold your working set. The moment your working set exceeds the about of RAM you have, you will start to notice the performance degradation as it has to pull stuff back off disk, so it&#39;s important to monitor the situation and react.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Crude Example&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Suppose you have 1 year&#39;s worth of data. For simplicity, each month relates to 1GB of data giving 12GB in total, and to cover each month&#39;s worth of data you have 1GB worth of indexes again totalling 12GB for the year.&lt;br /&gt;&lt;br /&gt;If you are always accessing the last 12 month&#39;s worth of data, then your working set is: 12GB (data) + 12GB (indexes) = 24GB.&lt;br /&gt;&lt;br /&gt;However, if you actually only access the last 3 month&#39;s worth of data, then your working set is: 3GB (data) + 3GB (indexes) = 6GB.&lt;br /&gt;&lt;br /&gt;You need to understand your data set, scenario and the usage patterns, in order to work out a ball park estimate of your working set. Don&#39;t expect a black and white answer for what your working set is in your environment, from someone who doesn&#39;t know these things.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What if my working set sky-rockets?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Add more RAM. This can be a case of adding more into your existing node(s). Or, if you need non-trivial increases, making use of sharding to split the data over a number of nodes and just bring more nodes online as you need. This provides incredible potential to scale out your workload.&lt;br /&gt;&lt;br /&gt;The key point is to ask yourself: do I have enough RAM for my working set? If the answer is: &quot;I don&#39;t know&quot;, then get yourself to the position of knowing.&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.adathedev.co.uk/feeds/5718442392417962451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.adathedev.co.uk/2011/06/importance-of-working-set.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/5718442392417962451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7592516066850632747/posts/default/5718442392417962451'/><link rel='alternate' type='text/html' href='http://www.adathedev.co.uk/2011/06/importance-of-working-set.html' title='The importance of &quot;Working Set&quot;'/><author><name>Adrian Hills</name><uri>http://www.blogger.com/profile/05471970588180455571</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//3.bp.blogspot.com/-pQ8OZ7oOBIU/XHkBx4BwbVI/AAAAAAAAOFs/p9YBjtADTTYIKbbgQAke__FEoEhm-o0eACK4BGAYYCw/s113/me.jpg'/></author><thr:total>0</thr:total></entry></feed>