<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Articles</title>
        <link>http://jasonfollas.com/blog/category/1.aspx</link>
        <description>Short articles written and published by Jason Follas</description>
        <language>en-US</language>
        <copyright>Jason Follas</copyright>
        <managingEditor>jason@jasonfollas.com</managingEditor>
        <generator>Subtext Version 2.0.0.43</generator>
        <item>
            <title>Windows Azure platform AppFabric Access Control: Introduction</title>
            <link>http://jasonfollas.com/blog/archive/2010/03/08/windows-azure-platform-appfabric-access-control-overview.aspx</link>
            <description>&lt;p&gt;  &lt;/p&gt;
&lt;p&gt;The &lt;a target="_blank" href="http://www.microsoft.com/windowsazure/appfabric/"&gt;Windows Azure platform AppFabric&lt;/a&gt; &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/ee732536.aspx"&gt;Access Control&lt;/a&gt; service was one aspect of the &lt;a target="_blank" href="http://www.microsoft.com/windowsazure/"&gt;Windows Azure&lt;/a&gt; platform that I found a bit challenging to understand, primarily because identity is not a domain that I regularly work with.  This post, the first in a planned series of articles, will explore what the Access Control service is and why it is useful.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;Three Geeks Walk Into a Bar…&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/WindowsAzureAccessControlService_11534/nightclub_access_control_8.png"&gt;&lt;img height="281" width="340" border="0" align="right" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/WindowsAzureAccessControlService_11534/nightclub_access_control_thumb_2.png" alt="nightclub_access_control" title="nightclub_access_control" style="border-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" /&gt;&lt;/a&gt;Let’s examine a common real-world scenario: Ordering a drink from a bartender at a nightclub.&lt;/p&gt;
&lt;p&gt;Before the nightclub opens, the bouncers inform the bartenders about the token that customers will bear on that night to indicate that they are of legal age.  This could be a stamp on the back of the hand, a colored wristband, etc.&lt;/p&gt;
&lt;p&gt;At the door, a customer will present their identification to the bouncer that shows, among other things, their date of birth.  This ID could be a state-issued driver's license, a passport, or even a school identification card.  The point is that the nightclub didn’t issue that ID, but they recognize the authority that did issue it, and will accept the claim (the birth date) that is displayed on that ID.&lt;/p&gt;
&lt;p&gt;If the bouncer determines that the ID is authentic and hasn’t been tampered with, then he will give the customer the token of the night (stamped hand or colored wristband), and the customer is free to enter the bar.&lt;/p&gt;
&lt;p&gt;Once inside, the customer only needs to show the token to the bartender in order to buy drinks.  They do not need to show their ID.&lt;/p&gt;
&lt;p&gt;The next night, the token will change, so a customer cannot use a token obtained the night before.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;Federated Access Control&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/WindowsAzureAccessControlService_11534/Azure_access_control_5.png"&gt;&lt;img height="189" width="340" border="0" align="right" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/WindowsAzureAccessControlService_11534/Azure_access_control_thumb_1.png" alt="Azure_access_control" title="Azure_access_control" style="border-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" /&gt;&lt;/a&gt; Now let’s look at a similar scenario: Calling a web service from an application.  Only, in this case, the web service should not fulfill requests from unauthorized clients.  Furthermore, it’s not the web service’s responsibility to authenticate the client; it is simply expecting the client to bear some verifiable proof that it is already authorized to use the service.&lt;/p&gt;
&lt;p&gt;Before calling the Web Service, the Service Consumer (application) must first obtain a token that is issued by an Access Control Service (ACS).  This is done by sending a number of claims to the ACS, with one of these claims being secrets belonging to an Issuer that the application is associated with.  &lt;/p&gt;
&lt;p&gt;If the Issuer is recognized and trusted by the ACS, then a token will be created.  This token (which is a collection of name/value pairs) will contain claims, an expiration date, and a signature that can be used to ensure that the token was not modified after it was created.&lt;/p&gt;
&lt;p&gt;Once the Service Consumer has a valid token, it can then call the Web Service and provide that token in a location where the service expects to find it, such as in a HTTP header.  The Web Service validates that the token is well-formed, has not expired, and has not been modified since it was created.  If anything fails validation, then the processing aborts.  Otherwise, the web service returns data to the Service Consumer, possibly using claims contained in the token as input in the process.&lt;/p&gt;
&lt;p&gt;This scenario is considered to be Federated because the ACS doesn’t actually maintain a list of usernames and passwords.  Instead, it maintains a list of trusted Issuers with the expectation is that the Issuer is responsible for authenticating its own users.  &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;Correlation&lt;/h3&gt;
&lt;p&gt;In these examples, the Web Service is analogous to the nightclub’s Bartender: it has something to provide to the Service Consumer (Customer), but the Service Consumer must present an appropriate token that is generated by the Access Control Service (Bouncer).&lt;/p&gt;
&lt;p&gt;The web service example above is intentionally vague in the part where a token is obtained.  There are a few different ways that an Issuer can be identified in a token request, and while passing the Issuer’s secret in plain text is one of those ways, it certainly shouldn’t be taken lightly.  Whoever has the Issuer’s key can spoof any of the claims, and that might prove to be a challenge for the service.  In the example where we need a Date of Birth claim to be presented, it would probably be a bad idea to allow the customer themselves to say “I’m from Ohio, here’s a blank driver's license that meets all of the standards of a proper ID, and, oh yeah… I’m writing on here that I am 21 years old.”  &lt;/p&gt;
&lt;p&gt;Instead, claims should originate from the Issuer in some way that cannot be tampered with by the application (using the assumption that the application itself should not be trusted).  With the plain text method, this might require having a separate service that runs within the Issuer’s domain and is aware of its users and also ACS.  This service would broker the ACS token request for the application, automatically providing any claim data that might be needed (like the Date of Birth) from the Issuer’s own user database.  The application would be provided with the same token, but would never have the Issuer’s secret that is required to obtain the token directly from ACS.&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/77.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2010/03/08/windows-azure-platform-appfabric-access-control-overview.aspx</guid>
            <pubDate>Tue, 09 Mar 2010 04:37:43 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/77.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2010/03/08/windows-azure-platform-appfabric-access-control-overview.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/77.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/77.aspx</trackback:ping>
        </item>
        <item>
            <title>You Need that Using Statement for Extension Methods!</title>
            <link>http://jasonfollas.com/blog/archive/2010/02/25/you-need-that-using-statement-for-extension-methods.aspx</link>
            <description>I was working through some &lt;a href="javascript:void(0);/*1267124492396*/"&gt;Windows Azure&lt;/a&gt; example code today, and came across a situation where IntelliSense did not show a method that the sample code used (&lt;span style="font-family: Courier New;"&gt;CreateCloudBlobClient()&lt;/span&gt;, in this case):&lt;br /&gt;
&lt;br /&gt;
&lt;pre type="c-sharp" name="code"&gt;var storageAccount = Microsoft.WindowsAzure.CloudStorageAccount.FromConfigurationSetting("DataConnectionString");&lt;br /&gt;var blobStorage = storageAccount.CreateCloudBlobClient();&lt;br /&gt;&lt;/pre&gt;
&lt;br /&gt;
A lot of times, when I'm exploring an API, I will type out the fully qualified class name in code so that I can use IntelliSense to see what the other members exist in the same namespace.  And, while I'm exploring, I usually don't bother to include a &lt;span style="font-family: Courier New;"&gt;using &lt;/span&gt;statement at the top of my code because it's so easy to just use the "Remove Type Qualifier" refactoring available in &lt;a target="_blank" href="http://devexpress.com/Products/Visual_Studio_Add-in/Coding_Assistance/"&gt;CodeRush&lt;/a&gt; to clean up the code when I'm ready to move on.&lt;br /&gt;
&lt;br /&gt;
Well, something that I guess I didn't remember soon enough was that &lt;span style="font-family: Courier New;"&gt;using &lt;/span&gt;statements are required in order to enable Extension Methods belonging to that namespace.  In this case, &lt;span style="font-family: Courier New;"&gt;CreateClousBlobClient() &lt;/span&gt;was not a method of&lt;span style="font-family: Courier New;"&gt; CloudStorageAccount&lt;/span&gt;, but rather an Extension Method (and this fact is not something that is very apparent when typing in sample code that you find in a document).&lt;br /&gt;
&lt;br /&gt;
IntelliSense showed that method (and more!) after adding the following line of code to the top of my file:&lt;br /&gt;
&lt;br /&gt;
&lt;pre type="c-sharp" name="code"&gt;using Microsoft.WindowsAzure.StorageClient;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/75.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2010/02/25/you-need-that-using-statement-for-extension-methods.aspx</guid>
            <pubDate>Thu, 25 Feb 2010 19:11:38 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2010/02/25/you-need-that-using-statement-for-extension-methods.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/75.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/75.aspx</trackback:ping>
        </item>
        <item>
            <title>Synchronizing Your Droid to Exchange Online (BPOS)</title>
            <link>http://jasonfollas.com/blog/archive/2009/11/08/synchronizing-your-droid-to-exchange-online-bpos.aspx</link>
            <description>&lt;p&gt;&lt;img alt="Motorola Droid - photo curtesy of Motorola" align="right" src="http://www.motorola.com/staticfiles/Consumers/Products/Mobile%20Phones/Motorola-SHOLES/_Images/_Staticfiles/B2C_DROID_Size.jpg" /&gt;Over the weekend, I bought a &lt;a target="_blank" href="http://www.motorola.com/Consumers/US-EN/Consumer-Product-and-Services/Mobile-Phones/ci.Motorola-DROID-US-EN.alt"&gt;Motorola Droid&lt;/a&gt;, a great new Android 2.0 phone on the Verizon network.  It's important to note that this is not only my first smart phone, but also my first hand-held device of this type (i.e., a PDA, etc), so I'm pretty stoked at the moment.&lt;/p&gt;
&lt;p&gt;The device synchronizes with Google applications extremely well (like GMail, Google Calendars, Google Voice, Contacts, etc).  Setting this up is just a matter of creating a new Account on the phone, providing your GMail account and password, and then everything else just magically syncs.  And, since I'm primarily a GMail user (who uses multiple Google Calendars to keep our hectic family schedule organized), this was a killer feature for me.&lt;/p&gt;
&lt;p&gt;But, I expect that this would be easy for Google content.  What about the real world?  What about synchronizing to an enterprise email and calendar, like Exchange?  And, since I'm a consultant, what about synchronizing to multiple Exchange servers at the same time?&lt;/p&gt;
&lt;p&gt;Well, I'm happy to report that there's an application for that... Or, rather, a couple of them: Email (note, not the GMail app) and Corporate Calendar.&lt;/p&gt;
&lt;p&gt;My company uses Lotus Notes for email.  Thankfully, though, our Microsoft Practice within the company has just started to use Exchange Online (as part of the &lt;a target="_blank" href="http://www.microsoft.com/online/trial-bpos.mspx"&gt;Microsoft Business Productivity Online Suite&lt;/a&gt;, or BPOS), so mail sent to my company account gets forwarded to the Exchange Online server.&lt;/p&gt;
&lt;p&gt;Configuring the Droid to synchronize with Exchange Online is probably not much different than synchronizing with any Exchange server.  It all starts with creating a new Account on the Droid:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;From the Settings application, touch "Accounts &amp;amp; sync".  &lt;/li&gt;
    &lt;li&gt;Touch the "Add account" button at the bottom&lt;/li&gt;
    &lt;li&gt;Choose "Corporate" as the type of account to create.&lt;/li&gt;
    &lt;li&gt;You'll be prompted for a Email Address and Password.  Enter the same email account and password that you use to log into BPOS, and then touch the "Next" button.&lt;/li&gt;
    &lt;li&gt;The next page is where it tried to be smart about what you entered on the previous page.  Basically, only the Password will be correct (at least in my case).&lt;/li&gt;
    &lt;li&gt;Replace the Domain\Username with the same email that you entered on the previous screen.  It's okay to have the backslash as the first character.&lt;/li&gt;
    &lt;li&gt;For the Exchange server, refer to the Mobile Device URLs for your region that are listed on the "&lt;a target="_blank" href="http://www.microsoft.com/online/help/en-us/bpos/html/c0a1a4b9-111f-4bd4-8fab-8147344cd278.htm"&gt;URLs for Microsoft Online Services&lt;/a&gt;" page.  For my account, I had to specify: &lt;strong&gt; &lt;/strong&gt;&lt;font face=""&gt;&lt;strong&gt;red001.mail.microsoftonline.com&lt;/strong&gt; &lt;/font&gt;&lt;/li&gt;
    &lt;li&gt;I also have "Use secure connections (SSL)" and  "Accept all SSL certificates" checked.&lt;/li&gt;
    &lt;li&gt;Click Next to go the the Account Options screen.&lt;/li&gt;
    &lt;li&gt;Set the "Email checking frequency" and "Amount to synchronize".  I used Automatic (Push) and One week, respectively.  &lt;/li&gt;
    &lt;li&gt;You can also choose to "Send email from this account by default" (I don't), "Notify me when email arrives" (I do), and "Sync contacts from this account" (I do).&lt;/li&gt;
    &lt;li&gt;Click Next to go to the final screen.  Give the account a friendly name, and enter your name (to be displayed on outgoing email).  Then touch the "Done" button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By default, it doesn't seem that Calendar sync'ing is automatically added to a new account.  I had to go into the Corporate Calendar app, after which it detected the new Corporate Account and started sync'ing the calendar (which can be verified by opening the account information from Settings).  &lt;/p&gt;
&lt;p&gt;Also, I'm sure that this is a bug at the moment, but it seems that only one Corporate Account (only the first one) is able to synchronize its calendar.  That is, the Corporate Calendar application does not seem to recognize when additional Corporate Accounts are added.  As a test, I deleted my first Corporate Account and then started the Corporate Calendar again, at which time it detected a new account (which was originally the second account) and added the Calendar sync to that.  But, recreating the original first account didn't result in an automatic Calendar sync.  Definite weirdness!&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/73.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2009/11/08/synchronizing-your-droid-to-exchange-online-bpos.aspx</guid>
            <pubDate>Mon, 09 Nov 2009 03:25:32 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2009/11/08/synchronizing-your-droid-to-exchange-online-bpos.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/73.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/73.aspx</trackback:ping>
        </item>
        <item>
            <title>Windows 7: Still No Love for Consultants</title>
            <link>http://jasonfollas.com/blog/archive/2009/07/15/windows-7-still-no-love-for-consultants.aspx</link>
            <description>&lt;p&gt;I'm a consultant.  I travel from client network to client network, sometimes within the same day.  My job involves interacting with various corporate servers using my trusty laptop, solve real-life business problems, and then move on to the next gig.  It's not practical for me to have my computer join a Windows Domain only to have to do it again and again with each new environment that I visit.  This is also not feasible because I cannot have my different clients become administrators on my company-provided laptop or have domain policies pushed down to my laptop, which is a byproduct of joining a domain.&lt;/p&gt;
&lt;p&gt;So, as a result, I'm forced to log onto my laptop as a local user.  This proved not to be a problem with Windows XP because I could set up a password rule (within the User Accounts control panel) to associate a username and password to use for each distinct domain that I access.&lt;/p&gt;
&lt;p&gt;But, beginning with Windows Vista, Microsoft's security model became what can only described as bi-polar.  If your machine was domain-connected, then everything worked fine.  But, if your machine was not domain-connected, then a different set of rules seemed to apply, and it suddenly became a frusterating chore to perform simple tasks against domain-protected resources.  &lt;/p&gt;
&lt;p&gt;Part of the problem stemmed from an apparent bug in the "Manage Network Passwords" interface that would seemingly not accept domain wildcards, either in the classic format (DOMAIN\*) or the FQDN format (*.DOMAIN.COM).  So, there was no way to instruct Windows to use a particular set of credentials for "any resource on this domain".  The best that you could do was save a password for each individual resource (server) that you needed to access.&lt;/p&gt;
&lt;p&gt;I've been experimenting with Windows 7 for a couple of weeks now to see how well it fits into the life of a consultant working within an enterprise environment.  I'm happy to report that the "Manage Network Passwords" bug appears to be fixed (using the FQDN wildcard format), so my network password list is not littered with scores of entries.  However, other things still remain broken or impossible to do.&lt;/p&gt;
&lt;p&gt;As an example, one of the tools that I use the most is SQL Server Management Studio (SSMS).  Out of the box, relying on a password rule to connect to a SQL Server protected by domain security simply does not work, and there is no other way to specify a Windows username/password as part of the Connect dialog.  I have to resort to using custom RUNAS shortcuts for each environment that I'm in &lt;a target="_blank" href="http://jasonf-blog.blogspot.com/2007/06/making-sql-server-management-server.html"&gt;(I previously blogged about this under Vista)&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Also, anything that uses Kerberos (i.e., for delegated authentication from a web application to a secondary server) simply does not work if your computer is not domain-connected.  Though, to be fair, I think that this was an issue for Windows XP machines that were not domain-connected as well, and is the result of how Microsoft implemented Kerberos security (their design assumes that all of the participating machines are domain members).&lt;/p&gt;
&lt;p&gt;Overall, the experience of using a standalone Windows 7 machine as a consultant within a corporate environment has proven to be better than Vista.  But, it's still not as good of an experience as XP was, and I totally blame this on the "enhancements" that were made to the underlying security model.  This is particularly disheartening because I'm in love with Windows 7 as an operating system and actually &lt;em&gt;want&lt;/em&gt; to use it for my day-to-day tasks.&lt;/p&gt;
&lt;p&gt;I welcome commentary from the OS team about what can be done to improve this experience! &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/72.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2009/07/15/windows-7-still-no-love-for-consultants.aspx</guid>
            <pubDate>Wed, 15 Jul 2009 16:34:45 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2009/07/15/windows-7-still-no-love-for-consultants.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/72.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/72.aspx</trackback:ping>
        </item>
        <item>
            <title>How To Force SSRS To Use Latest Data</title>
            <link>http://jasonfollas.com/blog/archive/2009/02/25/how-to-force-ssrs-to-use-latest-data.aspx</link>
            <description>&lt;p&gt;SQL Server Reporting Services is great in the fact that it comes with a handful of different rendering (export) formats out of the box.  If a particular report is intended to be paginated and printed, then from my web applications, I will often link to the report with a parameter instructing the render format to be PDF - this way, the user is never taken to the report viewer (they just get an Open/Save As dialog for the PDF itself).&lt;/p&gt;
&lt;p&gt;The URL for such a link may resemble the following:&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;http://server/ReportServer?/path/reportname&amp;amp;rs:Command=Render&amp;amp;rc:LinkTarget=_blank&amp;amp;&lt;strong&gt;&lt;u&gt;rs:Format=PDF&lt;/u&gt;&lt;/strong&gt;&amp;amp;SomeParameterValue=1&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;There are other interesting things that you can do with the rs:Format parameter.  For instance, say that you create a tiny report that only contains a chart.  Why not have this report rendered as an image, and then you can show this chart on your website (i.e., no extra charting component would be necessary on your web server).  Simply specify the URL to your report in an IMG tag's SRC property, and include the following parameters in that URL:&lt;/p&gt;
&lt;p&gt;rs:Format=IMAGE&amp;amp;rc:OutputFormat=PNG   (or GIF, or JPG, etc)&lt;/p&gt;
&lt;p&gt;One problem with not going through the default web-based report viewer, though: the report server likes to cache the output for your browser session.  Therefore, if the underlying data changes, re-running these reports will not show the updated information unless you close your browser and hit the page again.&lt;/p&gt;
&lt;p&gt;After a bit of headscratching and searching on the Internet, I found an easy solution that can be used to always force the reports to query the database.  Simply include the following parameter in your report's URL:&lt;/p&gt;
&lt;p&gt;rs:ClearSession=true&lt;/p&gt;
&lt;p&gt;Of course, you will take more of a hit on your database (because the report will always requery its datasets).  But, perhaps in your solution, you can selectively use this parameter if the user wants to force a refresh of a report that is automatically rendered to PDF or an image - that's a design decision that you'll have to make.  &lt;/p&gt;
&lt;p&gt;I'm just happy that a mechanism exists to get around the default behavior!&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" /&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/66.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2009/02/25/how-to-force-ssrs-to-use-latest-data.aspx</guid>
            <pubDate>Wed, 25 Feb 2009 19:02:27 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2009/02/25/how-to-force-ssrs-to-use-latest-data.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/66.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/66.aspx</trackback:ping>
        </item>
        <item>
            <title>Enterprise ASP.NET Application Performance Tip</title>
            <link>http://jasonfollas.com/blog/archive/2009/02/23/enterprise-asp.net-application-performance-tip.aspx</link>
            <description>&lt;p&gt;&lt;a target="_blank" href="http://www.theregion.com/"&gt;Microsoft Regional Director&lt;/a&gt; (and friend) &lt;a target="_blank" href="http://stevesmithblog.com/"&gt;Steven Smith&lt;/a&gt; has a great talk about ASP.NET performance tips - I've seen the presentation probably a handful of times already, and always seem to walk away with something new to try that I didn't quite "grok" previously.  &lt;/p&gt;
&lt;p&gt;But, here's one additional tip that I can offer that is easily overlooked, yet very important for enterprise development.&lt;/p&gt;
&lt;p&gt;Enterprise web applications (at least in my world) tend to use Integrated Security in order to provide Single Sign-On capability (i.e., automatically authenticating the user according to their Active Directory credentials).  As such, Anonymous access to the web application is usually disabled directly in the metabase using the IIS MMC.  But, with sites that use a lot of small images, there's a serious performance hit that you take when Anonymous access is disabled!&lt;/p&gt;
&lt;p&gt;A web browser will always try to submit a request anonymously.  In the case that I described where Anonymous access is disabled, the web server will generate a 403 response (and include a list of possible ways that the client can authenticate itself).  The web browser will then either prompt the user for credentials, or in the case of using Internet Explorer in the Intranet Zone with an application protected by Integrated Security, will automatically provide a response to the NTLM challenge.  The point being that it takes two separate requests for each resource (not to mention a higher computational cost, since there's a challenge/response included).  Multiply this by however many little images your page might have, and your load time increases significantly.&lt;/p&gt;
&lt;p&gt;Just today, I was asked to diagnose a load-time issue for a 3rd party web application that my client uses.  The site looks nice because it uses little images all over the place - to the effect of hundreds per page!  But, the price of these aesthetics was really aweful load times, especially over a VPN connection.&lt;/p&gt;
&lt;p&gt;For images and other resources that do not necessarily need authentication, you can get an immediate performance improvement by enabling Anonymous access to the resource directories/files themselves (leaving Anonymous access disabled for the rest of the application).  &lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/64.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2009/02/23/enterprise-asp.net-application-performance-tip.aspx</guid>
            <pubDate>Mon, 23 Feb 2009 19:15:00 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2009/02/23/enterprise-asp.net-application-performance-tip.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/64.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/64.aspx</trackback:ping>
        </item>
        <item>
            <title>Using SQL Server Spatial Objects as ADO.NET Parameter Values</title>
            <link>http://jasonfollas.com/blog/archive/2008/12/11/using-ado.net-with-sql-server-spatial-objects.aspx</link>
            <description>&lt;p&gt;I've &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;previously mentioned&lt;/a&gt; that the SQL Server 2008 Spatial data types are freely available for use in your .NET applications, regardless of whether you have SQL Server 2008 or not.  This allows you to incorporate some powerful spatial capabilities right into your application.  &lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Look for "&lt;font face="Arial"&gt;Microsoft SQL Server System CLR Types" on this page: &lt;font face="Arial"&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=228DE03F-3B5A-428A-923F-58A033D316E1&amp;amp;displaylang=en"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyID=228DE03F-3B5A-428A-923F-58A033D316E1&amp;amp;displaylang=en&lt;/a&gt; )&lt;/font&gt;&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;However, in most usage scenarios, there will come a time when you have an instance of a SQL Server spatial object in your .NET application, and need to commit it to your SQL Server 2008 database.  How would you do this, without losing fidelity or resorting to serialization of the object to WKT first?&lt;/p&gt;
&lt;p&gt;The solutions is to create a Parameter object of type System.Data.SqlDbType.Udt.  Then set the UdtTypeName parameter to the SQL Server-recognized type name (i.e., for SqlGeometry, you would simply use Geometry).&lt;/p&gt;
&lt;p&gt;The following code demonstrates executing an UPDATE statement that sets the value of a Spatial field to a newly constructed object.&lt;/p&gt;
&lt;pre class="c#" name="code"&gt;using (SqlConnection conn = new SqlConnection("Server=.;Integrated Security=true;Initial Catalog=scratch"))
{
   using (SqlCommand cmd = new SqlCommand("UPDATE fe_2007_us_zcta500 SET Boundary=@boundary WHERE id=@id", conn))
   {
      SqlParameter id = cmd.Parameters.Add("@id", System.Data.SqlDbType.Int);

      SqlParameter boundary = cmd.Parameters.Add("@boundary", System.Data.SqlDbType.Udt);
      boundary.UdtTypeName = "geometry";

      SqlGeometry geom = SqlGeometry.Parse("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))");
      
      boundary.Value = geom;
      id.Value = 123;

      conn.Open();
      cmd.ExecuteNonQuery();
      conn.Close();
   }
}
&lt;/pre&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/58.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/12/11/using-ado.net-with-sql-server-spatial-objects.aspx</guid>
            <pubDate>Thu, 11 Dec 2008 19:04:12 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2008/12/11/using-ado.net-with-sql-server-spatial-objects.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/58.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/58.aspx</trackback:ping>
        </item>
        <item>
            <title>SqlGeography: Ring Orientation of Polygon Interior Rings (Holes)</title>
            <link>http://jasonfollas.com/blog/archive/2008/11/25/sqlgeography-ring-orientation-of-polygon-interior-rings-holes.aspx</link>
            <description>&lt;p&gt;I have mentioned before how the Ring Orientation for the exterior ring of a Polygon is significant when instantiating a SqlGeography object.  In this case, a Counter-Clockwise orientation is required so that as an observer walks along the path, the interior of the Polygon is always to their left.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Ring Orientation for SqlGeography" hspace="5" align="right" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/r_RingOrientation.PNG" /&gt;But, what I have never really seen documented (or paid attention to, at least) is the fact that the interior rings, or holes, of a Polygon also have specific Ring Orientation requirements.  &lt;/p&gt;
&lt;p&gt;In keeping with the "Left-handed" rule, interior rings must be defined in a Clockwise manner - the opposite orientation of the shape's exterior ring.  This is because holes within a Polygon are considered to be part of the exterior of the shape, so the observer walking in a Clockwise direction is still keeping the Polygon's interior to their left.&lt;/p&gt;
&lt;p&gt;(I should note here that the Ring Orientation for SqlGeography is the exact opposite of ESRI's ShapeFile format, which is why Ring Orientation has been on my mind for the past few days).&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/55.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/11/25/sqlgeography-ring-orientation-of-polygon-interior-rings-holes.aspx</guid>
            <pubDate>Tue, 25 Nov 2008 14:14:18 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2008/11/25/sqlgeography-ring-orientation-of-polygon-interior-rings-holes.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/55.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/55.aspx</trackback:ping>
        </item>
        <item>
            <title>Spatial: Determining Ring Orientation</title>
            <link>http://jasonfollas.com/blog/archive/2008/11/24/spatial-determining-ring-orientation.aspx</link>
            <description>&lt;p&gt;A Ring is a list of points such that the starting point and ending point are the same (forming a closed shape).  The order the you define the points that make up a Ring  - known as Ring Orientation - is significant, for various data formats (including SQL Server's Geography type) imply special meaning for rings that are defined in a clockwise manner as opposed to a counter-clockwise manner.  &lt;/p&gt;
&lt;p&gt;Given a list of points with no additional context, it can be difficult to determine the Ring Orientation being used.  &lt;/p&gt;
&lt;p&gt;For example, suppose that you have a generic list of points that represent the boundary of a postal code, and that you wish to use these points in order to construct a Polygon instance using the SqlGeography type.  SqlGeography happens to use "Left-handed" ordering, so that as an observer walks along the set of points in the order defined, the "inside" of the polygon is always to their left.  This also implies that the exterior ring of a Polygon is defined in a counter-clockwise manner.&lt;/p&gt;
&lt;p&gt;If you try to define a polygon with an area greater than a single hemisphere (this is a nice way to say "if you screw up and use the wrong orientation"), then the SqlGeography type will throw an exception.  So, aside from using Try-Catch, what can you do?&lt;/p&gt;
&lt;p&gt;While researching solutions to this problem, I stumbled upon a paper entitled "&lt;a href="http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf"&gt;A Winding Number and Point-in-Polygon Algorithm&lt;/a&gt;" from the Colorado State University.  It turns out that a simple algorithm with O(n) complexity can be used to determine if a point is within a Polygon, and a side effect also provides the Ring Orientation.  The key to this algorithm is determining the trend of the ring at each crossing of an axis.&lt;/p&gt;
&lt;p&gt;Since I was only interested in Ring Orientation (and not point enclosure detection), I didn't need to use this particular algorithm.  Instead, I took inspiration from the winding concept, and created a simpler derivative algorithm:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Visual example of how to determine ring orientation at the extreme left point" hspace="5" align="right" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/o_orientation_example.png" /&gt; &lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Iterate the point collection and determine the extreme "left" and "right" points &lt;/li&gt;
    &lt;li&gt;Normalize the line segments connected to these points so that they each have the same "X" dimension length &lt;/li&gt;
    &lt;li&gt;Compare the "Y" values of the normalized segments to establish the trend through that extreme point (i.e., is the "previous" segment above or below the "next" segment) &lt;/li&gt;
    &lt;li&gt;In the spirit of the Winding algorithm, use opposite orientations for the left and right points so that the results coincide with one another &lt;/li&gt;
    &lt;li&gt;A negative result (negative indicates Clockwise orientation, positive result indicates Counter-Clockwise orientation, and a result of zero would be undefined &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I've actually written (and posted) several versions of this algorithm, each time discovering some edge case exception that would cause me to take down the post and rewrite the algorithm.  I believe the code below works for all simple polygons on a Cartesian coordinate system (read: I have more testing to see if this will work with an ellipsoidal model, like SqlGeography).  &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: The following code is generic in nature, and as such, I've defined my own Point structure instead of using a SqlGeometry or SqlGeography, etc. &lt;/em&gt;&lt;/p&gt;
&lt;pre class="c#" name="code"&gt;struct Point
{
    public double X { get; set; }
    public double Y { get; set; }
}

enum RingOrientation : int
{
    Unknown = 0,
    Clockwise = -1,
    CounterClockwise = 1
};

RingOrientation Orientation(Point[] points)
{
    // Inspired by http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf

    // This algorithm is to simply determine the Ring Orientation, so to do so, find the
    // extreme left and right points, and then check orientation

    if (points.Length &amp;lt; 4)
    {
        throw new ArgumentException("A polygon requires at least 4 points.");
    }

    if (points[0].X != points[points.Length - 1].X || points[0].Y != points[points.Length - 1].Y)
    {
        throw new ArgumentException("The array of points is not a polygon.  The first and last point must be identical.");
    }

    int rightmostIndex = 0;
    int leftmostIndex = 0;

    for (int i = 1; i &amp;lt; points.Length; i++)
    {
        if (points[i].X &amp;lt; points[leftmostIndex].X)
        {
            leftmostIndex = i;
        }
        if (points[i].X &amp;gt; points[rightmostIndex].X)
        {
            rightmostIndex = i;
        }
    }


    Point p0; // Point before the extreme
    Point p1; // The extreme point
    Point p2; // Point after the extreme

    double m; // Holds line slope

    double lenP2x;  // Length of the P1-P2 line segment's delta X
    double newP0y;  // The Y value of the P1-P0 line segment adjusted for X=lenP2x

    RingOrientation left_orientation;
    RingOrientation right_orientation;

    // Determine the orientation at the Left Point
    if (leftmostIndex == 0)
        p0 = points[points.Length - 2];
    else
        p0 = points[leftmostIndex - 1];

    p1 = points[leftmostIndex];

    if (leftmostIndex == points.Length - 1)
        p2 = points[1];
    else
        p2 = points[leftmostIndex + 1];

    m = (p1.Y - p0.Y) / (p1.X - p0.X);

    if (double.IsInfinity(m))
    {
        // This is a vertical line segment, so just calculate the dY to
        // determine orientation

        left_orientation = (RingOrientation)Math.Sign(p0.Y - p1.Y);
    }
    else
    {
        lenP2x = p2.X - p1.X;
        newP0y = p1.Y + (m * lenP2x);

        left_orientation = (RingOrientation)Math.Sign(newP0y - p2.Y);
    }



    // Determine the orientation at the Right Point
    if (rightmostIndex == 0)
        p0 = points[points.Length - 2];
    else
        p0 = points[rightmostIndex - 1];

    p1 = points[rightmostIndex];

    if (rightmostIndex == points.Length - 1)
        p2 = points[1];
    else
        p2 = points[rightmostIndex + 1];

    m = (p1.Y - p0.Y) / (p1.X - p0.X);

    if (double.IsInfinity(m))
    {
        // This is a vertical line segment, so just calculate the dY to
        // determine orientation

        right_orientation = (RingOrientation)Math.Sign(p1.Y - p0.Y);
    }
    else
    {
        lenP2x = p2.X - p1.X;
        newP0y = p1.Y + (m * lenP2x);

        right_orientation = (RingOrientation)Math.Sign(p2.Y - newP0y);
    }


    if (left_orientation == RingOrientation.Unknown)
    {
        return right_orientation;
    }
    else
    {
        return left_orientation;
    }
}


void Test()
{
    // Simple triangle - left extreme point is vertically "in between" line segments
    Point[] points = new Point[]
    {
        new Point(5,-1),
        new Point(0,0),
        new Point(5,1),
        new Point(5,-1)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);

    // Case where both line segments are above the left extreme point
    points = new Point[]
    {
        new Point(2,1),
        new Point(0,0),
        new Point(1,1),
        new Point(2,1)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);

    // Case where both line segments are below the left extreme point
    points = new Point[]
    {
        new Point(2,-1),
        new Point(0,0),
        new Point(1,-1),
        new Point(2,-1)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);

    // Case where line segment is vertical (slope cannot be determined)
    points = new Point[]
    {
        new Point(0,0),
        new Point(0,1),
        new Point(1,1),
        new Point(1,0),
        new Point(0,0)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);

    // Case where angle thru left extreme point is a right angle
    points = new Point[]
    {
        new Point(0,0),
        new Point(1,1),
        new Point(1,-1),
        new Point(0,0)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);

    // Real-world case from a SHP file
    points = new Point[]
    {
        new Point(-156.92467299999998,20.738695999999997),
        new Point(-156.924636,20.738822),
        new Point(-156.924608,20.73894),
        new Point(-156.92458,20.739082),
        new Point(-156.92460599999998,20.739234),
        new Point(-156.924551,20.739326),
        new Point(-156.924507,20.739241999999997),
        new Point(-156.924482,20.739082),
        new Point(-156.924466,20.738854999999997),
        new Point(-156.924387,20.738602999999998),
        new Point(-156.924308,20.738325),
        new Point(-156.924239,20.738063999999998),
        new Point(-156.92424,20.737887999999998),
        new Point(-156.924285,20.737811999999998),
        new Point(-156.924475,20.73762),
        new Point(-156.92458299999998,20.737603999999997),
        new Point(-156.924754,20.737579),
        new Point(-156.924851,20.737731),
        new Point(-156.924956,20.738101),
        new Point(-156.924909,20.738343999999998),
        new Point(-156.924818,20.738487),
        new Point(-156.92467299999998,20.738695999999997)
    };

    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.Clockwise);
    Array.Reverse(points);
    System.Diagnostics.Debug.Assert(Orientation(points) == RingOrientation.CounterClockwise);

}&lt;/pre&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/54.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/11/24/spatial-determining-ring-orientation.aspx</guid>
            <pubDate>Mon, 24 Nov 2008 18:00:46 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2008/11/24/spatial-determining-ring-orientation.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/54.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/54.aspx</trackback:ping>
        </item>
        <item>
            <title>Install Zune 3.0 Software on Windows 2008 Server</title>
            <link>http://jasonfollas.com/blog/archive/2008/09/16/install-zune-3.0-software-on-windows-2008-server.aspx</link>
            <description>&lt;p&gt;I use Windows 2008 Server as a workstation.  I find that certain things actually work better (like Sleep Mode, for instance, which is weird because servers shouldn't need to sleep, but Vista would often not go to sleep and drain my battery the entire couple of hours that it continued to run in my computer bag, and Windows Server 2008 has never done that.... but I digress).&lt;/p&gt;
&lt;p&gt;However, when the new Zune 3.0 software was released today, I found, like many others, that the software explicitly listed three operating systems that were supported, and Server 2008 was unfortunately not one of them.  The install refused to continue. &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/r_zune3pic2.png" /&gt;&lt;/p&gt;
&lt;p&gt;While experimenting, I found a way to install the Zune 3.0 software manually.  It appears to be fully functional on my machine, but I assume no responsibility and will provide no support if you decide to follow these instructions on your own machine.&lt;/p&gt;
&lt;p&gt;First, I noticed that the setup bootstrap (zunesetuppkg-x86.exe) says where it's extracting its files to:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/r_zune3pic1.png" /&gt;&lt;/p&gt;
&lt;p&gt;(This was weird to me, since the E: drive on my machine is an external USB hard drive...)&lt;/p&gt;
&lt;p&gt;Next, I opened that directory in Explorer.  There, I found a "packages" directory:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/r_zune3pic3.png" /&gt;&lt;/p&gt;
&lt;p&gt;And inside of there, I found a MSI file called Zune-x86.msi:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/9/r_zune3pic4.png" /&gt;&lt;/p&gt;
&lt;p&gt;Ok, learn from my trial-and-error here: uninstall any previous version of the Zune software, because this MSI will not do it for you, and will not install if a previous version exists.  Other than that, I just double-clicked on the MSI, and a minute later, I had the 3.0 software on my machine!&lt;/p&gt;
&lt;p&gt;Note that I am currently behind a ISA Server proxy/firewall that requires authentication with Active Directory.  Therefore, in order to download the firmware update, I had to change my UseLmCompat to "0" per this blog post:&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;a href="http://jasonfollas.com/blog/archive/2007/11/13/how-to-get-the-zune-2.2-firmware-when-your-corporate.aspx"&gt;http://jasonfollas.com/blog/archive/2007/11/13/how-to-get-the-zune-2.2-firmware-when-your-corporate.aspx&lt;/a&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/52.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/09/16/install-zune-3.0-software-on-windows-2008-server.aspx</guid>
            <pubDate>Tue, 16 Sep 2008 18:53:40 GMT</pubDate>
            <comments>http://jasonfollas.com/blog/archive/2008/09/16/install-zune-3.0-software-on-windows-2008-server.aspx#feedback</comments>
            <slash:comments>5</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/52.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/52.aspx</trackback:ping>
        </item>
    </channel>
</rss>