<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>System.Nerd.John.Blog &#187; admin</title>
	<atom:link href="http://jsprunger.com/author/admin/feed/" rel="self" type="application/rss+xml" />
	<link>http://jsprunger.com</link>
	<description>A Consultant&#039;s Adventures in Software Development</description>
	<lastBuildDate>Thu, 06 Jan 2011 18:06:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.3</generator>
		<item>
		<title>Mobile Architecture Best Practices</title>
		<link>http://jsprunger.com/mobile-architecture-best-practices/</link>
		<comments>http://jsprunger.com/mobile-architecture-best-practices/#comments</comments>
		<pubDate>Thu, 06 Jan 2011 18:06:14 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=176</guid>
		<description><![CDATA[Best Practices for Mobile Application Design and Development When developing mobile applications, there are a number of key challenges where architecture and design are fundamentally different from that of a typical enterprise application. Careful consideration should be given to these mobile architecture issues early in the development process in order to mitigate the downstream impact [...]]]></description>
			<content:encoded><![CDATA[<h2>Best Practices for Mobile Application Design and Development</h2>
<p>
When developing mobile applications, there are a number of key challenges where architecture and design are fundamentally different from that of a typical enterprise application. Careful consideration should be given to these mobile architecture issues early in the development process in order to mitigate the downstream impact of poor architectural decisions. While some of these best practices also make sense for the development of non-mobile applications, many will become more readily apparent when developing on a mobile platform. The five most important areas for consideration, which are detailed throughout this document, include: performance, usability, data access, security, and connectivity.
</p>
<h3>Performance</h3>
<p>
While more readily apparently in the previous years of mobile development, the computing power available on mobile devices still lags behind desktop and server counterparts and will continue to do so for the foreseeable future due to smaller device footprints and resource constraints. Even the most recent devices still boast only about one third to one half of the computing resources (CPU, RAM) of a low end desktop computer. Further, the quality of data connections available on a mobile device is often highly variable based on signal strength and is far inferior to broadband Internet access in most cases.
</p>
<p>
Often during rapid application development, performance considerations are ignored until the end of the project and optimized only when necessary. In mobile development, more consideration to performance constraints of the mobile device may need to be given up front in the design process. Each platform has different code-level best practices for performance optimization depending upon the programming language and frameworks available on the platform.  Some best practices, such as judicious usage of memory and limits on the number of unnecessary objects created, however, can be applied across all platforms.
</p>
<p>
Care should especially be given to architectural decisions that can limit performance and are also difficult to change later in the development cycle, such as the design of web service APIs and data formats. General best practices for the design of web service APIs for use in mobile development could be summed up as:
</p>
<ul>
<li>Only retrieve the data that the application needs</li>
<li>Only retrieve data when the application needs it</li>
</ul>
<p><br/></p>
<p>
These considerations stem mostly from the limited bandwidth available to mobile devices. If possible, APIs used by a mobile application should be designed to retrieve only the most relevant and useful information – excluding any extra data that is not used by the application. When designing APIs to communicate with mobile applications, one recommendation is to use a lightweight data format like JSON instead of more verbose format such as XML in order to make the best use of limited bandwidth available to mobile devices. The use of a lightweight format like JSON will conserve bandwidth, will allow results to be retrieved more quickly, and also will generally enable faster deserialization of the data as it arrives on the mobile client.
</p>
<p>
Another important performance consideration on a mobile device is battery life. If an application is constantly polling a web service for updates or continually processing data in the background, the battery will be drained much more quickly. If architecturally feasible (and if the push notification capabilities exist on the mobile platform), the use of push notifications for providing data updates is recommended over periodic polling. Push notification capabilities currently exist on the iPhone, Android, and Windows Phone 7 platforms. If an application needs to perform large amounts of data processing or analysis, consider uploading the necessary data to a server-side platform to perform the CPU-intensive processing and then return the results to the device to avoid draining the battery and to provide a more-responsive user experience.
</p>
<h3>Usability</h3>
<p>
	At the end of the day, usability is one of the key factors that will truly make or break user acceptance of an application.  Each of the major mobile platform software vendors (Microsoft, Google, Apple) have released user-experience specifications and guidelines specific to their own platforms in an attempt to foster a consistent look and feel across all applications on their platforms – and if the guidelines are enforced by the vendor and followed by developers, then the payoff is absolutely realized. The user experience across applications on most of the major platforms is seamless – for example, on the more stringent iPhone and Windows Phone 7 platforms, the navigation of menus and the look and feel of most applications (down to the fonts and color schemes) are almost identical. This allows users to learn quickly how to use a new application and instead focus on performing the task at hand, rather than “switching gears” between disparate experiences or puzzling over how to interact with a new application. Below are links to the user experience guidelines for each of the major platforms:
</p>
<ul>
<li><a href="http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices.html">iPhone</a></li>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/index.html">Android</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=183218">Windows Phone 7 (PDF)</a></li>
</ul>
<p>
While each platform may have specific user interface (UI) guidelines, the challenges of mobile application usability are ubiquitous and many best practices can be applied across all platforms.  Following are a few of the most important usability considerations.
</p>
<ul>
<li>
Limited screen real estate. No longer do users have access to a 23-inch widescreen monitor to display every single piece of information at once.  Only display the most relevant information and options on the screen. Menus and UI screens should not be cluttered within rarely used options; rather they should be buried deeper within a settings screen or a submenu. Conversely, if a feature is used on a regular basis, consider assigning it to a hardware button or making it readily available within the UI.  For the sake of accessibility, avoid the use of small font sizes in order to cram more information onto the screen. Scrolling in mobile applications can be difficult for the end user, so limit the need to scroll within screens where possible.
</li>
<li>
When displaying information, make use of the Summary / Detail / Edit UI paradigm. A ‘Summary’ view displays on the most important and relevant information and actions to the user. To access less important or less commonly used information, the user can then drill down to a more complete ‘Detail’ view.  If the user needs to edit the information, switch to an ‘Edit’ view that will provide richer functionality around editing, validating, and persisting the data.
</li>
<li>Lighting conditions. Important for both enterprise and consumer applications – consider whether the application will be primarily used indoors or outdoors. If the application will be used in low light or sunlight conditions, make use of high contrast, sufficiently bright colors. If the application is to be used in specific locations, such as a warehouse or factory floor, ensure that the application has undergone appropriate testing in the target setting.
</li>
<li>
Ensure that UI elements are sized appropriately. Each platform will have separate specifications for minimum button sizes relative to the screen resolution of the devices.  For example, Microsoft’s Windows Phone 7 guideline for minimum size for any interactive UI element is 7mm or 26 pixels. Take into consideration the conditions in which the mobile application will be used – for example, if an application will be used on a warehouse floor, the users of the application may be wearing gloves and would require larger buttons and increased spacing between UI elements.
</li>
</ul>
<h3>Data Access</h3>
<p>
One commonality between the most modern mobile platforms (iPhone, Android, Windows Phone 7) is that none of them offer any capability to connect directly to a database – for good reason.  The current mobile architecture paradigm simply doesn’t support this scenario for modern database platforms in their current state.  Given that most mobile applications communicate over the public Internet, access to a database would require exposing that database publicly – and in this age, no sane IT or database administrator would publicly expose an instance of Oracle, SQL Server, or MySQL outside the firewall without measures like a VPN or IP restrictions in place. While VPNs are becoming more available on modern mobile platforms, the complexities around cost, bandwidth, and end-user configuration simply don’t make business sense when compared with fronting a database with a more secure web service front-end.
</p>
<p>
Rather than attempting to provide support for database client connectivity, the current paradigm for data access from mobile applications is based around web services.  For the example scenario of extending a common two-tier enterprise application onto a mobile platform, usually a web services layer would first have to be created that would exist in front of the database or APIs of the enterprise application.  In the design of a web services layer for a mobile application, logic around authentication, authorization, validation, and business rules should all be executed on the server-side web services of the extended application. As the web services are now exposed publicly for use by any properly authenticated user of your application, the validity of the data and the user’s right to call the web service cannot be trusted without first performing additional server-side checks. Logic for validation and authorization can be duplicated on the mobile client side of the application to provide a more responsive user experience, but the user’s actions should be checked again on the server side after the data is passed to the web service.
</p>
<p>
The architecture diagram at right below depicts how an enterprise application could be extended onto a mobile platform by wrapping either the application’s APIs or database with a business layer that performs additional processing for validation and security. Note that if validation or authorization is built into the enterprise application’s APIs or data access mechanism, then it is not necessary to re-implement this functionality within the web services layer.
</p>
<div id="attachment_180" class="wp-caption aligncenter" style="width: 284px"><a href="http://jsprunger.com/wp-content/uploads/2011/01/mobile_architecture1.png"><img src="http://jsprunger.com/wp-content/uploads/2011/01/mobile_architecture1.png" alt="Enterprise Application Extended to Mobile Devices" title="mobile_architecture" width="274" height="500" class="size-full wp-image-180" /></a><p class="wp-caption-text">Enterprise Application Extended to Mobile Devices</p></div>
<h3>Security</h3>
<p>
As previously mentioned, data access on mobile platforms generally requires some form of Internet-facing service or data access point that can be communicated with via a mobile device.  Database servers and platforms in their current state are not good candidates for public exposure without additional layers of security that are generally not feasible or cost effective on mobile devices.  Web servers are generally more hardened to attack and, thus, web services are an excellent candidate for exposure outside the firewall to mobile devices over the Internet. But what about securing these web services?
</p>
<p>
In most cases, the use of a web service API first requires authentication to ensure that the caller of the web services is who they say they are. Usually, web service API security will use a form of token-based authentication – this could be something like OAuth or as simple as sessions built into any modern server-side framework, such as ASP.NET or Ruby on Rails.  In the general workflow of token based authentication, the web service caller sends a username and password and then receives a unique token back after his/her identity has been verified by the authentication service (e.g. LDAP). The token is then passed back to the web service on all subsequent requests and can be used on the server side to determine the identity of the user. Depending upon the security constraints of the application, the token generally expires after a certain period of inactivity. Regardless of the technology used to accomplish the token based authentication, all communication between the mobile client and the web server should be performed over an SSL-secured connection in order to prevent the token from being captured via packet sniffing on a wireless connection or any other “man-in-the-middle” attack. If the token were to be compromised by a third party, the third party would then be able to imitate the identity of the actual application user and would be able to make malicious requests, if inclined.
</p>
<p>
Another security issue inherent to mobile platforms is the security of data that exists locally on the device itself. Obviously, any mobile device can be compromised much easier than a server residing within a secure data center. If possible, confidential data should not be stored on the mobile device itself and should be stored instead on a back-end server and downloaded to the device when necessary. If for architectural reasons confidential data must be stored on the device, then measures should be taken to encrypt the data with a key that is not stored on the device, if possible. Fortunately, mobile platform vendors are providing more and more support for automatically encrypted disk storage, which makes implementation of secure data storage on the device much easier. One further consideration for mobile data storage security is that highly confidential data, such as private health information (PHI), should not be stored on a mobile device under any circumstances, encrypted or otherwise.
</p>
<h3>Connectivity</h3>
<p>
	The final major architecture consideration for mobile applications is connectivity. It can no longer be assumed that the application being built will have access to an “always-on” high-speed Internet connection. In the wild, mobile devices will frequently switch between different types of connections (e.g. Edge, 3G, or WiFi) with wildly varying speeds and will often have no data connection. Often, the implementation of offline access for a mobile application simply doesn’t make sense business-wise, architecturally – perhaps the application must have access to only the most relevant and up-to-date data (e.g., traffic conditions), or when data is persisted it must be immediately validated and processed (e.g., stock trades). For most business applications, however, there are use cases for which offline access is absolutely necessary in order to maintain the end user’s productivity. One simple way to design offline access and data synchronization involves the creation of two basic components within the mobile application – a caching mechanism and a queuing mechanism.
</p>
<p>
The caching component handles offline access for data that would normally be retrieved as needed from a server-side API.  The caching component can be designed to periodically (in a background thread) retrieve larger data sets that are potentially relevant to the needs of the end user, or it can be designed to only keep copies of any data previously retrieved from server.  Data stored in the cache on the device should generally expire after a certain time period has passed in which the data is no longer useful or relevant. Another feature that can be designed into the caching feature is some level of intelligence related to the current type of connection on the device. For instance, if the cache is designed to periodically download large data sets, then perhaps it will only do so when the device is connected to WiFi in order to conserve bandwidth when connected to slower connection types. The implementation of a caching component can also provide the benefit of a more responsive user experience, as data can then be retrieved from the local cache rather than round-tripping to a server over a slow connection.
</p>
<p>
The queuing component handles the persistence of data to the back-end services.  The queuing component can be designed to sit in front of the web service API client within the mobile application and check to see whether or not a connection is available when attempting to call the web services.  If a data connection is unavailable, then the update is placed into a first-in, first-out queue in memory. The queue should then periodically check (in a background thread) to see if a connection is available and then send all data updates to the back-end services in the order in which they were received. The queue should also be designed with business logic around the reconciliation of data conflicts. For example, if a data update is sent to the server and is determined to be out of date or invalid, then the end user should be notified of the error and given a mechanism to correct or discard the update. Another feature that should be designed into the queue is the persistence of the queue to local data storage on the device; if the application is closed or interrupted, then the queued updates will be kept safe until the next time the application is used. Below is a depiction of a mobile architecture using local caching and queuing services to provide offline data access and data synchronization.
</p>
<div id="attachment_181" class="wp-caption aligncenter" style="width: 360px"><a href="http://jsprunger.com/wp-content/uploads/2011/01/caching.png"><img src="http://jsprunger.com/wp-content/uploads/2011/01/caching.png" alt="Mobile Application Data Caching" title="caching" width="350" height="313" class="size-full wp-image-181" /></a><p class="wp-caption-text">Mobile Application Data Caching and Queuing Architecture</p></div>
<p>
As you can see, when designing an application that will be living “in the wild”, outside the corporate firewall, there are numerous challenges that simply don’t exist when building enterprise applications that run in well-known conditions, safe and secure within a corporate datacenter or colocation facility. Performance and usability will make or break the usage and acceptance of any mobile application. Now that users are used to the snappy and responsive interfaces of their modern iPhone, Android, and Windows Phone platforms, they will loathe using any application with a sluggish, unusable interface. Accessing data on a mobile device can be a whole new ball game for enterprise developers who haven’t worked with web services or have spent years writing and maintaining classic two-tier or mainframe-based applications. Security is rarely a concern for developers writing applications that are safely tucked away behind a corporate firewall and intrusion-protection systems, but when exposing APIs with access to business-critical information to the public Internet, there is no way that security-through-obscurity will suffice any longer. Connectivity is especially challenging to design around on a mobile device that will commonly have a very slow connection or no connection at all – for an enterprise application running in a data center, on the other hand, it can usually be assumed there is a redundant, high-availability, high-bandwidth Internet connection available.
</p>
<p>
In summary, while it can be challenging, there are well known solutions for each of the previously mentioned issues. And though each mobile platform will have its own specific best practices for each area, many of the best practices are standard across all mobile platforms, regardless of the technology used.
</p>
<p>
West Monroe Partners is a full-service provider of business and technology solutions. Our consulting professionals are experienced in developing enterprise and consumer mobile applications on the Apple iOS, Android, and Windows Phone 7 platforms. Please <a href="http://westmonroepartners.com/Contact-Us.aspx">contact us</a> for more information on how our mobile application development team can work with you to ensure that your mobile applications follow the appropriate best practices.  Click here to read more about West Monroe Partners’ <a href="http://westmonroepartners.com/Solutions/Technology/Mobile-Application-Solutions.aspx">Mobile Application Solutions</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/mobile-architecture-best-practices/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Getting started with Lucene.NET</title>
		<link>http://jsprunger.com/getting-started-with-lucene-net/</link>
		<comments>http://jsprunger.com/getting-started-with-lucene-net/#comments</comments>
		<pubDate>Fri, 17 Sep 2010 15:38:22 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=110</guid>
		<description><![CDATA[Have you ever built a search using a SQL LIKE statement, only to have your users complain about functionality? A simple SQL-based search doesn&#8217;t handle synonyms, misspellings, prefixes, suffixes, result rankings, weighting, and so on and so forth. Fret no longer, you can spend a little more time and build a &#8220;smart&#8221; search using Lucene [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever built a search using a SQL LIKE statement, only to have your users complain about functionality? A simple SQL-based search doesn&#8217;t handle synonyms, misspellings, prefixes, suffixes, result rankings, weighting, and so on and so forth. Fret no longer, you can spend a little more time and build a &#8220;smart&#8221; search using Lucene and get all of these features as well as the ability to tweak the search as much as you like.</p>
<p>Lucene.NET is a direct port of the popular open source Java Lucene project. Large companies such as EMC and Cisco have placed bets on Lucene and embedded the library within some of their products. The .NET version is a little bit behind the Java version in terms of features and releases, but by and large the library is very usable.  Lucene can be used to index just about any type of content &#8211; including files , database records, web pages, and can be used in any number of architectural scenarios &#8211; searching in an ASP.NET web site, searching within a desktop app, search as a web service or Windows service, etc.</p>
<p>In the most simple search scenario &#8211; architecturally, you have to build an Indexer and a Searcher. You can think of Lucene as a set of tools that will do most of the work for you in building these components &#8211; you have to use Lucene to build an index and dump your searchable content into that index, and you have to tell Lucene how to search the index that you&#8217;ve built. Conceptually, the index is built out of the content that you want to search, whether it be files or database records. If you change the content you want to search on (for example, you&#8217;ve added a new file), then you have to either append that content to your index or rebuild your index. One strategy is to set up a scheduled process (i.e. using Quartz.NET, a windows service, or scheduled task) to periodically re-index your content.</p>
<h2>Adding Lucene to your project</h2>
<p>First things first, you have to add the Lucene libraries to your project. On the Lucene.NET web site, you&#8217;ll see the most recent release builds of Lucene. These are two years old. Do not grab them, they have some bugs. There has not been an official release of Lucene for some time, probably due to resource constraints of the maintainers. Use Subversion (or TortoiseSVN) to browse around and grab the most recently updated Lucene.NET code from the Apache SVN Repository. The solution and projects are Visual Studio 2005 and .NET 2.0, but I upgraded the projects to Visual Studio 2008 without any issues.  I was able to build the solution without any errors. Go to the bin directory, grab the Lucene.Net dll and add it to your project.</p>
<h2>Building the Index</h2>
<p>Step two is building your searchable index. A Lucene index is usually stored as a set of files on the file system, but can also be stored in memory for performance &#8211; and there are even proof of concept projects available that allow you to store the index in a database (though I&#8217;m not sure why you would).</p>
<p>A couple of Lucene concepts/classes you should be aware of for indexing include Documents, Fields, Analyzers, and the IndexWriter. Documents are what you put into your index. They&#8217;re not &#8220;documents&#8221; in the traditional sense, like a Word document &#8211; rather, a Document is just an abstraction of an indexable piece of content. It is your responsibility to create the Document objects to place into your Index.</p>
<p>For example, let&#8217;s say we&#8217;re creating a product search, using Product objects pulled from our database. Our searches will be based on the Product Name.</p>
<div style="font-family: Courier New; font-size: 10pt; color: black; background: white;">
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">Product</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> Product() { }</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">string</span> ProductName { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">decimal</span> Price { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">string</span> Color { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">string</span> Id { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">//return a Lucene document for the product</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: #2b91af;">Document</span> GetDocument()</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Document</span> document = <span style="color: blue;">new</span> <span style="color: #2b91af;">Document</span>();</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; document.Add(<span style="color: blue;">new</span> <span style="color: #2b91af;">Field</span>(<span style="color: #a31515;">&quot;ProductName&quot;</span>, <span style="color: blue;">this</span>.ProductName, <span style="color: #2b91af;">Field</span>.<span style="color: #2b91af;">Store</span>.NO, <span style="color: #2b91af;">Field</span>.<span style="color: #2b91af;">Index</span>.ANALYZED));</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; document.Add(<span style="color: blue;">new</span> <span style="color: #2b91af;">Field</span>(<span style="color: #a31515;">&quot;Id&quot;</span>, <span style="color: blue;">this</span>.Id.ToString(), <span style="color: #2b91af;">Field</span>.<span style="color: #2b91af;">Store</span>.YES, <span style="color: #2b91af;">Field</span>.<span style="color: #2b91af;">Index</span>.NO));</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">return</span> document;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
</div>
<p><br/></p>
<p>
We&#8217;ll add fields to our Document to represent the values we want to search on or store in our Index. Field.Store.YES/NO indicates whether or not we want to actually store the field in our index. Note how I don&#8217;t store the Price or Color columns &#8211; we don&#8217;t want to store the complete objects in Lucene &#8211; it&#8217;s just our search index. Keep the complete objects stored in your database (or keep your files on the file system, etc.). We do want to store the Id because when we get our search result documents back from querying the index only the stored fields will be returned . We need to at least know the Product Id so we can go fetch our full objects that match our search results from the database. There is also a COMPRESS option that you can use if you need to store large fields or binary data.
</p>
<p>
Field.Index.ANALYZED/NO indicates whether or not we want to actually index the field. Indexing a field takes some minimal level of processing power, so we don&#8217;t want to index every field &#8211; only index what you want to search on. Thus we don&#8217;t want to Index the Product Id, Color, or Price &#8211; only the Name because that&#8217;s all we want to search on.
</p>
<p>
Next, we&#8217;ll create the index and add the documents to it. Below is an example of a very simple class with a single method that we can use to build our Product search index using a given list of products.
</p>
<div style="font-family: Courier New; font-size: 10pt; color: black; background: white;">
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">Index</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: blue;">void</span> BuildIndex(<span style="color: #2b91af;">List</span>&lt;<span style="color: #2b91af;">Product</span>&gt; products)</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">FSDirectory</span> directory = <span style="color: #2b91af;">FSDirectory</span>.Open(<span style="color: blue;">new</span> System.IO.<span style="color: #2b91af;">DirectoryInfo</span>(<span style="color: #a31515;">&quot;C:\\temp\\&quot;</span>));</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Analyzer</span> analyzer = <span style="color: blue;">new</span> <span style="color: #2b91af;">StandardAnalyzer</span>(Lucene.Net.Util.<span style="color: #2b91af;">Version</span>.LUCENE_29);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">IndexWriter</span> indexWriter = <span style="color: blue;">new</span> <span style="color: #2b91af;">IndexWriter</span>(directory, analyzer, <span style="color: blue;">true</span>, <span style="color: #2b91af;">IndexWriter</span>.<span style="color: #2b91af;">MaxFieldLength</span>.UNLIMITED);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">foreach</span> (<span style="color: #2b91af;">Product</span> product <span style="color: blue;">in</span> products)</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; indexWriter.AddDocument(product.GetDocument());</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; indexWriter.Optimize();</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; indexWriter.Close();</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
</div>
<p><br/></p>
<p>
The FSDirectory is just an abstraction of the storage of the index, and there are &#8220;directory&#8221; classes that represent in-memory storage, etc. that you can use as well. You can pass a DirectoryInfo object to the Open method to specify where to store the search index.
</p>
<p>
The Analyzer&#8217;s job is to parse, tokenize, and index your data. There are a number of different Analyzers implemented in Lucene, but the StandardAnalyzer is the most straightforward. The StandardAnalyzer will do a few things to your text &#8211; including removing junk search terms (aka &#8220;stop words&#8221;) and punctuation, and normalizing the case of your text. There are a number of constructors available for the StandardAnalyzer, and you can specify your own stop words if you like, but there is a list of common stop words built into Lucene. There is another good analyzer available called the SnowballAnalyzer, which will remove suffixes and prefixes from your text, which can greatly improve your search results. The SnowballAnalyzer is a separate Lucene project that is outside of the main source code, it can be found under the contrib folder in the Lucene source (not in the main Lucene.Net solution) &#8211; build it yourself and include it in your project if you would prefer to use it instead of the StandardAnalyzer.
</p>
<p>
The IndexWriter is responsible for creating the index. The IndexWriter is actually thread safe, and an index can be rebuilt while being read from at the same time without you having to manage the locking of the index files. Lucene takes care of that for you. There is a boolean parameter on the constructor that indicates whether or not to recreate or append to the index. Simply call the AddDocument method on the IndexWriter to write documents to the index. When you&#8217;re finished writing documents to the index, you must call the Close method. Optionally, you can call the Optimize method before closing the index which will greatly shrink the size of the index &#8211; however, this can take a few seconds sometimes so you may not want to call Optimize if you have indexing performance concerns.
</p>
<p>
Now that we have the Index built, we can move on to actually searching the index…
</p>
<h2>
Searching the Index<br />
</h2>
<p>Below is an example method that you could use to search your newly created product search index, you could potentially add it into your Index class.  You&#8217;ll see a few of the same classes from the indexing sample being used in the search method.  As in the previous example, you&#8217;ll use the FSDirectory class to specify where the index is located.  Then, you&#8217;ll need to create an IndexReader, passing in your directory object.  The second parameter of the IndexReader specifies whether or not to open the index in read-only mode &#8211; for our simple purposes, we only need to read from the index.  One thing to note about the IndexReader is that it is fairly expensive to create, so you don&#8217;t want to create one every time you&#8217;re doing a search in your web application for example. Create a single IndexReader &#8211; perhaps in a singleton pattern or by caching the IndexReader object, and re-use that IndexReader. Next, we need an IndexSearcher to actually search our index, fairly straightforward.  </p>
<p>When searching, the search queries  must be parsed and tokenized in the same way that the data was parsed when it was placed into the index.  Due to this, one very important thing to note is that when searching, the same type of Analyzer that was used to create the index must also be used to parse the search queries.  If a StandardAnalyzer is used to create the index, a StandardAnalyzer must also be used to parse search queries against the index.  The QueryParser actually parses the query text against the field that is going to be searched against &#8211;  as you can see in the QueryParser constructor, we&#8217;ll be searching against the &#8220;ProductName&#8221; field from our documents.  After that, simply call the Parse method on the QueryParser to get the Query that we&#8217;ll pass to the searcher.  To note, if you want to search on multiple fields &#8211; say we wanted to search on the Product Name and the Color, you can use the MultiFieldQueryParser class to query against multiple fields.  With the MultiFieldQueryParser, you can even do some clever things like weighting fields differently, i.e. if I wanted product name matches to rank higher than color matches.</p>
<p>Next, we&#8217;ll create a collector that will define how the search results are collected from the searcher &#8211; we&#8217;ll use a TopScoreDocCollector.  The first parameter is the maximum number of results, and the second parameter determines whether or not the results are sorted in order of search relevancy.  For our purposes, we want to show the customers the best results for their search query so we&#8217;ll obviously want our results sorted in order.  From there, simply call the Search method on the searcher, passing in the query and the document collector and receive a collection of scored matches based on the search query.  For each match, you can call the .Doc method on the searcher to retrieve the actual full Document that was placed in the Index originally. After I&#8217;ve collected up the Product IDs from the search result documents, I go back and fetch the full Product objects from the database. Depending on what fields you choose to store in your Lucene index, you may not need to re-fetch what you&#8217;re searching for from the database. It&#8217;s a good idea to store only enough data to display the search results, that way you don&#8217;t need to make a trip to the database just to display your search results.</p>
<div style="font-family: Courier New; font-size: 10pt; color: black; background: white;">
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">public</span> <span style="color: #2b91af;">List</span>&lt;<span style="color: #2b91af;">Product</span>&gt; SearchProductName(<span style="color: blue;">string</span> productName)</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">FSDirectory</span> directory = <span style="color: #2b91af;">FSDirectory</span>.Open(<span style="color: blue;">new</span> System.IO.<span style="color: #2b91af;">DirectoryInfo</span>(<span style="color: #a31515;">&quot;C:\\temp\\&quot;</span>));</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">IndexReader</span> reader = <span style="color: #2b91af;">IndexReader</span>.Open(directory, <span style="color: blue;">true</span>);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Searcher</span> searcher = <span style="color: blue;">new</span> <span style="color: #2b91af;">IndexSearcher</span>(reader);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Analyzer</span> analyzer = <span style="color: blue;">new</span> <span style="color: #2b91af;">StandardAnalyzer</span>(Lucene.Net.Util.<span style="color: #2b91af;">Version</span>.LUCENE_29);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">QueryParser</span> parser = <span style="color: blue;">new</span> <span style="color: #2b91af;">QueryParser</span>(Lucene.Net.Util.<span style="color: #2b91af;">Version</span>.LUCENE_29, <span style="color: #a31515;">&quot;ProductName&quot;</span>, analyzer);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Query</span> query = parser.Parse(productName);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">TopScoreDocCollector</span> collector = <span style="color: #2b91af;">TopScoreDocCollector</span>.create(100, <span style="color: blue;">true</span>);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; searcher.Search(query, collector);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">ScoreDoc</span>[] hits = collector.TopDocs().scoreDocs;</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">List</span>&lt;<span style="color: blue;">int</span>&gt; productIds = <span style="color: blue;">new</span> <span style="color: #2b91af;">List</span>&lt;<span style="color: blue;">int</span>&gt;();</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">foreach</span> (<span style="color: #2b91af;">ScoreDoc</span> scoreDoc <span style="color: blue;">in</span> hits)</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">//Get the document that represents the search result.</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">Document</span> document = searcher.Doc(scoreDoc.doc);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">int</span> productId = <span style="color: blue;">int</span>.Parse(document.Get(<span style="color: #a31515;">&quot;Id&quot;</span>));</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">//The same document can be returned multiple times within the search results.</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">if</span> (!productIds.Contains(productId))</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; productIds.Add(productId);</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: green;">//Now that we have the product Ids representing our search results, retrieve the products from the database.</span></p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #2b91af;">List</span>&lt;<span style="color: #2b91af;">Product</span>&gt; products = <span style="color: #2b91af;">ProductDAO</span>.GetProductsByIds(productIds);</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; reader.Close();</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; searcher.Close();</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; analyzer.Close();</p>
<p style="margin: 0px;">&nbsp;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: blue;">return</span> products;</p>
<p style="margin: 0px;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p>
</div>
<p><br/></p>
<p>
Again, keep in mind this is only an example method.  The examples above are based around searching rows that live in a database, but they could be easily adapted to searching through a directory of files, or searching through indexed web pages.  The Lucene class structure, to me seems highly abstracted &#8211; this is to allow for ultimate flexibility. Search is a finicky thing and you&#8217;ll always run into scenarios where your client doesn&#8217;t like the way the search works &#8211; that&#8217;s fine, because Lucene gives you the flexibility to change how the search works.</p>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/getting-started-with-lucene-net/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Exceptions while upgrading SQL Server CE project</title>
		<link>http://jsprunger.com/exceptions-while-upgrading-sql-server-ce-project/</link>
		<comments>http://jsprunger.com/exceptions-while-upgrading-sql-server-ce-project/#comments</comments>
		<pubDate>Sun, 28 Feb 2010 20:49:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[sql server ce]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=104</guid>
		<description><![CDATA[I was recently trying to ressurect an older project developed in Windows XP, .NET Framework 2.0, Visual Studio 2005, NHibernate, and SQL Server CE 3.1.  I&#8217;ve sinced moved to Windows 7 (64-bit) and Visual Studio 2008. I ran into a surprising number of hurdles while trying to get the application up and running again on [...]]]></description>
			<content:encoded><![CDATA[<p>I was recently trying to ressurect an older project developed in Windows XP, .NET Framework 2.0, Visual Studio 2005, NHibernate, and SQL Server CE 3.1.  I&#8217;ve sinced moved to Windows 7 (64-bit) and Visual Studio 2008.</p>
<p>I ran into a surprising number of hurdles while trying to get the application up and running again on 64-bit Windows 7.  I figure I would document this here, just in case anyone else runs into the same issues.</p>
<p>Step 1) Try to build the solution.  Everything builds fine after installing SQL Server Compact Edition.</p>
<p>Step 2) Try to run the application.  Get an exception immediately:</p>
<p>&#8220;Could not create the driver from NHibernate.Driver.SqlServerCeDriver.&#8221;</p>
<p>InnerException:</p>
<p>&#8220;The IDbCommand and IDbConnection implementation in the assembly System.Data.SqlServerCe could not be found. Ensure that the assembly System.Data.SqlServerCe is located in the application directory or in the Global Assembly Cache. If the assembly is in the GAC, use &lt;qualifyAssembly/&gt; element in the application configuration file to specify the full name of the assembly.&#8221;</p>
<p>Turns out the issue here is that the System.Data.SqlServerCe dll has to be in the same folder as the application executable.  Pretty easy fix &#8211; set Copy Local to &#8216;True&#8217; on the reference to System.Data.SqlServerCe.</p>
<p>Step 3) Run the application again &#8211; now I get a different exception:</p>
<p>&#8220;Unable to load DLL &#8216;sqlceme35.dll&#8217;: The specified module could not be found. (Exception from HRESULT: 0x8007007E)&#8221;</p>
<p>Turns out the issue with this exception is that SQL Server Compact Edition is built for x86 and has to run in WoW mode on x64 systems.  My solution platform is set to &#8216;Any CPU&#8217;, which worked fine when I was developing on Windows XP.  To fix the issue, go through all of the Visual Studio projects &#8211; go to Properties &gt; Build &gt; Platform Target, and set Platform Target to &#8216;x86&#8242; instead of &#8216;Any CPU&#8217;.</p>
<p>Step 4) Try to run the application again&#8230; and I get yet another exception:</p>
<p>&#8220;ADOException: cannot open connection&#8221; with InnerException of:</p>
<p>&#8220;The database file has been created by an earlier version of SQL Server Compact. Please upgrade using SqlCeEngine.Upgrade() method.&#8221;</p>
<p>This is kind of annoying &#8211; the Visual Studio 2008 Upgrade Wizard changed all my references from SQL Server CE 3.1 to SQL Server 3.5.  How thoughtful.  Unfortunately, I don&#8217;t know what the implications of &#8216;upgrading&#8217; the database are.  Everything worked fine with 3.1 &#8211; why introduce any more change to the application?  So, I set the references back to SQL Server CE 3.1 instead of 3.5.</p>
<p>Step 5) Run the application&#8230; again.</p>
<p>No exceptions! Everything works with SQL Server 3.1! Upgrade complete.</p>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/exceptions-while-upgrading-sql-server-ce-project/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Search Engine Optimization 101</title>
		<link>http://jsprunger.com/search-engine-optimization-seo-101/</link>
		<comments>http://jsprunger.com/search-engine-optimization-seo-101/#comments</comments>
		<pubDate>Thu, 11 Feb 2010 03:44:49 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[search engine optimization]]></category>
		<category><![CDATA[seo]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=88</guid>
		<description><![CDATA[One thing that any web developer worth their salt should know is the basics of search engine optimization (SEO).  Much of SEO comes down to basic code-level best practices, and it isn&#8217;t terribly difficult to simply bake SEO into your development process when working on public facing web applications.  However, keep in mind that SEO [...]]]></description>
			<content:encoded><![CDATA[<p>One thing that any web developer worth their salt should know is the basics of search engine optimization (SEO).  Much of SEO comes down to basic code-level best practices, and it isn&#8217;t terribly difficult to simply bake SEO into your development process when working on public facing web applications.  However, keep in mind that SEO will always be an evolving, fuzzy science, changing on the whim of the indexing strategies of major search engines.  Immediate results are rare, and a long term process should be in place to truly understand the benefit (or detriment) incurred.</p>
<p>I break the concept of SEO down into a few categories that I&#8217;ll explain further below&#8230;</p>
<ol>
<li>Content SEO (internal factors)</li>
<li>Strategic SEO (external factors)</li>
<li>Insight and Tracking</li>
</ol>
<h3>Content / Internal SEO</h3>
<p>These &#8216;Content / Internal&#8217; best practices are things that a developer or content creator can bake in during the site development process.  Only a few of these items will make a difference on their own, but as a whole can make an enormous impact.  These basic factors should lay the foundation for any SEO strategy. However, these internal factors absolutely cannot be the only part of your SEO strategy.  Here are a few of the most important ones&#8230;</p>
<ul>
<li>Page Titles.  Arguably one of the most important content level factor, this is one of the few that can make an enormous difference on their own.  Your page titles (what goes in the HTML &lt;title&gt; tag) should be relevant to what is on the page whereas I often come across page titles that only contain the name of the site.  Instead, you should have the &#8216;title&#8217; of the page prefixed or appended to the name of your site.  Some believe that appending the name of your site to the page title is better than prefixing.</li>
<li>Page URLs.  This goes hand in hand with your Page Titles, as page URLs carry almost equal important.  The URLs of your pages should mirror closely the titles of your pages, but don&#8217;t need to be exact.  Popular opinion is that the closer keywords in your URL are to the end of your domain name, the better.  Search engines have a very &#8216;human&#8217; behavior in this case&#8230; tell me, which URL is more descriptive about this post? &#8211; &#8220;http://jsprunger.com/search-engine-optimization-101/&#8221; or &#8220;http://jsprunger.com/?p=88&#8243;.  Search engines think the same way.</li>
<li>Freshly updated and unique content.  The more your web site content is updated, the more often it will be indexed by search engines.  Sites with freshly updated content seem to get a bonus from most search engines.  Bloggers in particular should ensure that their sites are configured to &#8216;ping&#8217; a service like <a href="http://pingomatic.com/">Ping-o-Matic</a> whenever you create a new post, this will immediately notify Google and many other services of your new content.  Having unique content is perhaps one of the most important factors, simply rehashing or copying content will get you absolutely nothing from most major search engines &#8211; in fact, duplicate content can seriously hurt your rankings.</li>
<li>Keyword usage in your content.  Also a highly important factor, whatever keywords that you want to rank for &#8211; make sure you&#8217;re using them in your content.  Think about what your customers or clients going to search for.  A few guidelines for keyword usage&#8230;
<ul>
<li>Don&#8217;t overuse your keywords, don&#8217;t be spammy.  Find the right balance between keyword usage and having readable, engaging content.</li>
<li>Make sure you have your keywords in your page title and URLs.</li>
<li>Use keywords within the first 100 words of the page or within HTML headers.</li>
<li>Get your keywords used in external links to your site.  More on this later&#8230;</li>
</ul>
</li>
<li>Image alt tags.  This is a pretty minor SEO factor, but very important if you have any interest in getting results from services like Google Image Search.  The productivity from image search results is usually pretty low for most businesses, but every little bit can help sometimes.  Some web sites (i.e. e-commerce, product catalogs) can benefit from image search much more than others.  Make sure you have descriptive &#8216;alt&#8217; attributes on your &lt;img&gt; tags &#8211; this is a best practice for usability and accessibility in general though.</li>
<li>Meta keywords and descriptions.  Long gone are the days of meta tags being useful for SEO.  However, the meta description tag can still play a huge role in your pages getting click-through <em>from the search results. </em>Google will use the meta description of your page as the &#8216;teaser&#8217; for the search result, but if you&#8217;re missing this tag you&#8217;ll often just see garbage or irrelevant content for the teaser.  Users are much more likely to click through to your content in search results if the result description is accurate and compelling.</li>
<li>Updated Sitemap and sitemap.xml file.  Keeping an up to date listing all of the content on your site in a sitemap will greatly enhance the ability of search engines to properly index 100% of the content on your web site.  You can use a tool like the <a href="http://code.google.com/p/googlesitemapgenerator/">Google Sitemap Generator</a> to keep a continually updated sitemap file.</li>
<li>Avoid so-called &#8216;black hat&#8217; or any sort of sneaky SEO techniques.  These strategies usually revolve around hiding or cloaking text on your pages in an attempt to fool search engines.   It isn&#8217;t worth it &#8211; leading search engines can easily detect and adapt to these techniques, resulting in your search rankings taking a dive or even a complete blacklisting of your site.</li>
</ul>
<h3>Strategic / External SEO</h3>
<p>Strategic SEO includes all of the factors external to your website that can affect your search engine rankings.  The number one external factor is getting &#8216;backlinks&#8217; to your content, this is what made Google so ridiculously powerful and accurate &#8211; and their rankings are still very much based on the number, diversity, and quality of links to to your site.</p>
<p>Backlinking can be explained with this anecdote: Several years ago you could search for &#8216;Miserable Failure&#8217; on Google and the number one result was the White House biography page for George Bush.  This was due to a simple viral campaign to get people to put links on their websites, comments, blog posts, etc. linking to the biography page with the anchor text &#8216;Miserable Failure&#8217;.  That&#8217;s how backlinks work.  The more external, inbound links to your site, the more &#8216;authoritative&#8217; your site appears to be in the eyes of major search engines.</p>
<p>But how can you get these backlinks? A few examples&#8230;</p>
<ul>
<li>Mainstream media and press releases.  Old fashioned, but if this is relevant to your industry, press releases for important announcements make their way around the internet very quickly.  This obviously works best if the press releases link back to your web site.</li>
<li>Getting linked and promoted in blog posts.  Do your friends, colleagues, or business partners have blogs or websites? Ask or barter with them to promote your content, requesting specific keywords be used in links to your web site.  This is a two way street &#8211; the more you&#8217;re willing to promote content from other sites, the more they&#8217;ll be willing to promote you back.  However, popular opinion is that one-way links are deemed to be of higher quality in the eyes of major search engines.</li>
<li>Twitter (annoying or ridiculous as many believe it to be) can be a great way to spread the word about your content.  Maybe you&#8217;ll get lucky and someone with 30,000 followers will retweet your link if you&#8217;ve included the proper hashtags.  After this happens, you&#8217;ll start to see your links pop up all over the internet.</li>
<li>Social Bookmarking.  Submitting your content to social bookmarking sites like Del.icio.us, Reddit, and Digg or more niche-specific sites can be a great way to spread the word about your content.  These services will also often directly link to your content with the exact text that you&#8217;ve specified &#8211; bonus!  Don&#8217;t be a spammer though, if you have high quality, unique content that people actually want to see &#8211; submit it.  If not, don&#8217;t bother.</li>
<li>Make it easy for your readers to submit your content to social bookmarking sites, for example &#8211; drop an AddThis button on your website like the one at the top of this post.  This allows your users to easily link and promote your content if they find it valuable.</li>
<li>Targeted submissions.  Do you have niche content? Find targeted venues for submitting your content and articles. For example, you&#8217;ve written an article relevant to the Healthcare industry. Track down some Healthcare industry groups on LinkedIn and submit your article to the news sections. Contact industry publications, they&#8217;re often happy to include high-quality articles.</li>
<li>Alliances and partnerships.  Work with your business partners and allies to cross promote each other where applicable.  For example, you&#8217;re a partner for a specific vendor. If you work closely with that vendor, they&#8217;re often more than happy to promote their most capable partners by linking them on pages within their own websites.</li>
</ul>
<h3>Insight and Tracking</h3>
<p>As mentioned previously, part of SEO includes a process testing out your SEO changes and tracking their effectiveness over time.  A variety of free and paid tools are available to assist you in analyzing your search rankings, search terms, and keyword effectiveness.  Below I&#8217;ve listed a few tools that can help.</p>
<ul>
<li>Google Analytics &#8211; by far the best free website traffic tracking software that I&#8217;ve ever used.  Formerly known as Urchin, Google Analytics allows you to slice, dice, drill down, and report into your tracking data any way you like.  Even better, Google Analytics allows you to configure &#8220;goals&#8221; for your web site which are basically actionable things that users of your site can perform that are of value to you, the business owner.  For example, submitting a contact form, downloading a white paper, completing a transaction, etc.  Dollar amounts, if applicable, can be tied to goals, allowing you to determine the exact revenue per visitor.  This effectively allows you to determine the most valuable incoming keywords and most effective traffic sources for your web site.  Beyond visitor value, Google Analytics can help you determine many more important statistics.  For example&#8230;
<ul>
<li>Most popular content on your website</li>
<li>Browser capabilities of your visitors</li>
<li>Location and language preferences of your visitors</li>
<li>Most popular search terms used to find your web site</li>
<li>Tracking of your CPC ad campaigns</li>
<li>Tracking visitor loyalty</li>
<li>Tracking the top exit pages for your web site (pages where visitors leave)</li>
</ul>
</li>
<li>Keyword ranking monitoring and reporting.  There are a variety of free and paid tools that will allow you to continually monitor and report on current and historical keyword rankings for your own website, as well as the keyword rankings for your competitor&#8217;s websites.  These tools will allow you to see if you&#8217;re making progress on increasing your search rankings.
<ul>
<li>Free Tools
<ul>
<li><a href="http://www.kpmrs.com/" target="_blank">Keyword Position Ranking Monitoring Service</a></li>
<li><a href="http://www.digitalpoint.com/tools/keywords/" target="_blank">DigitalPoint</a></li>
</ul>
</li>
<li>Paid Tools
<ul>
<li><a href="http://www.advancedwebranking.com/" target="_blank">Caphyon Advanced Web Ranking</a></li>
<li><a href="http://www.checkrankings.com/" target="_blank">CheckRankings</a></li>
</ul>
</li>
</ul>
</li>
<li>SEO analysis tools such as the <a href="http://www.iis.net/expand/SEOToolkit" target="_blank">Microsoft SEO Toolkit</a> allow you to analyze the your website to check for content level flaws such as broken links and duplicate content that can affect your search engine rankings.  The Microsoft SEO Toolkit allows you to view detailed information about SEO problems on your website using built-in reports and dashboards &#8211; an extremely useful tool to use when analyzing the state of SEO on an existing web site.</li>
</ul>
<p>There is much more to search engine optimization than can be written up in a single blog post (see also: thousands of blogs dedicated purely to the subject).  However, I hope this quick guide to the basics will give you the tools necessary to implement numerous high impact SEO quick wins for a client or personal web site. For web developers, the factors listed above should be kept in mind whenever developing customer-facing websites that could benefit from enhanced search results and search rankings.  Most of the &#8216;content / internal&#8217; best practices can be easily baked into the development process of almost any e-commerce or content management system implementation project.</p>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/search-engine-optimization-seo-101/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>.NET CF and Windows Mobile Performance Best Practices</title>
		<link>http://jsprunger.com/net-cf-and-windows-mobile-performance-best-practices/</link>
		<comments>http://jsprunger.com/net-cf-and-windows-mobile-performance-best-practices/#comments</comments>
		<pubDate>Mon, 01 Feb 2010 15:30:47 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[.net cf]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[windows mobile]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=78</guid>
		<description><![CDATA[The impact of performance is much more readily apparent in .NET Compact Framework applications.  The mobile devices commonly have a CPU that is 10 times slower than your desktop CPU, and possibly up to 100 times less RAM than a desktop or server.  In Agile or XP development, the mantra is often to ignore performance [...]]]></description>
			<content:encoded><![CDATA[<p>The impact of performance is much more readily apparent in .NET Compact Framework applications.  The mobile devices commonly have a CPU that is 10 times slower than your desktop CPU, and possibly up to 100 times less RAM than a desktop or server.  In Agile or XP development, the mantra is often to ignore performance considerations until necessary &#8211; I don&#8217;t think you can apply that to .NET CF development or it will really bite you in the end.  You don&#8217;t have to go nuts and optimize everything up front, but there are some very important things to keep in mind when developing a Windows Mobile application&#8230;</p>
<h3>Standard .NET Framework Performance Considerations</h3>
<p>Many of the standard .NET Framework performance best practices can become apparent very quickly including&#8230;</p>
<ul>
<li>Object Boxing and Unboxing.  Use generics wherever possible and avoid ArrayLists and type conversions.</li>
<li>String and StringBuilder.  Need to perform lots of string concatenations? Use a StringBuilder instead of the &#8216;+&#8217; operator.  When you use the &#8216;+&#8217; operator, a new string object is created each time you concatenate, increasing memory usage.  The &#8216;+&#8217; operator is much slower if you&#8217;re concatenating a large number of strings.</li>
<li>Memory leaks.
<ul>
<li>When doing .NET CF development, if an object implements the Dispose() method &#8211; call it when you are finished with the object.</li>
<li>One of the most common causes of memory leaks is unhandling events when they&#8217;re no longer needed.  If you manually hook up an event with the &#8216;+=&#8217; operator, ensure you&#8217;re unhandling it when finished with the &#8216;-=&#8217; operator.</li>
<li>Pre-allocate collections if possible.  Standard .NET behavior is to automatically double the size of a collection when the upper limit is reached while adding items.  If you know the number of elements that are are going to be in a collection, pre-allocate the size of the collection when instantiating it.</li>
</ul>
</li>
<li>Don&#8217;t use Exceptions for flow control in an application.  Exceptions are an expensive operation, performance wise.  I&#8217;m not saying don&#8217;t use exceptions, but don&#8217;t use them in areas where you can perform simple checks to prevent them from being thrown.  For example, if you might divide by zero &#8211; perform a simple check before the operation occurs rather than handling a DivideByZeroException.  The check is much less expensive than the exception.</li>
</ul>
<h3>.NET Compact Framework-Specific Performance Considerations</h3>
<p>However, the .NET Compact Framework is different than the full framework in many ways, leading to a slew of .NET CF specific performance considerations&#8230;</p>
<ul>
<li>Avoid making virtual function calls.  They are up to 40% slower than instance and static function calls.  I don&#8217;t completely understand the reason for this, but you can read more about it <a href="http://blogs.msdn.com/netcfteam/archive/2005/05/04/414820.aspx" target="_blank">here</a> if you&#8217;re interested.</li>
<li>There are a few things in .NET CF that are slow <em>because </em>of virtual calls and object boxing/unboxing.  These include:
<ul>
<li>Reflection.  Very slow in .NET CF.</li>
<li>XML Deserialization and DataSets.  Extremely slow  because reflection is slow.</li>
</ul>
</li>
<li>Avoid creating many copies of Form objects.  Creating a Form is an expensive operation, and unused Form objects are a common cause of memory leak issues. You may want to create your Forms once and cache them in the background for reuse.</li>
<li>You can increase the speed of binding data to controls by using the BeginUpdate and EndUpdate methods on a control before and after your data binding occurs.  This will cause the control to not repaint until the binding is finished.</li>
<li>Cache expensive resources.  For example, don&#8217;t create many different copies of a web service client.  Create a single, cached instance of it that can be used throughout your application.</li>
<li><strong>Always </strong>test your application on a wide range of physical devices.  If the target device is known, at least test on that device.  Some things seem to perform much betterwhen running on the emulator or when executing unit tests on your desktop environment.</li>
<li>This is a more general performance testing best practice, but <strong>always </strong>test with <strong>real data </strong>and <strong>real quantities of data. </strong>This can really bite you on deployment of your application.  I know this from experience &#8211; a great example is that deserializing a few hundred objects is MUCH much faster than deserializing 10,000 objects.  In my experience, deserializing 7,000 very simple DTO objects from an ASMX web service was taking up to 20 minutes in some cases.  To alleviate the issue, we ended up switching to a JSON web service, which was much faster to deserialize.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/net-cf-and-windows-mobile-performance-best-practices/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>.NET CF and Windows Mobile Usability Best Practices</title>
		<link>http://jsprunger.com/net-compact-framework-and-windows-mobile-usability-best-practices/</link>
		<comments>http://jsprunger.com/net-compact-framework-and-windows-mobile-usability-best-practices/#comments</comments>
		<pubDate>Sat, 30 Jan 2010 20:36:12 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[.net cf]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[compact framework]]></category>
		<category><![CDATA[usability]]></category>
		<category><![CDATA[ux]]></category>
		<category><![CDATA[windows mobile]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=75</guid>
		<description><![CDATA[I&#8217;m starting up a short Windows Mobile project again, so I thought it would be a good time to collect some of my best practices for .NET Compact Framework development and post them.  I&#8217;m going to break them down into two sections -  usability, and performance best practices (in another post). Windows Mobile Usability Best [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m starting up a short Windows Mobile project again, so I thought it would be a good time to collect some of my best practices for .NET Compact Framework development and post them.  I&#8217;m going to break them down into two sections -  usability, and performance best practices (in another post).</p>
<h3>Windows Mobile Usability Best Practices</h3>
<p>Microsoft has put together a very specific set of guidelines for Windows Mobile usability &#8211; the point of this is to get a consistent set of look and feel and application experiences on their platform.  Apple has the same sort of guidelines for iPhone development and it really pays off &#8211; most applications have the same consistent look and feel and excellent usability.  Of course, many of these usability guidelines are relevant across many development platforms, but there are some special considerations for mobile development.</p>
<p>Usability is a challenge in mobile development.  Some of the main concerns include&#8230;</p>
<ul>
<li>Limited screen real estate.  In Windows Mobile, the most common size is around 480 x 640 pixels.</li>
<li>Limited input options.  Touch screen.  Potentially no hardware keyboard.  No mouse, and no scroll wheel.</li>
<li>Lighting &#8211; Indoor / Outdoor usage.</li>
<li>Gloves (i.e. warehouse users)</li>
<li>Finger vs. Stylus</li>
</ul>
<p>Here are some of the most important usability guidelines that Microsoft has set forth&#8230;</p>
<ul>
<li>Only display the most relevant information and options on the screen, i.e. don&#8217;t clutter up the screen with 100 different rarely used options.  If a feature is rarely used, place it in a menu or submenu.  If a feature or action is used very often, think about assigning it to one of the standard left or right soft keys.</li>
<li>Use high contrast, sufficiently bright colors.   Lighting conditions are an important factor in mobile development.  For example, think about if your application could be used in low light or outdoor sunlight conditions.</li>
<li>Avoid very small font sizes.  The screen on a mobile device is very small as-is, and actions on a mobile device are often performed at arms length away from the user (in a warehouse, for example).  If a user has to interrupt their workflow to bring the device in front of their face to read the text, then your font is too small.</li>
<li>Make the user interface predictable and consistent in your application, keep &#8216;OK&#8217; and &#8216;Cancel&#8217; actions in the same location throughout your interface.  The same buttons should perform the same actions throughout your application.  To stay consistent with other Windows Mobile interfaces, one recommendation is to always assign the left soft key to &#8216;Back&#8217; or &#8216;Cancel&#8217; actions, and to assign the right soft key to &#8216;Next&#8217; or &#8216;OK&#8217; actions.  Another Microsoft recommendation is to avoid overriding the hardware buttons (i.e. the Home button).</li>
<li>Ensure your UI elements are appropriately sized.  Buttons sized for a stylus should be at least 21 pixels squared, buttons sized for fingerse should be at least 38 pixels squared.</li>
<li>Keep screen rotation in mind &#8211; developing to account for rotation is a pain, but very important for consumer applications.  Your options though are limited to either dynamically resizing the content, or to just design for a square screen.</li>
<li>Scrolling is discouraged in Windows Mobile applications, because it is kind of a pain for the end user.  Try to keep your content on one screen length/width if possible.</li>
<li>If your target devices may feature a keyboard, assign common actions to key shortcuts.  This can greatly increase efficiency for power users.</li>
<li>For displaying information, make use of Summary, Detail, and Edit views.  A &#8216;Summary&#8217; view displays only the most necessary and relevant information about an item.  To access less commonly used information about an item, the user can drill down to a more complete &#8216;Detail&#8217; view.  If a user needs to edit the information, they can access an &#8216;Edit&#8217; view.</li>
<li>Ensure you&#8217;re setting focus on the appropriate text entry fields in bar code scanning scenarios, etc.  If a user is wearing gloves and has to take them off to set focus on a field before they scan a pallet, they&#8217;re going to hate your application.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/net-compact-framework-and-windows-mobile-usability-best-practices/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ASP.NET WebForms Best Practices</title>
		<link>http://jsprunger.com/asp-net-webforms-best-practices/</link>
		<comments>http://jsprunger.com/asp-net-webforms-best-practices/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 00:00:25 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[webforms]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=66</guid>
		<description><![CDATA[Over time while using ASP.NET I&#8217;ve collected a pretty good handful of best practices that I try to employ on my projects &#8211; most of them are things that will simplify the ASP.NET development experience, solutions to common problems, or tips that will just make your life easier.  Most of the best practices are only [...]]]></description>
			<content:encoded><![CDATA[<p>Over time while using ASP.NET I&#8217;ve collected a pretty good handful of best practices that I try to employ on my projects &#8211; most of them are things that will simplify the ASP.NET development experience, solutions to common problems, or tips that will just make your life easier.  Most of the best practices are only applicable to WebForms, but some are applicable to ASP.NET MVC as well.</p>
<ul>
<li> Don&#8217;t write .NET code directly in your ASPX markup (unless it is for databinding, i.e. Eval statements). If you also have a code behind, this will put your code for a page in more than one place and makes the code less manageable. Put all .NET code in your code-behind.  Things can get complex and difficult to debug very quickly when you&#8217;re looking at code executing in two different places.</li>
<li>SessionPageStatePersister can be used in conjunction with ViewState to make ViewState useful without increasing page sizes. Overriding the Page&#8217;s PageStatePersister with a new SessionPageStatePersister will store all ViewState data in memory, and will only store an encrypted key on the client side.  This will make your pages smaller and download faster if you have a lot of ViewState data for some reason, however it will increase your memory usage on the server &#8211; so tread carefully.  See example below for how to use SessionPageStatePersister.</li>
</ul>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw1">override</span> PageStatePersister GetStatePersister<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
<span class="kw1">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> SessionPageStatePersister<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<ul>
<li>Create a BasePage that your pages can inherit from in order to reuse common code between pages.  Simple object oriented design principles &#8211; if you have common functions between pages, like security for example &#8211; put it in a base class that inherits from System.Web.Page, and have your pages inherit from that base page.</li>
<li>Create a MasterPage for your pages for visual inheritance.  Don&#8217;t use ASP server-side includes.  Pages with vastly different visual styles should use a different MasterPage.  Don&#8217;t use a Master page for code inheritance.</li>
<li>Make use of the ASP.NET Cache in order to cache frequently used information from your database.  Build (or reuse) a generic caching layer that will wrap the ASP.NET Cache.  If you&#8217;re loading the same list from the database into a drop down every time a page loads, you should be pulling that list from the cache based on how dynamic it needs to be.</li>
<li>Wrap ViewState objects with Properties on your Pages to avoid development mistakes in spelling, etc. when referencing items from the ViewState collection.  For example, you should only have ViewState["key"] once in your page per property.  See example below.</li>
</ul>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">private</span> <span class="kw4">int</span> SampleId<br />
<span class="br0">&#123;</span><br />
get <span class="br0">&#123;</span> <span class="kw1">return</span> ViewState<span class="br0">&#91;</span><span class="st0">&quot;SampleId&quot;</span><span class="br0">&#93;</span> <span class="sy0">==</span> <span class="kw1">null</span> <span class="sy0">?</span> 0 <span class="sy0">:</span> <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>ViewState<span class="br0">&#91;</span><span class="st0">&quot;SampleId&quot;</span><span class="br0">&#93;</span><span class="sy0">;</span> <span class="br0">&#125;</span></p>
<p>set <span class="br0">&#123;</span> ViewState<span class="br0">&#91;</span><span class="st0">&quot;SampleId&quot;</span><span class="br0">&#93;</span> <span class="sy0">=</span> value<span class="sy0">;</span> <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div>
</div>
<ul>
<li>Avoid putting large objects and object graphs in ViewState, use it mainly for storing IDs or very simple DTO objects.  This is the reason people always complain about huge viewstate &#8211; they&#8217;re storing something like DataSets in ViewState (terrible idea).  If you stick to small objects with a limited number of properties or just integer IDs, your ViewState data will not be unmanageably large and ViewState is totally usable.</li>
<li>Wrap the ASP.NET Session with a SessionManager class to avoid development mistakes in spelling, etc. when referencing items from Session.  Just another way to cut down simple development mistakes.</li>
<li>Make extensive use of the applicationSettings key/value configuration values in the web.config &#8211; wrap the Configuration.ApplicationSettings with a class that can be used to easily retrieve strongly-typed configuration settings without having to remember the keys from the web.config.  If you have settings, behaviors, etc. that need to change between different deployments of your application, those should be control via settings in the web.config.  For example, we&#8217;ll often get requests like &#8216;We want feature X to go live at the end of the month&#8221; &#8211; so build, test, and deploy the update ahead of time.  But, add a web.config value that controls whether or not the feature appears i.e. FeatureXEnabled=&#8221;False&#8221;, on the day of go live just flip it to &#8220;True&#8221;.</li>
<li>Avoid the easiness of setting display properties on your UI controls, instead use CSS styles and classes &#8211; this will make your styles more manageable.  Just a general web development best practice.</li>
<li>Create UserControls in your application in order to reuse common UI functionality throughout your pages. For example, if a drop down list containing a collection of categories will be used in many places in the site &#8211; create a CategoryPicker control that will data bind itself when the page is loaded.  This is my #1 time-saving best practice, yet I&#8217;m always surprised how often I see the same drop down list with the same data getting used the same way on 20 different pages &#8211; yet the same type-unsafe databinding logic is duplicated 20 times!</li>
<li>Use Properties on your UserControls to setup things like default values, different displays between pages, etc. Value type properties can be defined on your UserControls and then be set in your ASP.NET markup by using class level properties on UserControls.  This is a great way to get even more mileage out of reusing your UserControls &#8211; watch out for increased complexity of your UserControl logic though.</li>
<li>Make use of the ASP.NET validation controls to perform simple validations, or use the CustomValidator to perform complex validations.</li>
<li>Create an user-friendly error handling page that can be redirected to when an unhandled exception occurs within your website.  Log any exceptions that come to this page.  The redirection can occur via the Page_Error event in your Base Page, the Application_Error event in your Global.asax, or within the error handling section in the web.config.  Basically, whichever method you pick &#8211; make sure you&#8217;re not letting any exceptions go unhandled or unlogged!</li>
<li>When working with pages that use a highly dynamic data driven display, use the 3rd party (free) <a href="http://www.denisbauer.com/ASPNETControls/DynamicControlsPlaceholder.aspx">DynamicControlsPlaceholder</a> control created by Denis Bauer to simplify the code needed to save the state of dynamically added controls between postbacks.  This little control has saved me countless hours of pain in creating pages with highly dynamic UserControls.  One gotcha &#8211; if you use event handling delegates in a UserControl, you have to hook them up on every postback, little messy but not a big deal though usually.  Event handlers are the only &#8220;state&#8221; that isn&#8217;t saved between postbacks if you use this control.</li>
<li>Turn ViewState off on controls and UserControls that don&#8217;t need it.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/asp-net-webforms-best-practices/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Internet Explorer, iframe, and cross-domain cookies</title>
		<link>http://jsprunger.com/internet-explorer-iframe-and-cross-domain-cookies/</link>
		<comments>http://jsprunger.com/internet-explorer-iframe-and-cross-domain-cookies/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 04:36:41 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[asp.net]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=58</guid>
		<description><![CDATA[Ran into an interesting problem yesterday where a few months ago we helped a client redesign an ASP.NET web application to fit it into an iframe within their CMS rather than being a standalone site.  Easy enough task.  Testing is completed and site is rolled out. Now, several months down the road after the application [...]]]></description>
			<content:encoded><![CDATA[<p>Ran into an interesting problem yesterday where a few months ago we helped a client redesign an ASP.NET web application to fit it into an iframe within their CMS rather than being a standalone site.  Easy enough task.  Testing is completed and site is rolled out.</p>
<p>Now, several months down the road after the application has been iframe&#8217;d and in production &#8211; one random feature of the application is unexpectedly breaking, but it doesn&#8217;t make any sense &#8211; the only way the behavior could possibly occur would be that an object retrieved from Session is coming back as null, which turned out to be the case.  The browser was somehow losing the ASP.NET Session cookie.  Furthermore, the feature was working fine in Firefox but not in Internet Explorer, very strange.</p>
<p>The problem was that Internet Explorer will not accept cookies from a page within an iframe where the domain name is different from the top level page.  So, the url of the iframe&#8217;d page was www.clientsite1.com and the url of the page hosting the iframe was www.clientsite2.com.</p>
<p>To get around this, you need to add a <a href="http://en.wikipedia.org/wiki/P3P">P3P Compact Policy</a> to your HTTP responses.  P3P is a protocol that allows websites to pass information to the browser regarding their intent to use information collected from the user.  Internet Explorer is the only browser that implements the protocol, and only using it for cookie blocking at that.</p>
<p>To add a P3P in ASP.NET that will allow your cookies to be accepted by the browser from a different domain from within an iframe, add this block of code to your Global.asax.</p>
<div class="codesnip-container" >
<div class="csharp codesnip" style="font-family:monospace;"><span class="kw1">protected</span> <span class="kw1">void</span> Application_BeginRequest<span class="br0">&#40;</span><span class="kw4">object</span> sender, EventArgs e<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp;HttpContext.<span class="me1">Current</span>.<span class="me1">Response</span>.<span class="me1">AddHeader</span><span class="br0">&#40;</span><span class="st0">&quot;p3p&quot;</span>,<span class="st0">&quot;CP=<span class="es0">\&quot;</span>IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT<span class="es0">\&quot;</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/internet-explorer-iframe-and-cross-domain-cookies/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Microsoft Photosynth</title>
		<link>http://jsprunger.com/microsoft-photosynth/</link>
		<comments>http://jsprunger.com/microsoft-photosynth/#comments</comments>
		<pubDate>Mon, 09 Nov 2009 05:27:40 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[photosynth]]></category>
		<category><![CDATA[silverlight]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=52</guid>
		<description><![CDATA[Microsoft showed us Photosynth while we were touring the MTC in downtown Chicago a few months ago.  Photosynth is an impressive new Silverlight-based technology they&#8217;ve been working on that can be used to stitch hundreds of images together into a single explorable, zoomable, pannable, and web-viewable panoramic image.   I was looking for a situation where [...]]]></description>
			<content:encoded><![CDATA[<p>Microsoft showed us <a href="http://photosynth.net/default.aspx" target="_blank">Photosynth</a> while we were touring the MTC in downtown Chicago a few months ago.  Photosynth is an impressive new Silverlight-based technology they&#8217;ve been working on that can be used to stitch hundreds of images together into a single explorable, zoomable, pannable, and web-viewable panoramic image.   I was looking for a situation where I could possibly make use of Photosynth since I first saw it.   The amazing panaromic views of Isle Royale National Park in Michigan proved to be the perfect opportunity.</p>
<p>The photosynths are easy to make &#8211; just stand in one place, turn in a circle and take about 50-100 overlapping pictures.  Be sure to zoom in a few times and take some detail shots.  After downloading the Photosynth client and selecting photos to stitch, some pre-processing takes place on your computer and the images are uploaded to the Photosynth website where the final combined result can be viewed.  The resulting photosynth can be embedded in a web page.</p>
<p>You can see an example from Isle Royale below.  Give it a few seconds to load for a better experience while zooming around.  Use the mouse wheel to zoom in and out, and drag the picture around to pan.</p>
<p><iframe frameborder="0" src="http://photosynth.net/embed.aspx?cid=f91a3487-b4a7-4cad-8600-aaeaee855519&#038;delayLoad=true&#038;slideShowPlaying=false" width="500" height="300"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/microsoft-photosynth/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows Azure Storage Breakdown</title>
		<link>http://jsprunger.com/windows-azure-storage-breakdown/</link>
		<comments>http://jsprunger.com/windows-azure-storage-breakdown/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 17:37:50 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[sql azure]]></category>
		<category><![CDATA[windows azure]]></category>

		<guid isPermaLink="false">http://jsprunger.com/?p=43</guid>
		<description><![CDATA[In reading various blog posts, forums, and Stack Overflow questions there is still quite a bit of confusion around the different data storage options available in Windows Azure.  This is probably mostly due to Microsoft changing their strategy, architecture, and naming conventions a few times. For example, SQL Azure used to be called SQL Server [...]]]></description>
			<content:encoded><![CDATA[<p>In reading various blog posts, forums, and Stack Overflow questions there is still quite a bit of confusion around the different data storage options available in Windows Azure.  This is probably mostly due to Microsoft changing their strategy, architecture, and naming conventions a few times.</p>
<p>For example, SQL Azure used to be called SQL Server Data Services (SSDS), was non-relational, and used an Entity Attribute Value (EAV) schema &#8211; giving it a major overlap in the functionality of Azure Table Storage.  This obviously provided a pretty poor migration path to the cloud for existing applications using relational SQL Server storage.  It was also confusing for developers, seeing that there were two non-relational storage options.  So, Microsoft dumped/revamped SSDS and turned it instead into the now fully-relational hosted SQL Server offering, SQL Azure.</p>
<p>Below I&#8217;ve outlined some key bullet points around the various Azure storage options, as well as when each option should be used.</p>
<h3>Azure Blob Storage</h3>
<ul>
<li>You can think of blob storage as the file system of Windows Azure.</li>
<li>Blobs are stored in blob containers.  You can think of a blob container as a folder.</li>
<li>You can give a blob a primary key and some key/value metadata when you upload the blob to storage.</li>
<li>Use the StorageClient class included with the Windows Azure SDK Samples to interact with blob storage from .NET (rather than using the REST API directly).  This greatly simplifies things and adds some additional functionality like retries on failed calls.</li>
<li>Use blob storage to store anything that you would normally store as a file or database blob.</li>
<li>Querying is very limited &#8211; you can only pull a list of blobs by their container or by their primary key.
<ul>
<li>There are two steps to retrieving a blob &#8211; first you pull the metadata, and then you make a separate call to pull down the actual byte content.  For example, retrieving a list of blobs by their container retrieves a list of blob metadata.  This prevents you from pulling unnecessarily pulling down a ton of data.</li>
</ul>
</li>
<li>Azure Blob Storage can be accessed from within the cloud or from outside of the cloud (i.e. a desktop or intranet application).  Your blob storage API URL is publicly accessible, however it requires the use of a secret key to access unless you&#8217;re retrieving public blobs.</li>
</ul>
<h3>Azure Table Storage</h3>
<ul>
<li>Use Azure Table storage to store simple structured data and objects.</li>
<li>You can think of Azure Table Storage as a bunch of stand alone object tables with no relation to each other.  There are no real foreign keys except what you implement on your own.</li>
<li>Azure table storage is implemented as an Entity-Attribute-Value (EAV) database in the backend, but this is mostly abstracted via the StorageClient class included with the Windows Azure SDK samples.</li>
<li>You can store more than one type of object with varying properties in a table, but this is not recommended for the sake of keeping things simple.  This at least allows your objects to evolve over time as you add/remove fields.</li>
<li>Every object that you store in Azure Table Storage must have a PartitionKey and a RowKey.
<ul>
<li>The combination of the Partition Key and Row Key is the Primary Key.</li>
<li>The Partition Key and Row Key are the <strong>only </strong>indexed values on the objects.</li>
<li>The Partition Key can be used to logically partition data within your tables if required &#8211; though you can hard code the Partition Key if you don&#8217;t need it.</li>
<li>The Row Key should be a unique key within the partition.  You could use a Guid, for example.</li>
</ul>
</li>
<li>You can query Azure Table Storage via LINQ, though not all operations (i.e. scalars) are supported.  Performance would definitely be a factor on queries from an EAV database.  Make use of the indexed partition and row keys if possible for faster querying.</li>
<li>Azure Table Storage can also be accessed from within the cloud or from outside the cloud (i.e. a desktop or intranet application).  Your table storage API URL is publicly accessible, however it requires the use of a secret key to access.</li>
</ul>
<h3>Azure Queue Storage</h3>
<ul>
<li>The main use of Azure Queue Storage is for communication between your Azure web and worker roles or between worker roles, i.e. for picking up and dropping off data to be processed.</li>
<li>Can pass simple string messages in a queue.</li>
<li>Similar to other message queueing frameworks.</li>
<li>Also can use the StorageClient class in the Windows Azure SDK samples to abstract the HTTP REST API.</li>
</ul>
<h3>SQL Azure</h3>
<ul>
<li>SQL Azure is very similar to an on-premise SQL Server databases.</li>
<li>You can currently create a 1 GB database (currently $9.99/mo) or 10 GB database (currently $99/mo).  Much more expensive than Windows Azure Storage options (blob/table/queue).</li>
<li>Use SQL Azure for storing complex relational data &#8211; i.e. any time you would normally store data in a database.</li>
<li>For the most part, you can simply change your SQL Server connection string to point to your SQL Azure connection and your application will work fine in most cases.</li>
<li>Your SQL Azure instance is publically accessible via a SQL TCP connection over the internet.  You can retrieve the connection string for your database from the SQL Azure dashboard.
<ul>
<li>Though the database is publicly acessible, you are given access to create basic inbound firewall rules for which hosts are allowed to access your SQL Azure database.  For example, you can set it up such that your database is only accessible from within your Windows Azure roles or you could set it up such that it would only be accessible from IPs originating from your organization.</li>
</ul>
</li>
<li>SQL Azure has much of the same capabilities as on premise SQL Server, i.e. full T-SQL querying capabilities, indexing, stored procedures, triggers, views, etc.</li>
<li>You can connect to SQL Azure via all the standard methods, i.e. ODBC, ADO.NET, PHP Drivers, LLBL, NHibernate, Entity Framework, etc.</li>
<li>Some features are not supported including SQL Profiler, backup, replication, filegroups, manipulation of physical file resources, etc.</li>
<li>Many of the unsupported features are taken care of <em>for you</em> by Azure, i.e. backup, replication, high availability, resource governance, etc.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jsprunger.com/windows-azure-storage-breakdown/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

