<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8490763</id><updated>2012-02-02T12:33:24.775+01:00</updated><title type='text'>Failing The Turing Test</title><subtitle type='html'>On Laziness, Impatience and Hubris</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://arnosoftwaredev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default?start-index=101&amp;max-results=100'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>332</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8490763.post-1092918793532875286</id><published>2011-12-19T00:45:00.000+01:00</published><updated>2011-12-19T00:46:30.947+01:00</updated><title type='text'>While We Were Doing That...</title><content type='html'>&lt;a href="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/10000/2000/400/12434/12434.strip.gif" title="Dilbert.com"&gt;&lt;img src="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/10000/2000/400/12434/12434.strip.gif" border="0" alt="Dilbert.com" width="430"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1092918793532875286?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1092918793532875286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1092918793532875286'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/12/while-we-were-doing-that.html' title='While We Were Doing That...'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5445110963344447200</id><published>2011-12-18T22:49:00.016+01:00</published><updated>2011-12-22T23:38:47.126+01:00</updated><title type='text'>JAX-WS: WSDL Fetched During Service Proxy Creation</title><content type='html'>I did some webservice client programming again lately, and was surprised to learn that by default, the &lt;a href="http://jax-ws.java.net/"&gt;JAX-WS&lt;/a&gt; service client library fetches the WSDL from its original location again each time a service proxy is created. I am pretty sure that not everyone is aware of that, and that this leads to production system problems once WSDLs become unavailable.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://jax-ws.java.net/faq/index.html#wsdl_at_runtime"&gt;JAX-WS FAQs&lt;/a&gt; mention runtime binding as the reason behind this, but as the WSDL is not supposed to change, I wonder why it isn't simply mapped into a generated Java class, or added as a local resource. Anyway, this can be done manually using the &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/wsgen.html"&gt;JAX WS wsgen tool&lt;/a&gt; (commandline option "-wsdl"), adding the local WSDL file to your JAR file, and then &lt;a href="http://stackoverflow.com/a/1770860"&gt;setting the service proxy's WSDL_LOCATION constant to&lt;/a&gt;:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;MYSERVICE_WSDL_LOCATION = &lt;br /&gt;    new URL(&lt;br /&gt;        MyClass.class.getResource(""), &lt;br /&gt;        "MyWsdl.wsdl");&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5445110963344447200?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5445110963344447200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5445110963344447200'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/12/jax-ws-wsdl-fetched-during-service.html' title='JAX-WS: WSDL Fetched During Service Proxy Creation'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2017575697363258352</id><published>2011-12-14T22:00:00.016+01:00</published><updated>2012-01-09T22:36:19.899+01:00</updated><title type='text'>Chickens Can Do Your Job</title><content type='html'>&lt;a href="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/60000/9000/900/69997/69997.strip.sunday.gif" title="Dilbert.com"&gt;&lt;img src="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/60000/9000/900/69997/69997.strip.sunday.gif" alt="Dilbert.com" border="0" width="430" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And that's why we don't verify a candidate's skills by asking technical questions in interviews. That might hurt his feelings after all! We probably have heard of studies stating that, from a group of people with identical formal qualification, the top-notch folks can outperform their counterparts by a factor of 10 in efficiency (that is coding productivity, code quality, correctness and performance, whatever measure you choose). More than that, even people with lower formal qualification are more efficient by a factor of 10, compared to the other end of the spectrum.&lt;br /&gt;&lt;br /&gt;But does that mean we will be searching for great developers, and once we have found them, pay them accordingly, and give them tasks that match their skills? No! Developers are developers are developers. They are interchangable, and what they don't know now, they will simply learn on-the-job. It's that easy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2017575697363258352?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2017575697363258352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2017575697363258352'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/12/lets-train-chickens.html' title='Chickens Can Do Your Job'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4227129016034781299</id><published>2011-11-22T07:17:00.003+01:00</published><updated>2011-11-22T07:26:56.427+01:00</updated><title type='text'>COM Programming</title><content type='html'>Luckily, with all those ATL macros and wizards available, resp. with COM-specific annotations in case of .NET, COM development became a lot easier over the years. Still, I would recommend everyone who is programming in COM, or even just reusing an existing COM component, to at least once walk through the complete process of manually implementing and registrating a COM component (including ClassFactory and hand-written registration code), to find out what's going on under the hood. A very nice step-by-step tuorial can be found at &lt;a href="http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567"&gt;codeguru.com&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4227129016034781299?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4227129016034781299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4227129016034781299'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/11/com-programming.html' title='COM Programming'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-256925842697324823</id><published>2011-10-03T22:18:00.042+02:00</published><updated>2011-11-02T20:37:43.867+01:00</updated><title type='text'>Tips For Lightning-Fast Insert Performance On SQL Server</title><content type='html'>&lt;span style="font-weight:bold;"&gt;1.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Increase ADO.NET BatchSize&lt;/span&gt; to eliminate unnecessary network roundtrips, e.g. &lt;a href="http://msdn.microsoft.com/en-us/library/xw5z0hdd(v=VS.100).aspx"&gt;SqlDataAdapter.UpdateBatchSize&lt;/a&gt; when working with DataAdapters, or &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.batchsize.aspx"&gt;SqlBulkCopy.BatchSize&lt;/a&gt; when applying  SqlBulkCopy.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;2.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Limit the number of indices&lt;/span&gt; on the target table to what is really essential for query performance.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;3.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Place indices on master table columns&lt;/span&gt; referenced by the target table's foreign keys.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;4.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Choose the target table's clustered index wisely&lt;/span&gt;, so that inserts won't lead to clustered index node splits. Usually an identity column (AKA "autoinc") is a good choice. If you don't have autoinc primary keys, consider introducing a new identity column just for the sake of making it your clustered index.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;5.&lt;/span&gt; Let the client &lt;span style="font-weight:bold;"&gt;insert into a temporary heap table first&lt;/span&gt; (that is, a table that has no clustered index, resp. no index at all). Then, issue one big &lt;a href="http://blog.sqlauthority.com/2007/08/15/sql-server-insert-data-from-one-table-to-another-table-insert-into-select-select-into-table/"&gt;"insert-into-select"&lt;/a&gt; statement to push all that staging table data into the actual target table. The "insert-into-select"-statement must contain an "order-by"-clause which guarantees ordering by clustered index.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;6.&lt;/span&gt; Apply &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx"&gt;&lt;span style="font-weight:bold;"&gt;SqlBulkCopy&lt;/span&gt;&lt;/a&gt;.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;7.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Decrease transaction logging&lt;/span&gt; by choosing &lt;a href="http://msdn.microsoft.com/en-us/library/ms189275.aspx"&gt;bulk-logged recovery model&lt;/a&gt;, resp. setting &lt;a href="http://sqlserverplanet.com/sql-server-2008/sql-server-2008-minimally-logged-inserts/"&gt;SqlServer traceflag 610&lt;/a&gt;.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;8.&lt;/span&gt; If your business scenario allows for it, &lt;span style="font-weight:bold;"&gt;place a table lock before inserting&lt;/span&gt;. This will make any further locking unnecessary, and is especially a viable option on staging tables as described in &lt;span style="font-weight:bold;"&gt;(5)&lt;/span&gt;. SqlBulkCopy also supports table locks via &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopyoptions.aspx"&gt;SqlBulkCopyOptions&lt;/a&gt;.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;9.&lt;/span&gt; Place database &lt;span style="font-weight:bold;"&gt;datafile and logfile on two physically separated devices&lt;/span&gt;, e.g. on two disks or two SAN LUNs configured for different spindles.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;10.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Prefer server-side processsing&lt;/span&gt; (e.g. by means of "insert-into-select") to client-to-server-roundtrips wherever possible.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;11.&lt;/span&gt; This is &lt;span style="font-weight:bold;"&gt;probably the fastest insert-approach&lt;/span&gt; I have ever heard of (taken from this &lt;a href="http://www.sqlbi.eu/LinkClick.aspx?fileticket=svahq1Mpp9A%3D&amp;tabid=169&amp;mid=375"&gt;sqlbi whitepaper&lt;/a&gt;, see final paragraph): Create a new heap table just for the current insert batch, SqlBulk-Copy data into that table, then build a suited clustered index, and &lt;span style="font-weight:bold;"&gt;add the table as a new table partition to an existing partitioned table&lt;/span&gt;.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;12.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Check execution plan when inserting&lt;/span&gt;, and go sure it does not contain anything unexpected or dispensable that might slow down your inserts, e.g. UDF-calls during check constraint execution, heavyweight trigger code, referential integrity checks without index usage or indexed view updates.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-256925842697324823?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/256925842697324823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/256925842697324823'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/10/tips-for-lightning-fast-insert.html' title='Tips For Lightning-Fast Insert Performance On SQL Server'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3298088184908711438</id><published>2011-09-04T13:51:00.006+02:00</published><updated>2011-09-04T13:59:33.261+02:00</updated><title type='text'>How To Make A Borderless WPF Windows Movable</title><content type='html'>Say you have a borderless WPF window (WindowStyle="None"), the problem arises, how can one still move/drag the window around on the desktop (as there simply is no more window title bar)? &lt;br /&gt;&lt;br /&gt;It's actuelly pretty simple:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;MouseDown += delegate { DragMove(); };&lt;/pre&gt;&lt;br /&gt;Thanks to &lt;a href="http://marlongrech.wordpress.com/2009/02/27/title-less-windows-in-wpf/"&gt;Marlon&lt;/a&gt; for this hint.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3298088184908711438?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3298088184908711438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3298088184908711438'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/09/how-to-keep-borderless-wpf-windows.html' title='How To Make A Borderless WPF Windows Movable'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2896877624216794163</id><published>2011-09-03T07:46:00.003+02:00</published><updated>2011-09-05T18:44:32.034+02:00</updated><title type='text'>Is Your WPF ProgressBar Eating Up Unnecessary CPU Cycles?</title><content type='html'>When your WPF ProgressBar is causing lots of CPU usage, even if it is not visible any more, this most likely is caused by the IsIndeterminate property, which - when set to true - simply continues the animation to be running in the background. &lt;br /&gt;&lt;br /&gt;One solution is to bind the IsIndeterminate property to the same underlying value as the Visibility property, for example something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;&amp;lt;ProgressBar IsIndeterminate="{Binding IsBusy}" &lt;br /&gt;Visibility="{Binding IsBusy, &lt;br /&gt;Converter={StaticResource VisibilityConverter}}"/&amp;gt;&lt;/pre&gt;&lt;br /&gt;The IsBusy property of the bound DataSource can then represent whatever condition is suited in this scenario.&lt;br /&gt;&lt;br /&gt;Thanks to &lt;a href="http://cisforcoder.wordpress.com/2010/10/06/tips-for-progressbar-performance-in-wp7/"&gt;Adam&lt;/a&gt; for this tip!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2896877624216794163?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2896877624216794163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2896877624216794163'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/09/is-your-wpf-progressbar-eating-up.html' title='Is Your WPF ProgressBar Eating Up Unnecessary CPU Cycles?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3187194186510669555</id><published>2011-07-06T23:44:00.001+02:00</published><updated>2011-07-06T23:46:49.019+02:00</updated><title type='text'>Compuware Acquires DynaTrace</title><content type='html'>Congrats to the folks at DynaTrace: &lt;a href="http://enterpriseapplications.cbronline.com/news/compuware-acquires-dynatrace-software-for-256m-060711"&gt;Compuware acquires DynaTrace&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3187194186510669555?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3187194186510669555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3187194186510669555'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/07/compuware-acquires-dynatrace.html' title='Compuware Acquires DynaTrace'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6492863089321599550</id><published>2011-07-01T23:49:00.002+02:00</published><updated>2011-07-02T01:32:54.601+02:00</updated><title type='text'>Confessions Of An IT Manager</title><content type='html'>&lt;blockquote&gt;As I stumbled back out into the light of the car-park, I wondered, for the life of me, how the IT industry had got in such a mess that unqualified and untrained people were in responsible positions within organisations all over the country, managing the databases that are the lifeblood of the enterprise. &lt;br /&gt;&lt;br /&gt;What would happen if the same state of affairs infected Surgery, so that there was a chance of being operated on by someone whose only qualification was that he'd cut open his teddy bear as a child, using his 'My Little Doctor' kit, but managed to bullshit his way into a job armed with a bogus CV and a string of false references.&lt;/blockquote&gt;&lt;br /&gt;From: &lt;a href="http://www.red-gate.com/our-company/about/book-store/confessions"&gt;"Confessions of an IT Manager"&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6492863089321599550?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6492863089321599550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6492863089321599550'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/07/confessions-of-it-manager.html' title='Confessions Of An IT Manager'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6361967125099582883</id><published>2011-06-30T23:45:00.005+02:00</published><updated>2011-07-02T21:52:57.987+02:00</updated><title type='text'>Free EBooks On Redgate.Com</title><content type='html'>RedGate offers several &lt;a href="http://www.red-gate.com/our-company/about/book-store/"&gt;free eBooks&lt;/a&gt; on SqlServer and other topics (some of them only announced and yet to become downloadable) on their bookstore. I had a look at &lt;a href="http://www.red-gate.com/our-company/about/book-store/sql-server-execution-plans"&gt;"SQL Server Execution Plans"&lt;/a&gt; by Grant Fritchey, which seems to be pretty good. So now we can probably forgive RedGate for turning &lt;a href="http://reflector.red-gate.com"&gt;Reflector&lt;/a&gt; into a commercial product, after promising the opposite two years before, right?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6361967125099582883?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6361967125099582883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6361967125099582883'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/06/free-ebooks-on-redgatecom.html' title='Free EBooks On Redgate.Com'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7565917197496853528</id><published>2011-02-17T22:03:00.016+01:00</published><updated>2011-02-19T09:28:15.817+01:00</updated><title type='text'>When DataTable.Select() Is Slow, Use DataView.FindRows()</title><content type='html'>ADO.NET's &lt;a href="http://msdn.microsoft.com/en-us/library/det4aw50.aspx"&gt;DataTable.Select()&lt;/a&gt; comes with a runtime complexity of O(N). This turns into a problem when the DataTable contains a lot of rows, and DataTable.Select() is invoked many times. It's pretty obvious why - just like in LinqToObjects-queries - there is no indexed lookup. DataTable.Select() needs to scan through all rows, and compare all values.&lt;br /&gt;&lt;br /&gt;What I usually ended up doing in those cases, was to loop over the DataTable once, and fill a Dictionary using the Select's lookup-criteria as the entry's key, and the DataRow as its value, and use the Dictionary for lookups from this point on. This works fine, but it clutters the code with all those Dictionaries, which - in addition - must be synchronized manually each time changes occur in the underlying DataTable.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://msdn.microsoft.com/en-us/library/01s96x0z.aspx"&gt;DataView class&lt;/a&gt; offers an alternative. By defining &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.dataview.sort.aspx"&gt;DataView's Sort&lt;/a&gt;-property in accordance to the search criteria, &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.dataview.findrows.aspx"&gt;DataView.FindRows()&lt;/a&gt; then allows indexed lookup with the usual binary search tree lookup runtime complexity of O(log(n)) (yes I know, &lt;a href="http://arnosoftwaredev.blogspot.com/2010/10/hash-tables-vs-binary-search-tree.html"&gt;Hashtable lookup might even execute at O(1)&lt;/a&gt;, given an excellent hashing algorithm, but O(log(n)) will do here). For example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;// do this just one time&lt;br /&gt;DataView view = new DataView(table);&lt;br /&gt;view.Sort = "name, city";&lt;br /&gt;&lt;br /&gt;// do this N times&lt;br /&gt;DataRowView[] res = view.FindRows(&lt;br /&gt;    new object[] { "Earp", "Tombstone" });&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I should probably mention that DataTables and DataRows actually do offer indexed searching in two special cases:&lt;br /&gt;&lt;br /&gt;(1) When there is a primary key defined on a DataTable, &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.datarowcollection.find.aspx"&gt;DataTable.Rows.Find()&lt;/a&gt; (resp. DataTable.FindBy[KeyColName]() on typed DataSets) allows for indexed primary key loookup.&lt;br /&gt;(2) When there is a child relation defined on a DataTable, &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.datarow.getchildrows.aspx"&gt;DataRow.GetChildRows()&lt;/a&gt; (resp. DataRow.Get[ChildRelName]() on typed DataSets) will return the parent row's children by indexed lookup as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7565917197496853528?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7565917197496853528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7565917197496853528'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/02/when-datatableselect-is-slow-use.html' title='When DataTable.Select() Is Slow, Use DataView.FindRows()'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5614479042795020031</id><published>2011-02-06T21:40:00.032+01:00</published><updated>2011-03-27T22:13:41.914+02:00</updated><title type='text'>When SQL IS Broken</title><content type='html'>I think it was Mr. Benedict in "Last Action Hero", who said:&lt;br /&gt; &lt;br /&gt;&lt;blockquote&gt;"Eliminate all logical solutions to a problem - and the illogical becomes invariably true."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/TU8OGZyysXI/AAAAAAAAASM/LMPvvYMhDtI/s1600/benedict.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 189px;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/TU8OGZyysXI/AAAAAAAAASM/LMPvvYMhDtI/s320/benedict.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5570686767246520690" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I received a distress call the other day, concerning a legacy application locking a SqlServer 2000 database, together with some profiler tracefiles.&lt;br /&gt;&lt;br /&gt;Looking at the traces I indeed saw how certain statements were blocked for minutes, until other connections committed their transactions. Nothing extradordinary one might think, of course uncommitted data updates lead to data locks, which will block ensuing statements, that are trying to read or write the same data. Maybe those transactions were just taking too long. But here comes the catch: &lt;span style="font-weight:bold;"&gt;They did not contain any Inserts/Updates/Deletes, just Selects&lt;/span&gt; (the only reason for the existence of the transaction being, that a programmer some fifteen years ago had decided to activate &lt;a href="http://msdn.microsoft.com/en-us/library/ms187807.aspx"&gt;implicit transactions&lt;/a&gt;). And with &lt;a href="http://en.wikipedia.org/wiki/Isolation_%28database_systems%29"&gt;Isolation Level "Read Committed"&lt;/a&gt;, and those queries lasting only a few milliseconds, there should not be any long-living locks.&lt;br /&gt;&lt;br /&gt;Unfortunately, SqlServer 2000 tracing does not support &lt;a href="http://sqllearnings.blogspot.com/2009/04/blocked-process-report-in-sql-profiler.html"&gt;"Blocked process report"&lt;/a&gt; events. This tracing event was only introduced in SqlServer 2005, and delivers in detail which SQL statements block which other statements (you can find it in the "Errors and Warnings" event category. Hint: don't forget to activate 'show advanced options', and set an appropriate 'blocked process threshold' via sp_configure()). Of course one can always have a look at the Management Studio Acitivity Monitor, but the statement information there is not so fine-grained (all it displayes is the last statement within a batch, not necessarily the one causing blocking). And I hadn't received any activity monitor data for my analysis.&lt;br /&gt;&lt;br /&gt;So when I took a closer at the tracefile, all I could do was to scan through all of those Lock Acquired / Lock Released events. This is a really tedious task, not only because there might be hundreds of locks being acquired and released on any scope from row to table, but most of all because you only see which locks get acquired, and not which locks actually fail to get acquired (and hence lead to blocking).&lt;br /&gt;&lt;br /&gt;The statement being blocked turned out to be a single row update. I saw a key lock and a page lock being acquired, and then things stood still. So what I did was to look for an identical statement within the trace, but one which would not be blocked. Luckily, I found one, and by closer examination, I noticed that the next lock acquisition would have been a page level &lt;a href="http://msdn.microsoft.com/en-us/library/ms175519.aspx"&gt;lock mode&lt;/a&gt; upgrade from "Intent Update (IU)" to "Intent Exclusive (IE)". That's also not surprising, just standard procedure when a row is about to be updated.&lt;br /&gt;&lt;br /&gt;The only thing that &lt;a href="http://msdn.microsoft.com/en-us/library/ms186396.aspx"&gt;collides with such a lock upgrade&lt;/a&gt; would have been an existing "Shared (S)" lock on the same page (or a row within that page). But how could there be a shared lock, when no other query accessing the same data was currently running (taking in consideration Isolation Level "Read Committed")? OK, some minutes before, a query accessing the same table had been executed, and its transaction was still open (due to implicit transactions), but the statement's shared locks should be gone by now. OK, it didn't make a lot of sense to use a transaction for a read-only operation (again: Isolation Level "Read Committed", not "Repeatable Read"), and applying a clientside cursor was also kind of weird. But the cursor had been closed at this point, so it's locks should be gone. Legacy applications sometimes act weird, and we should still expect proper database locking.&lt;br /&gt;&lt;br /&gt;I built a repro testcase, and sure enough, could reproduce it on SqlServer 2000. There were several shared locks on the query's connection, even once the query's cursor had been closed. In comparison, running the same testcase on SqlServer 2005 resulted to no dagling IS locks, hence no blocking. Well, it's anyway about time to upgrade ten year old database systems. You see, &lt;a href="http://www.pragprog.com/the-pragmatic-programmer/extracts/tips"&gt;"SQL(Server 2000) WAS broken"&lt;/a&gt; in this case.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5614479042795020031?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5614479042795020031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5614479042795020031'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/02/when-sql-is-broken.html' title='When SQL IS Broken'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/TU8OGZyysXI/AAAAAAAAASM/LMPvvYMhDtI/s72-c/benedict.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4979620758138678859</id><published>2011-01-28T20:30:00.019+01:00</published><updated>2011-03-29T08:38:31.277+02:00</updated><title type='text'>SqlServer: When querying previously inserted rows within the same transaction is slow...</title><content type='html'>... this might have to do with table statistics not having been updated yet!&lt;br /&gt;&lt;br /&gt;I was banging my head on this problem today: One and the same query took minutes when run within a transaction, and only seconds when run right after transaction commit. What's special about that case is, that the query reads from a table, that undergoes massive inserting just the statement before (insert-by-select).&lt;br /&gt;&lt;br /&gt;As I was testing in an isolated environment without any other connections, it was pretty clear from that start, that this was not a locking issue. I suspected a query optimizer problem from the beginning, and tried "option(recompile)", rephrasing the query (it was pretty complex, three levels of subqueries and the like) and even indexed views, but to no avail. &lt;br /&gt;&lt;br /&gt;I was about to dig into a huge profiled transaction batch execution plan (the same query's post-transaction execution plan had looked fine), when I found &lt;a href="http://www.eggheadcafe.com/software/aspnet/30952497/sql-slow-performance-in-large-transaction.aspx"&gt;this posting&lt;/a&gt;, and scales fell from my eyes. My immediate guess: this was not an index issue (as the original poster had presumed), this was an "outdated statistics" issue! See, every index has statistics attached, and rebuilding an index automatically updates its statistics as well.&lt;br /&gt;&lt;br /&gt;But instead of calling some heavyweight "dbcc dbreindex" as explained in the posting (index rebuild should go to a maintenance plan, but not be placed in an application runtime SQL batch!), I simply invoked "update statistics" on the table in question, right between the insert statement and the query. Voila! Query optimizer is back on track, and now executes the same query about a hundred times faster. I should note though, that "update statistics" also comes with a certain overhead (two to three seconds in our case).&lt;br /&gt;&lt;br /&gt;I was aware that SqlServer decides on its own when to update statistics. Seems as if 50,000 new rows and a complex follow-up query isn't necessarily considered sufficient reason for a statistics update, at least not when being part of the same transaction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4979620758138678859?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4979620758138678859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4979620758138678859'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/01/sqlserver-when-querying-previously.html' title='SqlServer: When querying previously inserted rows within the same transaction is slow...'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-655352599763120078</id><published>2011-01-26T00:10:00.007+01:00</published><updated>2011-02-20T23:43:37.703+01:00</updated><title type='text'>Is "Select Distinct" a Code Smell?</title><content type='html'>From my experience, "Select distinct" indeed is a code smell (&lt;a href="http://www.databasejournal.com/features/postgresql/article.php/3437821/SELECT-DISTINCT-A-SQL-Case-Study.htm"&gt;and I am not alone&lt;/a&gt;). Why would I fetch something, just to get rid of it again in the next step? There are situations when "distinct" makes sense, but they are rare...&lt;br /&gt;&lt;br /&gt;In 9 out of 10 cases when I encountered a "Select distinct", the programmer tried to remove duplicate rows caused by joins along multiple too-many associations (AKA cartesian product). This not only is bad programming style, it might also kill performance.&lt;br /&gt;&lt;br /&gt;There is always a way to rephrase such a query in order to avoid join duplicates, e.g. by replacing joins with exist-clauses, using subselects within the select-clause, derived tables in the from-clause, etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-655352599763120078?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/655352599763120078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/655352599763120078'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/01/is-select-distinct-code-smell.html' title='Is &quot;Select Distinct&quot; a Code Smell?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-276723037937029165</id><published>2011-01-23T20:52:00.065+01:00</published><updated>2011-11-08T07:52:57.314+01:00</updated><title type='text'>Hibernate Performance Tuning Tips</title><content type='html'>&lt;span style="font-weight: bold;"&gt;(1)&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;Avoid join duplicates&lt;/span&gt; (AKA cartesian products) due to joins along two or more parallel to-many associations; use Exists-subqueries, multiple queries or fetch="subselect" (see (2)) instead - whatever is most appropriate in the specific situation. Join duplicates are already pretty bad in plain SQL, but things get even worse when they occur within Hibernate, because of unnecessary mapping workload and child collections containing duplicates.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(2)&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;Define lazy loading&lt;/span&gt; as the preferred association loading strategy, and consider applying &lt;span style="font-weight: bold;"&gt;fetch="subselect"&lt;/span&gt; rather than "select" resp. "batch-size". Configure eager loading only for special associations, but &lt;span style="font-weight: bold;"&gt;join-fetch selectively on a per-query basis&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(3)&lt;/span&gt; In case of read-only services with huge query resultsets,&lt;span style="font-weight: bold;"&gt; use projections and fetch into flat DTOs&lt;/span&gt; (e.g. via AliasToBean-ResultTransformer), instead of loading thousands of mapped objects into the Session.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(4)&lt;/span&gt; Take advantage of &lt;span style="font-weight: bold;"&gt;HQL Bulk Update and Delete&lt;/span&gt; statements, as well as &lt;span style="font-weight: bold;"&gt;Insert-By-Select&lt;/span&gt; (supported by HQL as well).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(5)&lt;/span&gt; Set &lt;span style="font-weight: bold;"&gt;FlushMode to "Never"&lt;/span&gt; on Queries and Criteria, when flushing is not necessary at this point.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(6)&lt;/span&gt; Set &lt;span style="font-weight: bold;"&gt;ReadOnly to "true"&lt;/span&gt; on Queries and Criteria, when objects returned will never be modified.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(7)&lt;/span&gt; Consider &lt;span style="font-weight: bold;"&gt;clearing the Session after flushing&lt;/span&gt;, when objects are not longer needed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(8)&lt;/span&gt; Define a suitable value for &lt;span style="font-weight: bold;"&gt;jdbc.batch_size&lt;/span&gt; (resp. adonet.batch_size under NHibernate).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(9)&lt;/span&gt; Use Hibernate &lt;span style="font-weight: bold;"&gt;Query-Cache&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;Second Level Caching&lt;/span&gt; where appropriate (but &lt;a href="http://www.javalobby.org/java/forums/t48846.html"&gt;go sure you are aware of the consequences&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(10)&lt;/strong&gt; Set &lt;strong&gt;hibernate.show_sql to "false"&lt;/strong&gt; and ensure that Hibernate logging is running at the &lt;strong&gt;lowest possible loglevel&lt;/strong&gt; (also check log4j/log4net root logger configuration).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;&lt;strong&gt;Special Addendum:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;NHibernate's 2.x &lt;strong&gt;ProjectionList implementation added considerable runtime-overhead&lt;/strong&gt;, namely by invoking some reflection API calls for every resultset row. This might slow down queries with large resultsets.&lt;br /&gt;&lt;br /&gt;As those reflection API calls were invariant in regard to the resultset rows being iterated over (as a matter of fact, what these calls actually did was to gather resultset column schema information), and I needed an immediate fix and did not have time to wait for any NHibernate patch, I forked my own ProjectionList from NHibernate's code, did all those reflection calls once, and re-used the result from that point on. Now I doubt this is still an issue under NHibernate 3.x (and I think Hibernate under Java was never affected), but if you are using NHibernate 2.x and your profiling tool shows lots of CPU cycles being burnt in ProjectionList, you might want to give this approach a try.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All those advices are Hibernate-specific. For additional database tuning posts on this blog, please read:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;Missing Indices&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html"&gt;The Importance Of Choosing The Right Clustered Index&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/06/more-performance-tuning.html"&gt;More Performance Tuning&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/09/sqlserver-favor-inline-udfs-over-table.html"&gt;SqlServer: Favor Inline UDFs Over Table Variables&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/09/sqlserver-standard-edition-does-support.html"&gt;SqlServer Standard Edition DOES Support Indexed Views&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/is-select-distinct-code-smell.html"&gt;Is "Select Distinct" a Code Smell?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/10/tips-for-lightning-fast-insert.html"&gt;Tips For Lightning-Fast Insert Performance On SQL Server&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;More Hibernate postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hibernate-reverse-engineering-and.html"&gt;Hibernate Reverse Engineering And SqlServer Identity Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/05/implementing-equals-and-hashcode-in.html"&gt;Implementing Equals And HashCode In Hibernate POCOs/POJOs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/06/nhibernate-criteria-query-child.html"&gt;NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/10/hibernate-joins-without-associations.html"&gt;Hibernate: Joins Without Associations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/fetching-strategies-in-hibernate.html"&gt;Fetching Strategies In Hibernate&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-276723037937029165?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/276723037937029165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/276723037937029165'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/01/hibernate-performance-tips.html' title='Hibernate Performance Tuning Tips'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4447432625351895799</id><published>2011-01-23T20:16:00.004+01:00</published><updated>2011-02-13T21:51:09.581+01:00</updated><title type='text'>Fetching Strategies In Hibernate</title><content type='html'>One thing always to remember is that the fetching strategies - as defined within Hibernate mapping configuration - only affect data retrieval via Session.get(), Session.load() and association navigation, plus Criteria queries, as long as they do not override fetch behavior via Criteria.setFetchMode(). HQL in contrast works in a kind of WYSIWYG-manner - whatever fetching is defined by the query will be done, not more, and not less, with mapping configuration being ignored as far as fetching is concerned.&lt;br /&gt;&lt;br /&gt;More Hibernate postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hibernate-reverse-engineering-and.html"&gt;Hibernate Reverse Engineering And SqlServer Identity Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/05/implementing-equals-and-hashcode-in.html"&gt;Implementing Equals And HashCode In Hibernate POCOs/POJOs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/06/nhibernate-criteria-query-child.html"&gt;NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/10/hibernate-joins-without-associations.html"&gt;Hibernate: Joins Without Associations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/hibernate-performance-tips.html"&gt;Hibernate Performance Tuning Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4447432625351895799?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4447432625351895799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4447432625351895799'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2011/01/fetching-strategies-in-hibernate.html' title='Fetching Strategies In Hibernate'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8417321230829326426</id><published>2010-11-30T22:26:00.002+01:00</published><updated>2010-11-30T22:34:54.597+01:00</updated><title type='text'>Will The Real Programmers Please Stand Up?</title><content type='html'>Arousing &lt;a href="http://www.rethinkdb.com/blog/2010/06/will-the-real-programmers-please-stand-up/"&gt;posting on the RethinkDB blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;In the interest of openness, we’ll post the smoke test that makes us turn away 19 out of 20 candidates within half an hour of a phone conversation (and that’s after screening the resumes). We don’t ask people to code a solution to a complex algorithms problem. We don’t ask to solve tricky puzzle questions. We don’t ask to do complex pointer arithmetic or manipulation. Here is the question that the vast majority of candidates are unable to successfully solve, even in half an hour, even with a lot of nudging in the right direction:&lt;br /&gt;&lt;br /&gt;Write a C function that reverses a singly-linked list.&lt;br /&gt;&lt;br /&gt;That’s it. We’ve turned away people with incredibly impressive resumes (including kernel developers, compiler designers, and many a Ph.D. candidate) because they were unable to code a solution to this problem in any reasonable amount of time.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8417321230829326426?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8417321230829326426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8417321230829326426'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/11/will-real-programmers-please-stand-up.html' title='Will The Real Programmers Please Stand Up?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3620914002071738949</id><published>2010-11-25T07:02:00.017+01:00</published><updated>2012-02-02T12:33:24.781+01:00</updated><title type='text'>Using Database Snapshots For Fast Testautomation DB Restore</title><content type='html'>The textbooks basically suggest the following alternatives for restoring databases to their original state after individual automated test runs:&lt;br /&gt;&lt;br /&gt;(1) Let each test insert its own testdata on initialization, and when done, revert those changes, as well as everything the test did update.&lt;br /&gt;(2) Embed the whole test run in a transaction, and rollback the transaction at the end.&lt;br /&gt;(3) Restore the database from its original state before each test.&lt;br /&gt;&lt;br /&gt;Unfortunately, none of those options worked for us. (1) is not practicable on complex database operations. (2) is not possible if there is no access to the database connection / transaction applied by the tested component. Plus we us a different data access layer for verifying test results (as the same data access layer could camouflage errors), so there is no way to work within the same transaction scope. Option (3) - when taken conventionally (applying traditional backup/restore) - was just too slow; because of several reasons, that very test database is about 150MB of size, and we have over 2000 automated tests to run.&lt;br /&gt;&lt;br /&gt;What helped me out was SqlServer's wonderful &lt;a href="http://msdn.microsoft.com/en-us/library/ms175158.aspx"&gt;Database Snapshot&lt;/a&gt; feature (available in Developer and Enterprise Editions). Snapshots are readonly-views of the database at a certain point in time. Their copy-on-write approach allows for much faster database restore, as the database now keeps track of which data pages have really changed. &lt;br /&gt;&lt;br /&gt;So what I basically did was to create a snapshot at the beginning, then run the test on the original database, and then restore that database from the snapshot. Lightning fast!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3620914002071738949?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3620914002071738949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3620914002071738949'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/11/using-database-snapshots-for-fast-unit.html' title='Using Database Snapshots For Fast Testautomation DB Restore'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6468184799657035127</id><published>2010-10-16T21:37:00.038+02:00</published><updated>2011-02-13T21:50:43.280+01:00</updated><title type='text'>Hibernate: Joins Without Associations</title><content type='html'>I think I have seen this topic come up dozens of times on the Hibernate discussion forums:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;How can I join a table in Hibernate when there is no association in the object model?&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And the usual answer is: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Use Theta-style Joins in HQL, and it can't be done in Criteria API.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;While this is basically true, I would like to elaborate a little bit more on that.&lt;br /&gt;&lt;br /&gt;First of all, we should distinguish between joining and join fetching in Hibernate. Are we using the join in order to fetch a child collection or a single entity within the same query, or not? Because if we are not, there is the chance we don't need a Join at all, and &lt;span style="font-weight:bold;"&gt;a simple Exists-subquery will do as well, because the joined entity only appears in the From-/Where-clauses, not in the Select-clause.&lt;/span&gt; And in a correlated subquery, we can connect pretty much everything to the main query. This has other advantages too, e.g. avoiding unwanted join duplicates and achieving better performance.&lt;br /&gt;&lt;br /&gt;Thinking about join-fetching, this might also give us an idea (at least by my interpretation) on why Theta-Joins are required in HQL. They basically look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;select foo, bar&lt;br /&gt;from   FooEntity as foo, BarEntity as bar&lt;br /&gt;where  foo.someothercol = 'foo' and &lt;br /&gt;       foo.somecol = bar.somecol&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So we will end up with a resultset that contains a list, with each list element being an object-array of size 2, with obj[0] representing a FooEntity and obj[1] representing a BarEntity .&lt;br /&gt;&lt;br /&gt;If we had by contrast an association between FooEntity and BarEntity, we could right something like:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;select foo&lt;br /&gt;from   FooEntity foo&lt;br /&gt;       inner join fetch foo.Bars&lt;br /&gt;where  foo.someothercol = 'foo'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looking at the query alone makes it pretty clear, that there must be a distinct definition on how the association between FooEntity and BarEntity looks like. &lt;span style="font-weight:bold;"&gt;This definition, usually representing a foreign key relation, will be applied on join fetches, lazy loading and - maybe most importantly - when updating associations.&lt;/span&gt; That is what Hibernate associations are all about. &lt;span style="font-weight:bold;"&gt;FooEntity.Bars will always contain child-rows following that association definition (either all child-rows, or a subset). This is why we can not choose any other arbitrary join criteria in order to fetch other BarEntities into FooEntity.Bars.&lt;/span&gt; It just does not make sense.&lt;br /&gt;&lt;br /&gt;"But I want to use Criteria API", I can hear you say. OK, I'll come to that in a moment.&lt;br /&gt;&lt;br /&gt;First I should mention at that point, that Hibernate also offers nice support for legacy databases in this context. Associations can be defined even when referencing non-primary keys. Unfortunately &lt;a href="http://216.121.112.228/browse/NH-1722"&gt;the NHibernate-guys have not ported that feature yet&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And I want to note that the restriction on associated rows can be narrowed down even more by providing an additional "With"-expression in de Join-Clause or within the Where-clause in HQL.&lt;br /&gt;&lt;br /&gt;But i digress. So we have a solution for HQL, and we know why associations are needed for join fetching, and when there is no join fetching but only joining, we might be better off using an Exists subquery anyway. So what about Criteria API?&lt;br /&gt;&lt;br /&gt;Fact is: Joining requires an association under Criteria API. Fact is also: There are join criterias that can not be expressed by an association. So the solution I came up with was: &lt;span style="font-weight:bold;"&gt;Use a database view as a kind of mediator.&lt;/span&gt; This view, let's call it FooBar, contains the join criteria expression in it's From/Where-clause, and selects the primary keys of the two associated tables Foo and Bar:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;create view FooBar as&lt;br /&gt;select Foo.Id as FooId,&lt;br /&gt;       Bar.Id as BarId&lt;br /&gt;from   Foo&lt;br /&gt;       inner join Bar on foo.somecol = bar.somecol&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The view can as well contain further columns, and even a primary key (constructed on-the-fly), or might be materialized for performance reasons. &lt;br /&gt;&lt;br /&gt;It is mapped into the Hibernate object model just as any table would be in case of a 1:1 or 1:N relation (and we can then define associations both to Foo and to Bar), or as a N:M mapping table. Hibernate won't even know (nor care), that it's a view. In our object model, we make those associations read-only (as we rather won't try to update the view), simply by providing public access only to an enumerator/iterator, resp. a getter but no setter for a single associated entity.&lt;br /&gt;&lt;br /&gt;We can now join-fetch formerly non-associated entities under Criteria API as well, or project some of their columns into a flat resultset (AKA DTO-fetching). And we are able to provide further criteria in our queries' Where-clauses. Works like charm!&lt;br /&gt;&lt;br /&gt;From a design viewpoint, we should not pollute our domain model with too many of those view-associations. What we can do is to provide an additional table mapping for those cases, e.g. by subclassing our main entities.&lt;br /&gt;&lt;br /&gt;With all those options at hand, there is very little we can NOT do with Joins in Hibernate. And in that case, there is always native SQL support in Hibernate as well.&lt;br /&gt;&lt;br /&gt;More Hibernate postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hibernate-reverse-engineering-and.html"&gt;Hibernate Reverse Engineering And SqlServer Identity Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/05/implementing-equals-and-hashcode-in.html"&gt;Implementing Equals And HashCode In Hibernate POCOs/POJOs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/06/nhibernate-criteria-query-child.html"&gt;NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/fetching-strategies-in-hibernate.html"&gt;Fetching Strategies In Hibernate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/hibernate-performance-tips.html"&gt;Hibernate Performance Tuning Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6468184799657035127?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6468184799657035127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6468184799657035127'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/10/hibernate-joins-without-associations.html' title='Hibernate: Joins Without Associations'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4438178569254808860</id><published>2010-10-15T21:33:00.075+02:00</published><updated>2012-01-07T20:03:43.060+01:00</updated><title type='text'>Hash Tables Vs. Binary Search Trees</title><content type='html'>This is one of my favorite programming interview questions: "What's the difference between Hash Tables and Binary Search Trees?". Let's see...&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tbody&gt;&lt;tr valign="top"&gt;&lt;td&gt;&lt;br /&gt;&lt;/td&gt;&lt;td&gt;&lt;span style="font-weight: bold;"&gt;Hash Tables&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span style="font-weight: bold;"&gt;Binary Search Trees&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;Algorithm&lt;/td&gt;&lt;td&gt;Keys are mapped to values by using hash functions. Hash functions transform the key to a numeric index (usually by calculating an integer value from the key first, and then applying a "modulo arraysize" operation). This index identifies an array element ("bucket"), where all corresponding values reside (values which all stem from keys with equal hash value). The key must still be looked up within the bucket, but as hashing algorithms are supposed to ensure a good hash value distribution, hence small bucket size, this lookup is very fast. The bucket array may have to be resized dynamically when a certain load factor is reached.&lt;/td&gt;&lt;td&gt;Node-based tree data structure, where every node contains one record (key-only, or key and value), and has a left subtree (with smaller keys) and a right subtree (with larger keys); hence keys must be comparable. Self-balancing trees, like &lt;a href="http://en.wikipedia.org/wiki/Red%E2%80%93black_tree"&gt;red-black-trees&lt;/a&gt;, keep their height as small as possible for fast searching and inserting.&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;Java Implementations&lt;/td&gt;&lt;td&gt;Hashtable&lt;br&gt;HashMap&lt;br&gt;HashSet&lt;/td&gt;&lt;td&gt;TreeMap&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;.NET Implementations&lt;/td&gt;&lt;td&gt;Hashtable&lt;br&gt;Dictionary&lt;br&gt;HashSet&lt;/td&gt;&lt;td&gt;SortedDictionary&lt;br /&gt;SortedList&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;C++ STL Implementations&lt;/td&gt;&lt;td&gt;std::unordered_map&lt;br&gt;std::unordered_set&lt;/td&gt;&lt;td&gt;std::map&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;Sorted&lt;/td&gt;&lt;td&gt;No&lt;br&gt;&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;td&gt;Range Search&lt;/td&gt;&lt;td&gt;No&lt;br&gt;&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;Runtime Complexity&lt;/td&gt;&lt;td&gt;Inserting: O(1) (normal case), O(N) (worst case, only with bad hash algorithm)&lt;br&gt;Searching: O(1) (normal case), O(N) (worst case, only with bad hash algorithm).&lt;br&gt;&lt;a href="http://en.wikipedia.org/wiki/Hash_table"&gt;More details&lt;/a&gt;.&lt;/td&gt;&lt;td&gt;Inserting: O(log(n)) (when balanced)&lt;br&gt;Searching: O(log(n)) (when balanced)&lt;br&gt;&lt;a href="http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree"&gt;More details&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="top"&gt;&lt;td&gt;Implementation Notes&lt;/td&gt;&lt;td&gt;Equal objects must produce the same hash code, however unequal objects need not produce distinct hash codes. Equals() implementations must be reflexive, symmetric, transitive, consistent. &lt;a href="http://www.technofundo.com/tech/java/equalhash.html"&gt;More details&lt;/a&gt;.&lt;br&gt;.NET's ValueType.Equals() and GetHashCode() methods are based on reflection, hence slow; you should provide your own implementation in structs.&lt;/td&gt;&lt;td&gt;CompareTo() implementations must be reflexive, symmetric, transitive, consistent. &lt;a href="http://msdn.microsoft.com/en-us/library/43hc6wht.aspx"&gt;More details&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4438178569254808860?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4438178569254808860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4438178569254808860'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/10/hash-tables-vs-binary-search-tree.html' title='Hash Tables Vs. Binary Search Trees'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5696426448715387003</id><published>2010-10-14T21:15:00.004+02:00</published><updated>2010-10-14T22:30:26.042+02:00</updated><title type='text'>The Story Behind Balsamiq Mockups</title><content type='html'>Peldi Guilizzoni writes on the &lt;a href="http://blogs.balsamiq.com/peldi/2008/10/14/personal-whats-your-story-why-start-balsamiq"&gt;creation of Balsamiq Mockups&lt;/a&gt;. They have indeed built a wonderful tool, elegant in its design, simple in its usage, yet extremely powerful in what can be achieved by applying it. Our UI folks have been using &lt;a href="http://balsamiq.com/products/mockups"&gt;Balsamiq Mockups&lt;/a&gt; since summer 2008, but I just happened to read the history of its creation today. What a truly inspiring story - sometimes one has to take risks to fulfill his dream.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5696426448715387003?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5696426448715387003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5696426448715387003'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/10/story-behind-balsamiq-mockups.html' title='The Story Behind Balsamiq Mockups'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8283905828760076287</id><published>2010-10-02T00:04:00.001+02:00</published><updated>2010-10-02T00:05:39.913+02:00</updated><title type='text'>The Best Programmers...</title><content type='html'>&lt;blockquote&gt;The best programmers are not marginally better than merely good ones. They are an order-of-magnitude better, measured by whatever standard: conceptual creativity, speed, ingenuity of design, or problem-solving ability.&lt;/blockquote&gt;&lt;div style="text-align: right;"&gt;(Randall E. Stross)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8283905828760076287?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8283905828760076287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8283905828760076287'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/10/best-programmers.html' title='The Best Programmers...'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8969408010763999861</id><published>2010-09-03T22:10:00.019+02:00</published><updated>2011-01-24T00:36:30.145+01:00</updated><title type='text'>SqlServer: Favor Inline UDFs Over Table Variables</title><content type='html'>T-SQL's Table Variables surely do have their benefits, but several drawbacks as well. We used to apply them in the past within larger T-SQL batches, in order to save and reuse some smaller query results, and to avoid code duplication:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;declare @MyTable table (...)&lt;br /&gt;&lt;br /&gt;insert into @MyTable(...)&lt;br /&gt;select ...&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;from   @MyTable MyTable&lt;br /&gt;       inner join ...&lt;br /&gt;where  ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But some databases grew in size far beyond original expectations, and those table variables that used to hold 100 rows, now hold 10.000 rows or more, and spill over into TempDB, hence perform badly.&lt;br /&gt;&lt;br /&gt;So I replaced them with Inline User Defined Functions (these are table-valued UDFs, which only consist of a single Select statement): &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;create function dbo.MyUdf(...)&lt;br /&gt;returns table&lt;br /&gt;as&lt;br /&gt;return (&lt;br /&gt;    select ...&lt;br /&gt;)&lt;br /&gt;go&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;They can then be inlined like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;select *&lt;br /&gt;from   (dbo.MyUdf(someparam)) MyVirtualTable&lt;br /&gt;       inner join ...&lt;br /&gt;where  ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This means high performance (due to the UDF query being inlined, query optimizer can do quite some magic) AND avoiding code duplication. Nice!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8969408010763999861?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8969408010763999861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8969408010763999861'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/09/sqlserver-favor-inline-udfs-over-table.html' title='SqlServer: Favor Inline UDFs Over Table Variables'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8925249936520138530</id><published>2010-09-02T22:44:00.017+02:00</published><updated>2010-09-03T23:36:31.290+02:00</updated><title type='text'>SqlServer Standard Edition DOES Support Indexed Views</title><content type='html'>Why on earth do nine out of ten articles on SqlServer &lt;a href="http://en.wikipedia.org/wiki/Materialized_view"&gt;Indexed Views&lt;/a&gt; state, that they are only available in Enterprise and Developer Edition? This is blatantly wrong, and prevents developers from using a great performance tuning feature. &lt;br /&gt;&lt;br /&gt;Standard Edition DOES support Indexed Views (and from what I have heard, but not tried: even so does Express Edition). The only difference is, that one has to provide a "with (noexpand)" query hint in order for the Indexed View to be applied in Standard Edition (I checked execution plans to go sure). Plus Enterprise Edition can choose to use an Indexed View for optimization, even if it does not appear in the original query.&lt;br /&gt;&lt;br /&gt;I think this whole confusion might be caused by &lt;a href="http://msdn.microsoft.com/en-us/library/cc645993.aspx"&gt;Microsoft's SqlServer feature matrices&lt;/a&gt;, which are pretty unclear on that topic, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8925249936520138530?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8925249936520138530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8925249936520138530'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/09/sqlserver-standard-edition-does-support.html' title='SqlServer Standard Edition DOES Support Indexed Views'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-547912033633901850</id><published>2010-09-01T23:53:00.030+02:00</published><updated>2010-09-07T06:31:13.545+02:00</updated><title type='text'>.NET ValueType Equals()/GetHashCode() Performance</title><content type='html'>As most people are aware of, .NET Framework's builtin implementations of Equals() and GetHashCode() for &lt;a href="http://msdn.microsoft.com/en-us/library/s1ax56ch%28VS.80%29.aspx"&gt;ValueTypes&lt;/a&gt; are based on &lt;a href="http://msdn.microsoft.com/en-us/library/ms173147%28VS.80%29.aspx"&gt;Value Equality&lt;/a&gt; (as opposed to many &lt;a href="http://msdn.microsoft.com/en-us/library/490f96s2.aspx"&gt;ReferenceTypes&lt;/a&gt;, where those methods are based on Reference Equality). The problem is, those ValueType.Equals() and GetHashCode() implementations are quite slow for user-defined structs - which is not surprising, as they must apply reflection for obtaining field and property values.&lt;br /&gt;&lt;br /&gt;That's why it's &lt;a href="http://msdn.microsoft.com/en-us/library/ms182276%28VS.80%29.aspx"&gt;better to code your own Equals() and GetHashCode() methods&lt;/a&gt; in these cases. The task is pretty much straightforward - just compare those fields and properties, that define equality, in Equals(), and XOR their values (or numeric representations of their values) in GetHashCode():&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public override bool Equals(object obj) {&lt;br /&gt;    Nullable&amp;lt;MyType&amp;gt; other = obj as Nullable&amp;lt;MyType&amp;gt;;&lt;br /&gt;    return other != null &amp;&amp; &lt;br /&gt;           this.X == other.Value.X &amp;&amp; &lt;br /&gt;           this.Y == other.Value.Y;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override int GetHashCode() {&lt;br /&gt;    return this.X ^ this.Y;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I recently was asked to tune some code, and replaced several nested loops with dictionary lookups - only to find out that this was not as fast as I had expected, due to structs being applied as dictionary keys. Provided my own Equals() and GetHashCode() implementations, and voila - ten-fold speedup!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-547912033633901850?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/547912033633901850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/547912033633901850'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/09/net-valuetype-equalsgethashcode.html' title='.NET ValueType Equals()/GetHashCode() Performance'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8471849240007230119</id><published>2010-08-02T15:36:00.004+02:00</published><updated>2010-08-02T15:45:28.331+02:00</updated><title type='text'>Developer Candidate Interviews</title><content type='html'>Uncle Bob / Robert Martin writes in &lt;a href="http://blog.objectmentor.com/articles/2010/03/07/developer-certification-wtf"&gt;Developer Certification WTF?&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;So what if we… interviewed ... candidate developers? What if we asked them what accomplishments they’d achieved in previous employment. What if we asked them who they used to work with. And… ok, this is even wilder ... what if we called those references and checked them out?&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Yeah, and what if we would additionally let them write some code during the interview? I know this sounds crazy to many software development companies, with some notable exceptions (Microsoft, Google and Apple for example - hmmm, maybe this should make us think?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8471849240007230119?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8471849240007230119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8471849240007230119'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/08/developer-candidate-interviews.html' title='Developer Candidate Interviews'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2333792501525814553</id><published>2010-05-02T21:26:00.026+02:00</published><updated>2011-11-21T22:40:49.409+01:00</updated><title type='text'>How to improve Visual Studio Build Performance</title><content type='html'>Funny how compile duration is sometimes seen as being carved in stone.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;"Building our solution takes 3 minutes, so each time I change a line of code in a base component and run the app, I must wait 3 minutes. Period."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Is this really how software development should work these days?&lt;br /&gt;&lt;br /&gt;As projects grow over time, compilation gets slower and slower. Productivity goes south when people are watching the compiler do its work half the time. The decline creeps in and people think this is just normal, until a new developer arrives and throws his hands up in horror.&lt;br /&gt;&lt;br /&gt;Also, Visual Studio seems to lack behind other IDEs (most notably Eclipse) on this topic. Eclipse has built-in support for REAL incremental background compilation for ages now (it compiles while the developer types (or during idle time), hence can start the application with no or little delay once he hits &amp;lt;Run&amp;gt;). Visual Studio still doesn't.&lt;br /&gt;&lt;br /&gt;So let's see what we can do to speed up things in Visual Studio.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(1) Decrease File IO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Use the same output path for every project in your solution, e.g. "..\Output\Debug"&lt;/span&gt; for "Debug" configuration (Project / Properties / Build / Output Path). All assemblies will be created there, so no need to copy files back and forth between project folders.&lt;br /&gt;&lt;br /&gt;If there is only one output path, project references can be set to &lt;span style="font-weight: bold;"&gt;"Copy Local = False"&lt;/span&gt; (References / Properties / Copy Local). This furthermore decreases File IO.&lt;br /&gt;&lt;br /&gt;I have experienced up to three-fold buildtime improvements by applying that simple configuration change.&lt;br /&gt;&lt;br /&gt;In addition, you might want to &lt;span style="font-weight: bold;"&gt;mitigate virus scanner policies&lt;/span&gt; for scans on devenv.exe file access.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(2) Prevent non-dependent project compilation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If a solution contains projects, that the current startup project does not depend upon (e.g. test projects), compiling those projects can be avoided by selecting the option &lt;span style="font-weight:bold;"&gt;"Only build startup projects and dependencies on Run"&lt;/span&gt; under Tools / Options / Projects and Solutions / Build and Run.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;(3) Prevent project dependency tree compilation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Visual Studio resp. MSBuild compiles all projects with changes, plus the complete project dependency tree underneath those projects. This is all good and fine, but it also implies that once only one line of code is changed in a commonly used base class, this might cause a complete solution recompilation. But why should we recompile everything, when say only the interior of a single method implementation has changed?&lt;br /&gt;&lt;br /&gt;When you know that you are going to change something deep down in your dependency tree, but won't be altering non-private method signatures, here is what you can do:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Option 3.1: Additional solution configurations&lt;/span&gt;&lt;br /&gt;Create &lt;span style="font-weight:bold;"&gt;additional solution configurations for partial builds&lt;/span&gt;, and apply identical output paths as defined in (1) (Solution / Properties / Configuration Properties / Configuration Manager / Active Solution Configuration / New).&lt;br /&gt;&lt;br /&gt;Solution configurations allow for defining which projects should be built, and which should not; this can lead to much smaller build dependency trees. We might provide configurations for framework class libraries, for functional modules or for data access projects, and so on. And once we know we only are going to change the inner workings of certain components, we switch to the suitable solution configuration first.&lt;br /&gt;&lt;br /&gt;Other dependent projects are not going to be compiled if they are not part of the active configuration. What does that imply at runtime? As there are no newly built dependent assemblies, the previously built assemblies (still residing in "..\Output\Debug") are going to be loaded, which is just what we want.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Option 3.2: Manually avoid dependent project recompile&lt;/span&gt;&lt;br /&gt;Under Tools / Options / Projects and Solutions / Build and Run, choose &lt;span style="font-weight:bold;"&gt;"On run, when projects are out of date: Never build"&lt;/span&gt;. Then, &lt;span style="font-weight:bold;"&gt;manually build the project&lt;/span&gt; only where sourcecode has been edited (Build &amp;lt;projectname&amp;gt; resp. Shift-F6), and finally run the startup project. This too only works when all projects use the same output folder (see (1)).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Option 3.3: Decouple projects, use assembly references instead of project references&lt;/span&gt;&lt;br /&gt;Decoupling is not only recommended for software architecture in general, it might also boost compiletime. When application layers are separated (e.g. coding against interfaces, and acquiring implementation classes only at runtime by using factories, dependency injection and the like), code changes other than interface modifications will not lead to dependent project compilations any more.&lt;br /&gt;&lt;br /&gt;Also, choose solution structure carefully. You don't want one project containing 80% of all code, and the rest spread on ten other projects. Or consider having Team A work on projectset #1, and Team B working on projectset #2, which depends on what Team A has produced. Provide separate solution files for each Team, &lt;span style="font-weight:bold;"&gt;let Team A deliver libraries to Team B, and integrate them in Team B's solution by using assembly references (instead of project references)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Take those options with a grain of  salt, though. Once signatures of methods invoked by other assemblies are actually altered, the compiler will not be able to pick  that up, and runtime errors will occure. In addition, these approaches require stable assembly version numbers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2333792501525814553?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2333792501525814553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2333792501525814553'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/05/how-to-improve-visual-studio-compile.html' title='How to improve Visual Studio Build Performance'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7483286977343448820</id><published>2010-03-31T21:28:00.013+02:00</published><updated>2010-08-31T16:50:53.507+02:00</updated><title type='text'>And I shall call it "Foreign Key Cascade Delete"</title><content type='html'>I know it sounds kind of stupid trying to implement cascade delete in T-SQL, when all you have to do is to activate the "cascade delete" option on each foreign key within the association graph. Similarly stupid as &lt;a href="http://thedailywtf.com/Articles/Classic_WTF_-_And_I_think_I_0x27_ll_call_it__0x2e__0x2e__0x2e__0x2e___0x26_quot_0x3b_Referential_Integrity_0x26_quot_0x3b__%21.aspx"&gt;implementing referential integrity by hand&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;But imagine a case where you generally don't want cascade delete in normal production mode (because no user should be able to delete a parent row which has any child rows still referencing it). And unfortunately there are those rare occasions, when just for the moment, for one single parent row, cascade delete would make sense again, because you really have to get rid of it, only exceptionally.&lt;br /&gt;&lt;br /&gt;Of course one could simply activate cascade deletes for this single transaction. But I don't like to change database schemas in production.&lt;br /&gt;&lt;br /&gt;The guys at sqlteam.com came up with a &lt;a href="http://www.sqlteam.com/article/performing-a-cascade-delete-in-sql-server-7"&gt;different solution&lt;/a&gt;: A stored procedure, invoked recursively, traversing over all foreign key relations and deleting child rows bottom-up.&lt;br /&gt;&lt;br /&gt;Sounded nice, but did not work in our case. The approach was based on nested subqueries, and from about 8 nested queries on, SqlServer refused to even calculate a query execution plan (I do have sympathy for SqlServer here, I wouldn't enjoy building an execution plan on queries like this either. And even with a plan, the performance would most likely be extremely poor). Plus the script could not handle foreign key relation cycles (I mean cycles on a table-basis, not on a row-basis; the latter can't be solved anyway), a fact that caused a stored procedure "stack overflow" (the limit is 32 recursive calls) on our database.&lt;br /&gt;&lt;br /&gt;I then started to adapt the sqlteam.com script in order to make it work, and was about halfway done, when I noticed that &lt;a href="http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=14594"&gt;commentator GreySky had already provided a solution&lt;/a&gt; which was following the same approach I had planned for. He solved the execution plan / performance issue by storing parent primary key values in a temptable, together with information about the current call level, so that the delete criteria would be ONE simple IN-subselect instead of many nested subselects.&lt;br /&gt;&lt;br /&gt;So GreySky's script worked fine, with the exception that it still did not handle cycles in foreign key relations. But at this point, with a temptable holding all parent primary keys at hand, this was a simple task. I only had to add an extra column storing the table name that the primary keys belonged to.  Thus it was possible to extend the statement's where-criteria to "visit" only those child rows, that had not been marked for deletion before. (Nearly) like &lt;a href="http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm"&gt;Dijkstra's algorithm&lt;/a&gt;, you know ;-) Works like a charm!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7483286977343448820?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7483286977343448820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7483286977343448820'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2010/03/and-i-shall-call-it-foreign-key.html' title='And I shall call it &quot;Foreign Key Cascade Delete&quot;'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4686662270263598186</id><published>2009-06-24T18:17:00.005+02:00</published><updated>2009-06-24T18:28:41.647+02:00</updated><title type='text'>Composite WPF ("Prism") DelegateCommand.CanExecuteChanged Memory Leak</title><content type='html'>I was happy to hear that the &lt;a href="http://msdn.microsoft.com/en-us/practices/default.aspx"&gt;Microsoft Patterns and Practices Team&lt;/a&gt; has picked up &lt;a href="http://compositewpf.codeplex.com/WorkItem/View.aspx?WorkItemId=4065"&gt;this Composite WPF ("Prism") bug report&lt;/a&gt; that I submitted several weeks ago. From the issue description:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;When profiling my application I noticed that plenty of EventHandlers had never been deregistered from DelegateCommand's CanExecuteChanged-Event. So those EventHandlers were never been garbage-collected, which caused a severe memory leak.&lt;br /&gt;&lt;br /&gt;As registering CanExecuteChanged-EventHandles is done outside application code scope I had expected them to be deregistered automatically as well. At this point I thought this might as well be a ThirdParty WPF control issue, but digging further I read a blog post stating that "WPF expects the ICommand.CanExecuteChanged-Event to apply WeakReferences for EventHandlers". I had a look into RoutedCommand, and noticed it uses WeakReferences as well.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Now this is no showstopper for us as we are very early in the development cycle, and we simply patched &lt;a href="http://msdn.microsoft.com/en-us/library/cc707894.aspx"&gt;DelegateCommand&lt;/a&gt; in the meantime by using &lt;a href="http://msdn.microsoft.com/en-us/library/system.weakreference.aspx"&gt;WeakReferences&lt;/a&gt; for its &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged.aspx"&gt;CanExecuteChanged&lt;/a&gt;-EventHandlers.&lt;br /&gt;&lt;br /&gt;As far as I know the issue has been fixed already, and it is currently going through testing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4686662270263598186?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4686662270263598186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4686662270263598186'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/composite-wpf-prism-delegatecommandcane.html' title='Composite WPF (&quot;Prism&quot;) DelegateCommand.CanExecuteChanged Memory Leak'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8655879655225874153</id><published>2009-06-17T01:51:00.050+02:00</published><updated>2011-02-20T22:22:27.567+01:00</updated><title type='text'>NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table</title><content type='html'>Example taken from &lt;a href="http://nhjira.koah.net/browse/NH-381"&gt;NHibernate Bugreport #381&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;&lt;br /&gt;session.CreateCriteria(typeof(Contact))&lt;br /&gt;   .Add(Expression.Eq("Name", "Bob")&lt;br /&gt;   .SetFetchMode("Emails", FetchMode.Join)&lt;br /&gt;   .CreateCriteria("Emails")&lt;br /&gt;      .Add(Expression.Eq("EmailAddress", &lt;br /&gt;      "Bob@hotmail.com"))&lt;br /&gt;   .List();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The resulting SQL will include a Join to Emails as expected, the resultset returned by the database is OK, but within the object model Contact.Emails is not going to be populated with data. Which means once Contact.Emails is being accessed in code, lazy loading occurs, which probably was not the coder's intention. This is not the case when&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;CreateCriteria("Emails")&lt;br /&gt;      .Add(Expression.Eq("EmailAddress", &lt;br /&gt;      "Bob@hotmail.com"))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;is omitted.&lt;br /&gt;&lt;br /&gt;The bug report was closed without fix, but contained a comment that "According to Hibernate guys this is correct behavior" and a link to &lt;a href="http://opensource2.atlassian.com/projects/hibernate/browse/HB-918"&gt;Hibernate bug report #918&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;To me that does not sound completely implausible. Hibernate's interpretation ot this criteria tree is that the EMail-criteria is meant to narrow down die Contact parent-row, not the EMail child-rows. HQL queries act just the other way around. Under HQL, additionaly Join-With- or Where-expressions can limit which child rows are loaded into the child collection. I know that HQL - in contrast to Criteria queries - does not apply the fetching strategy defined in the mapping configuration. But with an explicit FetchMode.Join I would have expected Criteria query to do the same.&lt;br /&gt;&lt;br /&gt;Apparently under Criteria API this can be worked around by applying an outer Join (which of course is somewhat semantically different):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;session.CreateCriteria(typeof(Contact))&lt;br /&gt;   .Add(Expression.Eq("Name", "Bob")&lt;br /&gt;   .CreateCriteria("Emails", JoinType.LeftOuterJoin)&lt;br /&gt;      .Add(Expression.Eq("EmailAddress", &lt;br /&gt;      "Bob@hotmail.com"))&lt;br /&gt;   .List();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which seems kind of inconsistent compared to the inner join scenario, and there is even a &lt;a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-2049"&gt;Hibernate bug report&lt;/a&gt; on that.&lt;br /&gt;&lt;br /&gt;What I would recommend anyway: If the goal is to narrow the parent data, but then fetch all the children, why not apply an Exists-subquery for narrowing, and in the same query fetch-join all children without further narrowing. Or, if you prefer lazy loading, simply define fetchmode="subselect" on the association.&lt;br /&gt;&lt;br /&gt;On a related topic, eagerly joining several child associations has the drawback that the resultset consists of a cartesian product over all children - lots of rows with duplicate data. Let's say there are three child associations A, B and C with 10 rows each for a given parent row, joining all three associations will blast up the resultset to 1 x 10 x 10 x 10 = 1000 rows, when only 1 + 10 + 10 + 10 = 31 rows would be needed. &lt;br /&gt;&lt;br /&gt;And while those duplicates will only lead to duplicate references in the object model (and not to duplicate instances), and even those duplicate references can be eliminated again by using Maps or Sets for child collections, these Joins impose a severe performance and memory drawback on the database resp. ADO.NET level. &lt;br /&gt;&lt;br /&gt;Of course one could simply issue N single select statements, one for each table, with equaivalent where-clauses. But that implies N database roundtrips as well. Not so good.&lt;br /&gt;&lt;br /&gt;The means to avoid this are Hibernate Criteria- and HQL-MultiQueries. Gabriel Schenker has posted a really &lt;a href="http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/04/06/eager-loading-aggregate-with-many-child-collections.aspx"&gt;nice article on MultiQueries with NHibernate&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;More Hibernate postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hibernate-reverse-engineering-and.html"&gt;Hibernate Reverse Engineering And SqlServer Identity Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/05/implementing-equals-and-hashcode-in.html"&gt;Implementing Equals And HashCode In Hibernate POCOs/POJOs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/10/hibernate-joins-without-associations.html"&gt;Hibernate: Joins Without Associations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/fetching-strategies-in-hibernate.html"&gt;Fetching Strategies In Hibernate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/hibernate-performance-tips.html"&gt;Hibernate Performance Tuning Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8655879655225874153?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8655879655225874153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8655879655225874153'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/nhibernate-criteria-query-child.html' title='NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4862314854020808668</id><published>2009-06-16T01:34:00.005+02:00</published><updated>2009-06-16T01:40:18.422+02:00</updated><title type='text'>My Amazon Listmania Lists</title><content type='html'>I had nearly forgotten about them, and was surprised to see that over time nearly 25,000 people have viewed &lt;a href="http://www.amazon.com/gp/richpub/listmania/byauthor/ANFZ5EB5H75PI/ref=cm_pdp_lm_all"&gt;my Amazon Listmania Book Recommendation Lists&lt;/a&gt;. Hey I should have received commission! ;-) Subtle hint: The J2EE list is a little bit out-dated by now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4862314854020808668?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4862314854020808668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4862314854020808668'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/my-amazon-listmania-lists.html' title='My Amazon Listmania Lists'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3095151315296173942</id><published>2009-06-12T20:23:00.058+02:00</published><updated>2009-06-13T16:04:55.441+02:00</updated><title type='text'>Visualizing TFS Work Items With Treemaps</title><content type='html'>Microsoft &lt;a href="http://msdn.microsoft.com/en-us/teamsystem/default.aspx"&gt;Team System&lt;/a&gt; / &lt;a href="http://en.wikipedia.org/wiki/Team_Foundation_Server"&gt;Team Foundation Server&lt;/a&gt; is a really nice line of products. Besides version control we heavily rely on TFS Work Items for organizing development tasks. One of our largest project is conducted using &lt;a href="http://en.wikipedia.org/wiki/Scrum_%28development%29"&gt;Scrum&lt;/a&gt;, so we are utilizing &lt;a href="http://www.scrumforteamsystem.com/en/default.aspx"&gt;Conchango's Scrum Plugin for Team System&lt;/a&gt;, plus &lt;a href="http://www.scrumforteamsystem.com/en/TaskBoard/default.aspx"&gt;Conchango Taskboard&lt;/a&gt; for Sprint planning. Taskboard is better suited than the general purpose Work Item lists and forms that are part of Visual Studio Team Explorer. Let's compare.&lt;br /&gt;&lt;br /&gt;Visual Studio Work Item list:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/SjKzGu-zm9I/AAAAAAAAAQk/AY0nd1nUouE/s1600-h/vs.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 353px;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/SjKzGu-zm9I/AAAAAAAAAQk/AY0nd1nUouE/s400/vs.gif" alt="" id="BLOGGER_PHOTO_ID_5346532635914247122" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Conchango Taskboard:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/SjNHeG2bDEI/AAAAAAAAARE/JTI8Rd35jaU/s1600-h/conchango.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 296px;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/SjNHeG2bDEI/AAAAAAAAARE/JTI8Rd35jaU/s400/conchango.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5346695765179436098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;From a certain project size on the Visual Studio Work Item lists just won't scale and end up with heaps of data that one can scroll through forever. Don't get me wrong, those lists are sufficient for standard tasks, but they are cumbersome for gaining insight into the project's big picture. And Conchango Taskboard is for Sprint planning and Product Backlog maintenance only. The Conchango Scrum Plugin does have a set of really nice reports though.&lt;br /&gt;&lt;br /&gt;So this is where I decided to ramp up my own little solution, which would be based on rendering Work Item data into &lt;a href="http://en.wikipedia.org/wiki/Treemap"&gt;Treemaps&lt;/a&gt;. This week I hacked out a little prototype in my sparetime (working title "Aurora"):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/SjLKKPVUBzI/AAAAAAAAAQ8/JXGqBwQmbRg/s1600-h/aurora2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/SjLKKPVUBzI/AAAAAAAAAQ8/JXGqBwQmbRg/s400/aurora2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5346557984905627442" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This configuration example provides an overall impression of the sample project's progress: Green tasks are done, blue tasks are not done, and their size represents their complexity. And this is by no means limited to Scrum projects, it works for all kinds of TFS project templates.&lt;br /&gt;&lt;br /&gt;Three simple input parameters are all it takes:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Work Item type&lt;/span&gt; (e.g. Product Backlog Item, Sprint Backlog Item, Bug)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Work Item attribute defining Treemap size&lt;/span&gt; (e.g. Storypoints or any other numeric data, or none in case all items should be rendered with equal size)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Work Item attribute defining Treemap color&lt;/span&gt; (e.g. State, Sprint ID, etc)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Plus an optional query string for narrowing the list of items.&lt;br /&gt;&lt;br /&gt;This approach possibly allows to visualize about 70% of the reports I could think of. I am still wondering how to implement the missing 30%, as they cannot be covered that easily. For instance I want to group Area Paths with equal prefixes by rendering them with the same color. Or simplify the creation of queries (can't expect everyone to know &lt;a href="http://msdn.microsoft.com/en-us/library/bb130319(VS.80).aspx"&gt;WIQL&lt;/a&gt; by heart). And I don't want to over-complicate things either. Any suggestions regarding those matters are highly welcome! Another requirement is to let the user define color mapping. And item hierarchies are still missing, too (that's the "Tree" in "Treemaps" after all).&lt;br /&gt;&lt;br /&gt;By the way, I am using woopef's &lt;a href="http://www.codeplex.com/treemaps"&gt;WPF TreeMap control&lt;/a&gt;, thanks a lot for making it publicly available. I am also going to open-source Aurora once it provides basic functionality and reaches a certain level of stability, most likely on &lt;a href="http://www.codeplex.com/"&gt;CodePlex&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3095151315296173942?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3095151315296173942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3095151315296173942'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/visualizing-tfs-work-items-with.html' title='Visualizing TFS Work Items With Treemaps'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_G0oBN6-Lb48/SjKzGu-zm9I/AAAAAAAAAQk/AY0nd1nUouE/s72-c/vs.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1654292252269792885</id><published>2009-06-08T00:15:00.012+02:00</published><updated>2009-06-26T23:29:48.294+02:00</updated><title type='text'>Vanilla Data Access Layer Library 0.6.0 Released</title><content type='html'>I have just updated &lt;a href="https://sourceforge.net/projects/vanilla-dal/"&gt;Vanilla DAL on Sourceforge&lt;/a&gt;. Release 0.6.0 is still in Alpha state, and comes with improved automatic transaction management. I now chose am approach similar to &lt;a href="http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope(VS.80).aspx"&gt;System.Transactions.TransactionScope&lt;/a&gt;. Of course nothing as sophisticated, Vanilla DAL's TransactionScope is a simple &lt;a href="http://msdn.microsoft.com/en-us/library/system.idisposable(VS.80).aspx"&gt;IDisposable&lt;/a&gt; object that consists of a &lt;a href="http://msdn.microsoft.com/de-de/library/system.threading.thread.setdata(VS.80).aspx"&gt;thread-bound&lt;/a&gt; transaction and a does some reference-counting. The transaction will be commited when the last TransactionScope is being disposed, resp. rollbacked in case of any exception during execution:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;&lt;br /&gt;    using (accessor.CreateTransactionScope()) &lt;br /&gt;    {&lt;br /&gt;        accessor.Update(new UpdateParameter(northwindDataset.Customers));&lt;br /&gt;        accessor.ExecuteNonQuery(new NonQueryParameter(new Statement("DeleteTestCustomers")));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;My main problem was how to find out whether the current call to &lt;a href="http://msdn.microsoft.com/en-us/library/system.idisposable.dispose(VS.80).aspx"&gt;IDisposable.Dispose()&lt;/a&gt; happens within the process of &lt;a href="http://en.wikipedia.org/wiki/Exception_handling"&gt;exception unwinding&lt;/a&gt;. Several people have recommended &lt;a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getexceptionpointers(VS.80).aspx"&gt;Marshal.GetExceptionPointers()&lt;/a&gt;, which is the only working solution I have found so far. But I consider this a semi-hack. Any better ideas?&lt;br /&gt;&lt;br /&gt;By the way, Luis Ramirez has written a &lt;a href="http://www.codeproject.com/KB/dotnet/SelectDataNetDevTools.aspx"&gt;nice article on Codeproject.Com&lt;/a&gt;, comparing &lt;a href="https://sourceforge.net/projects/vanilla-dal/"&gt;Vanilla DAL&lt;/a&gt; to plain ADO.NET, &lt;a href="http://msdn.microsoft.com/en-us/library/cc309504.aspx"&gt;Microsoft Data Access Application Block&lt;/a&gt; and &lt;a href="http://www.sqlnetframework.com/"&gt;SqlNetFramework&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1654292252269792885?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1654292252269792885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1654292252269792885'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/vanilla-data-access-layer-library-060.html' title='Vanilla Data Access Layer Library 0.6.0 Released'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8096817956087222519</id><published>2009-06-07T21:35:00.000+02:00</published><updated>2009-06-08T01:49:59.763+02:00</updated><title type='text'>Code Coverage Analysis With QmCover</title><content type='html'>My cousin has developed a graphical &lt;a href="http://en.wikipedia.org/wiki/Code_coverage"&gt;code coverage&lt;/a&gt; &lt;a href="http://www.qm-technologies.com/prod_qmCover_Overview.html"&gt;analysis tool&lt;/a&gt; for the &lt;a href="http://en.wikipedia.org/wiki/GNU_toolchain"&gt;GNU toolchain&lt;/a&gt;. Now I am not really up-to-date regarding the current state of code coverage tools in Unix land (during my old &lt;a href="http://en.wikipedia.org/wiki/Solaris_(operating_system)"&gt;Solaris&lt;/a&gt; days all I needed was &lt;a href="http://en.wikipedia.org/wiki/Vi"&gt;vi&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/GNU_Compiler_Collection"&gt;gcc&lt;/a&gt;), but &lt;a href="http://www.qm-technologies.com/prod_qmCover_Overview.html"&gt;QmCover&lt;/a&gt; certainly looks cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8096817956087222519?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8096817956087222519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8096817956087222519'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/06/gcc-code-coverage-analysis-with-qmcover.html' title='Code Coverage Analysis With QmCover'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4704257760755808904</id><published>2009-05-30T21:15:00.017+02:00</published><updated>2011-02-13T21:49:01.728+01:00</updated><title type='text'>Implementing Equals And HashCode In Hibernate POCOs/POJOs</title><content type='html'>Nearly everybody who uses (N)Hibernate stumbles over this question sooner or later: How to implement Equals() And (Get)HashCode() in Hibernate POCOs/POJOs?&lt;br /&gt;&lt;br /&gt;What's the problem? At first sight a naturaly approach seems to apply the object's primary key in Equals And HashCode implementations - simply delegate the calls to Equals and HashCode to the primary key type. But think about it for a second: Most primary keys are AutoIncrement / Identity columns which the database will set when inserting rows. So the primary key's value becomes available only after that - which is too late in most cases. E.g. if there is more than one newly created object (Hibernate state "transient") at one point in time, all these objects would appear to be "equal". Additionally when the newly created object has been added to a child collection of type Map or Set, it will be replaced by the next newly created object that comes along. Undoubtedly this is unwanted behavior.&lt;br /&gt;&lt;br /&gt;Moreover this breaks the Equals/HashCode contract, because the HashCode value changes once the primary key has been set by the database. This may lead to all kinds of weird things, e.g. Maps / IDictionaries whose implementations depend on HashCode will not be able to find their objects any more.&lt;br /&gt;&lt;br /&gt;So what to do about it? &lt;a href="https://www.hibernate.org/109.html"&gt;Articles on hibernate.org&lt;/a&gt; and the holy book of Hibernate &lt;a href="http://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1932394885/ref=pd_bxgy_b_img_c"&gt;"Java Persistence With Hibernate"&lt;/a&gt; recommend applying business keys instead of surrogate keys in Equals / HashCode. Business key values are unique too, plus they are available BEFORE the data has been inserted into the database.&lt;br /&gt;&lt;br /&gt;I used to follow that advice in former projects, but at times found it difficult to determine which would be my business keys. Sometimes they just don't exist in the application domain. Plus business key values are supposed to never change. This might be the case today, but change tomorrow (admitted, then those columns probably never were real business keys in the first place).&lt;br /&gt;&lt;br /&gt;Plenty of postings point out similar problems. Some people came up with quite sophisticated solutions, e.g. a combined surrogate / business key approach. In case an object is newly created and has not been assigned its database surrogate key yet, it will apply a business key. The business key values and hashcodes will be cached and re-used for the lifetime of the object, even after inserting it into the database table. Hence the Equals / HashCode contract is not broken.&lt;br /&gt;&lt;br /&gt;This should work, but seems unnecessarily complicated for me, at least in my scenario. Reading hibernate.org's famous &lt;a href="https://www.hibernate.org/109.html"&gt;109-article&lt;/a&gt; once again, I figured that my current project falls into the first of the following three categories:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_G0oBN6-Lb48/SiGPlt46fkI/AAAAAAAAAQE/d6Zcmjo44n8/s1600-h/hibernate109.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 73px;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/SiGPlt46fkI/AAAAAAAAAQE/d6Zcmjo44n8/s400/hibernate109.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5341708511174360642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We don't have composite keys, and there is no need to compare objects from different Hibernate sessions. We do have multiple new instances in Sets, and collections must stay intact after saving. Which means I can omit any custom Equals / HashCode implementations altogether, and can go along with the default reference-based implementations (instead of value-based). And it works like a charm!&lt;br /&gt;&lt;br /&gt;More Hibernate postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hibernate-reverse-engineering-and.html"&gt;Hibernate Reverse Engineering And SqlServer Identity Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2009/06/nhibernate-criteria-query-child.html"&gt;NHibernate Criteria-Query: Child Collection Not Populated Despite FetchMode.Join When Criteria Exists For Child Table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2010/10/hibernate-joins-without-associations.html"&gt;Hibernate: Joins Without Associations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/fetching-strategies-in-hibernate.html"&gt;Fetching Strategies In Hibernate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2011/01/hibernate-performance-tips.html"&gt;Hibernate Performance Tuning Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4704257760755808904?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4704257760755808904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4704257760755808904'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/05/implementing-equals-and-hashcode-in.html' title='Implementing Equals And HashCode In Hibernate POCOs/POJOs'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_G0oBN6-Lb48/SiGPlt46fkI/AAAAAAAAAQE/d6Zcmjo44n8/s72-c/hibernate109.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1578748840112166522</id><published>2009-04-23T23:32:00.022+02:00</published><updated>2009-05-30T22:39:18.908+02:00</updated><title type='text'>Codegeneration With CodeSmith</title><content type='html'>It has been a while since I blogged about the sad state of NHibernate tool support in .NET land, compared with &lt;a href="http://tools.hibernate.org/"&gt;Hibernate Tools for Eclipse&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I then discovered &lt;a href="http://code.google.com/p/codesmith/source/browse#svn/trunk/Templates/Frameworks/NHibernate"&gt;Tom DuPont's Codesmith templates for NHibernate&lt;/a&gt;, which was a nice starting point. I added some functionality, e.g. support for one-to-one associations, self-referring tables, and of course I had to adapt the output to our needs. By now our develpoment team can create:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.nhibernate.org/"&gt;NHibernate&lt;/a&gt; POCOs&lt;/li&gt;&lt;li&gt;NHibernate mapping configuration&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Data_Access_Object"&gt;DAOs&lt;/a&gt; (yeah I know some folks will complain at this point that with Hibernate there is no need for DAOs - but I mean DAOs in a &lt;a href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/orm/hibernate/support/HibernateDaoSupport.html"&gt;Spring HibernateDAOSupport&lt;/a&gt; sense), DAO-Interfaces&lt;/li&gt;&lt;li&gt;Additionally - and although I generally try to avoid &lt;a href="http://weblogs.asp.net/cibrax/archive/2007/01/25/crud-interface-for-a-service-is-a-bad-practice.aspx"&gt;CRUDY Service interfaces&lt;/a&gt; - I provided the option for generating entity-based Service implementations and interfaces (but hey, at least they are based on DTOs). We have some situations where this is necessary&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Data_Transfer_Object"&gt;DTOs&lt;/a&gt;, incl. validation attributes&lt;/li&gt;&lt;li&gt;Mapping code for NHibernate POCO &lt;=&gt; DTO conversion&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.springframework.net/"&gt;Spring.NET&lt;/a&gt; XML-configuration for DAOs and Services&lt;/li&gt;&lt;li&gt;Unit Test Templates&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;All of this is generated based on the database schema information plus some &lt;a href="http://www.mssqltips.com/tip.asp?tip=1499"&gt;SqlServer extended properties&lt;/a&gt; on a table- and column-level. Those properties may define name aliases, whether associations are bidirectional or not, which validations to apply, how to do the DTO-assembling, which DB-columns to ignore or which .NET type to map to (if it differs from the default type), stuff like that. They are optional, so without extended properties we end up with a default generation style.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.codesmithtools.com/"&gt;CodeSmith&lt;/a&gt; also comes with a nice integration into Visual Studio. So our developers just right-click the code generation project configuration, choose some database tables and which code fragments to generate, and seconds later the generated components are either added or updated within Visual Studio. Some of those fragments are split to partial classes and are then attached as code-behind files, so one can easily overwrite those parts in follow-up code-generations without losing hand-crafted code. Sweet! This is saving my development team tons of time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1578748840112166522?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1578748840112166522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1578748840112166522'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/04/codegeneration-with-codesmith.html' title='Codegeneration With CodeSmith'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7155584959706023583</id><published>2009-03-31T01:35:00.004+02:00</published><updated>2009-04-25T00:04:07.600+02:00</updated><title type='text'>Programming Contests</title><content type='html'>After last year's &lt;a href="http://arnosoftwaredev.blogspot.com/2008_05_01_archive.html"&gt;Cubido C# Pirates Programming Competition&lt;/a&gt; I consider entering the &lt;a href="http://www.catalysts.cc/events/2009/coding-contest/"&gt;Catalysts Coding Contest&lt;/a&gt; this time. That one is about implementation speed, so I was wondering which development environment to use. Most of my programming lately has been in C#, so Visual Studio would be a natural choice. But I consider Eclipse to be a more productive IDE. And there is still the option for a real radical approach - applying LISP for example (oh well, two months left for intensive LISP training then).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7155584959706023583?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7155584959706023583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7155584959706023583'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/03/programming-contests.html' title='Programming Contests'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8254087441281094894</id><published>2009-02-28T18:43:00.001+01:00</published><updated>2009-04-25T00:05:11.775+02:00</updated><title type='text'>Teaching C++</title><content type='html'>I recently taught two sessions of C++ programming to a group of mobile computing students. Noticing my C++ knowledge was getting a little bit rusty this kind of refurbishing was really welcome. I was also half sick, and found it hard to concentrate, but at the end all of my students passed their exam, so it didn't seem to have been THAT bad...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8254087441281094894?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8254087441281094894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8254087441281094894'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/02/teaching-c.html' title='Teaching C++'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5711461666829682705</id><published>2009-01-21T22:22:00.000+01:00</published><updated>2009-04-24T01:52:05.762+02:00</updated><title type='text'>Stressed!</title><content type='html'>Lots of work currently, and my kids keep me busy on the weekends. So unfortunately no more postings this months.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5711461666829682705?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5711461666829682705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5711461666829682705'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2009/01/stressed.html' title='Stressed!'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7127676620393439163</id><published>2008-12-29T20:03:00.015+01:00</published><updated>2008-12-31T21:39:11.355+01:00</updated><title type='text'>Programming: Love It Or Leave It?</title><content type='html'>&lt;a href="http://www.codinghorror.com/blog/archives/001202.html"&gt;Nice post&lt;/a&gt; here from Jeff Atwood:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Unless you're fortunate enough to work for a top tier software development company, like Google, Microsoft, or Apple, you've probably experienced first hand the huge skill disparities in your fellow programmers. I'm betting you've also wondered more than once why some of your coworkers can't, well, program. Even if that's what their job description says.&lt;br /&gt;&lt;br /&gt;Over the last twenty years, I've worked with far too many programmers who honestly had no business being paid to be a programmer. Now, I'm not talking about your average programmer here. We're all human, and we all make mistakes. I'm talking about the Daily WTF crew. People that actively give programming a bad name, and you, as their coworker, a constant headache.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;On the other hand, I would not go as far as Jeff when he states:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;So if a programmer ever hints, even in passing, that they might possibly want to exit the field -- they probably should.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Some of the most talented people I know have had thoughts like this once in a while. And the reason was mainly one of two things: Horrible management mistakes or incompetent co-workers (or even worse, both). &lt;br /&gt;&lt;br /&gt;Which brings us back to Jeff's original argument: Most programmers don't know how to program. And not only does that screw the associated work of the highly-skilled folks, it kills their motivation too.&lt;br /&gt;&lt;br /&gt;If just more bozos would decide to leave the field, well - problem solved! Unfortunately they don't. As they are missing one of the most importat qualifications for this job, namely a healthy bit of self-reflection and the drive to improve, it's quite unlikely most of them have even noticed their deficiencies yet.&lt;br /&gt;&lt;br /&gt;I was tempted to start another long rambling article on this whole topic, but instead of repeating myself let me just shortly refer to several of my older postings:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2004/10/curious-perversions-in-information.html"&gt;Top Ten List Of Programming Perversions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2004/10/fallacy-of-cheap-programmers.html"&gt;The Fallacy Of Cheap Programmers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/handling-bozo-invasion.html"&gt;Handling The Bozo Invasion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2004/11/hire-right-people.html"&gt;Hire The Right People&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/02/why-cant-programmers-program.html"&gt;Why Can't Programmers... Program?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://www.softwarereality.com/soapbox/cheap_programmers.jsp"&gt;Robin Sharp writes&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;A lot of research on programmer productivity shows that the best programmers are up to 20 times more productive than the worst programmers. If the income differential between the best and worst programmers is even 5 times, it means employers are getting incredible value for money hiring the best programmers.&lt;br /&gt;&lt;br /&gt;Why then don't companies hire a few very good programmers and leave the rest flipping Big Macs? There is one very good reason: the psychology of managers. Managers simply can't believe that one programmer can be as productive as 3, let alone 5 or even 20 times. Managers believe that productivity is a management issue.&lt;br /&gt;&lt;br /&gt;They believe that simply by re-organising their human resources it is they who can gain leaps in productivity, and reap the rewards. But the reality of management, as we all know, is that most projects are late and over budget.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And from Jeff's older articles (&lt;a href="http://www.codinghorror.com/blog/archives/000781.html"&gt;Why Can't Programmers.. Program?&lt;/a&gt;, &lt;a href="http://www.codinghorror.com/blog/archives/000072.html"&gt;Skill Disparities in Programming&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;In programming specifically, many studies have shown order of magnitude differences in the quality of the programs written, the sizes of the programs written, and the productivity of the programmers. [...] They studied professional programmers with an average of 7 years' experience and found that the ratio of intitial coding time between the best and worst programmers was about 20:1; the ratio of debugging times over 25:1; of program sizes 5:1; and of program execution speed about 10:1.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Unless you truly enjoy programming you should seek another profession. Be realistic: are you programming to collect a paycheck, or are you programming because you are driven to? I know this sounds harsh, but it's an economic reality-- in an enviroment of global offshoring, the world simply can't support any more highly paid mediocre coders. There are a hundred thousand well educated Indian developers who will do what you do at a fraction of the price, and thousands more coming of age in other third world countries. Blame the Internet if you want, but just being "good with computers" is no longer a free ticket to a high paying tech job.&lt;/blockquote&gt; &lt;br /&gt;&lt;br /&gt;Finally, here is a very good quote from the comment section on Jeff's site:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Actually most programmers are bad programmers. No really, they are. The problem is that everyone can be a programmer. This isn't true for other jobs. Not everyone can work as a lawyer or doctor for example. However nobody forbids you to work as a programmer, you don't need a special license, there is no mandatory education either. If a company hires you as a coder, no matter if you ever had any education on this matter or not, you work as a coder; period. And this itself is not even bad. Some of the best coders on earth never had any education in computer science, still they are brilliant coders. However, many people had no education and are just horrible coders.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7127676620393439163?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7127676620393439163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7127676620393439163'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/12/programming-love-it-or-leave-it.html' title='Programming: Love It Or Leave It?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1350532498024602809</id><published>2008-12-03T21:31:00.018+01:00</published><updated>2008-12-10T05:26:53.904+01:00</updated><title type='text'>Disappearing Permissions On MS Team Foundation Server 2008</title><content type='html'>We had just finished migrating one of our Team System projects from TFS 2005 to a new TFS 2008 machine, when we suddenly noticed that all Team Foundation Server group permissions that had been set on certain project folders via Properties / Security would not show up in the Security tab the next time the Property dialog was opened. The funny thing is - the permissions themselves seemed to work! There was just no way to view, edit or remove them. Same was true for the tf.exe commandline tool ("tf permission ...") - it did not list any existing permissions.&lt;br /&gt;&lt;br /&gt;After hours of internet research I finally found &lt;a href="http://blogs.msdn.com/buckh/archive/2008/10/20/why-is-the-tfs-2008-version-control-security-dialog-blank.aspx "&gt;this posting&lt;/a&gt;. And sure enough, after checking which .NET 3.5 Framework servicepack level was installed on the server (SP1 in our case), and identifying the Team Foundation Server 2008 servicepack level (TFS 2008 RTM, which is not so easy to find out and includes checking the assembly version number of Microsoft.TeamFoundation.Server.dll located in %PROGRAMFILES%\Microsoft Visual Studio 2008 Team Foundation Server\Web Services\Services\Bin (simply google the version number to obtain the release definition)), we had confirmation that it was really that combination of product releaes which caused the group permissions to not show up.&lt;br /&gt;&lt;br /&gt;It's highly recommended to have matching .NET Framework and Team System releases installed side-by-side (.NET 3.5 SP1 and TS 2008 SP1 in this example).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1350532498024602809?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1350532498024602809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1350532498024602809'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/12/disappearing-permissions-on-ms-team.html' title='Disappearing Permissions On MS Team Foundation Server 2008'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-777280878948263669</id><published>2008-12-01T21:30:00.001+01:00</published><updated>2008-12-01T21:31:34.143+01:00</updated><title type='text'>Nietzsche On Software Development</title><content type='html'>&lt;blockquote&gt;What we do is never understood, but only praised and blamed.&lt;/blockquote&gt; &lt;div style="text-align: right;"&gt;&lt;span style="font-style: italic;"&gt;(Friedrich Nietzsche)&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-777280878948263669?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/777280878948263669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/777280878948263669'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/12/nietzsche-on-software-development.html' title='Nietzsche On Software Development'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8795661269364851614</id><published>2008-09-30T16:48:00.040+02:00</published><updated>2008-11-08T15:06:13.095+01:00</updated><title type='text'>Update And Quotes On Programming</title><content type='html'>It has been a while since my last posting. We are currently starting a new project and I deeply buried myself into some technology evaluation. So for the meantime, just some random thoughts...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;How come there is no usable Database-to-HBM/POCO reverse engineering tool for &lt;a href="http://www.hibernate.org/343.html"&gt;NHibernate&lt;/a&gt; out there? I must have tried five or six of them, some didn't work at all (but one Visual Studio Add-In screwed my IDE altogether), some managed do produce some kind of output, but were missing essential parts like relationship definitions, etc. I am really tempted to write one on my own.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa697427(VS.80).aspx"&gt;Entity Framework&lt;/a&gt; is nice from an API point of view, but lacks some first-class support for disconnected / n-tier scenarios (yes you can detach entities, but it gets kind of complicated when re-attaching them including change state).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Microsoft's new IoC-framework &lt;a href="http://msdn.microsoft.com/en-us/library/cc468366.aspx"&gt;Unity&lt;/a&gt; is a big step ahead in comparison to &lt;a href="http://www.codeplex.com/ObjectBuilder"&gt;ObjectBuilder&lt;/a&gt;. Nice IoC core. &lt;a href="http://www.springframework.net/"&gt;Spring.NET&lt;/a&gt; is another IoC framework out there, and includes an additional feature stack: support for AOP (e.g. declarative transaction management), NHibernate, Quartz.NET, etc.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I especially like .NET 3.5, Visual Studio 2008 and Team System. Great stuff. WPF is outstanding - what an improvement in productivity!&lt;br /&gt;&lt;br /&gt;I am also involved in some old legacy projects. I don't want to go into much detail, but can only reinforce the importance of:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Plan for the future, choose wisely which path to take both in a microcosm of software components as in the realm of the whole project.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Avoid mistakes at the beginning, or pay for them big time later.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;People make products. No tools, no process model, no extra hours will counterbalance for poor staffing.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Time and time again I am amazed by the difference in developers' productivity and the variance in code quality. I wonder why our profession is so prone to that dilemma?&lt;br /&gt;&lt;br /&gt;These quotes from a &lt;a href="http://stackoverflow.com/questions/58640/great-programming-quotes"&gt;stackoverflow.com thread&lt;/a&gt; might provide some further insights:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"A computer lets you make more mistakes faster than any other invention in human history, with the possible exceptions of handguns and tequila."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"The trouble with programmers is that you can never tell what a programmer is doing until it's too late."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC. As potential programmers, they are mentally mutilated beyond hope of regeneration."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"Three virtues of a programmer:&lt;br /&gt;&lt;br /&gt;Laziness - The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful, and document what you wrote so you don't have to answer so many questions about it. Hence, the first great virtue of a programmer. Also hence, this book. See also impatience and hubris.&lt;br /&gt;&lt;br /&gt;Impatience - The anger you feel when the computer is being lazy. This makes you write programs that don't just react to your needs, but actually anticipate them. Or at least pretend to. Hence, the second great virtue of a programmer. See also laziness and hubris.&lt;br /&gt;&lt;br /&gt;Hubris - Excessive pride, the sort of thing Zeus zaps you for. Also the quality that makes you write (and maintain) programs that other people won't want to say bad things about. Hence, the third great virtue of a programmer. See also laziness and impatience."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8795661269364851614?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8795661269364851614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8795661269364851614'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/09/update.html' title='Update And Quotes On Programming'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2958772023299198440</id><published>2008-07-04T21:53:00.029+02:00</published><updated>2010-03-30T23:12:20.496+02:00</updated><title type='text'>ADO.NET Entity Framework Vote of No Confidence?</title><content type='html'>Several MVPs and other &lt;a href="http://msdn.microsoft.com/en-us/library/bb399572.aspx"&gt;.NET Entity Framework&lt;/a&gt; testers (partly from the &lt;a href="http://www.hibernate.org/343.html"&gt;NHibernate&lt;/a&gt; camp) signed an &lt;a href="http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/"&gt;open letter&lt;/a&gt; some weeks ago describing their concerns on some design decisions and the current state of what is about to become version 1.0 of the ADO.NET Entity Framework. Here is Microsoft's &lt;a href="http://blogs.msdn.com/timmall/archive/2008/06/24/vote-of-no-confidence.aspx"&gt;Tim Mallalieu's response&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now I won't pretend to be an expert on Entity Framework (I spent two or three days playing around with it). I do have some Hibernate experience though (admitted, in the Java space). So this clearly qualifies me to throw in my 5 cents too, now doesn't it? ;-)&lt;br /&gt;&lt;br /&gt;Here is what I think:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;INORDINATE FOCUS THE DATA ASPECT OF ENTITIES LEADS TO DEGRADED ENTITY ARCHITECTURES&lt;br /&gt;&lt;/span&gt;To be honest, my Hibernate POJOs never contained any logic code, they were plain data holders. This was a design decision that always worked fine for me, even if luminaries like Martin Fowler tend to &lt;a href="http://www.martinfowler.com/bliki/AnemicDomainModel.html"&gt;criticize it&lt;/a&gt;. I think the requirement to combine data and logic code in entity objects, although comprehendible from an OOP perspective, is kind of overrated. Anyway one can still place that code in partial classes, what else can we ask for? What might be problematic in some application scenarios is that entity classes are linked to the whole Entity Framework by inheritance and code. They cannot exist "standalone" (e.g. in another tier without EF being around). One alternative are dynamic proxy classes being generated at runtime, but they have some drawbacks as well. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;EXCESS CODE NEEDED TO DEAL WITH LACK OF LAZY LOADING&lt;/span&gt;&lt;br /&gt;Once again, some people can't live without lazy loading. But lazy loading also might open a can of worms, e.g. in distributed environments like the ones I have to deal with. &lt;a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/LazyInitializationException.html"&gt;LazyInitializationException&lt;/a&gt;, anybody? I mostly prefer other loading strategies, like &lt;a href="http://www.hibernate.org/315.html"&gt;"Join Fetch"&lt;/a&gt;. It boils down whether you (A) want to abstract the fact that there is a SQL database behind the hoods and only want to work on a cloud of objects or (B) the reason you use an ORM is to prevent tight coupling, database dependency and writing loads of mapping, routine SQL and infrastructure code (transaction management, etc) on your own, but still need to be in control of when and how data access actually happens. I am more of kind (B). &lt;a href="http://msdn.microsoft.com/en-us/library/bb896376.aspx"&gt;EntityCollection.Load&lt;/a&gt; is just fine for me!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;SHARED, CANONICAL MODEL CONTRADICTS SOFTWARE BEST PRACTICES&lt;/span&gt;&lt;br /&gt;In this case the "best practice" depends on the type of application one is building. Nobody is being forced to have only "one model to rule them all" - one can as well apply the EF model within a single layer only.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;LACK OF PERSISTENCE IGNORANCE CAUSES BUSINESS LOGIC TO BE HARDER TO READ, WRITE, AND MODIFY, CAUSING DEVELOPMENT AND MAINTENANCE COSTS TO INCREASE AT AN EXAGGERATED RATE&lt;br /&gt;&lt;/span&gt;While I agree that PI-support is desirable in this area, this is not really a killer argument in my current project scenario. And from what I have heard this issue is going to be addressed in EF V2.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;EXCESSIVE MERGE CONFLICTS WITH SOURCE CONTROL IN TEAM ENVIRONMENTS&lt;br /&gt;&lt;/span&gt;This is true, that was one of the first things I noticed when I got introduced to the Entity Framework.&lt;br /&gt;&lt;br /&gt;But hey guys, this is only version 1.0 of the Entity Framework. To me it still looks promising. Please keep up the good work. And you have a &lt;strong&gt;Vote of Confidence&lt;/strong&gt; from me!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2958772023299198440?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2958772023299198440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2958772023299198440'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/07/ado-net-entity-framework-vote-of-no.html' title='ADO.NET Entity Framework Vote of No Confidence?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6752799284202975891</id><published>2008-07-01T23:37:00.018+02:00</published><updated>2010-03-28T17:47:27.754+02:00</updated><title type='text'>Battleship Game Algorithm Explained (Part 4)</title><content type='html'>This is part 4 of the &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;mini-series&lt;/a&gt; explaining the inner workings of the &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/443/Default.aspx"&gt;Cubido C# Pirates Winner Algorithm&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Please note: This code was not optimized for speed and has plenty of potential of performance improvement&lt;/span&gt;, esp. regarding nested looping. The competition rules included an upper time limit of 500ms per shot. This implementation runs at 1ms per shot in average, so there was no point in tuning that esp. as readability might have suffered.&lt;br /&gt;&lt;br /&gt;Here is the resulting sourcecode:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using SoftwareArchitects.Battleships;&lt;br /&gt;&lt;br /&gt;namespace Battleships&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  [Player(Name = &amp;quot;The Privateer&amp;quot;)]&lt;br /&gt;  public class ThePrivateer : Player&lt;br /&gt;  {&lt;br /&gt;    private class ShipPositionOption&lt;br /&gt;    {&lt;br /&gt;      private int free;&lt;br /&gt;      private bool done;&lt;br /&gt;      private Direction dir;&lt;br /&gt;      private Coordinates nextCoord;&lt;br /&gt;&lt;br /&gt;      public ShipPositionOption(Direction posDir)&lt;br /&gt;      {&lt;br /&gt;        dir = posDir;&lt;br /&gt;        free = 0;&lt;br /&gt;        done = false;&lt;br /&gt;        nextCoord = null;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public void Reset()&lt;br /&gt;      {&lt;br /&gt;        free = 0;&lt;br /&gt;        done = false;&lt;br /&gt;        nextCoord = null;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public void MarkInvalid()&lt;br /&gt;      {&lt;br /&gt;        free = 0;&lt;br /&gt;        done = true;&lt;br /&gt;        nextCoord = null;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public Coordinates NextCoord&lt;br /&gt;      {&lt;br /&gt;        get { return nextCoord; }&lt;br /&gt;        set { nextCoord = value; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public int Free&lt;br /&gt;      {&lt;br /&gt;        get { return free; }&lt;br /&gt;        set { free = value; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public bool Done&lt;br /&gt;      {&lt;br /&gt;        get { return done; }&lt;br /&gt;        set { done = value; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public Direction Dir&lt;br /&gt;      {&lt;br /&gt;        get { return dir; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class Direction&lt;br /&gt;    {&lt;br /&gt;      private Orientation orient;&lt;br /&gt;      private int dX;&lt;br /&gt;      private int dY;&lt;br /&gt;&lt;br /&gt;      public Direction(Orientation or, int x, int y)&lt;br /&gt;      {&lt;br /&gt;        orient = or;&lt;br /&gt;        dX = x;&lt;br /&gt;        dY = y;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public Orientation Orient&lt;br /&gt;      {&lt;br /&gt;        get { return orient; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public int DX&lt;br /&gt;      {&lt;br /&gt;        get { return dX; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public int DY&lt;br /&gt;      {&lt;br /&gt;        get { return dY; }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class SinkTryResult&lt;br /&gt;    {&lt;br /&gt;      private Direction dir;&lt;br /&gt;      private Coordinates sinkCoord;&lt;br /&gt;&lt;br /&gt;      public SinkTryResult(Coordinates sinkCoordParam, Direction dirParam)&lt;br /&gt;      {&lt;br /&gt;        sinkCoord = sinkCoordParam;&lt;br /&gt;        dir = dirParam;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public Coordinates SinkCoord&lt;br /&gt;      {&lt;br /&gt;        get { return sinkCoord; }&lt;br /&gt;        set { sinkCoord = value; }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public Direction Dir&lt;br /&gt;      {&lt;br /&gt;        get { return dir; }&lt;br /&gt;        set { dir = value; }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private enum Orientation : int&lt;br /&gt;    {&lt;br /&gt;      EAST = 0,&lt;br /&gt;      WEST = 1,&lt;br /&gt;      NORTH = 2,&lt;br /&gt;      SOUTH = 3&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private enum Mode { ScanMode, SinkMode };&lt;br /&gt;&lt;br /&gt;    private static readonly IDictionary&amp;lt;Orientation, Direction&amp;gt; DIRECTIONS = new Dictionary&amp;lt;Orientation, Direction&amp;gt;();&lt;br /&gt;    private const int FIELD_DIM_X = 10;&lt;br /&gt;    private const int FIELD_DIM_Y = 10;&lt;br /&gt;&lt;br /&gt;    private const int EAST_IDX = (int)Orientation.EAST;&lt;br /&gt;    private const int WEST_IDX = (int)Orientation.WEST;&lt;br /&gt;    private const int SOUTH_IDX = (int)Orientation.SOUTH;&lt;br /&gt;    private const int NORTH_IDX = (int)Orientation.NORTH;&lt;br /&gt;&lt;br /&gt;    private Random rnd = new Random();&lt;br /&gt;    private Mode mode = Mode.ScanMode;&lt;br /&gt;    private Coordinates sinkCoord = null;&lt;br /&gt;    private bool[,] impossibleCoords = new bool[10, 10];&lt;br /&gt;&lt;br /&gt;    private int seekedShipSize = 5;&lt;br /&gt;    private int hitCount = 0;&lt;br /&gt;&lt;br /&gt;    private int[] sinksRequired = new int[] { 0, 0, 1, 2, 1, 1 };&lt;br /&gt;&lt;br /&gt;    static ThePrivateer()&lt;br /&gt;    {&lt;br /&gt;      DIRECTIONS[Orientation.EAST] = new Direction(Orientation.EAST, 1, 0);&lt;br /&gt;      DIRECTIONS[Orientation.WEST] = new Direction(Orientation.WEST, -1, 0);&lt;br /&gt;      DIRECTIONS[Orientation.SOUTH] = new Direction(Orientation.SOUTH, 0, 1);&lt;br /&gt;      DIRECTIONS[Orientation.NORTH] = new Direction(Orientation.NORTH, 0, -1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ThePrivateer()&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override void Move(PlayingField playingField)&lt;br /&gt;    {&lt;br /&gt;      if (mode == Mode.ScanMode)&lt;br /&gt;      {&lt;br /&gt;        Coordinates coord = GetNextScanCoord(playingField);&lt;br /&gt;        if (playingField.Fire(coord.X, coord.Y) == State.HitShip)&lt;br /&gt;        {&lt;br /&gt;          sinkCoord = coord;&lt;br /&gt;          mode = Mode.SinkMode;&lt;br /&gt;          hitCount = 1;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      else if (mode == Mode.SinkMode)&lt;br /&gt;      {&lt;br /&gt;        SinkTryResult sinkTry = GetNextSinkTry(playingField);&lt;br /&gt;&lt;br /&gt;        State res = playingField.Fire(sinkTry.SinkCoord.X, sinkTry.SinkCoord.Y);&lt;br /&gt;&lt;br /&gt;        if (res == State.HitShip &amp;#124;&amp;#124; res == State.SunkShip)&lt;br /&gt;        {&lt;br /&gt;          sinkCoord = sinkTry.SinkCoord;&lt;br /&gt;          hitCount++;&lt;br /&gt;&lt;br /&gt;          if (res == State.SunkShip)&lt;br /&gt;          {&lt;br /&gt;&lt;br /&gt;            sinksRequired[hitCount] = sinksRequired[hitCount] - 1;&lt;br /&gt;&lt;br /&gt;            int sunkX = sinkCoord.X;&lt;br /&gt;            int sunkY = sinkCoord.Y;&lt;br /&gt;&lt;br /&gt;            for (int i = 0; i &amp;lt; hitCount; i++)&lt;br /&gt;            {&lt;br /&gt;              SetImpossibleCoord(sunkX - 1, sunkY - 1);&lt;br /&gt;              SetImpossibleCoord(sunkX - 1, sunkY);&lt;br /&gt;              SetImpossibleCoord(sunkX - 1, sunkY + 1);&lt;br /&gt;              SetImpossibleCoord(sunkX, sunkY - 1);&lt;br /&gt;              SetImpossibleCoord(sunkX, sunkY);&lt;br /&gt;              SetImpossibleCoord(sunkX, sunkY + 1);&lt;br /&gt;              SetImpossibleCoord(sunkX + 1, sunkY - 1);&lt;br /&gt;              SetImpossibleCoord(sunkX + 1, sunkY);&lt;br /&gt;              SetImpossibleCoord(sunkX + 1, sunkY + 1);&lt;br /&gt;&lt;br /&gt;              sunkX = sunkX - sinkTry.Dir.DX;&lt;br /&gt;              sunkY = sunkY - sinkTry.Dir.DY;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            while (seekedShipSize &amp;gt; 0 &amp;amp;&amp;amp; sinksRequired[seekedShipSize] == 0)&lt;br /&gt;            {&lt;br /&gt;              seekedShipSize--;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            hitCount = 0;&lt;br /&gt;            sinkCoord = null;&lt;br /&gt;            mode = Mode.ScanMode;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private ShipPositionOption[] CreateShipPositionOptions()&lt;br /&gt;    {&lt;br /&gt;      ShipPositionOption[] options = new ShipPositionOption[4];&lt;br /&gt;      options[EAST_IDX] = new ShipPositionOption(DIRECTIONS[Orientation.EAST]);&lt;br /&gt;      options[WEST_IDX] = new ShipPositionOption(DIRECTIONS[Orientation.WEST]);&lt;br /&gt;      options[SOUTH_IDX] = new ShipPositionOption(DIRECTIONS[Orientation.SOUTH]);&lt;br /&gt;      options[NORTH_IDX] = new ShipPositionOption(DIRECTIONS[Orientation.NORTH]);&lt;br /&gt;      return options;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private Coordinates GetNextScanCoord(PlayingField playingField)&lt;br /&gt;    {&lt;br /&gt;      ShipPositionOption[] options = CreateShipPositionOptions();&lt;br /&gt;      Coordinates bestCoord = null;&lt;br /&gt;      int bestCnt = 0;&lt;br /&gt;      int bestNeighborFact = 0;&lt;br /&gt;      bool bestMatching = false;&lt;br /&gt;&lt;br /&gt;      for (int x = 0; x &amp;lt; FIELD_DIM_X; x++)&lt;br /&gt;      {&lt;br /&gt;        for (int y = 0; y &amp;lt; FIELD_DIM_Y; y++)&lt;br /&gt;        {&lt;br /&gt;          if (IsUnknown(playingField, x, y))&lt;br /&gt;          {&lt;br /&gt;            int cnt = 0;&lt;br /&gt;            int neighborFact = 0;&lt;br /&gt;&lt;br /&gt;            for (int shipSize = 0; shipSize &amp;lt; sinksRequired.Length; shipSize++)&lt;br /&gt;            {&lt;br /&gt;              if (sinksRequired[shipSize] &amp;gt; 0)&lt;br /&gt;              {&lt;br /&gt;                for (int optIdx = 0; optIdx &amp;lt; options.Length; optIdx++)&lt;br /&gt;                {&lt;br /&gt;                  ShipPositionOption option = options[optIdx];&lt;br /&gt;                  option.Reset();&lt;br /&gt;                  for (int d = 1; d &amp;lt; shipSize &amp;amp;&amp;amp; !option.Done; d++)&lt;br /&gt;                  {&lt;br /&gt;                    if (IsUnknown(playingField, x + d * option.Dir.DX, y + d * option.Dir.DY))&lt;br /&gt;                    {&lt;br /&gt;                      option.Free = option.Free + 1;&lt;br /&gt;                    }&lt;br /&gt;                    else&lt;br /&gt;                    {&lt;br /&gt;                      option.Done = true;&lt;br /&gt;                    }&lt;br /&gt;&lt;br /&gt;                  }&lt;br /&gt;&lt;br /&gt;                }&lt;br /&gt;                int cntHor = Math.Max(0, options[EAST_IDX].Free + options[WEST_IDX].Free + 2 - shipSize);&lt;br /&gt;                int cntVer = Math.Max(0, options[SOUTH_IDX].Free + options[NORTH_IDX].Free + 2 - shipSize);&lt;br /&gt;                cnt += cntHor + cntVer;&lt;br /&gt;&lt;br /&gt;              }&lt;br /&gt;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            foreach (Direction dir1 in DIRECTIONS.Values)&lt;br /&gt;            {&lt;br /&gt;              int x1 = x + dir1.DX;&lt;br /&gt;              int y1 = y + dir1.DY;&lt;br /&gt;              if (IsUnknown(playingField, x1, y1))&lt;br /&gt;              {&lt;br /&gt;                foreach (Direction dir2 in DIRECTIONS.Values)&lt;br /&gt;                {&lt;br /&gt;                  int x2 = x1 + dir2.DX;&lt;br /&gt;                  int y2 = y1 + dir2.DY;&lt;br /&gt;                  if (x2 != x &amp;#124;&amp;#124; y2 != y)&lt;br /&gt;                  {&lt;br /&gt;                    if (IsWaterOrImpossible(playingField, x2, y2))&lt;br /&gt;                    {&lt;br /&gt;                      neighborFact++;&lt;br /&gt;                    }&lt;br /&gt;                  }&lt;br /&gt;                }&lt;br /&gt;              }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            bool matching = MatchesScanPattern(x, y);&lt;br /&gt;            if (cnt &amp;gt; bestCnt &amp;#124;&amp;#124; (cnt == bestCnt &amp;amp;&amp;amp; (neighborFact &amp;gt; bestNeighborFact &amp;#124;&amp;#124; (neighborFact == bestNeighborFact &amp;amp;&amp;amp; ((!bestMatching &amp;amp;&amp;amp; matching) &amp;#124;&amp;#124; (bestMatching == matching &amp;amp;&amp;amp; rnd.Next(2) == 1))))))&lt;br /&gt;            {&lt;br /&gt;              bestCnt = cnt;&lt;br /&gt;              bestNeighborFact = neighborFact;&lt;br /&gt;              bestMatching = matching;&lt;br /&gt;              bestCoord = new Coordinates(x, y);&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      return bestCoord;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private SinkTryResult GetNextSinkTry(PlayingField playingField)&lt;br /&gt;    {&lt;br /&gt;      ShipPositionOption[] options = CreateShipPositionOptions();&lt;br /&gt;&lt;br /&gt;      for (int i = 1; i &amp;lt; seekedShipSize &amp;amp;&amp;amp; (!options[EAST_IDX].Done &amp;#124;&amp;#124; !options[WEST_IDX].Done &amp;#124;&amp;#124; !options[SOUTH_IDX].Done &amp;#124;&amp;#124; !options[NORTH_IDX].Done); i++)&lt;br /&gt;      {&lt;br /&gt;        for (int optIdx = 0; optIdx &amp;lt; options.Length; optIdx++)&lt;br /&gt;        {&lt;br /&gt;          ShipPositionOption option = options[optIdx];&lt;br /&gt;          if (!option.Done)&lt;br /&gt;          {&lt;br /&gt;            Coordinates nextCoord = new Coordinates(sinkCoord.X + i * option.Dir.DX, sinkCoord.Y + i * option.Dir.DY);&lt;br /&gt;            if (!IsValidCoord(nextCoord) &amp;#124;&amp;#124; impossibleCoords[nextCoord.X, nextCoord.Y])&lt;br /&gt;            {&lt;br /&gt;              option.Done = true;&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;              State state = playingField.GetState(nextCoord.X, nextCoord.Y);&lt;br /&gt;              if (state == State.Unknown)&lt;br /&gt;              {&lt;br /&gt;                if (option.NextCoord == null)&lt;br /&gt;                {&lt;br /&gt;                  option.NextCoord = nextCoord;&lt;br /&gt;                }&lt;br /&gt;                option.Free = option.Free + 1;&lt;br /&gt;              }&lt;br /&gt;              else if (state == State.HitShip)&lt;br /&gt;              {&lt;br /&gt;                if (option.Dir.Orient == Orientation.EAST &amp;#124;&amp;#124; option.Dir.Orient == Orientation.WEST)&lt;br /&gt;                {&lt;br /&gt;                  options[SOUTH_IDX].MarkInvalid();&lt;br /&gt;                  options[NORTH_IDX].MarkInvalid();&lt;br /&gt;                }&lt;br /&gt;                else&lt;br /&gt;                {&lt;br /&gt;                  options[EAST_IDX].MarkInvalid();&lt;br /&gt;                  options[WEST_IDX].MarkInvalid();&lt;br /&gt;                }&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                option.Done = true;&lt;br /&gt;              }&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      ShipPositionOption bestOpt;&lt;br /&gt;      ShipPositionOption option1;&lt;br /&gt;      ShipPositionOption option2;&lt;br /&gt;&lt;br /&gt;      int horFree = options[EAST_IDX].Free + options[WEST_IDX].Free;&lt;br /&gt;      int verFree = options[SOUTH_IDX].Free + options[NORTH_IDX].Free;&lt;br /&gt;      int minHorFree = Math.Min(options[EAST_IDX].Free, options[WEST_IDX].Free);&lt;br /&gt;      int minVerFree = Math.Min(options[SOUTH_IDX].Free, options[NORTH_IDX].Free);&lt;br /&gt;&lt;br /&gt;      if (horFree &amp;gt; verFree &amp;#124;&amp;#124; (horFree == verFree &amp;amp;&amp;amp; (minHorFree &amp;gt; minVerFree &amp;#124;&amp;#124; (minHorFree == minVerFree &amp;amp;&amp;amp; rnd.Next(2) == 1))))&lt;br /&gt;      {&lt;br /&gt;        option1 = options[EAST_IDX];&lt;br /&gt;        option2 = options[WEST_IDX];&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        option1 = options[SOUTH_IDX];&lt;br /&gt;        option2 = options[NORTH_IDX];&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      if (option1.Free &amp;gt; option2.Free)&lt;br /&gt;      {&lt;br /&gt;        bestOpt = option1;&lt;br /&gt;      }&lt;br /&gt;      else if (option2.Free &amp;gt; option1.Free)&lt;br /&gt;      {&lt;br /&gt;        bestOpt = option2;&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;        bestOpt = rnd.Next(2) == 1 ? option1 : option2;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      return new SinkTryResult(bestOpt.NextCoord, bestOpt.Dir);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool IsUnknown(PlayingField playingField, int x, int y)&lt;br /&gt;    {&lt;br /&gt;      return IsValidCoord(x, y) &amp;amp;&amp;amp; !impossibleCoords[x, y] &amp;amp;&amp;amp; playingField.GetState(x, y) == State.Unknown;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool IsWater(PlayingField playingField, int x, int y)&lt;br /&gt;    {&lt;br /&gt;      return IsValidCoord(x, y) &amp;amp;&amp;amp; playingField.GetState(x, y) == State.Water;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool IsWaterOrImpossible(PlayingField playingField, int x, int y)&lt;br /&gt;    {&lt;br /&gt;      return IsValidCoord(x, y) &amp;amp;&amp;amp; (impossibleCoords[x, y] &amp;#124;&amp;#124; playingField.GetState(x, y) == State.Water);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool MatchesScanPattern(Coordinates coord)&lt;br /&gt;    {&lt;br /&gt;      return coord != null &amp;amp;&amp;amp; MatchesScanPattern(coord.X, coord.Y);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool MatchesScanPattern(int x, int y)&lt;br /&gt;    {&lt;br /&gt;      return (x + y) % 2 == 0;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool IsValidCoord(Coordinates coord)&lt;br /&gt;    {&lt;br /&gt;      return IsValidCoord(coord.X, coord.Y);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private bool IsValidCoord(int x, int y)&lt;br /&gt;    {&lt;br /&gt;      return x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; FIELD_DIM_X &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt; FIELD_DIM_Y;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void SetImpossibleCoord(int x, int y)&lt;br /&gt;    {&lt;br /&gt;      if (IsValidCoord(x, y))&lt;br /&gt;      {&lt;br /&gt;        impossibleCoords[x, y] = true;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Previous Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 1)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;Battleship Game Algorithm Explained (Part 2)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html"&gt;Battleship Game Algorithm Explained (Part 3)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6752799284202975891?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6752799284202975891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6752799284202975891'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/07/battleship-game-algorithm-explained.html' title='Battleship Game Algorithm Explained (Part 4)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8763957648916410065</id><published>2008-06-21T23:48:00.083+02:00</published><updated>2008-08-23T15:09:57.960+02:00</updated><title type='text'>Where Jeff Atwood Is Wrong On Stackoverflow.Com</title><content type='html'>I have been reading Jeff Atwood's &lt;a href="http://www.codinghorror.com/blog/"&gt;Coding Horror Blog&lt;/a&gt; for many years and have high regards for it, but I am starting to get a little disillusioned by some of his later writings and also several comments on the &lt;a href="http://blog.stackoverflow.com/"&gt;Stackoverflow.Com Podcast&lt;/a&gt;. Anyway, considering all that interesting and entertaining stuff he has been blogging over time I suppose it's also OK if I can't identify with his opinion once in a while.&lt;br /&gt;&lt;br /&gt;There is this &lt;a href="http://www.codinghorror.com/blog/archives/001112.html"&gt;blog entry on the Model/View/Controller design pattern&lt;/a&gt;, and Jeff and &lt;a href="http://www.joelonsoftware.com/"&gt;Joel Spolsky&lt;/a&gt; also discussed that during their podcast. I consider the "Model=HTML, View=CSS and Controller=Browser"-explanation misleading (I would not go so far to call it "completely wrong", it's just not a great analogy). HTML and CSS are declarative languages, not Models/Views in an OOP sense. The browser application's code and data structures are the building blocks for Model, View and Controller. So the browser IS Model, View and Controller.&lt;br /&gt;&lt;br /&gt;By definition, the Model represents application state, the View defines how Model data is rendered into the UI and the Controller takes user input and changes the Model accordingly, resp. invokes application functionality. &lt;br /&gt;&lt;br /&gt;One could favorably argue that HTML is a source of data for the browser's Model, and that CSS also affects the way the View does the rendering. But Model and View themselves are something else, they are browser-internal software components that represent web content (Model) resp. are responsible for visualizing that web content on a UI platform (View). Remember the View serves as an &lt;a href="http://en.wikipedia.org/wiki/Observer_pattern"&gt;Observer&lt;/a&gt; on Model data - that just doesn't fit with CSS and HTML. And that's why I think the comparison is a little bit far-fetched.&lt;br /&gt;&lt;br /&gt;Take a CAD application as another example of a Model that contains some kind of graphical artefacts simply because that's the nature of the application (just like web content in case of a browser). The CAD Model might consist of shapes and polygon definitions, and the CAD View then renders that into a two-dimensional UI.&lt;br /&gt;&lt;br /&gt;Here is a diagram taken from the &lt;a href="http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/app-arch/app-arch2.html"&gt;J2EE Architecture blueprints&lt;/a&gt; that pretty much sums up things (yeah I know this article refers to N-tier architecture, but the diagram is valid for all MVC-secnarios). &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/SF2AUeVdRCI/AAAAAAAAAOQ/r5ndgprmlJY/s1600-h/mvc.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/SF2AUeVdRCI/AAAAAAAAAOQ/r5ndgprmlJY/s400/mvc.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5214465032793703458" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Jeff Atwood noted that he could not find any MVC documentation that really pleased him and that he could do better (hey, why not take the &lt;a href="http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612"&gt;GoF book&lt;/a&gt; - MVC is described in the introduction chapter). Sorry to say that in this case he didn't comply with his own high standards.&lt;br /&gt;&lt;br /&gt;Then there was a remark about SqlServer as being pretty much standalone self-tuning. As much as I appreciate SqlServer, it's really not self-tuning. Don't get me started on all that query optimizer issues I have been through (admitted this has gotten better in the latest versions). It's also a must to define a customized maintenance plan on SqlServer. But maintenance plans are kind of cumbersome or even impossible on desktop/express engines, and on those engines self-tuning would be essential (as there is no database admin to take care of such things). If I had to name a self-tuning database, it would be &lt;a href="http://www.sybase.com/products/databasemanagement/sqlanywhere/database-technology/sqlanywherepersonalserver"&gt;Sybase SQLAnywhere Personal Server&lt;/a&gt; - no big surprise as it has been designed for smaller-than-enterprise usage.&lt;br /&gt;&lt;br /&gt;Finally Jeff stated that he sees no difference between computer science and software engineering. I'd like to quote &lt;a href="http://www.stevemcconnell.com/psd/04-senotcs.htm"&gt;Steve McConnell&lt;/a&gt; at this point (and by the way, coincidently the term "Coding Horror" descends from Steve McConnell's book "Code Complete"):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Scientists learn what is true, how to test hypotheses, and how to extend knowledge in their field. Engineers learn what is true, what is useful, and how to apply well-understood knowledge to solve practical problems. &lt;br /&gt;&lt;br /&gt;Scientists must keep up to date with the latest research. Engineers must be familiar with knowledge that has already proven to be reliable and effective. If you are doing science, you can afford to be narrow and specialized. If you are doing engineering, you need a broad understanding of all the factors that affect the product you are designing. &lt;br /&gt;&lt;br /&gt;Scientists don't have to be regulated because they are chiefly accountable to other scientists. Engineers do have to be regulated because they are chiefly accountable to the public. &lt;br /&gt;&lt;br /&gt;An undergraduate science education prepares students to continue their studies. An undergraduate engineering education prepares students to enter the workforce immediately after completing their studies.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;But hey, just as Jeff remarked repeatedly, it's also fine to disagree every now and then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8763957648916410065?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8763957648916410065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8763957648916410065'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/06/jeff-atwood-on-stackoverflowcom-podcast.html' title='Where Jeff Atwood Is Wrong On Stackoverflow.Com'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_G0oBN6-Lb48/SF2AUeVdRCI/AAAAAAAAAOQ/r5ndgprmlJY/s72-c/mvc.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3339048271128854845</id><published>2008-06-19T01:44:00.032+02:00</published><updated>2008-07-09T23:49:12.998+02:00</updated><title type='text'>Battleship Game Algorithm Explained (Part 3)</title><content type='html'>This is part 3 of the &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;mini-series&lt;/a&gt; explaining the inner workings of the &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/443/Default.aspx"&gt;Cubido C# Pirates Winner Algorithm&lt;/a&gt;. We have talked about  &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;how to find the cell best suited for a trial shot&lt;/a&gt;. Now let's see what we can do to improve the accuracy of sinking shots, that is the follow-up shots once a trial shot hit on a ship.&lt;br /&gt;&lt;br /&gt;So what needs to be decided at this point is in which direction to continue shooting after the first hit. The stakes are high - after all it's possible to produce three unnecessary misses until we know for sure were the ship is really located.&lt;br /&gt;&lt;br /&gt;Let's have a look at the following scenario, and suppose that there is one ship left of size 4, one ship of size 3 and one ship of size 2:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/SFrTrq3aICI/AAAAAAAAANA/ERz8qg78zmg/s1600-h/field6.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/SFrTrq3aICI/AAAAAAAAANA/ERz8qg78zmg/s400/field6.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5213712265829294114" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It somehow seems tempting to select the eastern neighbor cell for the next sink shot, because of the large unknown space in that direction. But as it turns out that would be a mistake.&lt;br /&gt;&lt;br /&gt;A better approach is to calculate the probabilities for the ships that are still left on the battlefield to be located on each unknown neighbor cell (of course considering the fact where we have hit the ship already). This is very similar to &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;our strategy when placing trial shots&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The ship of size 2 can be located in either of the four directions, so this does not really help. &lt;br /&gt;&lt;br /&gt;There is 1 possibility for the ship of size 3 to be located on the western neighbor cell, 2 possibilities on the northern neighbor cell, 2 possibilities on the eastern neighbor cell and 2 possibilities on the southern neighbor cell. So the western cell is out of the game.&lt;br /&gt;&lt;br /&gt;Regarding the ship of size 4, there is 1 possibility to the west, 2 to the north, 2 to the east and 3 to the south. The southern neighbor is our target of choice.&lt;br /&gt;&lt;br /&gt;In case we miss we'll give it another try the next round by applying the same mechanism. After all the probabilities have changed by then (the ship is then more likely to be positioned horizontally). &lt;br /&gt;&lt;br /&gt;Once we hit a second ship cell we can be sure about its orientation, but may still not know about the exact location. So we continue to calculate probabilities for neighbor cells on each side until the ship has been sunk. After that we store the information about which ship size just has been sunk, mark all cells surrounding the ship with State &lt;span style="font-style:italic;"&gt;Impossible&lt;/span&gt;, and switch back to trialshot mode (as explained in &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;part 1&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://arnosoftwaredev.blogspot.com/2008/07/battleship-game-algorithm-explained.html"&gt;part 4&lt;/a&gt; we are going to put all of that together, and have a look at the resulting sourcecode.&lt;br /&gt;&lt;br /&gt;Previous Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 1)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;Battleship Game Algorithm Explained (Part 2)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Followup Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/07/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 4)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3339048271128854845?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3339048271128854845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3339048271128854845'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html' title='Battleship Game Algorithm Explained (Part 3)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/SFrTrq3aICI/AAAAAAAAANA/ERz8qg78zmg/s72-c/field6.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-244389873275735264</id><published>2008-06-18T18:13:00.038+02:00</published><updated>2009-04-24T01:55:52.076+02:00</updated><title type='text'>Battleship Game Algorithm Explained (Part 2)</title><content type='html'>This is part 2 of the &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;mini-series&lt;/a&gt; explaining the inner workings of the &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/443/Default.aspx"&gt;Cubido C# Pirates Winner Algorithm&lt;/a&gt;. We &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;previously examined&lt;/a&gt; how to basically choose the next battlefield cell to shoot at, that is by calculating each cell's probability to contain a ship. If there is more than one top-ranked cell, we need to decide among those cells.&lt;br /&gt;&lt;br /&gt;So far we have done our best to hit a ship. But let's face it, it's more likely to hit the water. How can we benefit from a missed shot as well?&lt;br /&gt;&lt;br /&gt;By applying a checkerboard-like shooting pattern we will find each ship by only having to hit only 50% of the cells (and that's a worst case scenario). E.g. there is no way for a ship of size 2 to hide anywhere here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFqZpceRyFI/AAAAAAAAALg/O5-bxlhjH2g/s1600-h/field3.gif"&gt;&lt;img id="BLOGGER_PHOTO_ID_5213648455931643986" style="margin: 0px auto 10px; display: block; text-align: center;" alt="" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFqZpceRyFI/AAAAAAAAALg/O5-bxlhjH2g/s400/field3.gif" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Great, we now have an additional selection criteria for choosing the next target cell: the checkerboard pattern.&lt;br /&gt;&lt;br /&gt;Still there might be plenty of cells left with equal ship location probabilities which also fit into the checkerboard. Which other value can be gained from a missed shot? The value of learning something about unknown neighbor cells! When an unknown cell A has a water cell neighbor B (or an impossible cell neigbor, which is a cell that adjoins a sunk ship), this clearly limits the options for a ship to be located on cell A - because cell B is out of the game for a joined ship location.&lt;br /&gt;&lt;br /&gt;If cell A is surrounded by two water/impossible cells the probabilities decrease further, and even more of course with three water/impossible neighbors (just one direction left to place a ship). Finally if we happen to shoot at the last remaining unknown neighbor cell of A (and hit water only), we can abandon that cell A for future consideration all together.&lt;br /&gt;&lt;br /&gt;So what we are going to do is to count the water and impossible neighbors for each of our potential target's unknown neighbors (our target's unknown neighbors' water/impossible neighbors so to speak), and prefer the one target with the highest score.&lt;br /&gt;&lt;br /&gt;This cell here might be a good choice for a shot (neighbor score is 7, 2 water neighbors for the western unknown neighbor + 2 water/impossible neighbors for the northern unknown neighbor + 0 for the eastern impossible neighbor + 3 water/impossible neighbors for the southern unknown neighbor):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_G0oBN6-Lb48/SFqZMtCmrVI/AAAAAAAAALM/J3o2dOAu8D8/s1600-h/field4.gif"&gt;&lt;img id="BLOGGER_PHOTO_ID_5213647962162769234" style="margin: 0px auto 10px; display: block; text-align: center;" alt="" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/SFqZMtCmrVI/AAAAAAAAALM/J3o2dOAu8D8/s400/field4.gif" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In the following case that cell would score even higher (neighbor score is 9, 3 water neighbors for the western unknown neighbor + 3 water/impossible neighbors for the northern unknown neighbor + 0 for the eastern impossible neighbor + 3 water/impossible neighbors for the southern unknown neighbor):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/SFtHxQrnlDI/AAAAAAAAANw/sCFzy0m4cM8/s1600-h/field5.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/SFtHxQrnlDI/AAAAAAAAANw/sCFzy0m4cM8/s400/field5.gif" alt="" id="BLOGGER_PHOTO_ID_5213839905228559410" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So the neigbor score becomes our second target selection criteria, right after to the ship location probability.&lt;br /&gt;&lt;br /&gt;If there are cells with equal ship location probability and equal neighbor score, we choose one that fits the checkerboard (by the way, applying the neighbor score almost naturally leads to a checkerboard as well, so this is just to go sure for special cases, e.g. a more or less empty battlefield). If more than one of the top-ranked cells fits the checkerboard, we'll randomly select any of them.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html"&gt;part 3&lt;/a&gt; we will discuss how to proceed once the first cell of a ship has been hit. There is a lot of potential for tuning as well.&lt;br /&gt;&lt;br /&gt;Previous Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 1)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Followup Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html"&gt;Battleship Game Algorithm Explained (Part 3)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/07/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 4)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-244389873275735264?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/244389873275735264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/244389873275735264'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html' title='Battleship Game Algorithm Explained (Part 2)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/SFqZpceRyFI/AAAAAAAAALg/O5-bxlhjH2g/s72-c/field3.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5524917534452117027</id><published>2008-06-17T16:22:00.026+02:00</published><updated>2011-11-02T08:08:25.182+01:00</updated><title type='text'>Battleship Game Algorithm Explained (Part 1)</title><content type='html'>Several people have approached me about this, so here is the first article in a mini-series describing the  &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/443/Default.aspx"&gt;Cubido C# Pirates Winner Algorithm&lt;/a&gt;. It's final score was 38.19 shots (average shot count on 10,000 battleship games). 17 contentors scored under 39.00 shots, so this clearly was a very close match. 83 algorithms took part overall.&lt;br /&gt;&lt;br /&gt;Here is the spec: The battlefield consists of 10x10 cells. Ships are laid out either horizontally or vertically, with a margin of at least one cell between each ship. There is one ship of size 5, one of size 4, two of size 3 and one of size 2. Battleship algorithms are being called back on each round and must return the cell to shoot at next. The state of each cell can be queried through the C# Pirates API. Those states are: &lt;em&gt;Unknown&lt;/em&gt; (cell has not been shot at yet), &lt;em&gt;Water&lt;/em&gt; (cell has been shot at before and contains no ship) and &lt;em&gt;Ship&lt;/em&gt; (cell has been shot at before and contains a ship).&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFzL_6P9ORI/AAAAAAAAAN4/05Xv--oqn1c/s1600-h/unknown.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFzL_6P9ORI/AAAAAAAAAN4/05Xv--oqn1c/s400/unknown.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5214266767416572178" /&gt;&lt;/a&gt;Unknown cell&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/SFzMMozNIiI/AAAAAAAAAOA/K8chSCkRQA0/s1600-h/water.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/SFzMMozNIiI/AAAAAAAAAOA/K8chSCkRQA0/s400/water.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5214266986070876706" /&gt;&lt;/a&gt;Water cell&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFzMXmJ2wzI/AAAAAAAAAOI/C92lbEFaIPo/s1600-h/ship.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFzMXmJ2wzI/AAAAAAAAAOI/C92lbEFaIPo/s400/ship.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5214267174339134258" /&gt;&lt;/a&gt;Ship cell&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;So on each round of the game there is a decision to be made: at which cell to shoot at next. It soon becomes clear that we need two different modes of operation: Trialshot mode and Sinkshot mode. Trial shots occure while scanning the battlefield for ships. Sink shots happen once a ship has been hit in order to continue sinking it. We are going to talk about trial shots in part 1 and &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;part 2&lt;/a&gt;, while sink shots will be covered in &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html"&gt;part 3&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In trialshot mode it's clearly a good idea to choose the cell that is most likely to be occupied by a ship. The probability of a ship residing at a certain cell can be calculated by scanning for unknown neighbor cells in each direction.&lt;br /&gt;&lt;br /&gt;Let's have a look at an example. Say that there are the following ships left to be sunk: One of size 5, one of size 3 and one of size 2 (we know that because we kept track on which ships we sank already), and we are about to examine the cell marked with a shot impact.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFpwmNeCt3I/AAAAAAAAAJ8/1Zi51cN2FEQ/s1600-h/field1.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/SFpwmNeCt3I/AAAAAAAAAJ8/1Zi51cN2FEQ/s400/field1.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5213603320387778418" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are following possibilities for this cell to contain a ship: 1 option for a ship of size 5 (filling out the whole horizontal space), 1 option for a ship of size 3 (to the east), and 2 options for a ship of size 2 (north and east). So this gives us 4 options for ships to be placed on that very cell.&lt;br /&gt;&lt;br /&gt;What about the next unknown cell?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_G0oBN6-Lb48/SFpwmlOFkGI/AAAAAAAAAKE/mqYDJvqeGxI/s1600-h/field2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/SFpwmlOFkGI/AAAAAAAAAKE/mqYDJvqeGxI/s400/field2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5213603326763307106" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Again there is 1 option for a ship of size 5 (horizontally), 2 options for a ship of size 3 (both horizontally, one to the east, the other one with the proposed target cell located in the center of the ship), and 3 options for a ship of size 2 (east, west and south). There is no way for a ship to fit to the north although the northern neighbor has not been shot at yet, as it's itself restricted by the sunken ship next to it (one cell margin between ships). Still, 6 options beat 4 options, so the algorithm would choose the second cell among those two cells.&lt;br /&gt;&lt;br /&gt;The fact that there is a margin of one cell between ships creates another cell state that we must consider: the cell has not been shot at, but is next to a ship, hence cannot be part of a ship itself. Let's call that state &lt;em&gt;Impossible&lt;/em&gt;, and keep this information in an internal data structure between callbacks, because the C# Pirates API does not support that state, and querying all neighbors of state Unknown each time is kind of cumbersome.&lt;br /&gt;&lt;br /&gt;So what we basically do is to iterate over all cells of state &lt;i&gt;Unknown&lt;/i&gt;, calculate their probabilities to contain a ship, and select the highest-rated option. The question is what to do when there are several target cells with the same probability (which by the way is quite likely to happen). Making a good decision at that point is crucial and will differentiate our algorithm from competing ones which apply the same basic strategy. We will discuss that in &lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;part 2&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Followup Postings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_19.html"&gt;Battleship Game Algorithm Explained (Part 2)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained_7929.html"&gt;Battleship Game Algorithm Explained (Part 3)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/07/battleship-game-algorithm-explained.html"&gt;Battleship Game Algorithm Explained (Part 4)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5524917534452117027?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5524917534452117027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5524917534452117027'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/06/battleship-game-algorithm-explained.html' title='Battleship Game Algorithm Explained (Part 1)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/SFzL_6P9ORI/AAAAAAAAAN4/05Xv--oqn1c/s72-c/unknown.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2815980390065260309</id><published>2008-05-23T15:28:00.019+02:00</published><updated>2008-05-26T09:10:35.612+02:00</updated><title type='text'>Victory!</title><content type='html'>As luck would have it &lt;a href="http://www.cubido.at/CPirates/CPiratesInternetBattle/tabid/444/Default.aspx"&gt;I won the Cubido C# Pirates Programming Competition&lt;/a&gt; for the best Battleship Game Algorithm last week, and took home an &lt;a href="http://en.wikipedia.org/wiki/Xbox_360"&gt;XBox 360 Premium Edition&lt;/a&gt; as first prize.&lt;br /&gt;&lt;br /&gt;In general my algorithm pretty much stayed the same since I &lt;a href="http://arnosoftwaredev.blogspot.com/2008/04/battleship-game-algorithm-competition.html"&gt;last blogged about it&lt;/a&gt; a while ago, but some fine-tuning brought the average winning shot count down to about 38 (compared to 39 shots achieved by the initial version) - that is the number of shots to sink five ships of size 2, 3 (2 times), 4 and 5 on a 10x10 battlefield. &lt;br /&gt;&lt;br /&gt;Tuning was absolutely necessary as &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/443/Default.aspx"&gt;17 algorithms scored under 39 shots at the end&lt;/a&gt; (around 80 players took part over all). I had submitted several slightly different versions, hence the victory was manifold. My best algorithm variation attained 38.19 shots, with the closest competitor finishing at 38.33.&lt;br /&gt;&lt;br /&gt;The final competition was a public event (a big thank you goes to the kind folks at &lt;a href="http://www.cubido.at/"&gt;Cubido&lt;/a&gt;), and many contestants dropped by at the &lt;a href="http://www.cubido.at/"&gt;Cubido Headquarters&lt;/a&gt; and watched the progress on a large scoreboard (which was also available online). The battle lasted nearly four hours. 10,000 games had been played eventually (each algorithm was required to finish 10,000 identical battlefields). With results so close this large number was a must to avoid any "accidental winner", a winner that would have benefited by certain random number distributions (as a matter of fact during the preliminary rounds, which only consisted of 50 games each, my algorithms scored at between 34.5 and 40.5 shots, so that's quite a spread - a spread that was caused both by the random distribution of ships on the battlefield, as well as the algorithm's internal randomizer, which is applied when choices are evaluated as being equal otherwise).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2008/04/battleship-game-algorithm-competition.html"&gt;As I have promised before&lt;/a&gt;, I am going to talk about the ideas behind the algorithm and post the complete sourcecode over the next few days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2815980390065260309?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2815980390065260309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2815980390065260309'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/05/victory.html' title='Victory!'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3285089824942320707</id><published>2008-04-07T21:25:00.024+02:00</published><updated>2008-04-08T17:42:05.946+02:00</updated><title type='text'>Battleship Game Algorithm Competition</title><content type='html'>I am one of the &lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/439/Default.aspx"&gt;C# Pirates&lt;/a&gt; taking part in &lt;a href="http://www.cubido.at/"&gt;Cubido's&lt;/a&gt; &lt;a href="http://www.cubido.at/CPirates/Einführung/tabid/433/Default.aspx"&gt;Battleship Game Algorithm Competition&lt;/a&gt;. Dozens of computer programs battling it out against each other - in batch mode on the server (&lt;a href="http://www.cubido.at/CPirates/Ranking/tabid/439/Default.aspx"&gt;current ranking&lt;/a&gt;), as well as viewable in any &lt;a href="http://silverlight.net/"&gt;Silverlight&lt;/a&gt;-enabled web browser.&lt;br /&gt;&lt;br /&gt;Last week I attended &lt;a href="http://www.microsoft.com/austria/events/bigdays/default.mspx"&gt;Microsoft Austria's Big Days 2008&lt;/a&gt; and found out about the contest - two nights of coding later, and Captain Alex (namesake of my older son) reached the top position. Captain Ahab, the old swashbuckler, is still a tough competitor though.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/R_p5oXsrVdI/AAAAAAAAAIY/7ixZOqtgTD4/s1600-h/pirates_result.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/R_p5oXsrVdI/AAAAAAAAAIY/7ixZOqtgTD4/s400/pirates_result.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5186591655333680594" /&gt;&lt;/a&gt;&lt;br /&gt;And by the way kudos to Cubido. What a great idea! This is the kind of stuff that  excites most software developers. And their Silverlight client does have really cool visuals.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/R_p5w3srVeI/AAAAAAAAAIg/udIsa3PcCC4/s1600-h/pirates_silverlight.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/R_p5w3srVeI/AAAAAAAAAIg/udIsa3PcCC4/s400/pirates_silverlight.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5186591801362568674" /&gt;&lt;/a&gt;&lt;br /&gt;I am going to blog about the Captain Alex algorithm once the contest has ended (it is quite simple).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3285089824942320707?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3285089824942320707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3285089824942320707'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/04/battleship-game-algorithm-competition.html' title='Battleship Game Algorithm Competition'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/R_p5oXsrVdI/AAAAAAAAAIY/7ixZOqtgTD4/s72-c/pirates_result.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6160801940679386875</id><published>2008-03-14T23:07:00.138+01:00</published><updated>2012-01-06T20:41:03.065+01:00</updated><title type='text'>DrawCircle Performance Tuning</title><content type='html'>A while ago a friend of mine asked me an interesting programming question: "How would you implement a DrawCircle() function?". "I'd loop and invoke Sin() and Cos() in order to obtain coordinates" I answered. "OK", he replied, "and how would you make it fast? There should be something like an incremental way, maybe without the need for floating point operations at all.".&lt;br /&gt;&lt;br /&gt;This kept me thinking. So why not give it a try right here and now! Let's start with something like this (C#, .NET 2.0 - using a managed environment is OK, as .NET is guaranteed to JIT this code immediately anyway):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle1(int radius)&lt;br /&gt;{&lt;br /&gt;  double angle = 0;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    double rad = angle * Math.PI / 180.0;&lt;br /&gt;    double x = Math.Cos(rad);&lt;br /&gt;    double y = Math.Sin(rad);&lt;br /&gt;&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;&lt;br /&gt;    angle++;&lt;br /&gt;  } while (angle &lt; 360);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Sure enough, a circle appears on the screen. It looks weird - the line is dotted, not solid. Right, incrementing the angle by 1 each time might imply advancing too fast. An increment value better suited is the angle between two pixels on the circle. Something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle2(int radius)&lt;br /&gt;{&lt;br /&gt;  double angle = 0;&lt;br /&gt;&lt;br /&gt;  double step = Math.Asin(1.0 / radius) * &lt;br /&gt;                180.0 / Math.PI;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    double rad = angle * Math.PI / 180.0;&lt;br /&gt;    double x = Math.Cos(rad) * radius;&lt;br /&gt;    double y = Math.Sin(rad) * radius;&lt;br /&gt;&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;&lt;br /&gt;    angle += step;&lt;br /&gt;  } while (angle &lt; 360);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This works, so that's something to start with. To get an idea about its speed, let's ignore the call to DrawPixel() for a second (and assume that we have a lightning-fast DrawPixel() implementation at that point - and by lightning-fast I mean inlined native code that just does something like an indexed array lookup, a little pointer arithmetic, and one or two bitwise operations on a bitmap bytearray, not slow stuff like &lt;a href="http://msdn2.microsoft.com/en-us/library/ms532304(VS.85).aspx"&gt;SetPixel()&lt;/a&gt;, &lt;a href="http://msdn2.microsoft.com/en-us/library/system.drawing.bitmap.setpixel.aspx"&gt;Bitmap.SetPixel()&lt;/a&gt; or &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/BufferedImage.html#setRGB(int,%20int,%20int)"&gt;BufferedImage.setRGB()&lt;/a&gt;). Oh yeah, and no synchronous screen refreshing ;-)&lt;br /&gt;&lt;br /&gt;Some profiling tells us that his function can calculate the pixels for 160,000 circles (of radius 100) per second (all numbers refer to release mode).&lt;br /&gt;&lt;br /&gt;Even with floating point processing units, floating point operations can be expensive in a certain sense - that is, avoiding them altogether, could still cause a measurable improvement. Sure we can build up a large lookup array with pre-calculates Cos- and Sin-values, and interpolate in between them. I used to do that on other occasions, e.g. in &lt;a href="http://www.weirdoz.org/visualchat/"&gt;my 3D chat project&lt;/a&gt;. But here there is no way of telling in advance how precise the lookup array needs to be, and with increasing precision the table will grow in size too.&lt;br /&gt;&lt;br /&gt;Another way of speeding up the algorithm is to take advantage of the fact that a circle is symmetric. Wouldn't it be enough to calculate one quarter of the circle, and then just mirror it on the x- and y-axis. But wait - what about calculating only one eighth of the circle, and mirroring it on the diagonal, too? No sooner said than done:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle3(int radius)&lt;br /&gt;{&lt;br /&gt;  double angle = 0;&lt;br /&gt;&lt;br /&gt;  double step = Math.Asin(1.0 / radius) * &lt;br /&gt;                180.0 / Math.PI;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    double rad = angle * Math.PI / 180.0;&lt;br /&gt;    double x = Math.Cos(rad) * radius;&lt;br /&gt;    double y = Math.Sin(rad) * radius;&lt;br /&gt;&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;    DrawPixel(x, -y);&lt;br /&gt;    DrawPixel(-x, y);&lt;br /&gt;    DrawPixel(-x, -y);&lt;br /&gt;    DrawPixel(y, x);&lt;br /&gt;    DrawPixel(y, -x);&lt;br /&gt;    DrawPixel(-y, x);&lt;br /&gt;    DrawPixel(-y, -x);&lt;br /&gt;&lt;br /&gt;    angle += step;&lt;br /&gt;  } while (angle &lt;= 45);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This gives us something close to the expected 8-fold speedup (1,200,000 circle calculations per second to be exact). Still it seems like a dead end. We need a different approach.&lt;br /&gt;&lt;br /&gt;Let's search for a way to draw a circle without the need of invoking Sin() and Cos(). What does a circle function look like? It's&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;x^2 + y^2 = 1&lt;/pre&gt;&lt;br /&gt;so:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;y = sqrt(1 - x^2)&lt;/pre&gt;&lt;br /&gt;Let's also take advantage of the fact that we know that x is always incremented while looping. How do we know that? Because we just calculate 1/8th of the circle. We are starting at coordinate (x = 0, y = radius), hence on each step will either move right only, or move right and down.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle4(int radius)&lt;br /&gt;{&lt;br /&gt;  double x = 0;&lt;br /&gt;  double y = radius;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;    DrawPixel(x, -y);&lt;br /&gt;    DrawPixel(-x, y);&lt;br /&gt;    DrawPixel(-x, -y);&lt;br /&gt;    DrawPixel(y, x);&lt;br /&gt;    DrawPixel(y, -x);&lt;br /&gt;    DrawPixel(-y, x);&lt;br /&gt;    DrawPixel(-y, -x);&lt;br /&gt;&lt;br /&gt;    x++;&lt;br /&gt;    //cast to get rid of decimal places&lt;br /&gt;    y = (int)((Math.Sqrt(1.0 - &lt;br /&gt;        Math.Pow(x / radius, 2)) * radius));&lt;br /&gt;  } while (x &lt;= y);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;100,000 circles per second, what a disappointment. I did some quick check, it seems that Math.Pow() does not have dedicated FPU-support, so this might be a reason.&lt;br /&gt;&lt;br /&gt;Still this might open a new perspective. Having a look at two neighboring pixels on the circle, what does make them neighbors? Their x- and y-coordinates differ by 1, either x does, or y, or both. Starting at a well-known pixel, e.g. (x = 0, y = radius) we could move along the circle just be deciding whether we increment x and leave y as it is, or decrement y and leave x as it is, or increment x and decrement y.&lt;br /&gt;&lt;br /&gt;Once more the circle function:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;x^2 + y^2 = 1&lt;/pre&gt;&lt;br /&gt;which equates to:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;x^2 + y^2 - 1 = 0&lt;/pre&gt;&lt;br /&gt;Drawing a perfect circle on a discrete field of pixels, there is always a deviation, an error. An error e that can be expressed like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;x^2 + y^2 - 1 = e&lt;/pre&gt;&lt;br /&gt;The pixel we draw is either inside the perfect circle or outside. When we have moved outside the perfect circle, we have to pull back to the inside, and vice versa. In each iteration, we must keep e as close to 0 as possible.&lt;br /&gt;&lt;br /&gt;How does e change on each iteration? Let's assume we increment x and decrement y, so the next pixel is at (x + 1, y - 1):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;e = x^2 + y^2 - 1&lt;br /&gt;nextE = (x + 1)^2 + (y - 1)^2 - 1 &lt;br /&gt;      = (x^2 + 2x + 1) + (y^2 - 2y + 1) - 1&lt;/pre&gt;&lt;br /&gt;The difference between nextE and e is influenced by the following factors:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;dx = (x^2 + 2x + 1) - x^2 = 2x + 1&lt;br /&gt;dy = (y^2 - 2y + 1) - y^2 = -2y + 1&lt;/pre&gt;&lt;br /&gt;This is how nextE looks like when x is incremented and y is decremented:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;nextE = e + dx + dy&lt;/pre&gt;&lt;br /&gt;If we decide only to increment x and leave y as it is, nextE would be:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;nextE = e + dx&lt;/pre&gt;&lt;br /&gt;And in case we only decrement y, nextE is:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp; gutter: false"&gt;nextE = e + dy&lt;/pre&gt;&lt;br /&gt;At this point we just have to figure out whether e stays smallest by moving along the x-axis only, or the y-axis only, or both.&lt;br /&gt;&lt;br /&gt;Wow, no need to invoke Sin(), Cos(), Sqrt() or Pow(), no need for floating point functions or values, we can do it all with integers only. Note that we could even avoid the multiplications by a factor of 2 simply by writing (x + x) and (- y - y), but compilers normally replace that by a left-shift anyway. Also, we do not invoke Math.Abs(), but get rid of minus signs on our own.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle5(int radius)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  int e = 0;&lt;br /&gt;  int x = 0;&lt;br /&gt;  int y = radius;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;    DrawPixel(x, -y);&lt;br /&gt;    DrawPixel(-x, y);&lt;br /&gt;    DrawPixel(-x, -y);&lt;br /&gt;    DrawPixel(y, x);&lt;br /&gt;    DrawPixel(y, -x);&lt;br /&gt;    DrawPixel(-y, x);&lt;br /&gt;    DrawPixel(-y, -x);&lt;br /&gt;&lt;br /&gt;    int dx = 2*x + 1;&lt;br /&gt;    int dy = -(2*y) + 1;&lt;br /&gt;&lt;br /&gt;    int e1 = e + dx;&lt;br /&gt;    int e2 = e + dx + dy;&lt;br /&gt;    int e3 = e + dy;&lt;br /&gt;&lt;br /&gt;    e1 = e1 &lt; 0 ? -e1 : e1;&lt;br /&gt;    e2 = e2 &lt; 0 ? -e2 : e2;&lt;br /&gt;    e3 = e3 &lt; 0 ? -e3 : e3;&lt;br /&gt;&lt;br /&gt;    if (e1 &lt;= e2 &amp;amp;&amp;amp; e1 &lt;= e3)&lt;br /&gt;    {&lt;br /&gt;      x++;&lt;br /&gt;      e += dx;&lt;br /&gt;    }&lt;br /&gt;    else if (e2 &lt;= e1 &amp;amp;&amp;amp; e2 &lt;= e3)&lt;br /&gt;    {&lt;br /&gt;      y--;&lt;br /&gt;      x++;&lt;br /&gt;      e += dx + dy;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      y--;&lt;br /&gt;      e += dy;&lt;br /&gt;    }&lt;br /&gt;  } while (x &lt;= y);&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;2,100,000 circle calculations a second. It cannot possibly get better, can it? &lt;br /&gt;&lt;br /&gt;Yes it can. Similar to DrawCircle4(), when we always incremented x because of calculating 1/8th of the circle only, we can assume here that we will never enter the last else-block in DrawCircle5(), as it does not increment x either.&lt;br /&gt;&lt;br /&gt;And since we are always moving to the right incrementing x, the delta of e depends entirely on whether we also move down and decrement y. The change in the error value caused by decrementing y is (-2*y + 1), so let's choose to decrement y each time e is greater or equal to y, as this will move e closer to 0 again. &lt;br /&gt;&lt;br /&gt;All of this allows us to get rid of several statements, and some local variables too. Also, let's do the left-shifting explicitly this time instead of multiplying by 2 (as mentioned above, we may expect our compiler to take care of that, but I have experienced cases when that did not happen, e.g. when writing -2*y instead of -(2*y)).&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;private static void DrawCircle6(int radius) &lt;br /&gt;{&lt;br /&gt;  int e = 0;&lt;br /&gt;  int x = 0;&lt;br /&gt;  int y = radius;&lt;br /&gt;&lt;br /&gt;  do&lt;br /&gt;  {&lt;br /&gt;    DrawPixel(x, y);&lt;br /&gt;    DrawPixel(x, -y);&lt;br /&gt;    DrawPixel(-x, y);&lt;br /&gt;    DrawPixel(-x, -y);&lt;br /&gt;    DrawPixel(y, x);&lt;br /&gt;    DrawPixel(y, -x);&lt;br /&gt;    DrawPixel(-y, x);&lt;br /&gt;    DrawPixel(-y, -x);&lt;br /&gt;&lt;br /&gt;    if (e &lt; y) &lt;br /&gt;    {&lt;br /&gt;      e += (x&lt;&lt;1) + 1;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      e += (x - y + 1)&lt;&lt;1;&lt;br /&gt;      y--;&lt;br /&gt;    }&lt;br /&gt;    x++;&lt;br /&gt;  } while (x &lt;= y);&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;6,400,000 circles per second. It is now 40 times faster than at the beginning.&lt;br /&gt;&lt;br /&gt;I guess this is a good example that there is always room for improvement, and the solution that comes to mind first probably is not the quickest.&lt;br /&gt;&lt;br /&gt;By the way, if you know an even faster algorithm, please drop me a line, and I will post a follow-up.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update 2011:&lt;/strong&gt; &lt;br /&gt;&lt;br /&gt;I did some further research recently, and found out that what I developed back then is kind of a variant of the &lt;a href="http://en.wikipedia.org/wiki/Midpoint_circle_algorithm"&gt;Midpoint Circle Algorithm&lt;/a&gt;, dating back to 1967. So I can't really claim ownership [ ;-) ].&lt;br /&gt;&lt;br /&gt;As mentioned above, I had received two initial hints, namely that there should be an algorithm without the need of floating point operations, and that it might work in a kind of incremental way. This is were I started, and then kept going. DrawCircle6() came to my mind only after sleeping over the problem for another night.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6160801940679386875?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6160801940679386875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6160801940679386875'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/03/performance-tuning-drawcircle.html' title='DrawCircle Performance Tuning'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7513022989344888782</id><published>2008-02-27T22:40:00.001+01:00</published><updated>2008-03-22T05:55:19.654+01:00</updated><title type='text'>Windows Forms DataBound-Controls On TabPages</title><content type='html'>I stumble over &lt;a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2863363&amp;SiteID=1"&gt;this issue&lt;/a&gt; every once in a while: Windows Forms controls will only data-bind when they are visible or have been visible once - this can become an issue when controls are located on a TabPage that has never been shown. The control's bound value then has never been pushed into the UI.&lt;br /&gt;&lt;br /&gt;Especially validation-code that checks on properties like &lt;a href="http://msdn2.microsoft.com/de-de/library/system.windows.forms.textbox.text(VS.80).aspx"&gt;TextBox.Text&lt;/a&gt; is vulnerable to this - e.g., no matter what the bound value is, TextBox.Text returns an empty string as long as it has not been visible (anyway, in my opinion validation code should always work on model values, not on view properties).&lt;br /&gt;&lt;br /&gt;One way to work around this is to check on &lt;a href="http://msdn2.microsoft.com/en-us/library/system.windows.forms.control.created.aspx"&gt;Control.Created&lt;/a&gt; before validating a control's content.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7513022989344888782?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7513022989344888782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7513022989344888782'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/02/windows-forms-databound-controls-on.html' title='Windows Forms DataBound-Controls On TabPages'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7524885685621990661</id><published>2008-01-21T21:39:00.006+01:00</published><updated>2008-03-23T20:27:27.489+01:00</updated><title type='text'>.NET Webcasts</title><content type='html'>I am currently watching lots of .NET Webcasts. Some of the best can be found at:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.dnrtv.com/"&gt;DotNetRocks TV&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.microsoft.com/events/series/msdnnetframework3.aspx?tab=webcasts&amp;amp;id=odall"&gt;MSDN On-Demand Webcasts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://channel9.msdn.com/ShowForum.aspx?ForumID=38"&gt;Channel 9 Screencasts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.microsoft.com/uk/msdn/screencasts/rss.aspx?t=presenter&amp;amp;pid=11"&gt;Mike Taulty's Screencasts&lt;/a&gt; from Microsoft UK&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Especially &lt;a href="http://mtaulty.com/"&gt;Mike Taulty's&lt;/a&gt; &lt;a href="http://www.microsoft.com/uk/msdn/screencasts/rss.aspx?t=presenter&amp;amp;pid=11"&gt;screencasts&lt;/a&gt; are great. They have the highest entropy - tons of information in a relatively short amount of time. He exactly delivers all the facts I need to know - straight to the point.&lt;br /&gt;&lt;br /&gt;I also try to save time when watching screencasts by switching to double playback speed in MediaPlayer.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/R-Qn0XsrVcI/AAAAAAAAAIQ/xjcOAjPdooA/s1600-h/fastplay.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/R-Qn0XsrVcI/AAAAAAAAAIQ/xjcOAjPdooA/s400/fastplay.gif" alt="" id="BLOGGER_PHOTO_ID_5180309252050998722" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;With some concentration its possible to do so without missing any piece of information.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7524885685621990661?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7524885685621990661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7524885685621990661'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2008/01/net-webcasts.html' title='.NET Webcasts'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/R-Qn0XsrVcI/AAAAAAAAAIQ/xjcOAjPdooA/s72-c/fastplay.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3395692719385126096</id><published>2007-12-23T20:41:00.000+01:00</published><updated>2007-12-25T12:52:22.393+01:00</updated><title type='text'>Arno's Software Development Bookshelf</title><content type='html'>Time for a picture of my software development bookshelf. It's basically divided into three sections: (A) Software development technology (by far the largest part), (B) Software development project management and (C) The history of the software industry. Please click on the image to zoom in.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_G0oBN6-Lb48/R3Du55B-M5I/AAAAAAAAAHs/CWywSdt1nFQ/s1600-h/bookshelf3.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/R3Du55B-M5I/AAAAAAAAAHs/CWywSdt1nFQ/s400/bookshelf3.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5147877052413522834" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3395692719385126096?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3395692719385126096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3395692719385126096'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/12/arnos-software-development-bookshelf.html' title='Arno&apos;s Software Development Bookshelf'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_G0oBN6-Lb48/R3Du55B-M5I/AAAAAAAAAHs/CWywSdt1nFQ/s72-c/bookshelf3.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5467609907932603628</id><published>2007-11-08T00:19:00.001+01:00</published><updated>2007-11-15T12:16:35.303+01:00</updated><title type='text'>Switching Source Control Providers Under Visual Studio</title><content type='html'>Visual Studio 2005 let's you switch between different source control providers (e.g. &lt;a href="http://www.microsoft.com/germany/msdn/vstudio/products/teamsystem/team/default.mspx"&gt;Team Foundation Server&lt;/a&gt; and another SCC provider for &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; or &lt;a href="http://www.nongnu.org/cvs/"&gt;CVS&lt;/a&gt;) during runtime under Tools / Options / Source Control / Plug-In Selection. But if you still have to maintain some old .NET 1.1 code in Visual Studio 2003, you may be out of luck. &lt;br /&gt;&lt;br /&gt;E.g. I recently had to access an old Sourcesafe repository, but since I had installed Team Foundation Server, Visual Studio 2003 would try to connect to the TFS server using a Sourcesafe URI. Not a good idea. And there is not way to change the provider back to VSS temporarily from within Visual Studio 2003.&lt;br /&gt;&lt;br /&gt;Luckily I found this nice little tool &lt;a href="http://www.eganmarketing.com/ccc/fc/files/Programs/SCCSwitch.zip"&gt;SCCSwitch&lt;/a&gt; which did the job.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5467609907932603628?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5467609907932603628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5467609907932603628'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/11/switching-source-control-providers.html' title='Switching Source Control Providers Under Visual Studio'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5337631643103403058</id><published>2007-11-04T20:39:00.001+01:00</published><updated>2007-12-07T07:08:14.116+01:00</updated><title type='text'>Poker Calculator</title><content type='html'>My friend and colleague Josef developed a &lt;a href="http://pokercalc.digitale-bilder.net/index_en.htm"&gt;pokerhand odds calculator&lt;/a&gt; in Java, and I asked him to throw it into an applet so I could host it on this blog as well. Here we go:&lt;br /&gt;&lt;br /&gt;&lt;applet archive="http://pokercalc.digitale-bilder.net/pokercalc.jar" code="at/pokercalc/PokerCalcApplet" codebase="http://pokercalc.digitale-bilder.net" width="470" height="440"&gt;&lt;/applet&gt;&lt;br /&gt;&lt;br /&gt;Great stuff, Josef! Handling the input is very easy, just click the cards of choice for the input field having the focus (yellow background), and press "Calculate" to compute the odds, resp. to finish dealing out. You can also undo/redo any action.&lt;br /&gt;&lt;br /&gt;The odds calculator uses a brute force approach. It looks quite fast, considering the fact it needs to process millions of combinations before the &lt;a href="http://en.wikipedia.org/wiki/Flop_(poker)"&gt;flop&lt;/a&gt;, and finishes in about a second on my PC. You can take a closer look at the code as well - it's &lt;a href="http://pokercalc.digitale-bilder.net/download/PokerCalc-Source.zip"&gt;open source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My blog stylesheet won't resize the content area's width, that's why the applet is cut off on the right side, otherwise my navigation panel would have disappeared (I'll probably ask Josef to let his &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/LayoutManager.html"&gt;LayoutManager&lt;/a&gt; do some resizing instead, so the applet will fit within smaller screen estate as well). You can see it &lt;a href="http://pokercalc.digitale-bilder.net/index_en.htm"&gt;here&lt;/a&gt; in full size.&lt;br /&gt;&lt;br /&gt;The applet requires &lt;a href="http://www.java.com/download"&gt;Java 5&lt;/a&gt;, which - if missing - should be installed automatically on MSIE, I hope also on Firefox (for Mozilla browsers I had to replace the &amp;lt;embed&amp;gt; tag by an &amp;lt;applet&amp;gt; tag in order for it to run at all inside a blogspot.com page).&lt;br /&gt;&lt;br /&gt;And by the way Josef, here are the odds for our game last week, when you went &lt;a href="http://en.wikipedia.org/wiki/Betting_%28poker%29#.22All_in.22"&gt;all in&lt;/a&gt;. Sorry to mention that 84% sometimes still is not enough. ;-)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_G0oBN6-Lb48/Ry4x8bsZ8AI/AAAAAAAAADI/ZhMq4BXyfzE/s1600-h/poker.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/Ry4x8bsZ8AI/AAAAAAAAADI/ZhMq4BXyfzE/s400/poker.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5129091939917492226" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; Some proxies seem to filter away &amp;lt;object&amp;gt; tags altogether, so I replaced it with an &amp;lt;applet&amp;gt; tag for all browsers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5337631643103403058?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5337631643103403058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5337631643103403058'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/11/poker-calculator.html' title='Poker Calculator'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/Ry4x8bsZ8AI/AAAAAAAAADI/ZhMq4BXyfzE/s72-c/poker.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3963398695947657699</id><published>2007-10-31T21:26:00.003+01:00</published><updated>2011-11-28T20:34:54.876+01:00</updated><title type='text'>How To Trim A .NET Application's Memory Workingset</title><content type='html'>.NET developers who have monitored their application's memory consumption in Windows Taskmanager, or slightly more sophisticated performance monitors like &lt;a href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarperfmo/html/perfmon.asp"&gt;PerfMon&lt;/a&gt;, might have noticed the effect when memory usage slowly rises, and hardly ever drops. Once the whole operating systems starts to run low on memory, the &lt;a href="http://msdn2.microsoft.com/en-us/netframework/aa497266.aspx"&gt;CLR&lt;/a&gt; finally seems to give back memory as well. And as some people have noted, memory usage also goes down once an application's main window is minimized.&lt;br /&gt;&lt;br /&gt;First of all it's important to note that by default the Windows Taskmanager only shows the amount of physical memory acquired. There is another column for displaying virtual memory usage, but it's not visible originally. So when physical memory usage drops, it's not always necessarily the CLR returning memory, but probably physical memory being swapped out to disk.&lt;br /&gt;&lt;br /&gt;So memory consumption drops at some point in time - just probably too late. Those symptoms give us a first clue that we are not dealing with &lt;a href="http://en.wikipedia.org/wiki/Memory_leak"&gt;memory leaks&lt;/a&gt; here (of course memory leaks are more unlikely to happen in managed environments than in unmanaged ones, still it's possible - e.g. static variables holding whole trees of objects that could otherwise be reclaimed, or that EventListener that should have been unregistered but wasn't). Also, whatever amount of native heap the CLR has allocated, the size of the managed heap within that native heap is a whole different story. The CLR might just have decided to keep native memory allocated even if it could be free'd after a garbage collection pass.&lt;br /&gt;&lt;br /&gt;And this does not look like a big deal at first glance - so what if the CLR keeps some more memory than necessary, as long as it's being returned once in a while? But the thing is, the CLR's decisions on when the right moment for freeing memory has arrived (or for that matter, the OS swapping unused memory pages to disk) might not always coincide with the users' expectations. And I have also seen &lt;a href="http://citrix.com/"&gt;Citrix&lt;/a&gt; installations with plenty of .NET Winforms applications running in parallel, soaking up a lot more resources than necessary, hence restraining the whole system.&lt;br /&gt;&lt;br /&gt;Some customers tend to get nervous when they watch a simple client process holding 500MB or more of memory. "Your application is leaking memory" is the first thing they will scream. And nervous programmers will profile their code, unable to find a leak, and then start invoking &lt;a href="http://msdn2.microsoft.com/en-us/library/system.gc.collect(VS.80).aspx"&gt;GC.Collect()&lt;/a&gt; manually - which not only doesn't help, but is a bad idea generally speaking.&lt;br /&gt;&lt;br /&gt;Under Java there is a way to limit the default maximum heap size (the default value depends on the Java VM), which can be overridden by passing the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/java.html"&gt;"-Xmx" commandline parameter&lt;/a&gt; to the runtime. Once the limit is reached, the garbage collector will be forced to run once more, and if that doesn't help any more either, an &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/OutOfMemoryError.html"&gt;OutOfMemoryError&lt;/a&gt; is thrown. This might be bad news for the Java application, but at least it will not bring down the whole system.&lt;br /&gt;&lt;br /&gt;I don't know of a counterpart to "-Xmx" in the .NET world. &lt;a href="http://msdn2.microsoft.com/en-us/library/system.diagnostics.process.maxworkingset.aspx"&gt;Process.MaxWorkingSet property&lt;/a&gt; allows for limiting the &lt;span style="font-style:italic;"&gt;physical memory&lt;/span&gt; a process may occupy. I have read several postings recommending this approach to keep the whole .NET memory footprint low, but I am not so sure, plus setting Process.MaxWorkingSet requires admin privileges - something that application users will not (and should not) have.&lt;br /&gt;&lt;br /&gt;A better choice is the Win32 API function &lt;a href="http://msdn2.microsoft.com/en-us/library/ms686234.aspx"&gt;SetProcessWorkingSetSize()&lt;/a&gt; with two special paramater values: -1.&lt;br /&gt;&lt;br /&gt;From MSDN:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;BOOL WINAPI SetProcessWorkingSetSize(&lt;br /&gt;  __in          HANDLE hProcess,&lt;br /&gt;  __in          SIZE_T dwMinimumWorkingSetSize,&lt;br /&gt;  __in          SIZE_T dwMaximumWorkingSetSize&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;If both dwMinimumWorkingSetSize and dwMaximumWorkingSetSize have the value (SIZE_T)-1, the function temporarily trims the working set of the specified process to zero. This essentially swaps the process out of physical RAM memory.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What SetProcessWorkingSetSize() does is to invalidate the process's memory pages.  what we have achieved at this point, is that our application's physical memory usage is limited to the bare minimum. And all that unused memory - as long as it is not being accessed, it will not be reloaded into physical memory. The same is true for .NET Assemblies which have been loaded, but are not currently used.&lt;br /&gt;&lt;br /&gt;And the good news: this does not require the user to have admin rights. By the way, SetProcessWorkingSetSize is what's being invoked when an application window is minimized, which explains the effect described above.&lt;br /&gt;&lt;br /&gt;I should not that there might be a performance penalty associated with that approach, as it might lead to a higher number of page faults following following the invocation, in case other processes regain physical memory in the meantime.&lt;br /&gt;&lt;br /&gt;Obviously Windows' virtual memory implementation can not always swap out unused memory as aggressively. And it's my guess that what might hinder it furthermore is the constant relocation of objects within the native heap caused by garbage collection (which means a lot of different memory pages are being accessed over time, hence hardly ever paged to disk).&lt;br /&gt;&lt;br /&gt;A &lt;a href="http://msdn2.microsoft.com/en-us/library/system.timers.timer.aspx"&gt;Timer&lt;/a&gt; can be applied for repeated invocations of SetProcessWorkingSetSize(), with a reasonable interval between two calls of maybe 15 or 30 minutes (this depends heavily on the kind of application and its workload). Another possibility is to check from time to time on the physical memory being used, and once a certain amount has been reached the call to SetProcessWorkingSetSize() will occurr. A word of warning though - I do not advocate to invoke it too often either. Also, don't set the minimum and maximum working sizes (let the CLR take care of that), just use the -1 parameter values in order to swap out memory, after all that's what we are trying to achieve.&lt;br /&gt;&lt;br /&gt;The complete code:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[DllImport("kernel32")]&lt;br /&gt;static extern bool SetProcessWorkingSetSize(IntPtr handle, int minSize, int maxSize);&lt;br /&gt;&lt;br /&gt;SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Anyway, our Citrix customers are happy again, and no one has ever screamed "Memory leak!" since we implemented that workaround.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3963398695947657699?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3963398695947657699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3963398695947657699'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/10/how-to-trim-net-applications-memory.html' title='How To Trim A .NET Application&apos;s Memory Workingset'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7689661273370945465</id><published>2007-10-25T20:45:00.002+02:00</published><updated>2008-02-23T09:34:23.326+01:00</updated><title type='text'>Five Easy Ways To Fail</title><content type='html'>Joel Spolsky describes the most common reasons for software projects to go awry in his latest articel &lt;a href="http://www.inc.com/magazine/20071101/how-hard-could-it-be-five-easy-ways-to-fail.html"&gt;"How Hard Could It Be? Five Easy Ways to Fail"&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As kind of expected, a "mediocre team of developers" comes up as number one, and as usual Joel Spolsky describes it much more eloquently than I ever could:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;#1: Start with a mediocre team of developers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Designing software is hard, and unfortunately, a lot of the people who call themselves programmers can't really do it. But even though a bad team of developers tends to be the No. 1 cause of software project failures, you'd never know it from reading official postmortems. &lt;br /&gt;&lt;br /&gt;In all fields, from software to logistics to customer service, people are too nice to talk about their co-workers' lack of competence. You'll never hear anyone say "the team was just not smart enough or talented enough to pull this off." Why hurt their feelings? &lt;br /&gt;&lt;br /&gt;The simple fact is that if the people on a given project team aren't very good at what they do, they're going to come into work every day and yet--behold!--the software won't get created. And don't worry too much about HR standing in your way of hiring a bunch of duds. In most cases, I assure you they will do nothing to prevent you from hiring untalented people.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I tend to question the four other reasons he mentions though (mainly estimating and scheduling issues). Don't get me wrong, he surely got his points, but I would rank other problem fields higher than that, lack of management support, amateurish requirements analysis or suffering from the &lt;a href="http://en.wikipedia.org/wiki/Not_Invented_Here"&gt;NIH-syndrome&lt;/a&gt; among them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7689661273370945465?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7689661273370945465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7689661273370945465'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/10/five-easy-ways-to-fail.html' title='Five Easy Ways To Fail'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8544404822388082901</id><published>2007-10-15T20:30:00.000+02:00</published><updated>2007-11-15T12:15:20.174+01:00</updated><title type='text'>Hints And Pitfalls In Database Development (Part 5): The Importance Of Choosing The Right Clustered Index</title><content type='html'>In database design, a clustered index defines the physical order in which data rows are stored on disk (Note: the most common data structure for storing rows both in memory and on disk are &lt;a href="http://en.wikipedia.org/wiki/B-tree"&gt;B-trees&lt;/a&gt;, so the term "page" can also be interpretated as "B-tree leaf node" in the following text, although it's not necessarily a 1:1 match - but you get the point). In most cases the default clustered index is the primary key. The trouble starts when people don't spend any further thought and stick with that setting no matter whether the primary key is a good choice for physical ordering or not...&lt;br /&gt;&lt;br /&gt;File I/O happens at a page level, so reading a row implies that all other rows stored within the same physical disk page are read as well. Wouldn't it make sense to align those rows together which are most likely to be fetched en bloc too? This limits the number of page reads, and avoids having to switch disk tracks (which would be a costly operation).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/RxWvQT0ugaI/AAAAAAAAADA/OcKSDYT3EGU/s1600-h/clustered_index.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/RxWvQT0ugaI/AAAAAAAAADA/OcKSDYT3EGU/s400/clustered_index.gif" alt="" id="BLOGGER_PHOTO_ID_5122192845938000290" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;Image courtesy of &lt;a href="http://msdn2.microsoft.com/en-us/library/ms177443.aspx"&gt;MSDN / SqlServer 2005 Database Engine Books Online&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So the secret is to choose an attribute for clustering which causes the least overhead for I/O. Those rows that are most likely going to be accessed together should reside within the same page, or at least in pages next to each other.&lt;br /&gt;&lt;br /&gt;Usually an auto-increment primary key is a good choice for a clustered index. Rows that have been created consecutively will then be stored consecutively, which fits in case they are likely to be accessed along with each other as well. On the other hand if a row contains a date column, and data is mainly being selected based on these date values, this column might be the right option for clustering. And for child rows it's probably a good idea to choose the foreign key column referencing the parent row for the table's clustered index - a parent row's child rows can then be fetched in one pass.&lt;br /&gt;&lt;br /&gt;I work on a project that applies unique identifiers for primary keys. This has several advantages, the client being able to create primary keys in advance among them. But unique identifier primary keys are a bad choice for a clustered index, as their values disperse more or less randomly, hence the physical order on disk will be just as random. We have experienced a many-fold performance speedup by choosing  more suitable columns for clustered indexing.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;Missing Indices&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database.html"&gt;Let The Database Enforce Data Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database_29.html"&gt; Database Programming Requires More Than SQL Knowledge&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/05/hints-and-pitfalls-in-database_5584.html"&gt; Do Not String-Concatenate SQL Parameter Values&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8544404822388082901?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8544404822388082901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8544404822388082901'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html' title='Hints And Pitfalls In Database Development (Part 5): The Importance Of Choosing The Right Clustered Index'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/RxWvQT0ugaI/AAAAAAAAADA/OcKSDYT3EGU/s72-c/clustered_index.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1395431862450421593</id><published>2007-10-10T22:42:00.001+02:00</published><updated>2007-11-15T12:15:05.870+01:00</updated><title type='text'>Software Development Projects: The Road To Success</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/Rw06Lz0ugZI/AAAAAAAAAC4/gfVwfyXgZ8Q/s1600-h/swdev_success.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/Rw06Lz0ugZI/AAAAAAAAAC4/gfVwfyXgZ8Q/s400/swdev_success.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5119812325954453906" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1395431862450421593?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1395431862450421593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1395431862450421593'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/10/software-development-projects-road-to.html' title='Software Development Projects: The Road To Success'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/Rw06Lz0ugZI/AAAAAAAAAC4/gfVwfyXgZ8Q/s72-c/swdev_success.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8969112025732200068</id><published>2007-10-05T22:14:00.001+02:00</published><updated>2007-11-15T12:14:48.667+01:00</updated><title type='text'>Fun With WinDbg</title><content type='html'>I did some debugging on an old legacy reporting system this week, using &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;WinDbg&lt;/a&gt;. The reporting engine terminated prematurely after something like 1000 printouts.&lt;br /&gt;&lt;br /&gt;After attaching WinDbg and letting the reporter run for half an hour, a first chance exception breakpoint hit because of this memory access violation:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;(aa8.a14): &lt;strong&gt;Access violation - code c0000005 (first chance)&lt;/strong&gt;&lt;br /&gt;First chance exceptions are reported before any exception handling.&lt;br /&gt;This exception may be expected and handled.&lt;br /&gt;eax=00000000 ebx=665b0006 ecx=7c80ff98 edx=00000000 esi=00000000 edi=00000000&lt;br /&gt;eip=665a384f esp=0012bdc4 ebp=00000005 iopl=0         nv up ei pl zr na pe nc&lt;br /&gt;cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246&lt;br /&gt;*** ERROR: Symbol file could not be found.  Defaulted to export symbols for GEEI11.dll - &lt;br /&gt;GEEI11!FUNPower+0x15f:&lt;br /&gt;665a384f 668b7804        &lt;strong&gt;mov     di,word ptr [eax+4]      ds:0023:00000004=????&lt;/strong&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Trying to access address 0x0000004 ([EAX+4]), one of the reporting DLLs was obviously doing some pointer arithmetics on a NULL pointer. The previous command was a call to GEEI11!WEP+0xb47c, which happened to be the fixup for &lt;a href="http://msdn2.microsoft.com/en-us/library/aa366574.aspx"&gt;GlobalAlloc&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;665a3849 ff157c445b66    &lt;strong&gt;call    dword ptr [GEEI11!WEP+0xb47c (665b447c)]&lt;/strong&gt;&lt;br /&gt;665a384f 668b7804        mov     di,word ptr [eax+4]      ds:0023:00000004=????&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;GlobalLock takes a global memory handle, locks it, and returns a pointer to the actual memory block, or NULL in case of an error. According to the &lt;a href="http://www.unixwiz.net/techtips/win32-callconv-asm.html"&gt;Win32 API calling conventions (stdcall)&lt;/a&gt;, EAX is used for 32bit return values.&lt;br /&gt;&lt;br /&gt;The reporting engine code calling into GlobalLock was too optimistic and did not test for a NULL return value.&lt;br /&gt;&lt;br /&gt;The next question was, why would GlobalLock return NULL? Most likely because of an invalid handle passed in. Where could the parameter be found? At the ESI register - it was the one pushed onto the stack before the call to GlobalAlloc, thus must be the one and only function parameter, and it is &lt;a href="http://msdn2.microsoft.com/en-us/library/6t169e9c(VS.80).aspx"&gt;callee-saved&lt;/a&gt;, so GlobalAlloc had restored it in its epilog.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;665a3848 56              &lt;strong&gt;push    esi&lt;/strong&gt;&lt;br /&gt;665a3849 ff157c445b66    call    dword ptr [GEEI11!WEP+0xb47c (665b447c)]&lt;br /&gt;&lt;br /&gt;0:000&gt; r&lt;br /&gt;eax=00000000 ebx=665b0006 ecx=7c80ff98 edx=00000000 &lt;strong&gt;esi=00000000&lt;/strong&gt; edi=00000000&lt;br /&gt;eip=665a384f esp=0012bdc4 ebp=00000005 iopl=0         nv up ei pl zr na pe nc&lt;br /&gt;cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As expected, ESI was 0x00000000, and GetLastError confirmed this as well:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0:000&gt; !gle&lt;br /&gt;LastErrorValue: (Win32) &lt;strong&gt;0x6 (6) - Das Handle ist ungültig.&lt;/strong&gt;&lt;br /&gt;LastStatusValue: (NTSTATUS) 0xc0000034 - Der Objektname wurde nicht gefunden.&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Doing some further research, I found out that the global memory handle was NULL because a prior invocation of &lt;a href="http://msdn2.microsoft.com/en-us/library/aa366574.aspx"&gt;GlobalAlloc&lt;/a&gt; had been unsuccessful. Again, the caller had not checked for NULL at that point. And GlobalAlloc failed because the system had run out of global memory handles, as there is an upper limit of 65535. The reporting engine leaked those handles, neglecting to call GlobalDelete() on time, and after a while (1000 reports) had run out of handles.&lt;br /&gt;&lt;br /&gt;By the way, I could not figure out how to dump the global memory handle table in WinDbg. It seems to support all kinds of Windows handles, with the exception of global memory handles. Please drop me a line in case you know how to do that.&lt;br /&gt;&lt;br /&gt;Now, there is no way to patch the reporting engine as it's an old third party binary, so the solution we will most likely implement is to restart the engine process after a while, so all handles are free'd by terminating the old process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8969112025732200068?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8969112025732200068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8969112025732200068'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/10/fun-with-windbg.html' title='Fun With WinDbg'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1184545603785414629</id><published>2007-09-29T21:39:00.000+02:00</published><updated>2007-11-15T12:14:21.141+01:00</updated><title type='text'>Disdain Mediocrity</title><content type='html'>&lt;a href="http://www.benrady.com/"&gt;Ben Rady&lt;/a&gt; &lt;a href="http://blog.objectmentor.com/articles/2007/09/05/hate-is-good"&gt;writes&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;I do know some traits that good software developers all seem to share. One of them is a healthy disdain for mediocrity.&lt;br /&gt;&lt;br /&gt;Good developers cannot stand sloppiness (in software, anyway). Apathy, haste, and carelessness send shivers down their spines. They may disagree on the best way to do things, but they all agree that things should be done the best way. And they’re constantly looking and learning to find exactly what the best way is. They realize that seeking it is an ever-changing, lifelong quest.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I couldn't agree more...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1184545603785414629?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1184545603785414629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1184545603785414629'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/disdain-mediocrity.html' title='Disdain Mediocrity'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4041723124999409017</id><published>2007-09-27T01:14:00.000+02:00</published><updated>2007-11-15T12:14:02.296+01:00</updated><title type='text'>Three Years Of Blogging</title><content type='html'>&lt;a href="http://arnosoftwaredev.blogspot.com/2004/09/first-posting.html"&gt;Three years of blogging&lt;/a&gt;, 290 postings full of insight and wisdom (yeah, right), and ranked within the top 5 on hit count is the entry about the old &lt;a href="http://arnosoftwaredev.blogspot.com/2005/04/retro-computing-best-joystick-of-all.html"&gt;Arcade joystick&lt;/a&gt;?!? I am flabbergasted... ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4041723124999409017?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4041723124999409017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4041723124999409017'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/three-years-of-blogging.html' title='Three Years Of Blogging'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6263385555491050301</id><published>2007-09-26T23:10:00.000+02:00</published><updated>2007-11-15T12:13:47.618+01:00</updated><title type='text'>The People Factor In Software Development</title><content type='html'>You may want to have a look at a presentation I assembled on &lt;a href="http://members.aon.at/arno.huetter/pub/sw_human.pdf"&gt;"The People Factor in Software Development"&lt;/a&gt; (german only).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6263385555491050301?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6263385555491050301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6263385555491050301'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/people-factor-in-software-development.html' title='The People Factor In Software Development'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-887481484860614978</id><published>2007-09-14T22:51:00.000+02:00</published><updated>2007-11-15T12:13:33.659+01:00</updated><title type='text'>Finding Unused Classes Under .NET</title><content type='html'>While integrating some .NET libraries (which by the way came from an external development partner) in our main project, we noticed several classes that never were utilized. Getting suspicious, we decided to search for all unused classes. The question was: How to do that?&lt;br /&gt;&lt;br /&gt;The .NET compiler is no big help on this, which is understandable - it can't emit warnings on apparently unused public classes within a class library, as they are most likely part of the library's public API, but happen not to referenced inside the library itself. The same is true for IDE-integrated refactoring tools like &lt;a href="http://www.jetbrains.com/resharper/"&gt;Resharper&lt;/a&gt;. Resharper points out private/internal methods never called and private/internal types never referenced, but public classes are another story.&lt;br /&gt;&lt;br /&gt;So my next bet was on static code analysis tools. They usually let you define the system boundaries, hence it should be possible to identify classes never referenced within those boundaries.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.gotdotnet.com/Team/FxCop/"&gt;FXCop&lt;/a&gt; was one of the most widely used tools in the early days of .NET, but seems a little bit abandoned now, and did not have any matching analysis rule (or at least I didn't find any).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.fmsinc.com/dotnet/Analyzer/index.asp"&gt;Total .NET Analyzer&lt;/a&gt; on the other hand looked very promising and supposedly includes this feature. In contrast to FXCop it parses the sourcecode as well, thus has the means for a more powerful breakdown. Unfortunately it ran out of memory when scanning our Visual Studio solution on my 2GB developer workstation.&lt;br /&gt;&lt;br /&gt;Finally I ended up applying &lt;a href="http://www.ndepend.com/"&gt;NDepend&lt;/a&gt;. NDepend has extensive code analysis capabilities, including the highly-anticipated search for unused classes. It also calculates all kinds of other metrics. I have only scratched the surface so far, but what I have seen is very convincing.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/Rur7h7eS24I/AAAAAAAAACM/Ij58eDp_qXY/s1600-h/ndepend.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/Rur7h7eS24I/AAAAAAAAACM/Ij58eDp_qXY/s320/ndepend.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5110173287524260738" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-887481484860614978?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/887481484860614978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/887481484860614978'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/finding-unused-classes-under-net.html' title='Finding Unused Classes Under .NET'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_G0oBN6-Lb48/Rur7h7eS24I/AAAAAAAAACM/Ij58eDp_qXY/s72-c/ndepend.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-3157392425139978120</id><published>2007-09-09T22:18:00.000+02:00</published><updated>2007-11-15T12:17:03.811+01:00</updated><title type='text'>Leading Software Development Teams: The Human Factor</title><content type='html'>I am currently preparing a presentation on the topic "Leading Software Development Teams: The Human Factor". Once I find time I will translate it to English and post it on this blog, for the moment here is my list of sources:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;DeMarco, Lister: &lt;a href="http://www.amazon.com/dp/0932633439"&gt;"Peopleware"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;McBreen, P.: &lt;a href="http://www.amazon.com/dp/0201733862"&gt;"Software craftsmanship - The new imperative"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Rainwater, H.: &lt;a href="http://www.amazon.com/dp/1590590171"&gt;"Herding cats: A primer for programmers who lead programmers"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Spolsky, J.: &lt;a href="http://www.amazon.com/dp/1590593898"&gt;"Joel on software"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Spolsky, J.: &lt;a href="http://www.amazon.com/dp/1590598385"&gt;"Smart and gets things done - Joel Spolsky's concise guide to finding the best technical talent"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Weinberg, G.: &lt;a href="http://www.amazon.com/dp/0932633021"&gt;"Becoming a technical leader - An organic problem-solving approach"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Weinberg, G.: &lt;a href="http://www.amazon.com/dp/0932633420"&gt;"The psychology of computer programming"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Yourdon, E.: &lt;a href="http://www.amazon.com/dp/0130146595"&gt;"Death March"&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-3157392425139978120?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3157392425139978120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/3157392425139978120'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/leading-software-development-teams.html' title='Leading Software Development Teams: The Human Factor'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6637283299410258770</id><published>2007-09-06T18:03:00.000+02:00</published><updated>2007-09-06T20:13:42.033+02:00</updated><title type='text'>Declare War On Your Enemies</title><content type='html'>While Joel Spolsky - as &lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/handling-bozo-invasion.html"&gt;recently mentioned&lt;/a&gt; - prefers to bury bozos under a pile of bug reports, Ted Neward goes on the offensive and &lt;a href="http://blogs.tedneward.com/2007/07/28/The+First+Strategy+Declare+War+On+Your+Enemies+The+Polarity+Strategy.aspx"&gt;declares war&lt;/a&gt; on such and other enemies of project success:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Software is endless battle and conflict, and you cannot develop effectively unless you can identify the enemies of your project. Obstacles are subtle and evasive, sometimes appearing to be strengths and not distractions. You need clarity. Learn to smoke out your obstacles, to spot them by the signs and patterns that reveal hostility and opposition to your success. Then, once you have them in your sights, have your team declare war. As the opposite poles of a magnet create motion, your enemies - your opposites - can fill you with purpose and direction. As people and problems that stand in your way, who represent what you loathe, oppositions to react against, they are a source of energy. Do not be naive: with some problems, there can be no compromise, no middle ground.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Highly recommended reading material!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6637283299410258770?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6637283299410258770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6637283299410258770'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/09/declare-war-on-your-enemies.html' title='Declare War On Your Enemies'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-496985373104410862</id><published>2007-08-31T22:07:00.000+02:00</published><updated>2007-10-15T20:36:37.419+02:00</updated><title type='text'>Word COM Interop: Problems With Application.Selection</title><content type='html'>Most code samples out there use the &lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word._application.selection(VS.80).aspx"&gt;Application.Selection&lt;/a&gt; component for creating Word documents with COM Interop. From the &lt;a href="http://msdn2.microsoft.com/en-us/library/kw65a0we(VS.80).aspx"&gt;Word object model documentation&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The Selection object represents the area that is currently selected. When you perform an operation in the Word user interface, such as bolding text, you select, or highlight, the text and then apply the formatting. The Selection object is always present in a document. If nothing is selected, then it represents the insertion point. In addition, it can also be multiple blocks of text that are not contiguous.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Often those code samples do not even bother to set the selection beforehand (e.g. by invoking &lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word._document(VS.80).aspx"&gt;Document&lt;/a&gt;.&lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word._document.range(VS.80).aspx"&gt;Range(begin, end)&lt;/a&gt;.&lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word.range.select(VS.80).aspx"&gt;Select()&lt;/a&gt;), as they misconceive that Application.Selection just keeps on matching while they insert content to the end of the document. Even with Range.Select() there are potantial race conditions, as we will soon see... &lt;br /&gt;&lt;br /&gt;The problem here becomes obvious when taking a closer look at the object model: the Selection object is attached to the Application object, not to the Document object. If the application's active document changes, its selection will refer to a different document than before. &lt;br /&gt;&lt;br /&gt;Now it still seems to be save at first sight as long as the caller operates on one document only, but not even this is the case: If the user opens a Word document from his desktop in the meantime, this is not going to fork a new Word process, but the new document will be  attached to the running Word Interop process (hence the same Application). This Application has run window-less in the background so far, but now will pop up a Word window, where the user can watch in real-time how the Interop code suddenly manipulates the WRONG document, because from this moment on Application.Selection refers to the newly opened document.&lt;br /&gt;&lt;br /&gt;How come this simple fact is not mentioned in the API documentation? I have found several newsgroup postings on that issue, people are struggling with this. So what might be a possible solution? Working with the &lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word.range_members(VS.80).aspx"&gt;Range API&lt;/a&gt; on document ranges (e.g. &lt;a href="http://msdn2.microsoft.com/de-de/library/microsoft.office.interop.word._document.range(VS.80).aspx"&gt;Document.Range(begin, end)&lt;/a&gt;), instead of application selections.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;Hosting MS Office Inside .NET Windows Forms Applications&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/06/com-registration-in-net.html"&gt;COM Registration In .NET&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/dsoframer-now-compatible-to-office-2007.html"&gt;DSOFramer Now Compatible To Office 2007&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/com-interop-performance.html"&gt;COM Interop Performance&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-496985373104410862?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/496985373104410862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/496985373104410862'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/08/word-com-interop-problems-with.html' title='Word COM Interop: Problems With Application.Selection'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8105230886357608596</id><published>2007-08-23T23:11:00.000+02:00</published><updated>2007-10-15T20:37:05.601+02:00</updated><title type='text'>COM Interop Performance</title><content type='html'>&lt;a href="http://msdn2.microsoft.com/en-us/library/kew41ycz(VS.71).aspx"&gt;COM Interop&lt;/a&gt; method calls are slow. When you think about it, that does not come as a surprise. Managed parameter types need to be &lt;a href="http://msdn2.microsoft.com/en-us/library/04fy9ya1(vs.71).aspx"&gt;marshalled&lt;/a&gt; to unmanaged parameter types and vice versa. This hurts performance especially with plenty of calls to methods which only accomplish a tiny piece of work, like setting or getting property values. 99% percent of the time might be lost on call overhead then.&lt;br /&gt;&lt;br /&gt;There are more issues, e.g. incompatible threading models that may lead to message posting and thread context switching, memory fragmentation due to all the parameter marshalling effort, unnecessarily long-lived COM objects which are not disposed by the caller, but by the garbage collector much, much later. So when handled improperly, systems applying COM Interop not only tend to be slow, but also degrade over time regarding performance and system resources.&lt;br /&gt;&lt;br /&gt;Most of our COM Interop code is related to &lt;a href="http://msdn2.microsoft.com/en-us/office/default.aspx"&gt;MS Office integration&lt;/a&gt;, assemblying word documents (content and formatting) for example, this kind of stuff. We had to do quite some tuning to kind of reach a level of acceptable runtime behavior. Limiting the number of method calls and keeping references to COM objects (respectively their COM interop wrappers) for reuse was one step towards improvement, while explicitly disposing them as soon as not needed any more was another one. And never forget to invoke &lt;a href="http://msdn2.microsoft.com/fr-fr/library/microsoft.office.interop.word._application.quit(VS.80).aspx"&gt;Application.Quit()&lt;/a&gt; when done, unless no one cares for zombie Word processes sucking up system resources.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;Hosting MS Office Inside .NET Windows Forms Applications&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/06/com-registration-in-net.html"&gt;COM Registration In .NET&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/dsoframer-now-compatible-to-office-2007.html"&gt;DSOFramer Now Compatible To Office 2007&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/word-com-interop-problems-with.html"&gt;Word COM Interop: Problems With Application.Selection&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8105230886357608596?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8105230886357608596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8105230886357608596'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/08/com-interop-performance.html' title='COM Interop Performance'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7772173640506384811</id><published>2007-08-18T22:24:00.001+02:00</published><updated>2008-03-22T11:51:27.439+01:00</updated><title type='text'>Clippy For Software Architects</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/R1lzZIWnyuI/AAAAAAAAAF0/__3mSDJ7sRg/s1600-h/clippy.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/R1lzZIWnyuI/AAAAAAAAAF0/__3mSDJ7sRg/s320/clippy.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5141267325196421858" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7772173640506384811?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7772173640506384811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7772173640506384811'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/08/clippy-for-software-architects.html' title='Clippy For Software Architects'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/R1lzZIWnyuI/AAAAAAAAAF0/__3mSDJ7sRg/s72-c/clippy.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6071563828611618124</id><published>2007-08-12T22:45:00.000+02:00</published><updated>2007-08-19T22:54:36.851+02:00</updated><title type='text'>Handling The Bozo Invasion</title><content type='html'>Invaluable advice from &lt;a href="http://www.joelonsoftware.com/"&gt;Joel Spolsky&lt;/a&gt; in &lt;a href="http://www.joelonsoftware.com/articles/fog0000000332.html"&gt;"Getting Things Done When You're Only a Grunt"&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;Strategy 4: Neutralize The Bozos&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Even the best teams can have a bozo or two. The frustrating part about having bad programmers on your team is when their bad code breaks your good code, or good programmers have to spend time cleaning up after the bad programmers.&lt;br /&gt;&lt;br /&gt;As a grunt, your goal is damage-minimization, a.k.a. containment. At some point, one of these geniuses will spend two weeks writing a bit of code that is so unbelievably bad that it can never work. You're tempted to spend the fifteen minutes that it takes to rewrite the thing correctly from scratch. Resist the temptation. You've got a perfect opportunity to neutralize this moron for several months. Just keep reporting bugs against their code. They will have no choice but to keep slogging away at it for months until you can't find any more bugs. Those are months in which they can't do any damage anywhere else.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I agree for the scenario described above, that is if you have to deal with one or two bozos under your coworkers. There might be other situations, though. For example bozos placed in strategic positions, so they can highly influence your work. Or company growth based on hiring legions of morons. An invasion of bozos usually is a sign that an organization is doomed anyway. The path to downfall might be long and winding, and being doomed doesn't necessarily mean going out of business - just a slow and steady decline with failed projects paving the way. This is just inevitable. It's better to leave the sinking ship earlier than later in those cases.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6071563828611618124?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6071563828611618124'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6071563828611618124'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/08/handling-bozo-invasion.html' title='Handling The Bozo Invasion'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1150191339987479834</id><published>2007-08-08T08:05:00.000+02:00</published><updated>2007-10-15T20:37:24.860+02:00</updated><title type='text'>DSOFramer Now Compatible To Office 2007</title><content type='html'>Just a short follow-up to my posting about &lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;hosting MS Office inside .NET WinForms applications&lt;/a&gt;, &lt;a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B311765&amp;x=12&amp;y=7"&gt;Microsoft's DSOFramer&lt;/a&gt; is now available in an &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=CE2CA4FD-2169-4FAC-82AF-770AA9B60D77"&gt;Office2007-compatible version&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;Hosting MS Office Inside .NET Windows Forms Applications&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/06/com-registration-in-net.html"&gt;COM Registration In .NET&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/com-interop-performance.html"&gt;COM Interop Performance&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/word-com-interop-problems-with.html"&gt;Word COM Interop: Problems With Application.Selection&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1150191339987479834?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1150191339987479834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1150191339987479834'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/08/dsoframer-now-compatible-to-office-2007.html' title='DSOFramer Now Compatible To Office 2007'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-352179263594958732</id><published>2007-07-22T00:58:00.000+02:00</published><updated>2007-07-22T01:04:41.808+02:00</updated><title type='text'>My Computer Museum (Update 2)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/RqKPr7yfNFI/AAAAAAAAACE/Jj8-XeA3JN8/s1600-h/computermuseum.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/RqKPr7yfNFI/AAAAAAAAACE/Jj8-XeA3JN8/s320/computermuseum.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5089788513828680786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Main table (left to right)&lt;br /&gt;(1) Sun SparcStation 5, NEC 21" VGA Monitor&lt;br /&gt;(2) Commodore 128D, Commodore 1901 Color Monitor&lt;br /&gt;(3) Commodore Amiga 500, Commodore 1085S Color Monitor&lt;br /&gt;(4) Atari 1040ST, Atari SM124 Monochrome Monitor, Atari SC1224 Color Monitor&lt;br /&gt;(5) Apple Macintosh LC, Apple Monochrome Monitor&lt;br /&gt;(6) Commodore 64, Commodore 1541 Floppy Drive, Commodore 1081 Color Monitor&lt;br /&gt;&lt;br /&gt;Back cross table (left to right)&lt;br /&gt;(1) Commodore CBM8032&lt;br /&gt;(2) Apple IIc, Apple II Green Composite Monitor&lt;br /&gt;(3) Sinclair ZX81, B+W TV Set&lt;br /&gt;&lt;br /&gt;Middle cross table (left to right)&lt;br /&gt;(1) Atari 2600&lt;br /&gt;(2) Magnavox Odyssey 2&lt;br /&gt;(3) Atari PONG&lt;br /&gt;(4) Magnavox Odyssey 1&lt;br /&gt;&lt;br /&gt;Front cross table (left to right)&lt;br /&gt;(1) Apple Macintosh Plus&lt;br /&gt;(2) IBM PC 5150, IBM 5151 Green Composite Monitor&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-352179263594958732?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/352179263594958732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/352179263594958732'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/07/my-computer-museum-update-2.html' title='My Computer Museum (Update 2)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/RqKPr7yfNFI/AAAAAAAAACE/Jj8-XeA3JN8/s72-c/computermuseum.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6068238176338773244</id><published>2007-07-18T00:39:00.000+02:00</published><updated>2007-07-18T00:41:38.671+02:00</updated><title type='text'>Parkinson's Law</title><content type='html'>&lt;blockquote&gt;In project management, individual tasks with end dates rarely finish early because the people doing the work expand the work to finish approximately at the end date. Coupled with deferment or avoidance of an action or task to a later time, individual tasks are nearly guaranteed to be late.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;From: &lt;a href="http://en.wikipedia.org/wiki/Parkinson's_law"&gt;Parkinson's Law&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Very true, especially for software projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6068238176338773244?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6068238176338773244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6068238176338773244'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/07/parkinsons-law.html' title='Parkinson&apos;s Law'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6103087624374338542</id><published>2007-07-14T00:46:00.001+02:00</published><updated>2007-07-14T01:00:43.264+02:00</updated><title type='text'>Vintage Computer / Video Console Purchases On Ebay</title><content type='html'>My latest vintage computer / video console purchases on Ebay include:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/IBM_PC"&gt;IBM 5150 PC&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Apple_Macintosh_Plus"&gt;Apple Macintosh Plus&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Magnavox_Odyssey"&gt;Magnavox Odyssey 1&lt;/a&gt; (!)&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Magnavox_Odyssey_2"&gt;Magnavox Odyssey 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/PONG"&gt;Atari PONG&lt;/a&gt; (Sears edition)&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Atari_2600"&gt;Atari 2600&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;There were other auctions on rare products, e.g. an &lt;a href="http://en.wikipedia.org/wiki/Apple_Lisa"&gt;Apple Lisa&lt;/a&gt; (went away for USD 700) and a &lt;a aiotarget="false" aiotitle="NextStation" href="http://en.wikipedia.org/wiki/NeXTstation"&gt;NeXTStation&lt;/a&gt; (sold for USD 200 - man, I should have bought it!).&lt;br /&gt;&lt;br /&gt;I am kind of running out of table space in my cellar, that's why I cannot provide any actual images yet of the entire collection, so for the meantime there is this &lt;a href="http://arnosoftwaredev.blogspot.com/2005/04/my-computer-museum-update.html"&gt;photo of a previous state&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6103087624374338542?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6103087624374338542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6103087624374338542'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/07/vintage-computer-video-console.html' title='Vintage Computer / Video Console Purchases On Ebay'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6875106414104701719</id><published>2007-07-13T23:25:00.000+02:00</published><updated>2007-07-14T17:05:09.731+02:00</updated><title type='text'>My IBM 5150 Is Booting</title><content type='html'>The DOS 2.10 disks - which by the way I purchased in Australia - arrived today, so I tried to convince my &lt;a href="http://en.wikipedia.org/wiki/IBM_PC"&gt;IBM 5150&lt;/a&gt; to boot. Unfortunately it did not recognize the disk at first, and continued to jump to the built-in Basic interpreter. I had no possibility to check the disk on any other system. What to do next? I mean this thing doesn't even have a BIOS, it has... DIP switches on the motherboard! I should note that my IBM did not come with the two standard 5.25" floppy drives, but one floppy drive and one 10MB hard drive, so I figured there might be some misconfiguration. Luckily I still found &lt;a href="http://www.computer-oldies.de/co_ibmpc.htm"&gt;documentation on the 5150 DIP switch settings&lt;/a&gt;, and after flipping one switch I was able to boot.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 204, 0); font-weight: bold;font-family:courier new;" &gt;Current date is Tue 1-01-1980&lt;br /&gt;Enter new date:&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(51, 204, 0); font-weight: bold;font-family:courier new;" &gt;Current time is 0:00:12.89&lt;br /&gt;Enter new time:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(51, 204, 0); font-weight: bold;font-family:courier new;" &gt;The IBM Personal Computer DOS Version 2.10&lt;br /&gt;(C)Copyright IBM Corp 1981, 1982, 1983&lt;br /&gt;&lt;br /&gt;A&gt; _&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Next steps: Backup the only DOS 2.10 disk I have, and try to install DOS on the hard disk. Hard disks were supported from DOS 2.0 on - so this should work as well. Oh yeah, DOS 2.0 also introduced subdirectories...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6875106414104701719?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6875106414104701719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6875106414104701719'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/07/my-ibm-5150-is-booting.html' title='My IBM 5150 Is Booting'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5650192413471963157</id><published>2007-07-10T23:52:00.000+02:00</published><updated>2007-07-16T22:33:07.603+02:00</updated><title type='text'>Hosting .NET Winforms Applications In MSIE</title><content type='html'>I built a little .NET 2.0 Winforms frontend for my brute-force &lt;a href="http://arnosoftwaredev.blogspot.com/2007/01/sudoku-benchmarking-net-20-java-5-and.html"&gt;Sudoku Solver&lt;/a&gt;, and played around with hosting it inside MSIE. In case you are using MSIE and have .NET Framework 2.0 installed, you can give it a try &lt;a href="http://members.aon.at/arno.huetter/sudoku/sudoku.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This is how it looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/RpQHL9vgWSI/AAAAAAAAAB0/46_OdrL_1VA/s1600-h/sudoku1.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/RpQHL9vgWSI/AAAAAAAAAB0/46_OdrL_1VA/s320/sudoku1.gif" alt="" id="BLOGGER_PHOTO_ID_5085697781341903138" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Weird, isn't it? What is that, the Black and White Look &amp; Feel? When running as a standalone application (&lt;a href="http://members.liwest.at/arno.huetter/sudoku/sudoku.zip"&gt;download link&lt;/a&gt;), of course everything is just fine:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/RpQHS9vgWTI/AAAAAAAAAB8/SAa_EWoLf6k/s1600-h/sudoku2.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/RpQHS9vgWTI/AAAAAAAAAB8/SAa_EWoLf6k/s320/sudoku2.gif" alt="" id="BLOGGER_PHOTO_ID_5085697901600987442" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I have seen the same effect on other sites as well. Maybe that's Microsoft's way of visually marking MSIE-hosted .NET applications. Won't be too beneficial to the success of that approach I am afraid.&lt;br /&gt;&lt;br /&gt;BTW, the Sudoku puzzle you see here is somehow famous for its problem depth. I compared the 62ms it took the algorithm on my old 2.4GHz Athlon to find the solution with other brute force Sudoku solvers, and it came off quite well in comparison.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previos Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/just-another-sudoku-solver.html"&gt;Just Another Sudoku Solver&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/java-vs-net-vs-c-performance.html"&gt; Java Vs. .NET Vs. C++ Performance&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/just-another-sudoku-solver-c.html"&gt; Just Another Sudoku Solver (C++)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/01/sudoku-benchmarking-net-20-java-5-and.html"&gt; Sudoku Benchmarking / .NET 2.0, Java 5 and Java 6 Results&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5650192413471963157?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5650192413471963157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5650192413471963157'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/07/hosting-net-winforms-applications-in.html' title='Hosting .NET Winforms Applications In MSIE'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_G0oBN6-Lb48/RpQHL9vgWSI/AAAAAAAAAB0/46_OdrL_1VA/s72-c/sudoku1.gif' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-9062060733749674214</id><published>2007-06-29T22:17:00.002+02:00</published><updated>2011-11-04T21:09:40.973+01:00</updated><title type='text'>More Performance Tuning</title><content type='html'>I was occupied with some performance tuning this week on your .NET client/server solution, here are some of the conclusions I have drawn.&lt;br /&gt;&lt;br /&gt;One of the bottlenecks turned out to be caused by some missing database indices. This again &lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;reminds me&lt;/a&gt; that I have hardly experienced a case when there were too many indices, but plenty of times when there were too few. Foreign keys AND where-criteria attributes are primary candidates for indices, and I'd rather have a real good reason when leaving out an index on those columns. I know some folks will disagree, and point out the performance penalty for maintaining indices, and that database size will grow. While I agree those implications exist, they are negligible in comparison to queries that run a hundred times faster with an index at the right place.&lt;br /&gt;&lt;br /&gt;Also, the reasoning that relatively small tables don't need indices at all simply does not catch up with reality. While an index seek might not be much faster than a scan on a small number of rows, missing an index on a column that is being searched for also implies that there are no &lt;a href="http://www.sql-server-performance.com/statistics.asp"&gt;statistics&lt;/a&gt; attached to this column (unless statistics are manually created - and guess how many developers will do so), hence the &lt;a href="http://en.wikipedia.org/wiki/Query_optimizer"&gt;query optimizer&lt;/a&gt; might be wrong about that data distribution which can lead to ill-performing query plans. Also, the optimizer might make different decisions because it considers the fact that there is no index on certain columns.&lt;br /&gt;&lt;br /&gt;There are several ways to find out about missing indices: Running the &lt;a href="http://technet.microsoft.com/en-us/library/ms173494.aspx"&gt;Tuning Advisor&lt;/a&gt;, &lt;a href="http://www.sql-server-performance.com/query_execution_plan_analysis.asp"&gt;checking execution plans&lt;/a&gt; for anomalies, and of course some excellent tool scripts from &lt;a href="http://sqlservercentral.com/scripts/"&gt;SqlServerCentral&lt;/a&gt; and from &lt;a href="http://www.microsoft.com/technet/scriptcenter/scripts/sql/sql2005/default.mspx?mfr=true"&gt;Microsoft&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A query construct that turns out problematic at times under SqlServer looks something like &lt;br /&gt;&lt;br /&gt;&lt;code&gt;where (table.attribute = @param or @param is null)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;While these expressions help to keep SQL statements simple when applying optional parameters, they can confuse the query optimizer. Think about it - with @param holding a value, the optimizer has a restraining criteria at hand, but with @param being null it doesn't. When the optimizer re-uses an execution plan previously compiled for the same statement, trouble is around the corner. This is specifically true for stored procedures. Invoking stored procedures &lt;a href="http://www.devx.com/tips/Tip/13386"&gt;"with recompile"&lt;/a&gt; usually solves this issue. But this may happen within client-generated SQL-code as well. Options then are &lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/2005/frcqupln.mspx"&gt;forcing an explicit execution plan for the query&lt;/a&gt; (tedious, plus if you still have to support SqlServer 2000 you are out of luck), using &lt;a href="http://msdn2.microsoft.com/en-us/library/ms173815.aspx"&gt;join hints&lt;/a&gt;, or finally re-phrasing the query (in one case we moved some criteria from the where- to the join-clause, which led the optimizer back to the right path again - also additional criterias might help).&lt;br /&gt;&lt;br /&gt;By the way I can only emphasize the importance of mass data tests and of &lt;a href="http://technet.microsoft.com/en-us/library/ms187929.aspx"&gt;database profiling&lt;/a&gt;. I don't want my customers to find out about my performance problems, and I want to know what is being shoveled hence and forth my database at any time.&lt;br /&gt;&lt;br /&gt;I was also investigating another performance problem that appeared when reloading a certain set of records with plenty of child data. Convinced of just having to profile for the slowest SQL, I was surprised that they all performed well. In reality, the time got lost within the UI, with too much refreshing going on (some over-eagerly implemented toolbar wrapping code which re-instantiated a complete &lt;a href="http://www.infragistics.com/dotnet/netadvantage.aspx"&gt;Infragistics Toolbar Manager&lt;/a&gt; being the reason - external code, not ours).&lt;br /&gt;&lt;br /&gt;I enjoy runtime tuning. A clear goal, followed through all obstacles, and at the end watching the same application running faster multiple times - those are the best moments for a developer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-9062060733749674214?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/9062060733749674214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/9062060733749674214'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/more-performance-tuning.html' title='More Performance Tuning'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1960746145216653457</id><published>2007-06-23T12:08:00.000+02:00</published><updated>2007-06-25T08:22:19.758+02:00</updated><title type='text'>Pure Ignorance Or The Not-Invented-Here Syndrome?</title><content type='html'>I just finished listening to &lt;a href="http://dotnetrocks.com/default.aspx?showID=236"&gt;this .NET Rocks episode&lt;/a&gt; with guest Jeff Atwood from &lt;a href="http://www.codinghorror.com/blog/"&gt;Coding Horror&lt;/a&gt; fame. I can really identify with a lot of what he says and what he propagates on his blog. One topic was brought up by Richard Campbell I think, when he mentioned that nothing else hurts him more than watching a 60K or 80K developer  working on problems for months or years (not to mention whether successfully or not), when there are existing solutions available either free in terms of open source projects, or as commerical products priced at some hundred bucks. Jeff wholeheartedly agreed.&lt;br /&gt;&lt;br /&gt;They hit the nail on the head. I don't know whether it's pure ignorance or just the infamous &lt;a aiotarget="false" aiotitle="Not-Invented-Here syndrome" href="http://en.wikipedia.org/wiki/Not_Invented_Here"&gt;Not-Invented-Here Syndrome&lt;/a&gt;, but this just seems to happen again and again. For my part, I blame decision makers approving budgets for such projects just as much as developers who try to solve problems that have been solved a hundred times better a hundred times before.&lt;br /&gt;&lt;br /&gt;Here are some examples:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Why not implement my own Database Diff tool? See &lt;a href="http://www.red-gate.com/products/SQL_Compare/index.htm"&gt;RedGate SQL Compare&lt;/a&gt; and &lt;a href="http://www.red-gate.com/products/SQL_Data_Compare/index.htm"&gt;SQL data Compare&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Why not implement my own O/R mapper? See &lt;a aiotarget="false" aiotitle="Hibernate" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Why not implement my own XML parser? See &lt;a href="http://xerces.apache.org/xerces-j/"&gt;&lt;span style="text-decoration: underline;"&gt;Apache Xerces&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Why not implement my own XML binding? See &lt;a href="http://java.sun.com/developer/technicalArticles/WebServices/jaxb/"&gt;JAXB&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Why not implement my own HTTP protocol? See package sun.net.www.http or &lt;a href="http://jakarta.apache.org/commons/httpclient/"&gt;Jakarta Commons HttpClient&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Why not implement my own Java Enterprise Framework? See &lt;a href="http://www.springframework.org/"&gt;Spring Framework&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1960746145216653457?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1960746145216653457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1960746145216653457'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/pure-ignorance-or-not-invented-here.html' title='Pure Ignorance Or The Not-Invented-Here Syndrome?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1607945224014992786</id><published>2007-06-22T01:35:00.000+02:00</published><updated>2007-06-23T13:51:17.864+02:00</updated><title type='text'>Bidding For An Atari Pong Videogame</title><content type='html'>Please don't tell anybody, but I am currently bidding for an &lt;a href="http://en.wikipedia.org/wiki/Pong"&gt;Atari Pong videogame&lt;/a&gt; (the so called Sears edition) on EBay.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_G0oBN6-Lb48/RnwmSXk4O0I/AAAAAAAAABc/X1pGcgVLpIg/s1600-h/pong.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/RnwmSXk4O0I/AAAAAAAAABc/X1pGcgVLpIg/s320/pong.jpg" alt="" id="BLOGGER_PHOTO_ID_5078976576775273282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_G0oBN6-Lb48/Rnwoj3k4O1I/AAAAAAAAABk/COq8f_eWR7E/s1600-h/pong2.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/Rnwoj3k4O1I/AAAAAAAAABk/COq8f_eWR7E/s320/pong2.gif" alt="" id="BLOGGER_PHOTO_ID_5078979076446239570" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1607945224014992786?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1607945224014992786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1607945224014992786'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/bidding-for-atari-pong-videogame.html' title='Bidding For An Atari Pong Videogame'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_G0oBN6-Lb48/RnwmSXk4O0I/AAAAAAAAABc/X1pGcgVLpIg/s72-c/pong.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6916773731530261434</id><published>2007-06-21T21:01:00.000+02:00</published><updated>2007-10-15T20:36:08.427+02:00</updated><title type='text'>COM Registration In .NET</title><content type='html'>Just a short follow-up to my posting about &lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;Hosting MS Office inside .NET WinForms applications&lt;/a&gt;: In case there is the need to install &lt;a href="http://en.wikipedia.org/wiki/ActiveX"&gt;ActiveX controls&lt;/a&gt; like &lt;a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B311765&amp;x=24&amp;amp;y=10"&gt;DSOFramer&lt;/a&gt; or other &lt;a href="http://en.wikipedia.org/wiki/Component_object_model"&gt;COM components&lt;/a&gt;, but no Installer that could do so (e.g. because of &lt;a href="http://msdn2.microsoft.com/en-us/library/ms235291%28VS.80%29.aspx"&gt;XCopy deployment&lt;/a&gt;), &lt;a href="http://vbusers.com/codecsharp/codeget.asp?PostID=1&amp;amp;ThreadID=59"&gt;this sample code&lt;/a&gt; shows how to accomplish on-the-fly COM registration in .NET.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/12/hosting-ms-office-inside-net-windows.html"&gt;Hosting MS Office Inside .NET Windows Forms Applications&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/dsoframer-now-compatible-to-office-2007.html"&gt;DSOFramer Now Compatible To Office 2007&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/com-interop-performance.html"&gt;COM Interop Performance&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/08/word-com-interop-problems-with.html"&gt;Word COM Interop: Problems With Application.Selection&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6916773731530261434?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6916773731530261434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6916773731530261434'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/com-registration-in-net.html' title='COM Registration In .NET'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-2047707459570598215</id><published>2007-06-20T21:27:00.000+02:00</published><updated>2007-06-23T13:50:52.506+02:00</updated><title type='text'>EBay'ed An IBM 5150</title><content type='html'>After weeks of trying I finally managed to buy an &lt;a href="http://en.wikipedia.org/wiki/IBM_PC"&gt;IBM 5150&lt;/a&gt; on EBay (yes, that's the first IBM PC). EUR 100 plus EUR 70 for shipping - but hey, it's worth the price.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/RnwkFnk4OzI/AAAAAAAAABU/SQ6442oKiUE/s1600-h/ibm5150.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/RnwkFnk4OzI/AAAAAAAAABU/SQ6442oKiUE/s320/ibm5150.jpg" alt="" id="BLOGGER_PHOTO_ID_5078974158708685618" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I found original IBM DOS 2.0 disks as well, which complete the whole purchase (IBM DOS 1.1 or 1.0 would have been even better, but they are also more rare).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-2047707459570598215?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2047707459570598215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/2047707459570598215'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/ebayed-ibm-5150.html' title='EBay&apos;ed An IBM 5150'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/RnwkFnk4OzI/AAAAAAAAABU/SQ6442oKiUE/s72-c/ibm5150.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5433548312969774132</id><published>2007-06-15T21:37:00.000+02:00</published><updated>2007-06-18T09:36:38.250+02:00</updated><title type='text'>In Defense Of Data Transfer Objects</title><content type='html'>In the &lt;a href="http://java.sun.com/javaee/"&gt;J2EE&lt;/a&gt; world, &lt;a href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html"&gt;Data Transfer Objects&lt;/a&gt; have been branded as bad design or even as an anti-pattern by several Enterprise Java luminiaries. Their bad image also stems back to the time when DTOs were a must at &lt;a href="http://en.wikipedia.org/wiki/EJB_container"&gt;EJB Container&lt;/a&gt; boundaries, as many developers ended up using &lt;a href="http://en.wikipedia.org/wiki/Stateless_Session_Beans"&gt;Session EJBs&lt;/a&gt; at the middle tier which received/passed DTOs from/to the client, tediously mapping them to &lt;a href="http://en.wikipedia.org/wiki/Entity_bean"&gt;Entity Beans&lt;/a&gt; internally.&lt;br /&gt;&lt;br /&gt;Then came &lt;a href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt;, which alongside its excellent O/R-mapping capabilities allowed for passing entities through all tiers, attached in case of a &lt;a href="http://www.hibernate.org/43.html"&gt;Open-Session-In-View scenario&lt;/a&gt;, &lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html#objectstate-detached"&gt;detached&lt;/a&gt; in case of a loosely coupled service layer. So no need of DTOs and that cumbersome mapping approach anymore, this seems to be the widely accepted opinion.&lt;br /&gt;&lt;br /&gt;And it's true, DTOs might be a bad idea in many, maybe even in most of the cases. E.g. it doesn't not make a lot of sense to map Hibernate POJOs to DTOs when POJO and DTO classes would just look the same.&lt;br /&gt;&lt;br /&gt;But what if internal and external domain models would differ? One probably does not want to propagate certain internal attributes to the client, because they only matter inside the business layer. Or some attributes just must be sliced off for certain services, because they are of no interest in their context.&lt;br /&gt;&lt;br /&gt;What if a system had been designed with a physical separation between web and middle tier (e.g. due to security and scalability reasons)? An EJB container hosting stateless session beans is still first class citizens in this case. Other services might be published as webservices. It's problematic to transfer Hibernate-specific classes over &lt;a href="http://en.wikipedia.org/wiki/RMI-IIOP"&gt;RMI/IIOP&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt;. Even if it's possible (as it is the case under RMI/IIOP) this necessarily makes the client Hibernate-aware.&lt;br /&gt;&lt;br /&gt;While it is true that Hibernate (and as well &lt;a href="http://java.sun.com/products/ejb/docs.html"&gt;EJB3&lt;/a&gt; resp. the &lt;a href="http://java.sun.com/developer/technicalArticles/J2EE/jpa/"&gt;Java Persistence API&lt;/a&gt;) are based on lightweight POJOs for entities, Hibernate has to inject it's own collection classes (&lt;a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/collection/PersistentSet.html"&gt;PersistentSet&lt;/a&gt;) and &lt;a href="http://cglib.sourceforge.net/"&gt;CGLib&lt;/a&gt;-generated entity subclasses. That's logical due the nature of O/R-mapping, but having these classes being transferred over service layer boundaries is not a great thing to happen. And there are more little pitfalls, for example state management on detached entities - how can an object be marked for deletion when it is detached from it's &lt;a href="http://www.hibernate.org/42.html"&gt;Hibernate session&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;Sorry, but I have to stand up and defend DTOs for these scenarios. Don't get me wrong, I appreciate Hibernate a lot and use it thoroughly within the middle tier, but I also don't see the problem of mapping Hibernate POJOs to DTOs on external service boundaries, especially when done in a non-invasive way. No mapping code has to pollute the business logic, no hardwiring is necessary, it can all be achieved by applying mapping frameworks like &lt;a href="http://dozer.sourceforge.net/documentation/whymap.html"&gt;Dozer&lt;/a&gt;, using predefined mapping configurations. What goes over the wire at runtime is exactly the same as declared at compiletime, a clear &lt;a href="http://en.wikipedia.org/wiki/Design_by_contract"&gt;service contract&lt;/a&gt;, no obsolete entity attributes, no object trees without knowing where the boundaries are, and no surprising &lt;a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/LazyInitializationException.html"&gt;LazyInitializationExceptions&lt;/a&gt; on the client.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5433548312969774132?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5433548312969774132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5433548312969774132'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/in-defense-of-data-transfer-objects.html' title='In Defense Of Data Transfer Objects'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4353043012878442254</id><published>2007-06-04T10:02:00.000+02:00</published><updated>2007-06-09T12:38:08.163+02:00</updated><title type='text'>Learning From Experience (Introduction)</title><content type='html'>Last weekend I digged out an old university project of mine - &lt;a href="http://www.weirdoz.org/visualchat"&gt;Visual Chat&lt;/a&gt;, a graphical Java chat system that I built for a class assignment exactly a decade ago. I was allowed to open-source Visual Chat back then, and I still receive questions from people today who are working on extending the application. Back in 1997 I was clearly still an apprentice, as this was one of my first Java resp. OOP projects at all. When I look at the sourcecode today, I remember what I learned in the process of developing it, but I also see I was still missing a lot of experience.&lt;br /&gt;&lt;br /&gt;Learning not only in theory but also from real projects is particularly important in software development, and this is something that - in my opinion - is done far too little during education. Designing a system at a larger scale is a completely different task than solving isolated algorithmic problems (which is what you normally get to do for homework). &lt;br /&gt;&lt;br /&gt;Class assignments that simulate real projects are great because one has the freedom to make mistakes and learn from them, much more than in professional life. Around 500.000 people have signed up at the Visual Chat test installation site so far, so I could learn a lot from monitoring what was going on at runtime. The worst thing that might have happened was that people would not like my chat program and would switch to another one (as I am sure, many did). There was no danger of financial losses (or worse) for any user in case of an application mistake. I am glad I had this opportunity - it helped me to build better products once the chips were down at work, e.g. when developing banking or hospital information systems.&lt;br /&gt;&lt;br /&gt;Receiving a solid training is important in our profession, but only as long as it is accompanied by applying what one has learned in classroom. Half-baked knowledge is dangerous, and it often takes two or three tries to make it right. Only a small fraction of people is able to do a perfect job from the beginning (and even a genius like Linus Torvalds openly admits that he also wrote some ugly code once upon a time).&lt;br /&gt;&lt;br /&gt;So I decided to do two things. I am going to provide a little series of what I learned back then, and since then (looking at the code today), hoping that novices reading those articles can benefit by taking this path as a short-cut instead of walking through the same of experience on their own (which would be far more time-consuming). And I will do some refactoring on the old code, so everyone who decided to continue development work on Visual Chat will be able to take advantage of that as well.&lt;br /&gt;&lt;br /&gt;Here is my topic list at this time of writing:&lt;br /&gt;&lt;br /&gt;1. OOP Design - On lose coupling, the sense of interfaces and the concept of Singletons&lt;br /&gt;2. Multithreading on the server - Gaining stability and performance through queuing&lt;br /&gt;3. Multithreading on the client - The secrets of the AWT EventDispatch thread, EventQueues and repainting&lt;br /&gt;4. How not to serialize a java.awt.Image&lt;br /&gt;5. What are asynchronous Sockets (and why are they not supported in JDK1.1)?&lt;br /&gt;6. Conclusions&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4353043012878442254?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4353043012878442254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4353043012878442254'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/06/learning-from-experience.html' title='Learning From Experience (Introduction)'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1112484907527149235</id><published>2007-05-31T19:10:00.000+02:00</published><updated>2007-06-02T18:13:32.085+02:00</updated><title type='text'>Bill Gates And Steve Jobs Interview</title><content type='html'>&lt;a href="http://d5.allthingsd.com/20070530/d5-gates-jobs-interview/"&gt;This recent interview&lt;/a&gt; is really a must see - I think they did not have that much fun since the &lt;a href="http://www.youtube.com/watch?v=Uau0aIbrzkQ"&gt;Macintosh Dating Game in 1984&lt;/a&gt; and the &lt;a href="http://www.youtube.com/watch?v=WxOp5mBY9IY&amp;mode=related&amp;search="&gt;Macworld Expo Keynote in 1997&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Uau0aIbrzkQ"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Uau0aIbrzkQ" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/WxOp5mBY9IY"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/WxOp5mBY9IY" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Not to forget &lt;a href="http://www.youtube.com/watch?v=cAZ7yzjL1Sg&amp;mode=related&amp;search="&gt;these previously unknown scenes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;BTW, I highly recommend the movie &lt;a href="http://www.amazon.com/dp/B0009NSCS0/"&gt;"Pirates Of Silicon Valley"&lt;/a&gt;, which - as far as I can tell - provides a quite accurate picture of the PC revolution, plus it's very entertaining. Years ago I bought both the english and the german movie version, still back on VHS. I must have watched it a dozen times since then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1112484907527149235?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1112484907527149235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1112484907527149235'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/05/bill-gates-and-steve-jobs-interview.html' title='Bill Gates And Steve Jobs Interview'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-5732414758408222734</id><published>2007-05-15T00:46:00.000+02:00</published><updated>2008-12-22T22:11:16.746+01:00</updated><title type='text'>Hints And Pitfalls In Database Development (Part 4): Do Not String-Concatenate SQL Parameter Values</title><content type='html'>Once again, this should be obvious. Unfortunately there are still plenty of articles and books out there with code examples that look something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;string sql = "select * from orders where orderdate = '" + myDate.ToShortDateString() + "'";&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I don't know why this is the case. Most authors are well aware of the problems that might arise, and actually comment that this should not be done in real-life projects, but guess what - it will end up in real-life projects if examples like that are floating around.&lt;br /&gt;&lt;br /&gt;So it's not really surprising people keep on writing this kind of statements. There are many reasons why this is just plain wrong. &lt;br /&gt;&lt;br /&gt;Instead of concatenating parameter values, it's much better to use parameter placeholders, e.g.:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;string sql = "select * from orders where orderdate = @myDate";&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The placeholder syntax might be varying and depends on whether we are talking about the JDBC or ODBC / OLEDB / ADO.NET world (under OLEDB the syntax is defined by the underlying database specific driver).&lt;br /&gt;&lt;br /&gt;The database API then provides some functionality to pass the actual parameter values , something like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;SqlCommand cmd = new SqlCommand();&lt;br /&gt;cmd.CommandText = sql;&lt;br /&gt;cmd.Parameters.Add("@myDate", myDate);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This has several advantages:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Avoiding SQL injection:&lt;/span&gt; No need to worry that any user will enter something like "'; drop database;" into an input field. The database vendor's driver is going to take care of that.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Taking advantage of prepared statements:&lt;/span&gt; As the SQL code always stays the same, and only parameter values change on consecutive invocations, the statement can be precompiled, which guarantees better performance.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Independence from database language settings:&lt;/span&gt; Expressions like myDate.ToShortDateString() produce language-specific formats. If your customer's database was set up with another language setting, or this code is being executed from a client with different language settings, you are out of luck.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Improved readability:&lt;/span&gt; SQL code can be defined in one piece at one place, maybe inside XML, without the need of cluttering it with of string-concatenation. Often those statements can just be copied and executed 1:1 within a database query tool for testing purposes. All that is necessary is to provide the parameters and set their values manually.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;Missing Indices&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database.html"&gt;Let The Database Enforce Data Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database_29.html"&gt; Database Programming Requires More Than SQL Knowledge&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html"&gt; The Importance Of Choosing The Right Clustered Index&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-5732414758408222734?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5732414758408222734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/5732414758408222734'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/05/hints-and-pitfalls-in-database_5584.html' title='Hints And Pitfalls In Database Development (Part 4): Do Not String-Concatenate SQL Parameter Values'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7943259013782516149</id><published>2007-04-29T00:45:00.000+02:00</published><updated>2007-10-30T00:50:40.769+01:00</updated><title type='text'>Hints And Pitfalls In Database Development (Part 3): Database Programming Requires More Than SQL Knowledge</title><content type='html'>First of all, "knowing SQL" is a rather broad term. SQL basics can be taught within a day, but gaining real in-depth SQL know-how, including all kind of database-specific extensions, might take years of practice.&lt;br /&gt;&lt;br /&gt;Then there are always plenty of ways of how to build a solution in SQL, and only a small subset of those are really good ones. It is important to be aware of the implications of certain SQL constructs - which kind of actions the database engine has to undertake in order to fulfill a given task.&lt;br /&gt;&lt;br /&gt;Let me provide a little checklist - those are things every database programmer should know about in my opinion:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ANSI SQL and vendor-specific additions (syntax, functions, procedural extensions, etc). What can be done in SQL, and when should it be done like that.&lt;/li&gt;&lt;li&gt;Database basics (ACID, transactions, locking, indexing, stored procedures, triggers, and so on).&lt;/li&gt;&lt;li&gt;Database design (normalization plus a dose of pragmatism, referential integrity, indices, table constraints, stuff like that).&lt;/li&gt;&lt;li&gt;Internal functioning (for instance B-trees, transaction logs, temp databases, caching, statistics, execution plans, prepared statements, file structure and requirements for physical media, clustering, mirroring, and so on).&lt;/li&gt;&lt;li&gt;How do certain tasks impact I/O, memory and CPU usage.&lt;/li&gt;&lt;li&gt;Query optimizer: what it can do, and what it can't do.&lt;/li&gt;&lt;li&gt;Error handling, security (for example how to avoid SQL injection, ...).&lt;/li&gt;&lt;li&gt;Database tools: profiling, index tuning, maintenance plans (e.g. backup and reindexing), server monitoring.&lt;/li&gt;&lt;li&gt;Interpretation of execution plans.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;Missing Indices&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database.html"&gt;Let The Database Enforce Data Constraints&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/05/hints-and-pitfalls-in-database_5584.html"&gt; Do Not String-Concatenate SQL Parameter Values&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html"&gt; The Importance Of Choosing The Right Clustered Index&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7943259013782516149?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7943259013782516149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7943259013782516149'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database_29.html' title='Hints And Pitfalls In Database Development (Part 3): Database Programming Requires More Than SQL Knowledge'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1364154923835935483</id><published>2007-04-05T11:17:00.000+02:00</published><updated>2007-10-30T00:49:59.714+01:00</updated><title type='text'>Hints And Pitfalls In Database Development (Part 2): Let The Database Enforce Data Constraints</title><content type='html'>Letting the database enforce data constraints might sound obvious, but as a matter of fact I had to listen to some people just a few months ago who advocated checking for referential integrity within business code instead of letting the database do so, because "foreign keys are slow" - I mean geez, what kind of nonsense is that?! I don't know, they probably forgot to define their primary keys as well, hence didn't have any index for fast lookup (*sarcasm*).&lt;br /&gt;&lt;br /&gt;No other entity is better suited to safeguard &lt;a href="http://en.wikipedia.org/wiki/Data_integrity"&gt;data integrity&lt;/a&gt; than the database. It's closest to the data, it knows most about the data, and it's the single point every chunk of data has to pass through before being persisted. &lt;br /&gt;&lt;br /&gt;And I am not only talking about primary keys and foreign key constraints. I put all kinds of checks on my databases, for example &lt;a href="http://en.wikipedia.org/wiki/Index_%28database%29"&gt;unique compound indices&lt;/a&gt; whenever I know that a certain combination of column values can only occur once. Or &lt;a href="http://en.wikipedia.org/wiki/Check_Constraint"&gt;table constraints&lt;/a&gt; that enforce certain rules on the data which are being checked by the database on each insert and update. &lt;a href="http://en.wikipedia.org/wiki/Database_trigger"&gt;Triggers&lt;/a&gt; are another possibility, but care must be taken - they might be slower than table constraints.&lt;br /&gt;&lt;br /&gt;Setting up constraints usually is quite easy, easier than implementing the same rules in business code. Of course performance matters, and it is important to balance costs and benefits. I do not recommend to code complex SQL-statements within constraint checks - they should be limited to simple logical expressions.&lt;br /&gt;&lt;br /&gt;During application development, the database will scream immediately when data integrity rules are being violated. In the unlikely case that such a programming mistake slips through into production code, the customer will be confronted with some error message, but his data is unendangered, any you will find out about it immediately and can provide a fix at no time - not months or years later, when data corruption would have occurred, which then might have been impossible to repair. This has saved us on several occasions. &lt;br /&gt;&lt;br /&gt;Plus you never know if other clients are going to be manipulating data in the future, clients that might not be aware of the existence of those data rules. Data normally lives longer than application code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previous Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html"&gt;Missing Indices&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database_29.html"&gt; Database Programming Requires More Than SQL Knowledge&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/05/hints-and-pitfalls-in-database_5584.html"&gt; Do Not String-Concatenate SQL Parameter Values&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html"&gt; The Importance Of Choosing The Right Clustered Index&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1364154923835935483?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1364154923835935483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1364154923835935483'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database.html' title='Hints And Pitfalls In Database Development (Part 2): Let The Database Enforce Data Constraints'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-4086556172159016108</id><published>2007-03-19T20:43:00.001+01:00</published><updated>2011-02-20T22:30:46.586+01:00</updated><title type='text'>Hints And Pitfalls In Database Development (Part 1): Missing Indices</title><content type='html'>I have been involved in a lot of database tuning lately. This has partly to do with the fact that some of our databases simply outgrew the originally anticipated data dimensions. Furthermore I sometimes have to deal with application modules from "external contributors".&lt;br /&gt;&lt;br /&gt;So I am trying to sum up some of my experiences on database development over the years in a little mini-series. Most of those things should be quite obvious to many developers, others might be piece of news for one or the other. I will fall back into some &lt;a href="http://www.microsoft.com/sql"&gt;SqlServer&lt;/a&gt; terminology at times, but many principles should apply to any major database.&lt;br /&gt;&lt;br /&gt;So let me start with the number one reason for bad database performance:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Missing Indices&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;It's just amazing how creating indices tends to be forgotten again and again. I don't know, it might have to do with the fact that on 4th dimension platforms like &lt;a href="http://office.microsoft.com/access"&gt;Microsoft Access&lt;/a&gt;, where a lot of folks come from, they did not have to worry too much about indexing. Databases were kind of smaller there, and the database schema wizard most likely made any foreign key an index by default anyway. &lt;br /&gt;&lt;br /&gt;I often hear the argument that "indices are going to slow down our updates". Well I don't know what is worse, slowing down updates by a factor of 0.1, or slowing down queries a factor of 100. The numbers vary of course. And it is true, indices that will not be applied by the &lt;a href="http://en.wikipedia.org/wiki/Query_optimizer"&gt;query optimizer&lt;/a&gt; hence won't lead to any performance gain should not be introduced in the first place. The best way to find out is to have a look at the &lt;a href="http://en.wikipedia.org/wiki/Query_plan"&gt;query execution plans&lt;/a&gt;, and see if the indices are being actually being used.&lt;br /&gt;&lt;br /&gt;So in general, foreign keys and other query criteria fields are candidates for indices, with the exception of attributes with small value ranges like booleans or a tiny set of numbers, large varchar columns and tables with a small number of rows. &lt;br /&gt;&lt;br /&gt;But sometimes even "golden rules" of when to create or not to create indices can turn out to be wrong. One of those rules is to avoid indices on columns with sparse value distribution, like a status attribute with 5 different values. But I experienced a case when the query optimizer made completely wrong assumptions about row counts for one of those status values. I added an index on this column, which implicitly induced statistic maintenance, and that in turn helped the optimizer to make the right decisions.&lt;br /&gt;&lt;br /&gt;That being said, it is undoubtedly important to avoid excessive index creation, as this can lead to performance penalties during inserts, updates and deletes.&lt;br /&gt;&lt;br /&gt;In addition, composite indices can lead to major speedups as well, when applied to tuples of columns which are typically jointly queried (note: leftmost index columns must all appear in a query for the index to be applied). &lt;br /&gt;&lt;br /&gt;The good news is: you are not alone. There are plenty of tools which help finding a good indexing strategy. I start up SqlServer's &lt;a href="http://msdn2.microsoft.com/en-us/library/ms173494.aspx"&gt;Database Engine Tuning Advisor&lt;/a&gt; (formerly known as Index Tuning Wizard) quite frequently, and in addition I have several &lt;a href="http://www.sqlservercentral.com/scripts/"&gt;schema check scripts&lt;/a&gt; at hand which - among other things - look for missing indices, e.g. on foreign key columns.&lt;br /&gt;&lt;br /&gt;And: &lt;span style="font-weight:bold;"&gt;Indices must be rebuilt frequently&lt;/span&gt;. This serves two purposes: (1) The index nodes then have a fill factor for optimal balance between read and write performance, and (2) Index rebuilding updates index statistics as well. Outdated statistics can be a major cause for slow query performance.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database.html"&gt;Let The Database Enforce Data Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/04/hints-and-pitfalls-in-database_29.html"&gt; Database Programming Requires More Than SQL Knowledge&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/05/hints-and-pitfalls-in-database_5584.html"&gt; Do Not String-Concatenate SQL Parameter Values&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/10/hints-and-pitfalls-in-database.html"&gt; The Importance Of Choosing The Right Clustered Index&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-4086556172159016108?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4086556172159016108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/4086556172159016108'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/03/hints-and-pitfalls-in-database.html' title='Hints And Pitfalls In Database Development (Part 1): Missing Indices'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-7543317941147265201</id><published>2007-03-13T20:50:00.000+01:00</published><updated>2007-05-28T21:42:06.504+02:00</updated><title type='text'>Automatic CompSci Research Paper Generator</title><content type='html'>&lt;a href="http://pdos.csail.mit.edu/scigen/"&gt;SCIgen&lt;/a&gt; is a program that generates random Computer Science research papers, including graphs, figures, and citations. While the content is completely gibberish, this did not stop SCIgen's creators from &lt;a href="http://pdos.csail.mit.edu/scigen/#talks"&gt;submitting to and presenting the generator's work at conferences&lt;/a&gt;. Conferences with such low submission standards that they accepted the papers.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_G0oBN6-Lb48/RfcCzjso_qI/AAAAAAAAABA/EDyNrNaZJ90/s1600-h/rooter.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/RfcCzjso_qI/AAAAAAAAABA/EDyNrNaZJ90/s320/rooter.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5041501392643620514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Those folks did not even bother to take a look at the auto-generated slides before giving their talk, they just went along - absolutely priceless!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-7543317941147265201?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7543317941147265201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/7543317941147265201'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/03/automatic-cs-paper-generator.html' title='Automatic CompSci Research Paper Generator'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_G0oBN6-Lb48/RfcCzjso_qI/AAAAAAAAABA/EDyNrNaZJ90/s72-c/rooter.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6114816005864887408</id><published>2007-03-05T22:49:00.000+01:00</published><updated>2007-05-28T21:41:58.834+02:00</updated><title type='text'>Windows For Warships 3.11</title><content type='html'>News of the day: British Royal Navy T45 destroyers &lt;a href="http://www.theregister.co.uk/2007/02/26/windows_boxes_at_sea/"&gt;to be equipped with Windows 2000&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_G0oBN6-Lb48/ReyRjm_BHFI/AAAAAAAAAAw/0HmuyE_CEBo/s1600-h/wfw.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_G0oBN6-Lb48/ReyRjm_BHFI/AAAAAAAAAAw/0HmuyE_CEBo/s320/wfw.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5038562124067052626" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;From the text:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Windows platforms may be troublesome to maintain, but most civilian sysadmins simply wouldn't believe the resources the navy can throw at problems. A present-day Type 42 destroyer carries at least four people who have absolutely nothing else to do but care for the ship's command system. As of just a few years ago, this was still a pair of antique 24-bit, 1MHz machines each with about 25KB of RAM.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;There may also be perfectly valid criticisms to be made regarding Windows usability. When triggering missile decoys with seconds to spare, one doesn't need a superfluous pop-up box saying "Do you want to use soft kill?" with "Confirm" and "Cancel" buttons. But this kind of thing isn't going to faze you if you're used to entering instruction sets such as "PE L5414.10N L00335.67E R6000 TMDA [INJECT]" from memory without backspace or delete. During combat, mind. The one group of users to whom Windows 2000 might look pretty marvellous are RN warfare operators.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6114816005864887408?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6114816005864887408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6114816005864887408'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/03/windows-for-warships-311.html' title='Windows For Warships 3.11'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_G0oBN6-Lb48/ReyRjm_BHFI/AAAAAAAAAAw/0HmuyE_CEBo/s72-c/wfw.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-6866308694123357552</id><published>2007-03-02T00:53:00.000+01:00</published><updated>2007-05-28T21:41:36.067+02:00</updated><title type='text'>Why Great Coders Get Paid Far Too Little</title><content type='html'>&lt;a href="http://codecraft.info/"&gt;Kevin Barnes&lt;/a&gt; picks up a similar topic &lt;a href="http://arnosoftwaredev.blogspot.com/2007/02/why-cant-programmers-program.html"&gt;I was talking about the other day&lt;/a&gt; - the question &lt;a href="http://codecraft.info/index.php/archives/78/"&gt;why great coders get paid far too little&lt;/a&gt;. Very insightful posting, just as some statements from the comments section. Let me quote:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Also, the industry keeps complaining about a labor shortage, while employees complain about a job shortage. I finally got to the bottom of this. Industry is finding a shortage of good people, whereas crappy people can’t get jobs. Universities, with dropping enrollment in engineering programs, are lowering standards so that more people can train to be engineers. Poor engineers enter the job market, can't get jobs, and when they do, they lower the overall pay range. As a result, good people see low wages and a job shortage, and go into other fields, increasing the problem.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Great coders do not fit in typical organizational hierarchies. Because the hierarchy rarely recognizes the value produced by higher quality developers until they are gone.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I've been in this market for over 20 years. What I noticed is that in 90% of the cases, the manager has no clue who is good and who's mediocre. Mostly, he criteria is based on how each one advertise himself.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The difference between programmers, doctors and lawyers is that if a doctor or a lawyer is mediocre you will notice it pretty soon. With the programmers, only time will show the truth.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-6866308694123357552?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6866308694123357552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/6866308694123357552'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/03/why-great-coders-get-paid-far-too.html' title='Why Great Coders Get Paid Far Too Little'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-1885558974209586832</id><published>2007-02-27T22:46:00.000+01:00</published><updated>2007-06-02T18:19:23.737+02:00</updated><title type='text'>Why Can't Programmers... Program?</title><content type='html'>&lt;p&gt;Excellent piece of writing today from &lt;a href="http://www.codinghorror.com/blog/archives/000781.html"&gt;Jeff Atwood&lt;/a&gt;, who poses the question how on earth the majority of applicants for a programming job can't even solve the most basic tasks, let's say implement a linked list, or a simple SQL join. Yeah I know there is &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedList.html"&gt;java.util.LinkedList&lt;/a&gt;, but guess what, &lt;a href="http://arnosoftwaredev.blogspot.com/2004/10/curious-perversions-in-information.html"&gt;I have seen people&lt;/a&gt; iterating over a LinkedList in a counting loop using &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/LinkedList.html#get(int)"&gt;get(int index)&lt;/a&gt; on each element. Can you say O(N&lt;sup&gt;2&lt;/sup&gt;)? That's just my point! That's why sites like &lt;a href="http://thedailywtf.com/"&gt;The Daily WTF&lt;/a&gt; never run short of stories on "curious perversions in information technology".&lt;br /&gt;&lt;br /&gt;So I openly admit that just like Jeff I am getting increasingly tired of being affiliated with a profession where let's say&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;4 out of 10 people don't know how to code at all&lt;/li&gt;&lt;br /&gt;&lt;li&gt;another 4 out of 10 people know how to code (kind of), but don't know how to design software systems&lt;/li&gt;&lt;br /&gt;&lt;li&gt;which leaves 2 out of 10 people who are actually qualified for what they are doing&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;What's really ironic is that underperformers often slip through the recruitment process quite easily, while some of top notch folks won't because they expect a somewhat higher salary, a nice working environment or an interesting project assignment.&lt;br /&gt;&lt;br /&gt;This usually happens when interviewing is exclusively done by human resource folks or other people who are not capable of letting candidates do some sample coding right on the spot, and who have never heard about &lt;a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html"&gt;Joel Spolsky's "The Guerrilla Guide to Interviewing"&lt;/a&gt; or the fact that the best developers are ten times more productive (that is ten times more features or the same feature-set ten times faster, with ten times fewer errors) than the worst (which refers to "the worst qualified" who might still be considered for doing the job - and many empirical studies compare people coming from the same training or working in the same team).&lt;br /&gt;&lt;br /&gt;I can't imagine this is usual in other areas (imagine that e.g. in the medical field). It must have to do with the unique nature of software development. And the fact that many decision makers don't quite grasp how it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-1885558974209586832?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1885558974209586832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/1885558974209586832'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/02/why-cant-programmers-program.html' title='Why Can&apos;t Programmers... Program?'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8403723453281775489</id><published>2007-02-19T22:24:00.000+01:00</published><updated>2007-05-28T21:41:20.386+02:00</updated><title type='text'>EBay'ed A PDP 11 CPU</title><content type='html'>Yet another show-piece for &lt;a href="http://arnosoftwaredev.blogspot.com/2005/04/my-computer-museum-update.html"&gt;my computer museum&lt;/a&gt;, and that for only 25GBP, a real bargain! ;-)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_G0oBN6-Lb48/RdoaZ9DfzKI/AAAAAAAAAAY/eUqYuaWp0xc/s1600-h/pdp11_cpu.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_G0oBN6-Lb48/RdoaZ9DfzKI/AAAAAAAAAAY/eUqYuaWp0xc/s320/pdp11_cpu.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5033364566728232098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I think this chipset comes from the &lt;a href="http://en.wikipedia.org/wiki/PDP-11"&gt;PDP-11/84&lt;/a&gt; series.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_G0oBN6-Lb48/RdoaaNDfzLI/AAAAAAAAAAg/UquIG0FgfrE/s1600-h/pdp11_84.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_G0oBN6-Lb48/RdoaaNDfzLI/AAAAAAAAAAg/UquIG0FgfrE/s320/pdp11_84.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5033364571023199410" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8403723453281775489?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8403723453281775489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8403723453281775489'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/02/ebayed-pdp-11-cpu.html' title='EBay&apos;ed A PDP 11 CPU'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_G0oBN6-Lb48/RdoaZ9DfzKI/AAAAAAAAAAY/eUqYuaWp0xc/s72-c/pdp11_cpu.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-8951958651424397599</id><published>2007-02-14T21:08:00.000+01:00</published><updated>2007-05-28T21:41:12.891+02:00</updated><title type='text'>The Power Of Google</title><content type='html'>Two weeks ago, my car began bucking. First only when starting to go or when switching gears, later on it also happened randomly just while driving. At my repair shop they changed the spark plugs, and also informed me that the clutch was a little loose, but fixing it would turn out quite expensive, and might actually not pay off any more considering the age of the car.&lt;br /&gt;&lt;br /&gt;Unfortunately the bucking did not stop, it got worse. I asked for advice again, and once more the mechanic recommended repairing the clutch. Originally I had been planning to buy a new car in the second half of 2007, so that would probably have meant to prepone that purchase. So I started skimming through car sales notes already...&lt;br /&gt;&lt;br /&gt;In a final attempt, I invoked a Google search on my car's model and construction generation, as well as several keywords describing the behavior. Hundreds of online forum posts showed up. I took a look at the first results which included possible explanations, and did a quick count on which parts might be involved. Seven out of ten postings listed "exhaust gas regulation valve" (I hope that's the term in in English), others such as "lambda probe", "fuel filter", "spark plugs" or "injector" scored between one and four (some articles specified several causes).&lt;br /&gt;&lt;br /&gt;At this point I should probably mention that I have absolutely no clue about cars in general, and just a very rough idea what an "exhaust gas regulation valve" might even be. But I went back to my repair shop and told them to give the exhaust gas regulation valve thingy a shot instead of fiddling with the clutch. They did, and sure enough the valve turned out to be the root cause.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_G0oBN6-Lb48/RdNzxEwTruI/AAAAAAAAAAM/F8lqJuY1ELw/s1600-h/agr.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer;" src="http://1.bp.blogspot.com/_G0oBN6-Lb48/RdNzxEwTruI/AAAAAAAAAAM/F8lqJuY1ELw/s320/agr.jpg" alt="" id="BLOGGER_PHOTO_ID_5031492495630118626" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;Thank you Google!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-8951958651424397599?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8951958651424397599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/8951958651424397599'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/02/power-of-google.html' title='The Power Of Google'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_G0oBN6-Lb48/RdNzxEwTruI/AAAAAAAAAAM/F8lqJuY1ELw/s72-c/agr.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-117088273140100323</id><published>2007-02-07T21:55:00.000+01:00</published><updated>2007-05-28T21:39:57.868+02:00</updated><title type='text'>System Calls On HTTP Request</title><content type='html'>I slightly doubt the claim that comparing the number of system calls while processing two HTTP requests on Linux/Apache and Windows/IIS inherently proves that Windows is less secure than Linux, but those two call graphs alone are worth looking at.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/x/blogger/5204/578/1600/794160/syscalls_linux.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/x/blogger/5204/578/400/854489/syscalls_linux.jpg" alt="" border="0" /&gt;&lt;/a&gt;Linux/Apache&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/x/blogger/5204/578/1600/310400/syscalls_windows.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/x/blogger/5204/578/400/943864/syscalls_windows.jpg" alt="" border="0" /&gt;&lt;/a&gt;Windows/IIS&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Thanks to &lt;a href="http://blogs.zdnet.com/threatchaos/?p=311"&gt;Richard Stiennon&lt;/a&gt; for this visualization, nice idea.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-117088273140100323?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117088273140100323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117088273140100323'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/02/system-calls-on-http-request.html' title='System Calls On HTTP Request'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-117070884661210962</id><published>2007-02-05T21:47:00.000+01:00</published><updated>2007-05-28T21:39:46.832+02:00</updated><title type='text'>SOAP: The S Stands For "Simple"</title><content type='html'>&lt;a href="http://wanderingbarque.com/nonintersecting/2006/11/15/the-s-stands-for-simple/"&gt;Pete Lacey&lt;/a&gt; pretty much sums up the horror I have gone through during the last five years when it came to web service integration.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;[...]&lt;br /&gt;&lt;br /&gt;Developer: (Reads XML Schema spec). Saints preserve us! Alexander the Great couldn't unravel that.&lt;br /&gt;&lt;br /&gt;SOAP Guy: Don’t worry about it. Your tools will create the schema for you. Really, its all about the tooling.&lt;br /&gt;&lt;br /&gt;[...]&lt;br /&gt;&lt;br /&gt;Developer: This is getting ugly. The WSDL my tools generated can't be consumed by the tools my partners use. Not only that, the schemas it generates are impenetrable and can't be reused. And no tool seems to have agreed on how best to handle the SOAPAction header.&lt;br /&gt;&lt;br /&gt;SOAP Guy: Sorry to hear that, buddy. On the bright side, nobody uses the Doc/Lit style anymore. In order to get transport independence back we’re all using wrapped-doc/lit now. Doesn't that sound cool: wrapped-doc/lit?&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-117070884661210962?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117070884661210962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117070884661210962'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/02/soap-s-stands-for-simple.html' title='SOAP: The S Stands For &quot;Simple&quot;'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-117054009000281985</id><published>2007-01-31T21:27:00.000+01:00</published><updated>2007-05-28T21:39:37.351+02:00</updated><title type='text'>Debugging The .NET Framework</title><content type='html'>Over the last weeks, a small fraction of our customers reported some application instability. The client, a .NET 1.1 WinForms program (due to .NET framework backward incompatibilities resp. several third party tool problems we have not been able to migrate to .NET 2.0 yet), would simply terminate occasionally without any error message, dump or whatsoever. Several modules were affected, and there seemed to be no systematic scheme behind it.&lt;br /&gt;&lt;br /&gt;After talking with some customers we figured out what those crashes had in common: they all occurred when a worker thread had been spawned to do some DB stuff, while the UI thread would display a modal progress dialog. Moving to a single thread (namely the UI thread) was no alternative, as the worker thread's task included some long running DB stuff, which would block the client, hence make the UI non-responsive, even when applying &lt;a href="http://msdn2.microsoft.com/en-us/library/bd65th41.aspx"&gt;Application.DoEvents&lt;/a&gt; in between).&lt;br /&gt;&lt;br /&gt;I suspected a concurrency issue, and had a closer look at the code - but there was nothing wrong about it, data access was cleanly separated between the two threads, which would only communicate with each other on a message basis.&lt;br /&gt;&lt;br /&gt;So the next thing we tried was to reproduce the error on one of our test machines. But bad luck again. We had recorded a macro which represented the scenario our customers had described, but our application wouldn't crash, not in hours, not in days.&lt;br /&gt;&lt;br /&gt;Finally we digged out an old developer PC which had actually been scrapped already, and there it happened, just as the customer had reported it - repeatedly, and within minutes. The .NET runtime simply terminated, do dump, no system log entry, nothing. Attaching the debugger didn't help either. Next I invoked several profilers (&lt;a href="http://www.compuware.com/products/devpartner/visualc.htm"&gt;Compuware BoundsChecker&lt;/a&gt;, &lt;a href="http://www.ibm.com/software/awdtools/purify/"&gt;Rational Purify&lt;/a&gt; and &lt;a href="http://www.jetbrains.com/profiler/"&gt;JetBrains DotTrace&lt;/a&gt;). Profiling also did not provide new insights. BoundsChecker and Purify originally are unmanaged code profilers - they do support .NET profiling, but still it doesn't seem to be a first class citizen, and DotTrace requires .NET 2.0 for memory profiling.&lt;br /&gt;&lt;br /&gt;So time for &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;WinDbg&lt;/a&gt;, Microsoft's system level debugger. I didn't really have a lot of necessity to apply WinDbg before, but this seemed to be one of those cases. Luckily I read Tess' &lt;a href="http://blogs.msdn.com/tess/"&gt;"If broken it is, fix it you should"-blog&lt;/a&gt; on Windows debugging at regular intervals, so this turned out to be helpful. Impatiently anticipating some kind of progress, I attached WinDbg to our application, and sure enough some time later it broke at a &lt;a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B105675&amp;x=10&amp;y=15"&gt;second chance exception&lt;/a&gt;. The call stack was full of entries like&lt;br /&gt;&lt;br /&gt;&lt;code&gt;mscorwks!GetCompileInfo&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Yes the .NET runtime crashed, and it was clear I needed some runtime symbol files ("GetCompileInfo" is just a placeholder).&lt;br /&gt;&lt;br /&gt;Fortunately there is the so called Microsoft symbol server, from which one can either download all kinds of system symbol files, or simply let WinDbg connect to it and do the job (File / Symbol File Path / "srv*c:\symbols*http://msdl.microsoft.com/download/symbols"). Furthermore I wanted better support for .NET debugging, so I loaded the SOS (Son of Strike) extension into WinDbg:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0:000&gt; .load clr10\sos&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;On the next crash, the callstack made more sense:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0:000&gt; kb&lt;br /&gt;00 0012d3d0 79216288 0162652c 00166c8c 00000001 mscorwks!gc_heap::mark_object_simple+0x77 (FPO: [Non-Fpo])&lt;br /&gt;01 0012d44c 79213753 79211d81 00000000 00000000 mscorwks!gc_heap::mark_through_cards_for_segments+0x28f (FPO: [Non-Fpo])&lt;br /&gt;02 0012d478 7921248e 00000001 00000000 00000000 mscorwks!gc_heap::mark_phase+0xb6 (FPO: [Non-Fpo])&lt;br /&gt;03 0012d4d4 7921301a 00000000 00000000 00000000 mscorwks!gc_heap::gc1+0x96 (FPO: [Non-Fpo])&lt;br /&gt;04 0012d514 79213c37 00000000 00000000 00000000 mscorwks!gc_heap::garbage_collect+0x1bf (FPO: [Non-Fpo])&lt;br /&gt;05 0012d534 79214e83 00000000 00000000 00000044 mscorwks!GCHeap::GarbageCollectGeneration+0x11b (FPO: [Non-Fpo])&lt;br /&gt;06 0012d564 792d176b 793df808 00000044 00000000 mscorwks!gc_heap::allocate_more_space+0x13a (FPO: [Non-Fpo])&lt;br /&gt;07 0012d788 791b3af0 00000044 00000002 0012d7b8 mscorwks!GCHeap::Alloc+0x77 (FPO: [Non-Fpo])&lt;br /&gt;08 0012d798 791d6280 00000044 00000000 00040000 mscorwks!Alloc+0x3a (FPO: [Non-Fpo])&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It was the garbage collector, and it looked like some managed heap corruption. "verifyheap" would tell me for sure:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0:000&gt; !verifyheap&lt;br /&gt;VerifyHeap will only produce output if there are errors in the heap&lt;br /&gt;object cbad00: bad member 162652c at 00cbad30&lt;br /&gt;curr_object : 0xcbad00 size = 0&lt;br /&gt;Last good object: 0xcbac94&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;OK, maybe the last good object would supply some further clues:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0:000&gt; !do 0xcbac94&lt;br /&gt;Name: System.Data.SqlClient.SqlCommand&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I tried this several times, the last good object always was either System.Data.SqlClient.SqlCommand or System.Data.SqlClient.SqlDataAdapter. I had a look at our code and checked which other objects were instantiated in this context. Besides SqlCommand and SqlDataAdapter there was System.Data.SqlClient.SqlConnection. So I googled for "managed heap corruption" for each of those three classes, and there it was: &lt;a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B323696&amp;x=7&amp;y=16"&gt;"Managed Heap Corruption When You Use SqlConnection"&lt;/a&gt;. From the text: &lt;span style="font-style:italic;"&gt;"[...] This causes the Microsoft .NET application to quit unexpectedly during the garbage collection phase"&lt;/span&gt;. &lt;br /&gt;&lt;br /&gt;This is an MDAC (&lt;a href="http://msdn2.microsoft.com/en-us/data/aa937729.aspx"&gt;Microsoft Data Access Components&lt;/a&gt;) 2.7 problem (MDAC 2.7 SP1 solves it), which occurrs when database connections are being rapidly opened and closed (logically, not physically) on multiple threads. As .NET 1.1 itself only requires MDAC 2.6 or above, older clients that have not been updated are most likely affected.&lt;br /&gt;&lt;br /&gt;It had been a hard day's work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-117054009000281985?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117054009000281985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/117054009000281985'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/01/debugging-net-framework.html' title='Debugging The .NET Framework'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-8490763.post-116881845100202858</id><published>2007-01-15T00:30:00.001+01:00</published><updated>2010-04-26T10:19:49.284+02:00</updated><title type='text'>Sudoku Benchmarking / .NET 2.0, Java 5 and Java 6 Results</title><content type='html'>&lt;a href="http://devdeacon.blogspot.com/2006/11/java-vs-c-vs-c.html"&gt;Brian Deacon&lt;/a&gt; asked me to add &lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/java-vs-net-vs-c-performance.html"&gt;Sudoku Solver&lt;/a&gt; benchmark results for .NET 2.0 and the latest Java releases, so I am happy to provide those numbers (plus this finally gave me a reason to download and install Java 6).&lt;br /&gt;&lt;br /&gt;.NET 2.0 and Java 6 both have improved a lot, and for the first time .NET without &lt;a href="http://msdn.microsoft.com/msdnmag/issues/05/04/NGen/default.aspx"&gt;NGEN&lt;/a&gt; overtakes Java (not applying NGEN is the fairest-possible comparison between .NET and Java in my opinion - and in this case it's a very close match), but both still don't quite reach C++ performance:&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;td&gt;&lt;span style="font-weight:bold;"&gt;Rank&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span style="font-weight:bold;"&gt;Platform&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span style="font-weight:bold;"&gt;Execution Time&lt;br&gt;(for several thousand valid solutions)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1.&lt;/td&gt;&lt;td&gt;C++ (&lt;a href="http://msdn2.microsoft.com/en-us/visualc/default.aspx"&gt;MSVC&lt;/a&gt;)&lt;/td&gt;&lt;td&gt;250ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.&lt;/td&gt;&lt;td&gt;C# (.NET 2.0) with &lt;a href="http://msdn2.microsoft.com/en-us/library/6t9t5wcf(VS.80).aspx"&gt;NGEN&lt;/a&gt;&lt;/td&gt;&lt;td&gt;375ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3.&lt;/td&gt;&lt;td&gt;C# (.NET 1.1) with &lt;a href="http://msdn2.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx"&gt;NGEN&lt;/a&gt;&lt;/td&gt;&lt;td&gt;390ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4.&lt;/td&gt;&lt;td&gt;C# (.NET 2.0)&lt;/td&gt;&lt;td&gt;406ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5.&lt;/td&gt;&lt;td&gt;Java (Java 6)&lt;/td&gt;&lt;td&gt;422ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6.&lt;/td&gt;&lt;td&gt;Java (Java 5 Update 10)&lt;/td&gt;&lt;td&gt;657ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7.&lt;/td&gt;&lt;td&gt;Java (Java 1.4.2)&lt;/td&gt;&lt;td&gt;680ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8.&lt;/td&gt;&lt;td&gt;C# (.NET 1.1)&lt;/td&gt;&lt;td&gt;790ms&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;What I did here was to feed the Sudoku solver with a sparsely populated puzzle that has thousands of valid solutions (Sudoku purists will point out that this is a non-well-formed puzzle). The algorithm uses a slightly optimized brute force approach (my intention was runtime comparison, not building a hyperfast solver algorithm) - so it finds all the solutions. Of course I took care to apply exactly the equivalent language constructs on all platforms, most importantly when it came to dynamic memory allocation.&lt;br /&gt;&lt;br /&gt;The usual one-solution, human-solvable puzzles are being processed at a rate of about 60,000 valid solutions per second (&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/just-another-sudoku-solver-c.html"&gt;C++ version&lt;/a&gt; on my 2.4GHz Athlon) - but the time it takes to solve one of those can hardly be measured, and I didn't want to inject the same puzzle a thousand times for the benchmark. There are harder puzzles though with one solution but a larger search space, which means it takes longer to solve them.&lt;br /&gt;&lt;br /&gt;And as I have mentioned before: &lt;em&gt;I doubt that there is a lot of optimization potential for &lt;a href="http://java.sun.com/javase/technologies/hotspot/index.jsp"&gt;Java Hotspot&lt;/a&gt; other but compiling the algorithm's main method (which only consists of a few lines of code anyway) to native code as soon as possible. Dynamic memory allocation only happens for storing away solution arrays, and those are not going to be garbage collected until the end, so there is not a lot of optimizing potential on the memory management side. The .NET CLR should &lt;a href="http://en.wikipedia.org/wiki/Just-in-time_compilation"&gt;JIT&lt;/a&gt; all of the code at startup anyway. I did some tests to go sure, and the numbers did not change neither under Java and nor under .NET even after running the same stack of puzzles many times in a row.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Previos Posts:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/just-another-sudoku-solver.html"&gt;Just Another Sudoku Solver&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/java-vs-net-vs-c-performance.html"&gt; Java Vs. .NET Vs. C++ Performance&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2006/10/just-another-sudoku-solver-c.html"&gt; Just Another Sudoku Solver (C++)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Follow-Ups:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://arnosoftwaredev.blogspot.com/2007/07/hosting-net-winforms-applications-in.html"&gt; Hosting .NET Winforms Applications In MSIE&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8490763-116881845100202858?l=arnosoftwaredev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/116881845100202858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8490763/posts/default/116881845100202858'/><link rel='alternate' type='text/html' href='http://arnosoftwaredev.blogspot.com/2007/01/sudoku-benchmarking-net-20-java-5-and.html' title='Sudoku Benchmarking / .NET 2.0, Java 5 and Java 6 Results'/><author><name>Arno</name><uri>http://www.blogger.com/profile/10541039623935410561</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
