<?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>Igor Ostrovsky Blogging &#187; Misc</title>
	<atom:link href="http://igoro.com/archive/category/misc/feed/" rel="self" type="application/rss+xml" />
	<link>http://igoro.com</link>
	<description>On programming, technology, and random things of interest</description>
	<lastBuildDate>Fri, 23 Jul 2010 05:24:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>7 tips for extending browser functionality to Silverlight apps</title>
		<link>http://igoro.com/archive/7-tips-for-extending-browser-functionality-to-silverlight-apps/</link>
		<comments>http://igoro.com/archive/7-tips-for-extending-browser-functionality-to-silverlight-apps/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 07:52:06 +0000</pubDate>
		<dc:creator>Igor Ostrovsky</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[Silverlight]]></category>

		<guid isPermaLink="false">http://igoro.com/?p=164</guid>
		<description><![CDATA[Silverlight 2 is an awesome platform for development of rich web applications. One issue to be aware of, though, is that some browser features do not extend into Silverlight apps. For example, the Back and Forward browser buttons do not always work as the user may expect with Silverlight apps. The set of browser features [...]]]></description>
			<content:encoded><![CDATA[<p>Silverlight 2 is an awesome platform for development of rich web applications. One issue to be aware of, though, is that some browser features do not extend into Silverlight apps. For example, the Back and Forward browser buttons do not always work as the user may expect with Silverlight apps. The set of browser features you&#8217;ll miss in Silverlight apps is pretty much identical to what you&#8217;d miss in Flash or AJAX, and some of them are about to be addressed in the Silverlight 3 release.</p>
<p>Let&#8217;s go over the affected browser features one by one, and discuss ways of getting them working in Silverlight apps.</p>
<h3>Feature 1: Bookmarking and deep linking</h3>
<p><span id="more-164"></span>Users like to be able to copy the link from the address bar, email it to a friend, bookmark it, or post it on Twitter or Facebook.</p>
<p>A Silverlight app will typically transition between different views without redirecting the user to a different URL. This makes the user experience smooth and polished. As an unfortunate consequence, a user that copies the URL from the address bar or bookmarks the page will not get a link to the current active view, but instead a link to the initial view of the app.</p>
<p>Thankfully, there is a solution in Silverlight. A Silverlight app can modify the &#8220;bookmark&#8221; section of the URL, which is the part of the URL following the # character. The app can update the URL each time the user transitions to a different view, and read the bookmark on startup. See <a href="http://silverlight.net/blogs/msnow/archive/2008/09/16/silverlight-tip-of-the-day-41-using-bookmarks-in-your-silverlight-application.aspx">this article</a> for details on how to implement this.</p>
<p>The <a href="http://www.silverlightshow.net/items/The-Silverlight-3-Navigation-Framework.aspx">Navigation Framework</a> feature in the upcoming Silverlight 3 release will provide an easy-to-use solution to this problem.</p>
<h3>Feature 2: Search engine discoverability</h3>
<p>If your Silverlight app links to another page, that link is invisible to search engines. As a result, pages on your site that cannot be reached via traditional HTML links will likely not get listed in search results.</p>
<p>The solution is simple: list all pages on your site in an XML document called a Sitemap. Host the Sitemap on your site, and submit the Sitemap link to search engines. Once you do that, the search engines should include all pages on your site in their indexes.</p>
<p>For details, see the <a href="http://en.wikipedia.org/wiki/Google_Sitemaps">Wikipedia article on Sitemaps</a>.</p>
<h3>Feature 3: Back and Forward browser buttons</h3>
<p>The Back and Forward browser buttons do not work in Silverlight apps. Switching to a different view in a Silverlight app does not insert a bookmark into the browser history, so the user can&#8217;t go to the previous view by clicking the Back button.</p>
<p>The problem is similar to the bookmarking issue (Feature 1), except that the solution is more complicated. If the Silverlight app modifies the bookmark part of the URL, the previous bookmark does not get recorded in the browser history, so the user still cannot use the Back button to return to the previous view.</p>
<p>It is in fact possible to get the Back and Forward buttons working. As of SP1 3.5, ASP.NET exposes a History Control that simplifies the solution significantly (see <a href="http://blog.webjak.net/2008/09/08/control-silverlight-by-using-browser-back-and-foward-buttons/">Control Silverlight by Using Browser Back and Forward Buttons</a>). There is a variety of solutions available online in case you are not using ASP.NET 3.5 SP1, but beware that they typically involve hacks that don&#8217;t necessarily work in all browsers.</p>
<p>Or, you can wait until Silverlight 3, because browser history is properly handled in the Navigation Framework.</p>
<h3>Feature 4: Support of external tools (translation, accessibility, &#8230;)</h3>
<p>Automated translation, special browsers for people with disabilities, automated RSS aggregating&#8230; these are just some examples of useful tools that consume web content. These tools typically process HTML, but don&#8217;t peek inside Silverlight applications.</p>
<p>Since HTML is the standard representation of online documents, consider whether it would make sense to expose a simplified version of your content as a simple HTML page, in addition to your Silverlight application.</p>
<h3>Feature 5: Printing</h3>
<p>Silverlight does not currently have much support for printing. In Internet Explorer 7, Silverlight applications show up on the printout, but in Firefox or Safary they do not.</p>
<p>So, a Firefox or Safari user will not be able to print out the information displayed by your Silverlight application (at least not without taking screenshots, etc).</p>
<p>If you need to support printing, you can try two different solution approaches. If you only need to print content that can be represented using HTML (e.g., no Silverlight-generated charts and such), see <a href="http://jonas.follesoe.no/PrintingInSilverlight2UsingCSSAndASPNETAJAX4.aspx">this solution</a>. An alternate approach is to <a href="http://blog.galasoft.ch/archive/2008/10/10/converting-and-customizing-xaml-to-png-with-server-side-wpf.aspx">convert XAML to an image format on the server</a>.</p>
<h3>Feature 6: Open link in new tab</h3>
<p>There doesn&#8217;t seem to be a very good way to implement the &#8216;Open link in new tab&#8217; feature in Silverlight. You can use a HyperlinkButton with a Target set to _blank, which seems to open a new tab in Firefox and a new Window in IE 7. But that still won&#8217;t allow the user to choose whether to open the page in the same or different tab/window.</p>
<p>If you are committed enough, you can probably implement a HyperlinkButton with a context menu allowing the user to open the link in a new tab/window. Right-clicking is not directly supported in Silverlight, but <a href="http://silverlight.net/blogs/msnow/archive/2008/07/01/tip-of-the-day-14-how-to-right-click-on-a-silverlight-application.aspx">workarounds do exist</a>.</p>
<p>Or, use hyperlinks sparingly in Silverlight apps.</p>
<h3>Feature 7: Find on this page</h3>
<p>I use the &#8216;Find on this page&#8217; feature (CTRL+F) extensively when browsing the web. But, content inside Silverlight apps is not searched by the browser.</p>
<p>A mitigation is to carefully design the app UI in a way that minimizes the user&#8217;s need to search. Data should be sortable and searchable in various ways, and important information should be shown prominently (e.g., the user&#8217;s own position on a scoreboard should be highlighted).</p>
<p>These are all good practices regardless of your development platform, but unavailability of the &#8216;Find on this page&#8217; feature makes them even more important.</p>
<h3>Conclusion</h3>
<p>Hopefully you&#8217;ll find these points helpful when designing your next Silverlight application. If you have other tips on extending browser features into Silverlight apps, let me know in the comments!</p>
]]></content:encoded>
			<wfw:commentRss>http://igoro.com/archive/7-tips-for-extending-browser-functionality-to-silverlight-apps/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Precomputed view: A cool and useful SQL pattern</title>
		<link>http://igoro.com/archive/precomputed-view-a-cool-and-useful-sql-pattern/</link>
		<comments>http://igoro.com/archive/precomputed-view-a-cool-and-useful-sql-pattern/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 08:55:17 +0000</pubDate>
		<dc:creator>Igor Ostrovsky</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://igoro.com/archive/precomputed-view-a-cool-and-useful-sql-pattern/</guid>
		<description><![CDATA[In database terminology, a view is a named query that typically aggregates data from multiple tables. When using views, it is important to remember that querying a view will evaluate the query that defines the view. Repeated evaluation of the view &#8211; say from within a nested query &#8211; may seriously impact or even kill [...]]]></description>
			<content:encoded><![CDATA[<p>In database terminology, a view is a named query that typically aggregates data from multiple tables. When using views, it is important to remember that querying a view will evaluate the query that defines the view. Repeated evaluation of the view &#8211; say from within a nested query &#8211; may seriously impact or even kill the performance of your application.</p>
<p>One solution to this performance problem is to use a &#8220;precomputed view&#8221;. Unlike an ordinary view, a precomputed view is stored in a table rather than computed on demand. When data in one of the aggregated tables changes, the update operation also updates the precomputed view table.</p>
<p><span id="more-137"></span></p>
<p>A great thing about precomputed views is that they can be implemented fully in SQL. Any code that accesses the database sees a precomputed view as a regular table. Also, if you have an existing regular view, you can change it into a precomputed view without having to modify any code that queries the view.</p>
<p>To explain the precomputed view pattern, let&#8217;s look at an example loosely inspired by the <a href="http://reddit.com/">reddit</a> social news site. Users submit articles to reddit and the articles receive up and down votes from other users. User&#8217;s &#8220;karma&#8221; is computed as a sum of votes on positively-voted articles submitted by the user.</p>
<p>To store the reddit data, you can store users in one table, articles in another table, and votes (+1 or -1) from users on articles in a third table:</p>
<p><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="251" alt="image" src="/wordpress/wp-content/uploads/2009/04/image.png" width="513" border="0"> </p>
<p>Now, say that we want to find the top 10 users with highest karma. To compute a user&#8217;s karma, we need to sum up the scores of all articles submitted by the user. And to compute each article score, we need to aggregate the votes for that article. It should be clear that this query is going to be very expensive, no matter how much you tune and optimize it.</p>
<p>Views could be used to factor the complex query into simpler pieces, but not to decrease its overall cost:</p>
<p><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="410" alt="image" src="/wordpress/wp-content/uploads/2009/04/image1.png" width="513" border="0">&nbsp;</p>
<p>It is easy to find the top 10 users, simply by querying User_View. Unfortunately, if the database contains millions and millions of votes, the query will take a long time to run. The query will have to group all votes by article, group all articles by user, and then pick the top users. Imagine the impact on performance if you wanted to show the top users on every page of your web app!</p>
<p>However, by changing the views into precomputed views, we can make the query for top users cheap:</p>
<p><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="425" alt="image" src="/wordpress/wp-content/uploads/2009/04/image2.png" width="572" border="0">&nbsp; </p>
<p>Let&#8217;s walk through the conversion of Article_View to a precomputed view. First, we&#8217;ll create a table for the precomputed view:
<pre class="code"><span style="color: blue">    CREATE TABLE </span>Article_PView<span style="color: gray">(
        </span>ArticleId <span style="color: blue">int </span><span style="color: gray">NOT NULL,
        </span>Title <span style="color: blue">varchar</span><span style="color: gray">(</span>40<span style="color: gray">) NOT NULL,
        </span>Url <span style="color: blue">varchar</span><span style="color: gray">(</span>250<span style="color: gray">) NOT NULL,
        </span>VoteSum <span style="color: blue">int </span><span style="color: gray">NOT NULL,
    )
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Second, we&#8217;ll need two triggers. This trigger inserts a row into Article_PView whenever a new row is inserted into the Article table:
<pre class="code"><span style="color: blue">    CREATE TRIGGER </span>TRG_ArticleInsert
        <span style="color: blue">ON  </span>Article
        <span style="color: blue">AFTER INSERT
    AS
    BEGIN
        SET NOCOUNT ON</span><span style="color: gray">;

        </span><span style="color: blue">INSERT INTO </span>Article_PView <span style="color: gray">(</span>ArticleId<span style="color: gray">, </span>Title<span style="color: gray">, </span>Url<span style="color: gray">, </span>VoteSum<span style="color: gray">)
        </span><span style="color: blue">SELECT </span>Id <span style="color: blue">As </span>ArticleId<span style="color: gray">, </span>Title<span style="color: gray">, </span>Url<span style="color: gray">, </span>0
        <span style="color: blue">FROM </span>Inserted
<span style="color: blue">    END</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>And this trigger recomputes a row in Article_PView whenever a vote is inserted, updated or deleted:
<pre class="code"><span style="color: blue">    CREATE TRIGGER </span>TRG_Vote
        <span style="color: blue">ON  </span>Vote
        <span style="color: blue">AFTER INSERT</span><span style="color: gray">, </span><span style="color: blue">UPDATE</span><span style="color: gray">, </span><span style="color: blue">DELETE
    AS
    BEGIN
        SET NOCOUNT ON</span><span style="color: gray">;

        </span><span style="color: blue">WITH </span>articleIds<span style="color: gray">(</span>ArticleId<span style="color: gray">) </span><span style="color: blue">As
        </span><span style="color: gray">(
            </span><span style="color: blue">SELECT </span>ArticleId <span style="color: blue">From </span>Inserted
            <span style="color: blue">UNION
            SELECT </span>ArticleId <span style="color: blue">From </span>Deleted
        <span style="color: gray">)
        </span><span style="color: blue">UPDATE </span>Article_PView
            <span style="color: blue">SET </span>VoteSum <span style="color: gray">= (
                </span><span style="color: blue">SELECT </span><span style="color: magenta">SUM</span><span style="color: gray">(</span>Vote<span style="color: gray">) </span><span style="color: blue">FROM </span>Vote
                <span style="color: blue">WHERE </span>Article_PView<span style="color: gray">.</span>ArticleId <span style="color: gray">= </span>articleIds<span style="color: gray">.</span>ArticleId<span style="color: gray">)
        </span><span style="color: blue">FROM </span>articleIds
        <span style="color: blue">WHERE </span>Article_PView<span style="color: gray">.</span>ArticleId <span style="color: gray">= </span>articleIds<span style="color: gray">.</span>ArticleId
<span style="color: blue">    END</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>And finally, we&#8217;ll populate the precomputed view with data that is already in the database:
<pre class="code"><span style="color: blue">    INSERT INTO </span>Article_PView <span style="color: gray">(</span>ArticleId<span style="color: gray">, </span>Title<span style="color: gray">, </span>Url<span style="color: gray">, </span>VoteSum<span style="color: gray">)
</span><span style="color: blue">    SELECT </span>Article<span style="color: gray">.</span>Id<span style="color: gray">, </span>Title<span style="color: gray">, </span>Url<span style="color: gray">, </span><span style="color: magenta">ISNULL</span><span style="color: gray">((</span><span style="color: blue">SELECT </span><span style="color: magenta">SUM</span><span style="color: gray">(</span>vote<span style="color: gray">) </span><span style="color: blue">FROM </span>Vote <span style="color: blue">WHERE </span>ArticleId <span style="color: gray">= </span>Article<span style="color: gray">.</span>Id<span style="color: gray">), </span>0<span style="color: gray">)
</span><span style="color: blue">    FROM </span>Article</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Article_PView precomputed view is now ready, and User_PView can be created in a similar fashion. </p>
<p><strong>Remarks</strong> </p>
<p>Note that my example assumes articles never get removed or updated. Adding support for that functionality is straightforward: you&#8217;ll need to extend the TRG_ArticleInsert trigger to also handle updates and deletes. This will be very similar to what TRG_Vote does, but I left it out from the sample for simplicity.
</p>
<p>There are several interesting variations of how Article_PView could be implemented. In the implementation above, the trigger aggregates the votes for an article each time someone votes on it. If you want to avoid this cost, you can change the trigger so that it only adjusts the article score instead of recomputing it. For example, if a vote was updated from -1 to +1, the trigger would add 2 to the score of the article. Avoiding concurrency issues may be tricky with that approach, though.</p>
<p>Another interesting variation is adding the VoteSum column to the Article table instead of creating a separate table. Choose the approach that better fits your table design.</p>
<p>Hope you find this pattern useful!</p>
]]></content:encoded>
			<wfw:commentRss>http://igoro.com/archive/precomputed-view-a-cool-and-useful-sql-pattern/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>How to recover a lost post in Windows Live Writer</title>
		<link>http://igoro.com/archive/how-to-recover-a-lost-post-in-windows-live-writer/</link>
		<comments>http://igoro.com/archive/how-to-recover-a-lost-post-in-windows-live-writer/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 07:16:53 +0000</pubDate>
		<dc:creator>Igor Ostrovsky</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://igoro.com/?p=84</guid>
		<description><![CDATA[Windows Live Writer is an awesome tool that I use to write all of my blog posts. I am so used to it that I can&#8217;t imagine blogging without it, but I have also found ways to shoot myself in the foot with it. Here are two ways in which I managed to lose a [...]]]></description>
			<content:encoded><![CDATA[<p>Windows Live Writer is an awesome tool that I use to write all of my blog posts. I am so used to it that I can&#8217;t imagine blogging without it, but I have also found ways to shoot myself in the foot with it. Here are two ways in which I managed to lose a nearly finished blog post:</p>
<ol>
<li>Live Writer clears its undo history when you switch between views. So, if you mess up your article in the HTML view, you won&#8217;t be able to revert the change when you notice the disaster after switching back into the Normal view.
<li>In the Preview view, Live Writer looks a lot like Internet Explorer. If you are not careful, it is not too hard to close Live Writer by accident. And, the confirmation dialog looks somewhat like IE&#8217;s closing dialog.</li>
</ol>
<p>Thankfully, I found a way to recover Live Writer posts, and saved myself some wasted work.</p>
<p><span id="more-84"></span></p>
<p>Every time you switch Live Writer to the Preview or the Normal View, a copy of the post will be written as a HTML file into a temporary directory. On my machine, each snapshot is saved under C:\Users\Igor\AppData\Local\Temp\WindowsLiveWriter1286139640:</p>
<p>&nbsp;</p>
<p>&nbsp;<img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="600" alt="image" src="http://igoro.com/wordpress/wp-content/uploads/2008/11/image.png" width="703" border="0"> </p>
<p>&nbsp;</p>
<p>Sorting the snapshots by &#8216;Date modified&#8217; column gives you the history of your edits, so long as you regularly switch between the different views in Live Writer.</p>
<p>With a bit of luck, you will find the right version of the lost article somewhere among the snapshots.</p>
]]></content:encoded>
			<wfw:commentRss>http://igoro.com/archive/how-to-recover-a-lost-post-in-windows-live-writer/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
