<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Michael Entin's notebook</title><subtitle type="html">Michael Entin's notebook</subtitle><id>http://blogs.msdn.com/b/michen/atom.aspx</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/b/michen/atom.aspx" /><generator uri="http://telligent.com" version="5.6.50428.7875">Telligent Evolution Platform Developer Build (Build: 5.6.50428.7875)</generator><updated>2007-10-03T15:31:00Z</updated><entry><title>Trust Services Lab updated</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2012/07/09/trust-services-lab-updated.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2012/07/09/trust-services-lab-updated.aspx</id><published>2012-07-10T01:56:00Z</published><updated>2012-07-10T01:56:00Z</updated><content type="html">&lt;p&gt;Trust Services Lab has been updated today:&lt;/p&gt;
&lt;p&gt;&lt;a title="Trust Services Lab Page" href="http://www.microsoft.com/en-us/sqlazurelabs/labs/trust-services.aspx" target="_blank"&gt;http://www.microsoft.com/en-us/sqlazurelabs/labs/trust-services.aspx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The update does not need registration code anymore (ignore "Get Invited" link - it is leftover from previous release and will be removed). Just &lt;a title="http://go.microsoft.com/fwlink/?LinkID=242244" href="http://go.microsoft.com/fwlink/?LinkID=242244" target="_blank"&gt;download SDK&lt;/a&gt;, visit &lt;a title="https://trustservices2.cloudapp.net/" href="https://trustservices2.cloudapp.net/" target="_blank"&gt;the portal&lt;/a&gt; to create server, and check the &lt;a title="http://social.technet.microsoft.com/wiki/contents/articles/7041.learn-more-about-microsoft-codename-trust-services.aspx" href="http://social.technet.microsoft.com/wiki/contents/articles/7041.learn-more-about-microsoft-codename-trust-services.aspx" target="_blank"&gt;documentation and samples&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Note that if you are using previous release - the current and previous services are&amp;nbsp;now both alive, as completely independent services. Each is only compatible with appropriate SDK. You cannot get to the previous service using new SDK or vise versa. Please update to the new SDK/service - it offers many new features (and the old service will be disabled some time in future).&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10328213" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Azure" scheme="http://blogs.msdn.com/b/michen/archive/tags/Azure/" /><category term="Trust Services" scheme="http://blogs.msdn.com/b/michen/archive/tags/Trust+Services/" /></entry><entry><title>Microsoft Codename "Trust Services" Lab at RSA</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2012/02/27/microsoft-codename-quot-trust-services-quot-lab-at-rsa.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2012/02/27/microsoft-codename-quot-trust-services-quot-lab-at-rsa.aspx</id><published>2012-02-28T02:33:15Z</published><updated>2012-02-28T02:33:15Z</updated><content type="html">&lt;p&gt;We have a Trust Services booth at the RSA conference in San Fancisco - good time to come, learn more&amp;nbsp;and see several cool demos.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10273717" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Trust Services" scheme="http://blogs.msdn.com/b/michen/archive/tags/Trust+Services/" /></entry><entry><title>Microsoft Codename "Trust Services" Lab</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2012/02/03/microsoft-codename-trust-services-lab.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2012/02/03/microsoft-codename-trust-services-lab.aspx</id><published>2012-02-03T20:07:00Z</published><updated>2012-02-03T20:07:00Z</updated><content type="html">&lt;p&gt;I did not post for a long time, was mostly working on internal and not-yet-released projects.&lt;/p&gt;
&lt;p&gt;But today the project I've been working recently has shipped our first Lab:&lt;/p&gt;
&lt;p&gt;&lt;a title="Trust Services Lab" href="http://www.microsoft.com/en-us/sqlazurelabs/labs/trust-services.aspx" target="_blank"&gt;Microsoft Codename "Trust Services"&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Trust and Security have been hot topics for the public cloud since its inception.&amp;nbsp; Corporate IT departments and CIOs have repeatedly expressed concerns over the loss of control associated with moving various levels of sensitive data to a public cloud.&amp;nbsp; At the same time, the overall benefits of a public cloud are tremendous and continue to gain momentum.&amp;nbsp; This means that many organizations have a pressing need to migrate to public cloud infrastructure in spite of ongoing concerns about security.&lt;/p&gt;
&lt;p&gt;Encryption is one of the fundamental required tools for protecting data in the cloud.&amp;nbsp;However, when you start encrypting data, you are facing with question how do you manage encryption keys and encryption policies? There are several options (you will probably recognize the products using them):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have keys owned by the cloud as well, but stored separately from data (Server Side Encryption) - this provides a bit of improvement, but not much of a real security&lt;/li&gt;
&lt;li&gt;Use&amp;nbsp;a single key that unlocks the data (Simple Encrypting Client) - secure, but not manageable, you have to give the same key to all the parties, have no way to revoke keys when one of the parties leaves, etc&lt;/li&gt;
&lt;li&gt;Have an on-premise&amp;nbsp;security middleware&amp;nbsp;- secure, but not very useful, since now all the users (even mobile users) have to go through this middleware - you lose most of the benefits of the cloud, plus have to purchase and maintain on-premises hardware and software&lt;/li&gt;
&lt;li&gt;Store keys, authorizations&amp;nbsp;and encryption policies in the cloud, but signed and encrypted with individual keys given to each party - so that&amp;nbsp;each party can locally verify authenticity of the policies and only authorized parties have access to&amp;nbsp;and are able to decrypt the sensitive data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can guess the Trust Services follows the latest approach, and provides a unique combination of end-to-end application level encryption and power of the cloud to roam encryption keys, while leaving the owner in full control of his data. It enables data driven applications to work with sensitive data, securely stored in different cloud-based storages while continuing to maintain control over access to this data.&lt;/p&gt;
&lt;p&gt;Today we shipped the service (&lt;a title="the service signup link" href="https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13835" target="_blank"&gt;sign up link)&lt;/a&gt;, the &lt;a title="Client SDK download page" href="http://www.microsoft.com/download/en/details.aspx?id=28791" target="_blank"&gt;client SDK&lt;/a&gt; and &lt;a title="Links to samples code and description" href="http://social.technet.microsoft.com/wiki/contents/articles/7041.learn-more-about-microsoft-codename-trust-services.aspx" target="_blank"&gt;four samples of client applications&lt;/a&gt; utilizing the service+SDK.&lt;/p&gt;
&lt;p&gt;Check out the video at the &lt;a title="Trust Services Lab" href="http://www.microsoft.com/en-us/sqlazurelabs/labs/trust-services.aspx" target="_blank"&gt;Trust Services Lab Link&lt;/a&gt;, then read more at the &lt;a title="Trust Services TechNet article" href="http://social.technet.microsoft.com/wiki/contents/articles/7041.learn-more-about-microsoft-codename-trust-services.aspx" target="_blank"&gt;Trust Services TechNet article&lt;/a&gt;.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10263840" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Azure" scheme="http://blogs.msdn.com/b/michen/archive/tags/Azure/" /><category term="Trust Services" scheme="http://blogs.msdn.com/b/michen/archive/tags/Trust+Services/" /></entry><entry><title>Computer display size calculator</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2011/07/07/computer-display-size-calc.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2011/07/07/computer-display-size-calc.aspx</id><published>2011-07-07T07:31:00Z</published><updated>2011-07-07T07:31:00Z</updated><content type="html">&lt;p&gt;
&lt;script type="text/javascript"&gt;// &lt;![CDATA[
        function update_area() {
            var diagonal = parseFloat(document.getElementById("Diagonal").value);
            var hr = parseInt(document.getElementById("HResolution").value);
            var vr = parseInt(document.getElementById("VResolution").value);
            var aspect = 1.0 * hr / vr;
            var w = diagonal / Math.sqrt(1 + 1 / (aspect * aspect));
            var h = diagonal / Math.sqrt(1 + (aspect * aspect));
            var a = Math.round(w * h * 100) / 100;
            var w = Math.round(w * 100) / 100;
            var h = Math.round(h * 100) / 100;
            var dens = Math.round( hr * 100 / w ) / 100;
            document.getElementById("Area").innerHTML = a;
            document.getElementById("Width").innerHTML = w;
            document.getElementById("Height").innerHTML = h;
            document.getElementById("Density").innerHTML = dens;
        }
        function set_resolution() {
            var preset = document.getElementById("Preset").value.split("x");
            document.getElementById("HResolution").value = preset[0];
            document.getElementById("VResolution").value = preset[1];
            update_area();
        }
        function set_diagonal() {
            var preset = document.getElementById("StdDiagonal").value;
            document.getElementById("Diagonal").value = preset;
            update_area();
        }
        function check_and_init() {
            if (document.readyState == "complete") {
                update_area();
            }
        }
        document.onreadystatechange = check_and_init;
        check_and_init();
// ]]&gt;&lt;/script&gt;
Surprisingly, could not find quick tool to compare various screen sizes and resolutions.&lt;/p&gt;
&lt;p&gt;It is useful to understand why a modern 14 inch display (with 16:9 ratio) is almost a centimeter shorter vertically than older 12 inch (with 4:3 ratio).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;So here it is:&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Diagonal&lt;/td&gt;
&lt;td&gt;&lt;input style="width: 4em;" id="Diagonal" onchange="update_area()" value="27" type="text" /&gt; inches&lt;/td&gt;
&lt;td&gt;or select &lt;select id="StdDiagonal" onchange="set_diagonal()"&gt; &lt;option value="13.3"&gt;13.3&lt;/option&gt; &lt;option value="14"&gt;14&lt;/option&gt; &lt;option value="15.6"&gt;15.6&lt;/option&gt; &lt;option value="20"&gt;20&lt;/option&gt; &lt;option value="23"&gt;23&lt;/option&gt; &lt;option value="24"&gt;24&lt;/option&gt; &lt;option value="26"&gt;26&lt;/option&gt; &lt;option selected="selected" value="27"&gt;27&lt;/option&gt; &lt;option value="28"&gt;28&lt;/option&gt; &lt;option value="29"&gt;29&lt;/option&gt; &lt;option value="30"&gt;30&lt;/option&gt;&lt;/select&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolution&lt;/td&gt;
&lt;td&gt;&lt;input style="width: 4em;" id="HResolution" onchange="update_area()" value="1920" type="text" /&gt; x &lt;input style="width: 4em;" id="VResolution" onchange="update_area()" value="1080" type="text" /&gt;&lt;/td&gt;
&lt;td&gt;or select &lt;select id="Preset" onchange="set_resolution()" name="D1"&gt; &lt;option value="1024x768"&gt;1024x768 (4:3)&lt;/option&gt; &lt;option value="1280x800"&gt;1280x800 (16:10)&lt;/option&gt; &lt;option value="1366x768"&gt;1366x768 (16:9)&lt;/option&gt; &lt;option value="1600x1200"&gt;1600x1200 (4:3)&lt;/option&gt; &lt;option selected="selected" value="1920x1080"&gt;1920x1080 (16:9)&lt;/option&gt; &lt;option value="1920x1200"&gt;1920x1200 (16:10)&lt;/option&gt;&lt;/select&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;Results:&lt;/p&gt;
&lt;table border="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Screen area: &lt;span style="background-color: #eeeeee;" id="Area"&gt;&lt;/span&gt; square inches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Width: &lt;span style="background-color: #eeeeee;" id="Width"&gt;&lt;/span&gt;, height: &lt;span style="background-color: #eeeeee;" id="Height"&gt;&lt;/span&gt; inches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel density: &lt;span style="background-color: #eeeeee;" id="Density"&gt;&lt;/span&gt; pixels per inch.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10184027" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="tools" scheme="http://blogs.msdn.com/b/michen/archive/tags/tools/" /></entry><entry><title>PowerPivot beginner journey 3</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2011/02/15/powerpivot-beginner-journey-3.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2011/02/15/powerpivot-beginner-journey-3.aspx</id><published>2011-02-15T23:38:00Z</published><updated>2011-02-15T23:38:00Z</updated><content type="html">&lt;p&gt;PowerPivot lesson #3 - if the data can't be refreshed, it should be in Excel, or copied to PowerPivot.&lt;/p&gt;
&lt;p&gt;I added zip code data from CSV file by loading to PowerPivot, and discarded the CSV since I did not plan to update it. Bad idea. PowerPivot wants to update all data when you do Refresh All, and missing CSV file prevents it from refreshing all other tables too.&lt;/p&gt;
&lt;p&gt;What I should have done: load data to my Excel file, then import it to PowerPivot from Excel. Or load data to temporary Excel and copy/paste it to PowerPivot. Later solution is better, since it avoids data duplication.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10129891" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="powerpivot" scheme="http://blogs.msdn.com/b/michen/archive/tags/powerpivot/" /></entry><entry><title>PowerPivot beginner journey 2</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2011/01/28/more-powerpivot-beginner-problems.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2011/01/28/more-powerpivot-beginner-problems.aspx</id><published>2011-01-29T06:00:00Z</published><updated>2011-01-29T06:00:00Z</updated><content type="html">&lt;p&gt;I was&amp;nbsp;exploring the data, and&amp;nbsp;found lots of interesting info from my data set. But&amp;nbsp;I could not find how to join some tables in PowerPivot, and after looking at the source data - it turned out&amp;nbsp;my source data used&amp;nbsp;GUID columns, stored as binary, for row IDs&amp;nbsp;- and PowerPivot silently hid it.&amp;nbsp;I would expect it at least show the column and tell me it can't use it. Even better - offer convertion options. But PowerPivot does not even divulge column existense.&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;lesson - learn your data &lt;strong&gt;before &lt;/strong&gt;importing it to PowerPivot, afterward it might be too late.&lt;/p&gt;
&lt;p&gt;The workaround is of course easy - use SQL query rather than table import, but tedious since PowerPivot makes you re-run the wizard for every single table. Here is the query just in case:&lt;/p&gt;
&lt;pre class="scroll"&gt;&lt;code class="mysql"&gt;select *, convert(VARCHAR(40), guid, 1) AS GuidStr from MyTable&lt;/code&gt;&amp;nbsp;&lt;/pre&gt;
&lt;p&gt;Then I've imported data, added relationships,&amp;nbsp;and started to create calculated columns. One of the DAX expressions got wrong results, and I realized I missed one relationship which would make the expression return correct result. I've tried to add relationship, but PowerPivot did not let me do it, failing with some very obscure error message (An unexpected error occurred, file 'xmvsquery.cpp', line 1739, ...). Only once I've deleted that calculated column, I could create relationship, then add the column back, and finally get the correct results.&lt;/p&gt;
&lt;p&gt;Second lesson for me - tell PowerPivot about all the relationships before doing anything with the data.&lt;/p&gt;
&lt;p&gt;Overall, I find PowerPivot useful, but such problems make it way less useable as exploratory tool - you've to know all your columns and all your relationships in advance, and tell PowerPivot about everything. If you miss something you might need to go back and start from scratch,&amp;nbsp;e.g. delete a table with all the calculated columns, and re-import it again to get binary column, or delete the calculated columns to be able to define relationships.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10121814" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="powerpivot" scheme="http://blogs.msdn.com/b/michen/archive/tags/powerpivot/" /></entry><entry><title>Undocumented PowerPivot installation requirement</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2011/01/28/undocumented-powerpivot-installation-requirement.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2011/01/28/undocumented-powerpivot-installation-requirement.aspx</id><published>2011-01-28T10:13:00Z</published><updated>2011-01-28T10:13:00Z</updated><content type="html">&lt;p&gt;Trying to use PowerPivot, just found undocumented PowerPivot installation requirement: Excel .NET Programmability Support.&lt;/p&gt;
&lt;p&gt;If PowerPivot silently fails to load, and in Event Viewer you've got an event like below, run Office Setup / Add or Remove Features, then check .NET Programmability Support under Microsoft Excel.&lt;/p&gt;
&lt;p&gt;Here is the event you might find in event viewer:&lt;/p&gt;
&lt;p&gt;&lt;span lang="EN"&gt;
&lt;p&gt;Customization URI: file:///C:/Program Files (x86)/Microsoft Analysis Services/AS Excel Client/10/Microsoft.AnalysisServices.Modeler.FieldList.vsto&lt;/p&gt;
&lt;p&gt;Exception: Customization could not be loaded because the application domain could not be created.　&lt;/p&gt;
&lt;p&gt;************** Exception Text **************&lt;/p&gt;
&lt;p&gt;Microsoft.VisualStudio.Tools.Applications.Runtime.CannotCreateCustomizationDomainException: Customization could not be loaded because the application domain could not be created. ---&amp;gt; System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---&amp;gt; System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. The system cannot find the file specified.&lt;/p&gt;
&lt;p&gt;File name: 'Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'&lt;/p&gt;
&lt;p&gt;at Microsoft.AnalysisServices.Modeler.FieldList.ThisAddIn..ctor()&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10121418" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="powerpivot" scheme="http://blogs.msdn.com/b/michen/archive/tags/powerpivot/" /></entry><entry><title>Making Asynchronous Programming Easy</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2010/10/30/making-asynchronous-programming-easy.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2010/10/30/making-asynchronous-programming-easy.aspx</id><published>2010-10-30T07:30:00Z</published><updated>2010-10-30T07:30:00Z</updated><content type="html">&lt;p&gt;Great post by&amp;nbsp;Somasegar about upcoming&amp;nbsp;language support for async programming:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/somasegar/archive/2010/10/28/making-asynchronous-programming-easy.aspx"&gt;http://blogs.msdn.com/b/somasegar/archive/2010/10/28/making-asynchronous-programming-easy.aspx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I guess soon the &lt;a href="http://blogs.msdn.com/b/michen/archive/2006/03/30/using-c-2-0-iterators-to-simplify-writing-asynchronous-code.aspx"&gt;&lt;span style="text-decoration: line-through;"&gt;idea&lt;/span&gt;&amp;nbsp;hack of using C#&amp;nbsp;iterators&lt;/a&gt; as poor-man's &lt;a href="http://blogs.msdn.com/b/michen/archive/2006/04/01/using-c-2-0-iterators-to-simplify-writing-asynchronous-code-part-2.aspx"&gt;language support&lt;/a&gt; (that caused me to start this blog :)) will finally&amp;nbsp;be deprecated.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10083394" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term=".NET" scheme="http://blogs.msdn.com/b/michen/archive/tags/-NET/" /></entry><entry><title>SSIS custom component performance tidbit</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2010/05/24/ssis-custom-component-performance-tidbit.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2010/05/24/ssis-custom-component-performance-tidbit.aspx</id><published>2010-05-25T05:27:00Z</published><updated>2010-05-25T05:27:00Z</updated><content type="html">&lt;p&gt;I was helping a friend of mine to debug performance issue with his SSIS package. The package was pretty simple &amp;ndash; he needed to read data, &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Shard_(database_architecture)" title="Sharding @ wikipedia"&gt;shard&lt;/a&gt; it using some partitioning scheme, then insert the results into 8 SQL destinations. The hashing algorithm used for partitioning was rather complex for conditional split, so he created a simple custom transform component that&amp;nbsp;calculates the partition ID and&amp;nbsp;adds it as a new column; the package then used this column in conditional split and finally there were OLEDB destinations for each shard.&lt;/p&gt;
&lt;p&gt;But performance was not good &amp;ndash; it took about 10 minutes to push ~32mln rows.&lt;/p&gt;
&lt;p&gt;I started with simplest debugging technique &amp;ndash; remove destination to check if it is the cause, then remove conditional split, etc. We quickly found the problem is caused by the custom component. But it is very simple:&lt;/p&gt;
&lt;pre&gt;public override void ProcessInput(int inputID, PipelineBuffer buffer)
{
    while( buffer.NextRow() )
    {&lt;br /&gt;        byte[] guid = buffer.GetBytes(m_inputColumn);&lt;br /&gt;        int partition = Partition(guid);&lt;br /&gt;        buffer.SetInt32(m_outputColumn, partition);

        buffer.DirectRow(ComponentMetaData.OutputCollection[0].ID);
    }
}&lt;/pre&gt;
&lt;p&gt;Can you guess why is it so slow?&lt;/p&gt;
&lt;p&gt;There are many potential inefficiencies here, e.g. due to the buffer API we&amp;rsquo;ve to allocate and copy byte array for every row. But the code that took most of the overall execution time was&lt;/p&gt;
&lt;pre&gt;    buffer.DirectRow(ComponentMetaData.OutputCollection[0].ID);&lt;/pre&gt;
&lt;p&gt;Why so slow? You see, the PipelineBuffer object is optimized for performance, but ComponentMetaData object and various collections of inputs/outputs/columns/etc are mostly for design time and were not optimized for performance at all. And there is a good reason for this: these collections do not change during runtime, so one should not call them at runtime. So we moved their usage to PreExecute and then used the cached output ID:&lt;/p&gt;
&lt;pre&gt;public override void PreExecute ()&lt;br /&gt;{&lt;br /&gt;    m_outputID = ComponentMetaData.OutputCollection[0].ID;&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;and somewhere in ProcessInput(..) - &lt;/p&gt;
&lt;pre&gt;    buffer.DirectRow(m_outputID);&lt;/pre&gt;
&lt;p&gt;Actually, this particular component did not need to call DirectRow at all, and using default exclusion group was enough, so we finally removed it. But if you do need to call it &amp;ndash; cache the output ID in advance. Our performance has improved about 20 times by this simple change.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10014310" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /><category term="Perf" scheme="http://blogs.msdn.com/b/michen/archive/tags/Perf/" /><category term="SSIS Programming" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Programming/" /></entry><entry><title>When April Fools jokes become reality</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/12/11/when-jokes-becomes-reality.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/12/11/when-jokes-becomes-reality.aspx</id><published>2008-12-11T20:02:00Z</published><updated>2008-12-11T20:02:00Z</updated><content type="html">&lt;P&gt;Couple years ago Charles Petzold wrote about &lt;A href="http://www.charlespetzold.com/etc/CSAML.html" mce_href="http://www.charlespetzold.com/etc/CSAML.html"&gt;C# Application Markup Language&lt;/A&gt; - XML syntax for C#. Go read it if you've not seen it yet.&lt;/P&gt;
&lt;P&gt;Well, it was a fool day's joke of course (the publishing date at the bottom hints about it).&lt;/P&gt;
&lt;P&gt;But yesterday, when playing with Windows Workflow Foundation rules, I've looked at the ruleset file, and discovered exactly that - huge XML file that looked familiar :), very close to Charles joke. This is how WWF internally stores the simple one-line rules that one types in the editor. Luckily for us, we don't have to type this XML, and WWF convert the human-readable C# into this monstrosity itself, so it is more an internal details than a language humans would ever use.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9198050" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="wwf" scheme="http://blogs.msdn.com/b/michen/archive/tags/wwf/" /><category term="humor" scheme="http://blogs.msdn.com/b/michen/archive/tags/humor/" /></entry><entry><title>The way NOT to write HTTPS server</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/12/04/the-way-not-to-write-https-server.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/12/04/the-way-not-to-write-https-server.aspx</id><published>2008-12-05T10:28:00Z</published><updated>2008-12-05T10:28:00Z</updated><content type="html">&lt;P&gt;Note: posted under &lt;STRONG&gt;rant&lt;/STRONG&gt; tag, so you are warned ;)&lt;/P&gt;
&lt;P&gt;I've just got a new wi-fi router, DIR-655 from DLink. Seems like a nice router, but at least one feature is just plain horrible broken. And it is security feature, which makes me wonder how secure the rest of the code is.&lt;/P&gt;
&lt;P&gt;The router can be accessed and managed using HTTP server, like most any other one. It also has an option to enable HTTPS server, which was probably supposed to make management more secure. This is not very important if you connect from local network, but very important if you do remote management from the internet.&lt;/P&gt;
&lt;P&gt;So I decided to enable it and connect using HTTPS. Internet explorer immediately complained that there are problems with certificate and advised me against proceeding :). IE&amp;nbsp;warned me that (1) the certificate cannot be validated, (2) the certificate has expired, and (3) the certificate was issued to incorrect site. I proceeded nevertheless, checked the certificate, and indeed - it is self-signed, expired in&amp;nbsp;September 2008, and issued to site &lt;A href="http://www.dlink.com/" mce_href="http://www.dlink.com/"&gt;www.dlink.com&lt;/A&gt;, which is obviously different from 192.168.0.1 :). Worse, I asked other guys with same router to compare certificate hash - it turned out all the routers are shipped with the same certificate!&lt;/P&gt;
&lt;P&gt;I can understand the self-signing nature of the certificate - obviously DLink cannot put real certificate to the router. But why has it expired, shared by all routers, and most bizzar - why it indicates &lt;A href="http://www.dlink.com/" mce_href="http://www.dlink.com/"&gt;www.dlink.com&lt;/A&gt; as site name?&lt;/P&gt;
&lt;P&gt;They could have&amp;nbsp;easily generated an individual certificate for each router, issued to correct internal IP address&amp;nbsp;for internal-facing server and to DynDNS name for internet-facing server. It does not add any hardware cost; the software could just generate a random self-signed certificate the very first time the router boots with a new configuration. User could then configure his browser to trust this particular certificate, and know he connects to his own router, not any of the thousands other routers with same certificate.&lt;/P&gt;
&lt;P&gt;The way they did this feature, it is totally broken and makes no sense at all.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9179082" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Rant" scheme="http://blogs.msdn.com/b/michen/archive/tags/Rant/" /><category term="non-secure" scheme="http://blogs.msdn.com/b/michen/archive/tags/non_2D00_secure/" /></entry><entry><title>Does buffer.NextRow() skips the first row in a buffer?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/10/19/does-buffer-nextrow-skips-the-first-row-in-a-buffer.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/10/19/does-buffer-nextrow-skips-the-first-row-in-a-buffer.aspx</id><published>2008-10-19T10:33:40Z</published><updated>2008-10-19T10:33:40Z</updated><content type="html">&lt;p&gt;I got a follow up question to &lt;a href="http://blogs.msdn.com/michen/archive/2007/08/31/Buffer.EndOfRowset.aspx" target="_blank" mce_href="http://blogs.msdn.com/michen/archive/2007/08/31/Buffer.EndOfRowset.aspx"&gt;my old post regarding enumerating rows in SSIS buffer&lt;/a&gt;, that suggested using following code to process rows in custom SSIS transform:&lt;/p&gt;  &lt;p&gt;&lt;font face="Consolas"&gt;&lt;font color="#0000ff"&gt;while &lt;/font&gt;&lt;font color="#000000"&gt;(buffer.NextRow())        &lt;br /&gt;{         &lt;br /&gt;&amp;#160; &lt;/font&gt;&lt;font color="#008000"&gt;// do something with the row        &lt;br /&gt;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Here is the question:&lt;/p&gt;  &lt;blockquote class="Q"&gt;buffer.NextRow() moves the pointer forward, so following your code to the letter will make you skip the first row. &lt;/blockquote&gt;  &lt;p&gt;Does it? Actually, no. It behaves like many other enumeration interfaces, e.g. COM's IEnumVariant and .NET's IEnumerator, so I'll quote documentation from &lt;a class="" href="http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.movenext.aspx" target="_blank" mce_href="http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.movenext.aspx"&gt;IEnumerator::MoveNext&lt;/a&gt;:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;After an enumerator is created or after the &lt;span sdata="cer" target="M:System.Collections.IEnumerator.Reset"&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl33" onclick="javascript:Track(&amp;#39;ctl00_rs1_mainContentContainer_cpe691486_c|ctl00_rs1_mainContentContainer_ctl33&amp;#39;,this);" href="http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx"&gt;&lt;font color="#0033cc"&gt;Reset&lt;/font&gt;&lt;/a&gt;&lt;/span&gt; method is called, an enumerator is positioned before the first element of the collection, and the first call to the &lt;span sdata="cer" target="M:System.Collections.IEnumerator.MoveNext"&gt;&lt;span class="selflink"&gt;&lt;strong&gt;MoveNext&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt; method moves the enumerator over the first element of the collection. &lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Can you guess why it was designed this way? Let's think what would happen, if the enumerator was positioned on the first element (row in SSIS case) initially. How would we know if the first element exists at all? The enumerator would have to provide another property, like EndOfCollection - making everything more complicated. Note that as explained in my linked post, buffer.EndOfRowset() is not such an indicator. It does not tell you that enumerator finished enumerating rows in current buffer, it tells you that current buffer is the very last buffer you will receive. &lt;/p&gt;  &lt;p&gt;With enumerator initially positioned before first row, you call NextRow() which will immediately return false if the collection is empty, and position iterator to first row if it exists. So the code is correct.&lt;/p&gt;  &lt;p&gt;A note regarding my original post: the SSIS team found that this change caused too many problems for the users, so the final release of SSIS 2008 reverted back to the SSIS 2005 behavior. Interestingly, it was not a simple undo of code change, as the data flow engine has been substantially rewritten, but a new code to simulate the old behavior. Thus the &amp;quot;wrong&amp;quot; code will keep working in 2008. I still recommend changing it according to my previous blog - I think my loop just looks cleaner :).&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9006104" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /><category term="SSIS Programming" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Programming/" /></entry><entry><title>It's Not Easy Being BI</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/06/18/it-s-not-easy-being-bi.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/06/18/it-s-not-easy-being-bi.aspx</id><published>2008-06-18T22:45:00Z</published><updated>2008-06-18T22:45:00Z</updated><content type="html">&lt;P&gt;A very&amp;nbsp;funny post by Matthew Roche, for everybody working in Business Intelligence:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://bi-polar23.blogspot.com/2008/06/it-not-easy-being-bi.html"&gt;http://bi-polar23.blogspot.com/2008/06/it-not-easy-being-bi.html&lt;/A&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8618039" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="BI" scheme="http://blogs.msdn.com/b/michen/archive/tags/BI/" /></entry><entry><title>Lookup multiple rows?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/06/07/lookup-multiple-rows.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/06/07/lookup-multiple-rows.aspx</id><published>2008-06-07T11:37:00Z</published><updated>2008-06-07T11:37:00Z</updated><content type="html">&lt;P&gt;Can SSIS Lookup do what this user wants it to do? 
&lt;BLOCKQUOTE class=Q&gt;
&lt;P&gt;I have a problem with a lookup output, I get this warning: The Lookup transformation encountered duplicate reference key values when caching reference data. I know what it is, but I don't like to avoid this warning, I'd like to get all the rows (two in this case) that the lookup output provides me.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Unfortunately, no - the reason is that Lookup transform is &lt;A class="" href="http://msdn.microsoft.com/en-us/library/ms136027.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/ms136027.aspx"&gt;synchronous&lt;/A&gt;, i.e. it does not add new rows or remove rows*, it just modifies the values - i.e. it can't produce two output rows for&amp;nbsp;one input&amp;nbsp;row.&lt;/P&gt;
&lt;P&gt;It would of course be possible to make an asynchronous Lookup, or provide an option, but the current Lookup is complex enough, that I think more options would kill it :)&lt;/P&gt;
&lt;P&gt;If you need this functionality, you can use Merge Join transform.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;Notes&lt;/U&gt;&lt;BR&gt;*What happens with the rows that are redirected to "not found" output in SSIS 2008? They are not deleted from the buffer (synchronous transform can't do it), they are just marked as belonging to the other path, and the components on the main path do not see them.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8579759" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /><category term="SSIS Lookup" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Lookup/" /></entry><entry><title>Application termination when user logs off</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/04/04/application-termination-when-user-logs-off.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/04/04/application-termination-when-user-logs-off.aspx</id><published>2008-04-05T03:02:00Z</published><updated>2008-04-05T03:02:00Z</updated><content type="html">&lt;P&gt;Do you know how windows terminates all the applications when user logs off? I did not think too much about this, and assumed that it is a normal process - after all the WM_QUERYENDSESSION and WM_ENDSESSION processing, the application main window closes, posts WM_QUIT and the application quits in a regular way.&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;But a recent bug reported for one of my GUI applications caused me to look deeper. The application settings, normally saved at application's exit,&amp;nbsp;were&amp;nbsp;not saves&amp;nbsp;if user logged off, and this made me look closer at what happens at logoff.&lt;/P&gt;
&lt;P&gt;This was a managed application with related code in Main() function that looked like this:&lt;/P&gt;&lt;PRE&gt;static void Main()&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; LoadPreferences();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Application.Run(new MainForm());&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SavePreferences();&lt;BR&gt;}&lt;/PRE&gt;
&lt;P&gt;This works great when the application is closed normally. But if user logs off, the application closes, but the preferences are not saved - the code following Application.Run is never called at all. &lt;/P&gt;
&lt;P&gt;What happens? Does Application.Run throw any exception that causes the following code to be bypassed? This was ruled out rather quickly by debugger. I then assumed something in Windows Forms calls ExitProcess in response to WM_ENDSESSION, or maybe default message handler does this - but that was proved wrong too. A repro with unmanaged ATL code showed it is not related to managed libraries at all.&lt;/P&gt;
&lt;P&gt;Finally my colleguae debugged this issue a little deeper, and found that CSRSS is blatantly terminating the process after it processed the WM_ENDSESSION event. Here are the outlines of the whole sequence (for non-console applications).&lt;/P&gt;
&lt;P&gt;The user selects Start/Log off and then selects “OK” in the confirmation dialog. This calls ExitWindowsEx(EWX_LOGOFF); if during the OK button click, the CTRL key was down, it also adds the EWX_FORCE flag which makes it ignore the result of the WM_QUERYENDSESSION message (as if the applications always returned TRUE). ExitWindowsEx(EWX_LOGOFF) causes roughly the following activity to occur in the logon session’s instance of CSRSS:&lt;/P&gt;
&lt;P&gt;For each process in the session&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; For each UI thread in the process&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; For each top-level window in the thread&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Send WM_QUERYENDSESSION and get back the result&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Wait with a short timeout and show an “End program” dialog if it takes too long; if the user says “kill”, call TerminateProcess on the process and continue the “for process” loop&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If EWX_FORCE was not specified and WM_QUERYENDSESSION returned FALSE, break (and continue "UI thread loop");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Note that even if one window in one thread returned FALSE to WM_QUERYENDSESSION, windows in the other threads in the same process are still sent WM_QUERYENDSESSION&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bool bDoShutdown = (all threads agreed (i.e., all windows returned TRUE to WM_QUERYENDSESSION)) or EWX_FORCE&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; For each UI thread in the process&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; For each top-level window in the thread&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Send WM_ENDSESSION(bDoShutdown);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If (bDoShutdown) wait for return (wait with a short timeout and show an “End program” dialog if it takes too long; whatever the user says, consider that the return from WM_QUERYENDSESSION)&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If (bDoShutdown)&amp;nbsp;TerminateProcess on the process&lt;BR&gt;}&lt;BR&gt;I have omitted some details, in particular related to no-UI and console processes, but the general picture should be clear. Probably the Windows guys decided that graceful cleanup is not needed when user logs off, and all the application are closed.&lt;/P&gt;
&lt;P&gt;Since the application is forecefully terminated, the application should not rely on being able to execute any code after the message loop. Even classes like SafeHandle are not finalized.&amp;nbsp;All really important cleanup and termination code should be executed when the main form closes, or by providing a handler for the WM_ENDSESSION message . Another option (although I did not try it) is to catch WM_ENDSESSION message and terminate the message loop, then exit the application&amp;nbsp;gracefully - although this goes contrary to Windows design of killing applications fast to ensure quick log off.&lt;/P&gt;
&lt;P&gt;Update: Raymond Chen described the reasons for this behavior:&lt;BR&gt;&lt;A href="http://blogs.msdn.com/oldnewthing/archive/2008/04/21/8413175.aspx"&gt;http://blogs.msdn.com/oldnewthing/archive/2008/04/21/8413175.aspx&lt;/A&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8359007" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Win32" scheme="http://blogs.msdn.com/b/michen/archive/tags/Win32/" /></entry><entry><title>Configuring .NET for running SSIS packages from custom applications</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/03/14/configuring-net-for-running-ssis-packages-from-custom-applications.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/03/14/configuring-net-for-running-ssis-packages-from-custom-applications.aspx</id><published>2008-03-15T01:45:00Z</published><updated>2008-03-15T01:45:00Z</updated><content type="html">&lt;P&gt;If you&amp;nbsp;&lt;A class="" href="http://blogs.msdn.com/michen/archive/2007/03/22/running-ssis-package-programmatically.aspx" mce_href="http://blogs.msdn.com/michen/archive/2007/03/22/running-ssis-package-programmatically.aspx"&gt;execute SSIS packages&lt;/A&gt; from custom applications, you own the application and thus you are responsible for configuring .NET runtime properly to get the maximum performance.&lt;/P&gt;
&lt;P&gt;.NET configuration is usually performed using .exe.config files, so it is a just matter of providing good config file. How do you know what is good? The simplest way is to look at the config file that SSIS provides, and copy the appropriate settings. Now let's take a look at DTExec.exe.config provided with SQL Server 2008 and discuss the choices made by SSIS team.&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=2&gt;&amp;lt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;configuration&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;startup&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;BR&gt;&amp;nbsp; &amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;requiredRuntime&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#ff0000 size=2&gt;&lt;FONT color=#ff0000 size=2&gt;version&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;=&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;v2.0.50727&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;/&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;lt;/&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;startup&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;runtime&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;BR&gt;&amp;nbsp; &amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;gcServer&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#ff0000 size=2&gt;&lt;FONT color=#ff0000 size=2&gt;enabled&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;=&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;true&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;/&amp;gt;&lt;BR&gt;&amp;nbsp; &amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;disableCommitThreadStack&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#ff0000 size=2&gt;&lt;FONT color=#ff0000 size=2&gt;enabled&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;=&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;true&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;/&amp;gt;&lt;BR&gt;&amp;nbsp; &amp;lt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;generatePublisherEvidence&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#ff0000 size=2&gt;&lt;FONT color=#ff0000 size=2&gt;enabled&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;=&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;false&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;"&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;/&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;lt;/&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;runtime&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;BR&gt;&amp;lt;/&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;configuration&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt;&amp;gt;&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;
&lt;P&gt;&lt;FONT color=#a31515&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/a5dzwzc9.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/a5dzwzc9.aspx"&gt;&lt;STRONG&gt;requiredRuntime&lt;/STRONG&gt;&lt;/A&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;- this line simply tells which version of .NET you want to use. 2.0 or above should be good.&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#a31515&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/ms229357.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms229357.aspx"&gt;&lt;STRONG&gt;gcServer&lt;/STRONG&gt;&lt;/A&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;- DTEXEC uses the "server" version of garbage collector. Server GC performs better for typical SSIS load, especially&amp;nbsp;on multiprocess machines. This is important if your SSIS package uses managed transforms (e.g. ADO.NET source, script transform, or custom transform written in .NET).&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#a31515&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/bb882564.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb882564.aspx"&gt;&lt;STRONG&gt;disableCommitThreadStack&lt;/STRONG&gt;&lt;/A&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;- by default .NET commits the thread stacks (i.e. reserves memory for stacks&amp;nbsp;- usually from page file). But SSIS creates a lot of threads, while typically uses little stack space, so it performs better if stack memory is not immediately committed when the thread is created. With&amp;nbsp;this option&amp;nbsp;your application&amp;nbsp;might perform a bit better and require smaller page file (note that anyway, the page file&amp;nbsp;is not really used until needed). The drawback of this choice is that application might fail if Windows is totally out of memory, and application&amp;nbsp;can't extend its stack. But in this situation something has already gone bad, and it is probably better to fail fast in this case anyway.&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#a31515&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/bb629393.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb629393.aspx"&gt;&lt;STRONG&gt;generatePublisherEvidence&lt;/STRONG&gt;&lt;/A&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&lt;FONT color=#0000ff size=2&gt; &lt;/FONT&gt;&lt;/FONT&gt;- this tells .NET runtime the DTEXEC does not use Publisher evidence, and thus .NET does not&amp;nbsp;have to verify authenticode signatures. This increases the startup performance a little bit, but mainly prevents &lt;A class="" href="http://support.microsoft.com/kb/918644" target=_blank mce_href="http://support.microsoft.com/kb/918644"&gt;problems that may occur&amp;nbsp;when authenticode checks certificate revocation list&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Now that you know these options made by SSIS team, you may test and decide whether they are appropriate for your application as well, and copy them to your application's config file if needed.&lt;/P&gt;
&lt;P&gt;P.S. Also make sure you create SSIS package using MTA thread, see Matt's blog for details:&lt;BR&gt;&lt;A href="http://blogs.msdn.com/mattm/archive/2007/09/14/running-packages-from-custom-applications.aspx"&gt;http://blogs.msdn.com/mattm/archive/2007/09/14/running-packages-from-custom-applications.aspx&lt;/A&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8212671" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /><category term="SSIS Programming" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Programming/" /></entry><entry><title>Random() is only random if you are using it right</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/02/05/random_28002900_-is-only-random-if-you-are-using-it-right.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/02/05/random_28002900_-is-only-random-if-you-are-using-it-right.aspx</id><published>2008-02-05T11:53:00Z</published><updated>2008-02-05T11:53:00Z</updated><content type="html">&lt;P&gt;I like the quote "With great power comes great responsibility" when used in regards to .NET - .NET gives one great powers, but use it wisely and know how this stuff works. &lt;/P&gt;
&lt;P&gt;Recently I saw code (it was written by a guy interviewing to our team) that demonstrated interesting problem with incorrect usage of Random class. The code generated a random position for an object, tested if it satisfied some condition; if it did not work - generated another random position, tested the new position and so on until it generated something that did work. The code to generate each position was pretty simple: create new instance of Random class, use it to generate 4 random integers. The percentage of failed position was not very big (but noticeable), the code to test the position was rather fast, and&amp;nbsp;we needed just 10 good positions to create complete configuration (all this was about a&amp;nbsp;variant of &lt;A class="" title="Battleship game on Wikipedia" href="http://en.wikipedia.org/wiki/Battleship_game" target=_blank mce_href="http://en.wikipedia.org/wiki/Battleship_game"&gt;Battleship game&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;But it took very long to generate the whole configuration - about a second on average.&lt;/P&gt;
&lt;P&gt;Playing with this code, I suddenly found that moving Random object to a member variable, so it is re-used, rather than creating new Random object&amp;nbsp;for each position, made the code 10000 times faster! Well, obviously I expected some savings since&amp;nbsp;it creates less objects, but ten thousand times? My first reaction - is the Random really such a heavy object?&lt;/P&gt;
&lt;P&gt;It turned out the problem was more interesting, and caused by the usage of default constructor. As MSDN puts it, if you use default constructor the "seed is initialized to a value based on the current time."&amp;nbsp;It actually uses GetTickCount() as the seed. Can you now spot the problem? The Random() constructor was very quick, but during the period of time when GetTickCount()&amp;nbsp;returns the same value (which is about 20ms for most chipsets),&amp;nbsp;all the Random&amp;nbsp;objects created this way&amp;nbsp;will have the same seed and generate the same &lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'; mso-ansi-language: EN-US; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-fareast-language: EN-US; mso-bidi-language: AR-SA"&gt;positions&lt;/SPAN&gt;! Instead of&amp;nbsp;trying new positions, the code tried the same position again and again until the GetTickCount() returned a new value. So don't generate too many Random instances - just one will behave much better in most cases.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7460700" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term=".NET" scheme="http://blogs.msdn.com/b/michen/archive/tags/-NET/" /></entry><entry><title>SSIS event handler threading</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2008/02/01/event-handler-threading.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2008/02/01/event-handler-threading.aspx</id><published>2008-02-01T23:25:00Z</published><updated>2008-02-01T23:25:00Z</updated><content type="html">&lt;P&gt;I've got an interesting question/statement about event handlers:&lt;/P&gt;
&lt;BLOCKQUOTE class=Q&gt;Tasks fire the same EH at the same time. My understanding is all EHs fire at the same time (Parallel).&lt;/BLOCKQUOTE&gt;
&lt;P&gt;If I understand the question correctly,&amp;nbsp;the&amp;nbsp;package has an &lt;STRONG&gt;event handler&lt;/STRONG&gt; that can&amp;nbsp;handle multiple &lt;STRONG&gt;events&lt;/STRONG&gt;, and these events fire at about the same time. What happens?&lt;/P&gt;
&lt;P&gt;Well, I did not work much with event handlers, so let's experiment and test how it works. This is really simple. I've create a package with two dummy script tasks A and B (not connected with any precedence constraints) that fail (by returning failure) and OnError event handler on the Package. The event handler contains a simple script task that prints the name of the event source:&lt;/P&gt;&lt;FONT size=2&gt;
&lt;P&gt;MsgBox(Dts.Variables(&lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;"SourceName"&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;).Value, MsgBoxStyle.Information, &lt;/FONT&gt;&lt;FONT color=#a31515 size=2&gt;&lt;FONT color=#a31515 size=2&gt;"Event Handler"&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;What will be the result of executing this? Will we get two message boxes at the same time, or sequentially two message boxes? &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;The answer is - two message boxes popup sequentially one after another, but never at the same time. Ssometimes A is the first, sometimes B. The event handler (like any other task) is not reentrable and only a single instance of an event handler can be running at a time, thus the second event has to wait for the event handler to finish processing the first event before processing the next one.&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7379625" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /></entry><entry><title>Functional sort in C#</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/12/11/functional-sort-in-c.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/12/11/functional-sort-in-c.aspx</id><published>2007-12-11T22:46:00Z</published><updated>2007-12-11T22:46:00Z</updated><content type="html">&lt;P&gt;On an internal mailing list, we were discussing functional languages, and this Haskell sort code:&lt;/P&gt;
&lt;P&gt;&lt;SPAN lang=EN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 'Courier New'; mso-ansi-language: EN"&gt;qsort []&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = []&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN lang=EN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 'Courier New'; mso-ansi-language: EN"&gt;qsort (x:xs) = qsort (&lt;A href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:filter"&gt;&lt;SPAN style="BORDER-RIGHT: windowtext 1pt; PADDING-RIGHT: 0in; BORDER-TOP: windowtext 1pt; PADDING-LEFT: 0in; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt; COLOR: #006600; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt; TEXT-DECORATION: none; text-underline: none; mso-border-alt: none windowtext 0in"&gt;filter&lt;/SPAN&gt;&lt;/A&gt; (&lt;/SPAN&gt;&lt;SPAN lang=EN style="FONT-SIZE: 10pt; COLOR: #006600; FONT-FAMILY: 'Courier New'; mso-ansi-language: EN"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN lang=EN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 'Courier New'; mso-ansi-language: EN"&gt; x) xs) &lt;A href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:."&gt;&lt;SPAN style="BORDER-RIGHT: windowtext 1pt; PADDING-RIGHT: 0in; BORDER-TOP: windowtext 1pt; PADDING-LEFT: 0in; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt; COLOR: #006600; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt; TEXT-DECORATION: none; text-underline: none; mso-border-alt: none windowtext 0in"&gt;++&lt;/SPAN&gt;&lt;/A&gt; [x] &lt;A href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:."&gt;&lt;SPAN style="BORDER-RIGHT: windowtext 1pt; PADDING-RIGHT: 0in; BORDER-TOP: windowtext 1pt; PADDING-LEFT: 0in; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt; COLOR: #006600; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt; TEXT-DECORATION: none; text-underline: none; mso-border-alt: none windowtext 0in"&gt;++&lt;/SPAN&gt;&lt;/A&gt; qsort (&lt;A href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:filter"&gt;&lt;SPAN style="BORDER-RIGHT: windowtext 1pt; PADDING-RIGHT: 0in; BORDER-TOP: windowtext 1pt; PADDING-LEFT: 0in; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt; COLOR: #006600; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt; TEXT-DECORATION: none; text-underline: none; mso-border-alt: none windowtext 0in"&gt;filter&lt;/SPAN&gt;&lt;/A&gt; (&lt;A href="http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:&amp;amp;gt;="&gt;&lt;SPAN style="BORDER-RIGHT: windowtext 1pt; PADDING-RIGHT: 0in; BORDER-TOP: windowtext 1pt; PADDING-LEFT: 0in; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt; COLOR: #006600; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt; TEXT-DECORATION: none; text-underline: none; mso-border-alt: none windowtext 0in"&gt;&amp;gt;=&lt;/SPAN&gt;&lt;/A&gt; x) xs)&lt;/SPAN&gt;&lt;SPAN lang=RU style="mso-ansi-language: RU"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;While trying to explain how this code works (which is very different from what it looks like&amp;nbsp;to C++/C# programmers due to &lt;A class="" href="http://en.wikipedia.org/wiki/Lazy_evaluation" target=_blank mce_href="http://en.wikipedia.org/wiki/Lazy_evaluation"&gt;lazy evaluation&lt;/A&gt;) I've come up with following C#&amp;nbsp;code (with Linq) that is logically similar to Haskell version. Obviously, Haskell code is much nicer though.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Consolas"&gt;static&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt; &lt;SPAN style="COLOR: #2b91af"&gt;IEnumerable&lt;/SPAN&gt;&amp;lt;T&amp;gt; Qsort&amp;lt;T&amp;gt;(&lt;SPAN style="COLOR: #2b91af"&gt;IEnumerable&lt;/SPAN&gt;&amp;lt;T&amp;gt; s) &lt;SPAN style="COLOR: blue"&gt;where&lt;/SPAN&gt; T : &lt;SPAN style="COLOR: #2b91af"&gt;IComparable&lt;/SPAN&gt;&amp;lt;T&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: #2b91af"&gt;IEnumerator&lt;/SPAN&gt;&amp;lt;T&amp;gt; e = s.GetEnumerator();&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;if&lt;/SPAN&gt; (e.MoveNext())&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; T x = e.Current;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;foreach&lt;/SPAN&gt; (T t &lt;SPAN style="COLOR: blue"&gt;in&lt;/SPAN&gt; Qsort(s.Skip(1).Where(y =&amp;gt; y.CompareTo(x) &amp;lt; 0)))&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;yield&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;return&lt;/SPAN&gt; t;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;yield&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;return&lt;/SPAN&gt; x;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;foreach&lt;/SPAN&gt; (T t &lt;SPAN style="COLOR: blue"&gt;in&lt;/SPAN&gt; Qsort(s.Skip(1).Where(y =&amp;gt; y.CompareTo(x) &amp;gt;= 0)))&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;yield&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;return&lt;/SPAN&gt; t;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;}&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Consolas"&gt;&lt;FONT face=Arial&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6738041" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term=".NET" scheme="http://blogs.msdn.com/b/michen/archive/tags/-NET/" /><category term="Functional" scheme="http://blogs.msdn.com/b/michen/archive/tags/Functional/" /></entry><entry><title>SQL 2008 &amp; VS 2008</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/12/04/sql-2008-and-vs-2008.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/12/04/sql-2008-and-vs-2008.aspx</id><published>2007-12-05T09:42:00Z</published><updated>2007-12-05T09:42:00Z</updated><content type="html">Currently SQL Business Intelligence Development Studio (BIDS) and all the project types (AS, IS and RS) live in Visual Studio 2005. So don't try to open a solution that contains IS project in VS 2008 yet. What about final SQL 2008 - now that Visual Studio 2008 is released - what are the plans for BIDS and support of BI projects VS 2008?...(&lt;a href="http://blogs.msdn.com/b/michen/archive/2007/12/04/sql-2008-and-vs-2008.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6663107" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /><category term="Katmai" scheme="http://blogs.msdn.com/b/michen/archive/tags/Katmai/" /></entry><entry><title>VALVe/Steam horrors</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/11/23/steam.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/11/23/steam.aspx</id><published>2007-11-24T02:20:00Z</published><updated>2007-11-24T02:20:00Z</updated><content type="html">&lt;P&gt;(Update: VALVe has removed the worst recommendations that I described below.&amp;nbsp;I don't know if my article&amp;nbsp;had any influence on this. They still want to run the Steam as a service though).&lt;/P&gt;
&lt;P&gt;I originally planned to use this blog for work-related stuff only, but the VALVe drove me mad, so I decided to write this.&lt;/P&gt;
&lt;P&gt;I run Vista on my home PC (and at work too, actually), and all of us run as non-admins (not Vista's protected admin, but real non-admin). My son plays Counter Strike, and thus has to run&amp;nbsp;&lt;A class="" title=Evil href="http://www.steampowered.com/" target=_blank mce_href="http://www.steampowered.com"&gt;Steam&lt;/A&gt;. It worked mostly fine, until recently it started to ask to install Steam as a service. Well, a service for internet-facing software seems not good. Before installing, I decided to find out more about it, and the finding are much worse than I've ever expected. Here is an official FAQ from VALVe that I've found:&lt;/P&gt;
&lt;P&gt;&lt;A class="" title="Evil FAQ" href="http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=460" target=_blank mce_href="http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=460"&gt;http://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=460&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Here are some of the recommendations from this FAQ:&lt;/P&gt;
&lt;BLOCKQUOTE class=QR&gt;
&lt;UL&gt;
&lt;LI&gt;Go to: Start &amp;gt; Run and type in: cmd &lt;/LI&gt;
&lt;LI&gt;Type in the following:&lt;BR&gt;net localgroup Administrators /add Local service &lt;/LI&gt;
&lt;LI&gt;Restart your computer. &lt;/LI&gt;&lt;/UL&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;To put it simply, they ask to add Local service account to Administrators group! Apparently, they did not bother to find out the list of specific permissions they need, but simply figured out that administrator permissions "fix" the problem. That alone would be pretty bad, but&amp;nbsp;they did even worse.&amp;nbsp;Instead of creating special account for Steam or using some account that already has administrative permissions,&amp;nbsp;they want to add account &lt;STRONG&gt;shared&lt;/STRONG&gt; by multiple services to Administrators group, thus making not only their service, but &lt;STRONG&gt;all the services&lt;/STRONG&gt; running on the machine much more dangerous and less secure.&lt;/P&gt;
&lt;P&gt;Then VALVe gives even stranger recommendation:&lt;/P&gt;
&lt;BLOCKQUOTE class=QR&gt;
&lt;P&gt;If during this process you receive the error: &lt;SPAN class=bold&gt;System error 5 has occurred. Access is denied&lt;/SPAN&gt; Please follow these instructions and then try the above steps again:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Go to &lt;SPAN class=bold&gt;Start &amp;gt; Control Panel &amp;gt; System &amp;amp; Maintenance &amp;gt; Administrative Tools &amp;gt; Local Security Policy&lt;/SPAN&gt;. &lt;/LI&gt;
&lt;LI&gt;In the left pane, expand &lt;SPAN class=bold&gt;Local Policies &amp;gt; Security Options&lt;/SPAN&gt;.&lt;/LI&gt;
&lt;LI&gt;Double-click &lt;SPAN class=bold&gt;Network Security LAN Manager Authentication Level&lt;/SPAN&gt;.&lt;/LI&gt;
&lt;LI&gt;In the drop down list, change the default setting (NTLMv2 only) to &lt;SPAN class=bold&gt;Send LM &amp;amp; NTLM - use NTLMv2 session if negotiated&lt;/SPAN&gt;.&lt;/LI&gt;&lt;/OL&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Changing LanMan level for Vista Home users to completely unsecure level? Maybe I'm missing something, but I can't see any reason to do this. This setting affects authentication to remove computers (mostly affects NT &lt;STRONG&gt;domain&lt;/STRONG&gt;, should be of no concern to internet game), it would not help&amp;nbsp;one to&amp;nbsp;avoid &lt;STRONG&gt;local &lt;/STRONG&gt;Access Denied&amp;nbsp;while performing the steps above. It is not only completely unsecure, but IMO it also does not make any sense at all.&lt;/P&gt;
&lt;P&gt;So these guys &lt;BR&gt;1) don't understand anything about security, &lt;BR&gt;2) can't write software that does not require administrative privileges,&lt;BR&gt;3) don't even try to use the specific minimum list of permissions,&lt;BR&gt;4) ask user to make completely unreasonable and unsecure configuration changes to OS configuration, that affect not just Steam service, but the whole machine,&lt;BR&gt;5) want to run network-facing service with admin rights.&lt;/P&gt;
&lt;P&gt;Now would you trust the guys who are clueless about security, obviously can't write secure code, and apparently don't care about YOUR machine security at all, to run network-facing service with admin rights on your machine? I don't. I think I'll try stay away from them from now on.&lt;/P&gt;
&lt;P&gt;P.S. As with everything else in this blog, this post represents my personal opinion, and does not represent the view of my employer.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6492442" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="Rant" scheme="http://blogs.msdn.com/b/michen/archive/tags/Rant/" /><category term="non-secure" scheme="http://blogs.msdn.com/b/michen/archive/tags/non_2D00_secure/" /></entry><entry><title>SQL Server ETL Survey: Win a Zune!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/11/20/etl-survey.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/11/20/etl-survey.aspx</id><published>2007-11-21T02:11:00Z</published><updated>2007-11-21T02:11:00Z</updated><content type="html">&lt;P&gt;Improve SSIS and win a Zune:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://mscuillume.smdisp.net/Collector/Survey.ashx?Name=SqlETLSurvey2"&gt;https://mscuillume.smdisp.net/Collector/Survey.ashx?Name=SqlETLSurvey2&lt;/A&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6445882" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>SQL 2008 November CTP is available</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/11/19/november_5F00_ctp.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/11/19/november_5F00_ctp.aspx</id><published>2007-11-19T20:56:00Z</published><updated>2007-11-19T20:56:00Z</updated><content type="html">&lt;P&gt;Get it from &lt;A href="https://connect.microsoft.com/SQLServer"&gt;https://connect.microsoft.com/SQLServer&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;One of the improvements in this build is the ability to persist Lookup&amp;nbsp;reference data&amp;nbsp;and use non-OLEDB sources for Lookup.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6405966" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS Lookup" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Lookup/" /><category term="Katmai" scheme="http://blogs.msdn.com/b/michen/archive/tags/Katmai/" /></entry><entry><title>Deploying packages</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/11/07/deploying-packages.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/11/07/deploying-packages.aspx</id><published>2007-11-07T12:48:00Z</published><updated>2007-11-07T12:48:00Z</updated><content type="html">&lt;P mce_keep="true"&gt;How can one deploy packages programmatically? Here is the original question - &lt;/P&gt;
&lt;BLOCKQUOTE class=Q&gt;
&lt;P mce_keep="true"&gt;Is it possible to deploy a package programmatically? We have an application which has a work flow for approval of object. If the object (ssis package) is approved by the concerned authority it has to be deployed to sql server. How can this code be integrated into the application?&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;First, what does it mean to deploy a package? The package is simply a document (DTSX file). SSIS&amp;nbsp;utilities (DTEXEC, DTUTIL and other)&amp;nbsp;natively support three locations for&amp;nbsp;such files:&amp;nbsp;the actual file in the OS file system, a row in SQL table, or virtualized file in SSIS storage (which wraps the previous two). If you use custom application to run your packages, you can store them anywhere you want (e.g. application resources) - and use &lt;A class="" href="http://technet.microsoft.com/en-us/library/microsoft.sqlserver.dts.runtime.package.loadfromxml.aspx" target=_blank mce_href="http://technet.microsoft.com/en-us/library/microsoft.sqlserver.dts.runtime.package.loadfromxml.aspx"&gt;LoadFromXML&lt;/A&gt; to load it.&lt;/P&gt;
&lt;P&gt;Since package is just a file, in most cases simply copying a file to destination directory location, or to SQL table is all it takes to deploy a package. One can use SSIS API&amp;nbsp;(like &lt;A class="" href="http://blogs.msdn.com/controlpanel/blogs/is%20it%20possivle%20to%20deploy%20a%20package%20programmatically? we have an application which have a work flow for approval of object. if the object(ssis package) is approved by the concerned authority it has to be deployed to sql server. How can this code be integrated into the application?" target=_blank mce_href="http://blogs.msdn.com/controlpanel/blogs/is it possivle to deploy a package programmatically? we have an application which have a work flow for approval of object. if the object(ssis package) is approved by the concerned authority it has to be deployed to sql server. How can this code be integrated into the application?"&gt;SaveToSqlServer&lt;/A&gt;) to do this, run DTUTIL, or copy file using Win32 or .NET APIs.&lt;/P&gt;
&lt;P&gt;In some cases it is necessary to make some adjustments to the package, e.g. if you have multiple packages calling each other - modify the parent package to correctly reference new location of child package(s). This can be done using SSIS API as well, but it is easier and usually better for maintainability to do this using SSIS configurations - edit the configuration values instead of the package itself.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5955354" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS/" /></entry><entry><title>SSIS Lookups modes or leaky abstractions</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/michen/archive/2007/10/03/ssis-lookups-modes.aspx" /><id>http://blogs.msdn.com/b/michen/archive/2007/10/03/ssis-lookups-modes.aspx</id><published>2007-10-03T22:31:00Z</published><updated>2007-10-03T22:31:00Z</updated><content type="html">&lt;p&gt;I've got a question&amp;nbsp;about SSIS Lookup,&lt;/p&gt;
&lt;blockquote class="Q"&gt;
&lt;p class="MsoPlainText" style="margin: 0in 0in 0pt;"&gt;&lt;span face="Consolas" size="3" style="font-family: Consolas; font-size: small;"&gt;how-to change Partial or No_Cache mode in Lookup task ?&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p mce_keep="true"&gt;I could give a quick answer, but&amp;nbsp;wanted to explain what I think we did wrong in SQL 2005, and how are we planning to fix this.&amp;nbsp;Before continuing, I recommend everyone to read this article:&lt;/p&gt;
&lt;p&gt;Joel Spolsky, The Law of Leaky Abstractions&lt;br /&gt;&lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html" mce_href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;http://www.joelonsoftware.com/articles/LeakyAbstractions.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What that article has to do with SSIS Lookup and the question in particular?&lt;/p&gt;
&lt;p&gt;If you look at the Lookup UI, you wound not find any reference to&amp;nbsp;Partial or NoCache mode (it does show up in Properties panel, but most users miss it).&amp;nbsp;The idea was that you don't have to know about all these details. The UI abstracts away these modes&amp;nbsp;by providing advanced options, where you can enable Memory Restrictions. If have enough memory, you would keep defaults. But if you have less memory, you would enable Memory Restrictions, and specify how much memory Lookup should use. It is all clean and simple, and ideally you don't need to know about all these internal modes and how Lookup operates. I wish :).&lt;/p&gt;
&lt;p&gt;And of course, like most abstractions, this one is very leaky. It is even mentioned in some Books Online articles, even though BOL does not mention the Lookup modes&amp;nbsp;when describing Lookup&amp;nbsp;Editor.&lt;/p&gt;
&lt;p&gt;Internally, SSIS Lookup has three caching modes: Full, Partial and None. They dramatically change how the Lookup operates. Full cache is used if you don't check &lt;strong&gt;Enable memory restrictions&lt;/strong&gt;, None if you check &lt;strong&gt;Enable memory restrictions&lt;/strong&gt; but&amp;nbsp;don't select &lt;strong&gt;Enable caching&lt;/strong&gt;, and Partial cache is used when you check both &lt;strong&gt;Enable memory restrictions&lt;/strong&gt; and &lt;strong&gt;Enable caching&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In Fully Cached mode, Lookup fetches all the data into memory first, then processes its input, matching (using internal &lt;strong&gt;binary comparison&lt;/strong&gt;) input data with reference data. Obviously, SQL is not used anymore to process all this data. So you don't need SQL indexes, but you better have enough &lt;strong&gt;RAM&lt;/strong&gt; to hold all the reference data.&lt;/p&gt;
&lt;p&gt;In Partial or No Cache mode, Lookup does not fetch any data in advance. It starts with empty internal cache. For each input row, it looks into the cache, and then if it does not find data, it executes SQL SELECT statement and saves the result in the cache. You set the size of the cache for Partial cache mode in the Lookup properties. For No Cache mode, only the very last row is cached (which makes No Cache a bit of confusing name, but it's been decided it does not make much sense to throw away this last row, the memory for it has already been allocated anyway). Obviously, you need an &lt;strong&gt;index&lt;/strong&gt; on the reference table for this to perform well. Also, SQL uses its own &lt;strong&gt;collation&lt;/strong&gt; rules that are usually different from internal binary comparison.&lt;/p&gt;
&lt;p&gt;Now you probably see why this abstraction was very leaky. The results could be different (SQL collation vs. binary collation), you need to create indexes for partial/no-cache mode, etc. And hopefully, now you know enough about what is hidden by this abstraction to use Lookup efficiently and correctly.&lt;/p&gt;
&lt;p&gt;P.S. In SQL 2008, we decided to stop hiding this abstraction, and provide direct access to the Lookup cache modes. Here is the first page of new Lookup editor:&lt;br /&gt;&lt;a href="http://blogs.msdn.com/photos/michen/images/5267231/original.aspx"&gt;http://blogs.msdn.com/photos/michen/images/5267231/original.aspx&lt;/a&gt;&lt;br /&gt;I'll probably blog about other new items you see here - Cache connection manager and handling of no-match entries later.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5267261" width="1" height="1"&gt;</content><author><name>Michael Entin</name><uri>http://blogs.msdn.com/michen/ProfileUrlRedirect.ashx</uri></author><category term="SSIS Lookup" scheme="http://blogs.msdn.com/b/michen/archive/tags/SSIS+Lookup/" /></entry></feed>