<?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-1688176320920284646</id><updated>2012-01-12T03:10:06.151Z</updated><category term='Mocks'/><category term='MVC'/><category term='Prism'/><category term='WWW'/><category term='Rails'/><category term='Misc'/><category term='ASP'/><category term='UML'/><category term='Humour'/><category term='MAC'/><category term='Politics'/><category term='C#'/><category term='C# CF'/><category term='MonoRail'/><category term='TDD'/><category term='Mathematics'/><category term='WCF'/><category term='ECO'/><category term='Delphi'/><category term='Onion'/><category term='Rhino-Mocks'/><category term='DDD'/><category term='CF'/><category term='WinForms'/><category term='WPF'/><category term='Silverlight'/><title type='text'>A day in the life of...</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default?start-index=101&amp;max-results=100'/><author><name>Peter Morris</name><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>204</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7152665691718107258</id><published>2009-06-23T09:35:00.003+01:00</published><updated>2009-06-23T09:36:36.472+01:00</updated><title type='text'>I'm moving</title><content type='html'>I've decided to use my own domain (which is currently unused) for my blog.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://blog.peterlesliemorris.com"&gt;http://blog.peterlesliemorris.com&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So this should be my last post here :-)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7152665691718107258?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7152665691718107258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7152665691718107258' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7152665691718107258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7152665691718107258'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/im-moving.html' title='I&apos;m moving'/><author><name>Peter Morris</name><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><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5192517280853590559</id><published>2009-06-12T17:15:00.006+01:00</published><updated>2010-01-12T10:58:14.857Z</updated><title type='text'>Has facebook accessed MSN API without permission?</title><content type='html'>&lt;div&gt;** &lt;span class="Apple-style-span" style="font-weight: bold; "&gt;UPDATE: I've started a group on (of all places) Facebook **&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;** &lt;a href="http://www.facebook.com/group.php?gid=108402052894"&gt;http://www.facebook.com/group.php?gid=108402052894&lt;/a&gt; **&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;My wife was confused recently.  Facebook suggested we might know a certain person.  We did know her, but how did FB know?  She had no friends in common with this person.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then today it showed her someone else she might know.  This time a guy we used to talk to on MSN years ago, again no common friends.  Then soon after yet another person we haven't spoken to in years, again no common friends.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So what was the pattern?  We looked at her MSN account and all of these people had previously been contacts but had since been removed - they were however all still in the "allow" list.  So how has FB come up with this list of people?  My suspicion is that it has used the MSN API to access my wife's account without permission.  For non vital web stuff such as MSN and FB she uses the same password, so it would have been possible to access her MSN details because the email/password are the same as her FB credentials.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I can see no other way that these people could have been suggested.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Person 1:&lt;/div&gt;&lt;div&gt;We have stayed on a farm in Cornwall a few times now.  The couple who own the farm were having problems with their computer and I agreed to use MSN to view thier screen and help them to install a virus killer etc.  We used their daughter's MSN account to connect up because they didn't have one.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Person 2:&lt;/div&gt;&lt;div&gt;A programmer from the #Delphi channel on Undernet.  Haven't spoken to this guy in years.  I added him on MSN at some point to arrange a visit to his house in Surrey about 8 years ago.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Person 3:&lt;/div&gt;&lt;div&gt;A guy we got chatting to on NetMeeting when we first got access to the Internet back in about 1994/1995 - we spoke to him on and off for a few months.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;None of these people are in my wife's MSN contact list any more, but they are in an "Allow" list.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This leaves me suspecting 2 things&lt;/div&gt;&lt;div&gt;1: FaceBook stores your password in a format that can be reversed and they can read what it is.&lt;/div&gt;&lt;div&gt;2: FaceBook have accessed my wife's MSN account for contacts without her permission.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5192517280853590559?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5192517280853590559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5192517280853590559' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5192517280853590559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5192517280853590559'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/has-facebook-accessed-msn-api-without.html' title='Has facebook accessed MSN API without permission?'/><author><name>Peter Morris</name><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><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6734860585794844728</id><published>2009-06-10T18:44:00.001+01:00</published><updated>2009-06-10T18:45:36.064+01:00</updated><title type='text'>A conversation about race</title><content type='html'>I just watched &lt;a href="http://vodpod.com/watch/1713267-a-conversation-about-race"&gt;this video&lt;/a&gt; (60 minutes long) and I really enjoyed it.  It sums up some of my feelings about racism.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6734860585794844728?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6734860585794844728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6734860585794844728' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6734860585794844728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6734860585794844728'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/conversation-about-race.html' title='A conversation about race'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4522367415576906283</id><published>2009-06-05T09:34:00.002+01:00</published><updated>2009-06-05T09:37:51.553+01:00</updated><title type='text'>Embarcadero should revive Bold for Delphi</title><content type='html'>I've recently been doing some work on a Bold for Delphi application.  I am really quite surprised how much I am still impressed with this framework, it really was (and still is) so much ahead of its time.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Embarcadero really should revive this product and put it back into Delphi.  They could make it a free addition to Delphi, or maybe even ship it with Delphi as an open source project.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Embarcadero!  You have a great tool here, don't hide it away, use it!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4522367415576906283?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4522367415576906283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4522367415576906283' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4522367415576906283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4522367415576906283'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/embarcadero-should-revive-bold-for.html' title='Embarcadero should revive Bold for Delphi'/><author><name>Peter Morris</name><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><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5584468137040674959</id><published>2009-06-03T11:02:00.001+01:00</published><updated>2009-06-05T10:19:06.015+01:00</updated><title type='text'>Free: One year TechNet Plus subscription</title><content type='html'>&lt;a href="http://arstechnica.com/microsoft/news/2009/06/free-one-year-technet-plus-subscription.ars"&gt;http://arstechnica.com/microsoft/news/2009/06/free-one-year-technet-plus-subscription.ars&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Update: The UK link from this site has been taken down :-)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5584468137040674959?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5584468137040674959/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5584468137040674959' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5584468137040674959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5584468137040674959'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/free-one-year-technet-plus-subscription.html' title='Free: One year TechNet Plus subscription'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4354635985938593520</id><published>2009-06-02T13:55:00.003+01:00</published><updated>2009-06-02T13:57:52.098+01:00</updated><title type='text'>Bing</title><content type='html'>I searched for "mrpmorris" on bing.com recently.  Now on Google it would show the address of my blog as the first result, which is very relevant.  On Bing it showed a page from a web chat from over 3 years ago, not very impressive.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Not sure how long this photo will remain on the web for, but I found it quite interesting...&lt;/div&gt;&lt;div&gt;&lt;a href="http://farm4.static.flickr.com/3604/3585051300_d23a37a32e_o.png"&gt;http://farm4.static.flickr.com/3604/3585051300_d23a37a32e_o.png&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4354635985938593520?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4354635985938593520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4354635985938593520' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4354635985938593520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4354635985938593520'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/06/bing.html' title='Bing'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7239295616214820841</id><published>2009-05-31T09:23:00.002+01:00</published><updated>2009-05-31T09:49:01.018+01:00</updated><title type='text'>MOZY backup - update</title><content type='html'>Someone higher up in MOZY support contacted me and put me in touch with a different support person.  Here is a summary of how things went&lt;br /&gt;&lt;br /&gt;&lt;div&gt;01: He agreed that 50GB was a lot to download.&lt;/div&gt;&lt;div&gt;Not only did he agree to allow me to receive a DVD restore but also sent it free of charge to help compensate for the trouble I had experienced with their first line support.  Very kind I thought!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;02: My DVDs turned up.&lt;/div&gt;&lt;div&gt;Being the sceptic I am I decided I would restore them to an alternate path on my hard disc.  I decided I would allow the download restore to complete and then do a directory comparison to see if both directories matched.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;03: My restore failed.&lt;/div&gt;&lt;div&gt;I went to bed one night with an estimated time to completion of something like 2 weeks.  The next morning I awoke to the following status&lt;/div&gt;&lt;div&gt;"Complete.  Restored 3.3GB"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3.3GB?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I reported this, but had decided not to persue it because I was now more interested in getting updates to my data backed up instead.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;04: My next backup was big.&lt;/div&gt;&lt;div&gt;I started a backup only to be told that the estimated time until completion was about a month.  It appeared that MOZY wanted to backup all the files it should already have.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;05: Backup only took a couple of days.&lt;/div&gt;&lt;div&gt;MOZY for some reason did upload some files that haven't changed (a video from 2005 for example).  In the end it took a few days to back everything up.  It was acceptible, but still wrong in my opinion as I had only altered a few text files so the backup at most should have only taken a few minutes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;What have I learned?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I have learned not to depend on a single backup strategy!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;LogMeInBackup&lt;/div&gt;&lt;div&gt;I decided to give LogMeInBackup a try.  LogMeInBackup copies my files to another computer of my choice, either on the LAN or the Internet, but a second computer I own or have control of.  I was very disapointed with this app.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I chose to backup to a laptop on my wireless LAN, the backup estimated 1 day (which is about right) but failed multiple times (probably about 8-10).  Once it finally completed I tested the restore only to be told it failed, testing it a second time told me it succeeded.  I'd expect a client/server app to work much better than this on a wireless LAN where both computers are in the same room and the router is only in the next room.  I am guessing that it failed each time my ISP went down (which has been quite a lot recently), although why I need access to the Internet to backup between two machines on a LAN I don't know.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;External drive&lt;/div&gt;&lt;div&gt;I decided to buy a 500GB external drive for £50.  It took 11 minutes to copy my 50GB of data across.  I can do this any time I add a significant amount of data (new family videos).  The plan is that if I have a complete system failure again I can copy the files back from the external hard disc and then use MOZY to restore the minor changes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In closing&lt;/div&gt;&lt;div&gt;Once I got through to someone with knowledge of their product my problem was solved very quickly (although not totally painlessly).  These are the steps I think MOZY need to take.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;01: &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Train your support staff!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Better still, stop using Indian labour just because it is cheap.  I'd rather pay double and get better support.  Double $4.95 isn't much to pay!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;02: &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Test your software properly!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I don't think MOZY has been tested in a "total failure" scenario for some time, or at least not properly.  You only need to install MOZY on a new Windows installation and try to restore to immediately find prominent problems&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  A: No restore button unless you first do a backup, even though you have nothing to backup.&lt;/div&gt;&lt;div&gt;  B: Even after executing a redundant backup there is no backup history in the tab, which is very unnerving.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have no idea why restoring my data when from 50GB at 1 month down to a few days at 3.3GB, and I expect it would be a much more difficult bug to reproduce, but this needs looking into.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;03: &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Support the DVD option to non US addresses *on your website*!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I would only need this option now if my whole computer system was stolen and I had no external HD to restore from.  I do think though that the MOZY website should allow non US residents to order DVD restores. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I was told that this is an option available to non US residents if you talk to support, but that was not my experience.  I was told quite categorically that it is available only to US residents.  Also, if it isn't on the website then it isn't "officially" supported, so just because you were able to get DVDs last week doesn't mean you can still get them this week.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"Oh no, person X shouldn't have done that.  We only ship DVDs to the US due to ........"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I will continue to use MOZY, but only rely on them for minor restores or in a disaster (computer + external HD both dead, or stolen).  Mostly I will depend on my external HD.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I would like to thank MOZY for making the extra effort to put things right.  My data is understandably very important to me!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7239295616214820841?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7239295616214820841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7239295616214820841' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7239295616214820841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7239295616214820841'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/mozy-backup-update.html' title='MOZY backup - update'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-448398269957225627</id><published>2009-05-31T09:21:00.002+01:00</published><updated>2009-05-31T09:23:33.797+01:00</updated><title type='text'>Generation gap - update</title><content type='html'>Martin wrote to me and explained something I wasn't aware of.  He doesn't catalogue best practises or only the ones he likes, he is cataloguing all patterns he can find.&lt;br /&gt;&lt;br /&gt;He said he would rather someone made an informed bad decision than an uninformed good one.  Not quite sure I agree with that either :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-448398269957225627?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/448398269957225627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=448398269957225627' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/448398269957225627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/448398269957225627'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/generation-gap-update.html' title='Generation gap - update'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-557074860933617626</id><published>2009-05-23T12:34:00.002+01:00</published><updated>2009-05-23T13:09:34.601+01:00</updated><title type='text'>Generation gap</title><content type='html'>I have just read &lt;a href="http://martinfowler.com/dslwip/GenerationGap.html"&gt;this blog&lt;/a&gt; by Martin Fowler.  I like a lot of what Martin writes, but this is not one of them.&lt;br /&gt;&lt;br /&gt;The article suggests using inheritance to separate code-generated classes and manually written classes.  You would descend from a code-generated class instead of trying to modify the code-generated source.&lt;br /&gt;&lt;br /&gt;The problems I see with this are&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Attributes&lt;/h2&gt;&lt;br /&gt;If I have a descendant of a code-generated class how do I attach .NET attributes to members?  I&amp;#8217;d have to override them and add the attribute in the descendant which would mean all of my members need to be virtual.  Obviously this wouldn&amp;#8217;t work for Private members.&lt;br /&gt;&lt;br /&gt;I expect you&amp;#8217;d have to attach all of your attributes in the tool which generates the source code, but I don&amp;#8217;t like this idea.  I will explain why later.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Sealed / final&lt;/h2&gt;&lt;br /&gt;What if we want to create a sealed/final class?  We can&amp;#8217;t.  What if we want a specific member to be sealed/final?  Then we couldn&amp;#8217;t add attributes to the members.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Partial&lt;/h2&gt;&lt;br /&gt;Microsoft introduced Partial classes/methods to try to get around the problem of mixing code-generated and manually written code.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Person&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected partial void BeforeSetName(ref string newValue); &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private string name;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public string Name&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return name; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BeforeSetName(ref value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name = value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The manually written part of the class can now optionally implement BeforeSetName&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public partial void BeforeSetName(ref string newValue)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (newValue == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;newValue = &amp;quot;&amp;quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and to decorate items with meta-data....&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[MetadataClass(typeof(PersonMetaData))]&lt;br /&gt;public class Person&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class PersonMetaData&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;[SomeAttributeINeed]&lt;br /&gt;&amp;nbsp;&amp;nbsp;public string Name { get; set; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I think the partial methods idea is a good one, but meta-data classes?  Yuck!  They violate the DRY principle, whenever I change the real Person class I have to update the PersonMetaData class too.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Solution&lt;/h2&gt;&lt;br /&gt;So, what is the solution?  How about this?&lt;br /&gt;&lt;b&gt;Code generation tools should be good at what they do!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When I specified the code-generation for ECO MOdeler there were no partial classes/methods, everything had to be generated to a single file which had to mix manually written and auto-generated code together...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;//User code here&lt;br /&gt;&amp;nbsp;&amp;nbsp;#region ECO Modeler generated&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&amp;nbsp;&amp;nbsp;//User code here&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The code-generator would only rip out and replace code within those regions, leaving the user written code intact.  Now at the time partial classes didn&amp;#8217;t exist so if I had the chance I would now most likely use BeforeX AfterX type partial methods instead of using *some* regions, but in places I still think they would be necessary.  &lt;br /&gt;&lt;br /&gt;For example I think that some attributes are quite valid to be entered into your code-generator and some aren&amp;#8217;t.  In such a case I would expect to see something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;#region auto-generated&lt;br /&gt;&amp;nbsp;&amp;nbsp;[SomeAttributeFromCodeGeneration]&lt;br /&gt;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&amp;nbsp;&amp;nbsp;[SomeManuallyAddedAttribute]&lt;br /&gt;&amp;nbsp;&amp;nbsp;public string Name { get; set; }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One example of what is or is not relevant to your code-generator is interface realisation.  The modeller I was using defined the business classes, from the point of view of the people within the business.  Now if one of those classes needs to implement IComparable&amp;lt;T&amp;gt; this is an implementation detail and doesn&amp;#8217;t belong in the business model (from which you generate source code) because it will just muddy the information presented to non programmers.  ECO Modeler handled this nicely.&lt;br /&gt;&lt;br /&gt;Step 1: Code is generated&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Person : ICloneable, IRoleHolder&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 2: Programmer implements IComparable&amp;lt;T&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Person : ICloneable, IComparable&amp;lt;Person&amp;gt;, IRoleHolder&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 3: Model is changed so that Person realises the IStockHolder interface and removes the ICloneable interface&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Person : IComparable&amp;lt;Person&amp;gt;, IRoleHolder, IStockHolder&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ECO Modeler did this by storing information about which interfaces it added to the class last time it generated code.  If they are no longer in the model they need to come out, any new ones in the model need to be added, and anything else needs to be left alone.&lt;br /&gt;&lt;br /&gt;My point here really is this.  Why should we be writing programming patterns which do not solve the problem at hand?  Why develop programming patterns around holes in our technology?  If it is causing us problems then our technology should improve!  Code generators should be capable of parsing source code as well as generating it.  They should be able to work in their own code arena without messing up ours!&lt;br /&gt;&lt;br /&gt;So on this one I disagree, the problem lies in the tools and should be addressed there, not addressed in the coding of our applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-557074860933617626?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/557074860933617626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=557074860933617626' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/557074860933617626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/557074860933617626'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/generation-gap.html' title='Generation gap'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7532628672124203918</id><published>2009-05-16T15:09:00.002+01:00</published><updated>2009-05-16T15:11:18.405+01:00</updated><title type='text'>PayPal are paying out!</title><content type='html'>I saw a blog post on Ayende's blog about PayPal where he said he had spoken to someone at PayPal.  I didn't know you could actually TALK to a PERSON.  He sent me the phone number (a USA number) and I called it.  I phoned that number today and the issue was resolved in about 15 minutes.&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The lady on the phone said that PayPal should in fact return the exact amount of money taken from my account in cases of fraud, she apologised and said the difference would be paid in within 2 working days.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Yes it's only £20, but I don't like people stealing from me!&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7532628672124203918?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7532628672124203918/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7532628672124203918' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7532628672124203918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7532628672124203918'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/paypal-are-paying-out.html' title='PayPal are paying out!'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8036547374063855996</id><published>2009-05-16T11:48:00.002+01:00</published><updated>2009-05-16T12:00:33.530+01:00</updated><title type='text'>I've just ordered some books</title><content type='html'>Someone kindly gave me a £100 gift certificate for Amazon.  I posted a request for book suggestions to 3 groups where I would expect to receive good recommendations&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  &lt;a href="http://tech.groups.yahoo.com/group/altdotnet/"&gt;http://tech.groups.yahoo.com/group/altdotnet/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;  &lt;a href="http://tech.groups.yahoo.com/group/domaindrivendesign/"&gt;http://tech.groups.yahoo.com/group/domaindrivendesign/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;  &lt;a href="http://groups.google.com/group/RhinoMocks"&gt;http://groups.google.com/group/RhinoMocks&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I received a lot of recommendations (thanks everyone!) - here are the ones I have bought:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  Building Domain Specific Languages in Boo&lt;/div&gt;&lt;div&gt;  Writing Secure Code, Second Edition&lt;/div&gt;&lt;div&gt;  Analysis Patterns Reusable Object Models (OBT)&lt;/div&gt;&lt;div&gt;  How to Solve It: A New Aspect of Mathematical Method&lt;/div&gt;&lt;div&gt;  Object Oriented Project Design&lt;/div&gt;&lt;div&gt;  Extreme Programming Explained: Embrace Change&lt;/div&gt;&lt;div&gt;  Getting Things Done: How to Achieve Stress-free Productivity&lt;/div&gt;&lt;div&gt;  &lt;/div&gt;&lt;div&gt;Here are the books that made it onto my "future" list which I didn't have enough cash for this time around.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  http://www.amazon.co.uk/gp/registry/wishlist/13GXONVVMLHZZ&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I generally went for the cheaper books first, so that I could get as much input as possible.  "The art of computer programming" looks good but I'd have hardly been able to get much else - especially as I considered Oren's book on DSLs a "must buy". &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The "Patterns of software architecture" series look good, although I suspect the 3rd in the series is the equivalent of "Nightmare on Elm Street 2" because the table of contents don't look too good and all the second hand prices are much lower :-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I wish I could just download this stuff straight into my head!  Reading is so time consuming.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8036547374063855996?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8036547374063855996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8036547374063855996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8036547374063855996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8036547374063855996'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/ive-just-ordered-some-books.html' title='I&apos;ve just ordered some books'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2177534629154027830</id><published>2009-05-14T14:02:00.002+01:00</published><updated>2009-05-14T14:09:37.109+01:00</updated><title type='text'>MUMS - How good you are when things go wrong</title><content type='html'>I always say this so I have probably blogged it in the past, in light of my MOZY incident probably recently, but "It's not how good you are when things go right, it's how good you are when things go wrong".&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My wife and I are (unexpectedly) expecting our 4th baby.  We paid for a private scan at &lt;a href="http://www.mums.me.uk/"&gt;MUMS&lt;/a&gt; last week.  Part of the package is a DVD of the scan.  The scan was a really good one, but unfortunately when we got home we realised our DVD was completely blank.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I phone them up and they offered a free scan.  I was still disappointed because there was a beautiful part of our previous scan that would be lost, where we were zoomed right into our baby's hand as she repeatedly clenched and reopened her hand, but I agreed because at least we would have &lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;something&lt;/span&gt;&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When we had our scan they gave us probably double the normal amount of time.  In addition to that they upgraded our scan from a plain ultra sound to one of those 4D scans.  We were given twice as many printed photos of the scan, and absolutely loads of still images were written to the DVD.  They more than made up for the initial unfortunate mistake.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.mums.me.uk/"&gt;MUMS&lt;/a&gt; is a good company, because they are good when things go wrong!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2177534629154027830?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2177534629154027830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2177534629154027830' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2177534629154027830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2177534629154027830'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/mums-how-good-you-are-when-things-go.html' title='MUMS - How good you are when things go wrong'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-788391432521466579</id><published>2009-05-13T12:12:00.002+01:00</published><updated>2009-05-13T12:18:41.502+01:00</updated><title type='text'>Bletchley Park</title><content type='html'>&lt;div&gt;If you are unaware Bletchley Park played a vital part in World War II, it undoubtedly shortened the war (possibly by years) by decoding intercepted Nazi messages which had been encoded on the Enigma machine.  This site is credited with being the place where the first programmable computer was used to crack the more complex Lorenz Cipher Machine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;According to this petition number 10 Downing Street...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://petitions.number10.gov.uk/BletchleyPark/"&gt;http://petitions.number10.gov.uk/BletchleyPark/&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;...the park is severly lacking in funds and has at best 2 to 3 years before it is forced to close down.  The idea that a site with such a historical significance could be bulldozed and redeveloped is astonishing!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Please help to preserve this computer related historical UK site by making as many UK residents as possible aware of this petition!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="color: rgb(85, 26, 139); text-decoration: underline;"&gt;&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/1688176320920284646-788391432521466579?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/788391432521466579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=788391432521466579' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/788391432521466579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/788391432521466579'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/bletchley-park.html' title='Bletchley Park'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6659769405997093649</id><published>2009-05-11T14:08:00.003+01:00</published><updated>2009-05-11T14:36:32.553+01:00</updated><title type='text'>MOZY backup</title><content type='html'>Vista screwed up on me recently.  It totally refused to boot either in normal or safe mode.  I ended up having to delete my partition and re-installing Windows, I decided to move back to Windows XP.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have 54.2GB of personal data backed up using Mozy Home.  This files are mainly photos and videos of my children growing up and are obviously irreplacable.  Imagine my shock when&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;After reinstallation MozyHome showed that I have no backup history.&lt;/li&gt;&lt;li&gt;MozyHome had no Restore button - it wasn't disabled, there just wasn't one.&lt;/li&gt;&lt;li&gt;The web restore showed me as having 141GB of data to restore.&lt;/li&gt;&lt;li&gt;When *I* worked out what caused #3 the support engineer said "I guess".&lt;/li&gt;&lt;li&gt;The web restore then said I only had 16GB of data, then it said I had 42.8GB of data.&lt;/li&gt;&lt;li&gt;MozyHome showed 0.6GB too little in my backup history, the file that was missing was nothing important - just an ultrasound scan of our unborn child - but it's okay, restarting my computer brought it back.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;When the engineers I talk to say things like "I guess", and "Ooops" it doesn't fill me with a sense of security that my data is safe.  Needless to say I shall be finding a new provider.  iDrive looks quite interesting, I wonder if that is any good?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For anyone who is interested, here is a log of my support chat.  At first I had a guy named "Vinod" who thought it was okay to guess answers rather than checking them, so I didn't want to talk to him again....&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 102, 0);"&gt;Please wait for a site operator to respond.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 102, 0);"&gt;You are now chatting with 'Chandra '&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: **DO NOT** TRANSFER ME TO VINOD!!!&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Welcome to Mozy Live Support. May I have your account email address please?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Please reassure me you wont transfer me&lt;/div&gt;&lt;div&gt;Peter Morris: I do not want to talk to Vinod&lt;/div&gt;&lt;div&gt;Peter Morris: (My email address)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, how may I assist you today?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I have just re-installed Windows and want to restore&lt;/div&gt;&lt;div&gt;Peter Morris: The "Restore" context menu is not on "My computer" in Explorer after installing MozyHome&lt;/div&gt;&lt;div&gt;Peter Morris: And the "History" tab in MozyHome shows no history&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Which version of Windows did you install ?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I was on vista, now I am on XP&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I understand that you wish to restore you data. Is that correct?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Last time this happened to me someone had to associate my backups from my previous machine to my new machine&lt;/div&gt;&lt;div&gt;Peter Morris: Yes I do&lt;/div&gt;&lt;div&gt;Peter Morris: It's 54GB&lt;/div&gt;&lt;div&gt;Peter Morris: Which is larger than recommended for web-restore&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Please wait till I check your records.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: and I am concerned by my History tab having no history&lt;/div&gt;&lt;div&gt;Peter Morris: thanks&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, I have checked your account and have found out that you have opted for a web restore that is now building. You will receive a email once the restore is built. Is there any concern you are having currently?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: The website says to use web restore for up to 20GB&lt;/div&gt;&lt;div&gt;Peter Morris: Mine is 54GB&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : yes&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: So I think it would be more efficient to restore through the mozyhome application&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : How much data did you select?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : no.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: *and* I am concerned that it is not showing my backup history&lt;/div&gt;&lt;div&gt;Peter Morris: I selected all 54GB&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : You have to choose DVD restore above 20 GB&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;NOTE: DVD restore would cost me £60, and is only available to USA addresses.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153); font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I have to pay to get my data back?&lt;/div&gt;&lt;div&gt;Peter Morris: I didn't have to before&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : If you have to select few files then client restore is efficient else web restore below 20 gb and DVD restore above 20 GB&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Last time this happened I spoke to someone on here and they linked my new installation with my old history&lt;/div&gt;&lt;div&gt;Peter Morris: then I did a restore through the Windows application&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Perer, You have to associate you computer with your old data&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I will assist you in doing so&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: When given the option&lt;/div&gt;&lt;div&gt;Peter Morris: "Add new computer"&lt;/div&gt;&lt;div&gt;Peter Morris: "Replace PMORRIS-PC" (old computer)&lt;/div&gt;&lt;div&gt;Peter Morris: Vinod told me to choose replace&lt;/div&gt;&lt;div&gt;Peter Morris: is that incorrect?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : no, that is correct if you replace with your old machine, you will be associated with your old data&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : If you add, then another comp will be added to your account &lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Good. So please do associate my backup history with my new installation&lt;/div&gt;&lt;div&gt;Peter Morris: So I can restore through Windows, and keep my history too&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Did you perform a backup after associating your computer/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: no&lt;/div&gt;&lt;div&gt;Peter Morris: My last was yesterday morning&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : one moment please...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: and I have installed MozyHome today&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Please wait a moment....&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, I am sorry to inform you that your old backup history will not be available to see since you have migrated your operating system from Vista to XP.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I only need APPS and DATA&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: the rest is irrelevant&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : You have associated your computer but since there &lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: but since there?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I am unable to trace your requirement since I am unable to understand your requirement of History of backups.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: The windows application&lt;/div&gt;&lt;div&gt;Peter Morris: 1: Double click in the tray to bring it up&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 2: Click the "History" button&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Expected: All backup history for my account so that I can restore&lt;/div&gt;&lt;div&gt;Peter Morris: Actual: It is empty&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : For restoring your data You have to go into Restore tab. History tab is just for your backup reference&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: There is no Restore tab&lt;/div&gt;&lt;div&gt;Peter Morris: I suspect because Mozy thinks there is no history&lt;/div&gt;&lt;div&gt;Peter Morris: My history is missing&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : oops....&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;NOTE: "Oops" is not a word I like to see when discussing how to restore my lost files!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, I suspect your installation is corrupt since it is not possible that you don't have a restore tab. Please cross check once again and if not available we have to perform a repair install of the client.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: This has happened before&lt;/div&gt;&lt;div&gt;Peter Morris: It is because there is no backup history associated with my account&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : That's quite strange&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: that is what it was last time at least&lt;/div&gt;&lt;div&gt;Peter Morris: you want me to uninstall the software?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : no&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Ealier you said "Perer, You have to associate you computer with your old data"&lt;/div&gt;&lt;div&gt;Peter Morris: That is what I need you to do&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Login to your account and download the latest client. Install over the existing and when it asks for repair install click OK&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: just Apps and Data will do&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, I think you have already associated you computer, I can find one comp by the name IMAC. is that the new comp?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: yes it is&lt;/div&gt;&lt;div&gt;Peter Morris: PMORRIS-PC was the name I used previously&lt;/div&gt;&lt;div&gt;Peter Morris: IMAC is this installation&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : That means you have associated your comp.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: but there is no history&lt;/div&gt;&lt;div&gt;Peter Morris: I have done a repair, still no Restore button&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : History is not at all a basic requirement, all you need is to backup and to restore your data&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: History is important if like me you sometimes need an old file which was not present in your last backup&lt;/div&gt;&lt;div&gt;Peter Morris: but anyway, at this point I would be happy with a restore &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, just try running a backup.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: starting now&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : good&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: with a new folder called "DeleteMe"&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: (Communicating with Mozy servers)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : It will take some time.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Please wait and check what happens.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Still waiting, I will tell you when it changes&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok. please wait and see for some more time. If it is not connecting, then I would suggest you to send me the log file that is located in the following location:  C:\Program Files\MozyHome\Data\mozy.log&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;Approximately 10-15 minutes later.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 10May2009 20:05:11 mozybackup.exe: Setting encryption type on servers... 10May2009 20:05:49 mozybackup.exe: Retrieving manifest from servers...&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: It's a large backup set, so might take a while&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: oh no, the original backup set was large&lt;/div&gt;&lt;div&gt;Peter Morris: this is 1 folder and 1 file&lt;/div&gt;&lt;div&gt;Peter Morris: should have finished&lt;/div&gt;&lt;div&gt;Peter Morris: shall I cancel then retry?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : sure.. go ahead&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: okay, says "Communicating with MozyHome servers"&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 10May2009 20:13:22 mozybackup.exe: Retrieving manifest from servers...&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I would suggest you to wait and check if the backup happens. If not, send me the log file that is located in the following location: C:\Program Files\MozyHome\Data\mozy.log&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Apart from that, I would request you to send me the screenshot of your backup console that is without a restore tab. It's quite wierd...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: where shall I send them to?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : We will create a case ticket and send it across to you. Please reply to the mail with the attachments. We will precisely look into the issue and will resolve the same at the earliest.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: email (a temporary email address)&lt;/div&gt;&lt;div&gt;Peter Morris: that's easier for me at the moment&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, we cannot use the email address other than your Mozy account email address.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: okay, use that one then&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Thanks. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: How long should this web-restore take?&lt;/div&gt;&lt;div&gt;Peter Morris: It hasn't finished generating yet&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : It will take some time since you have almost 54 GB to download.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : You will receive a mail once the building of your restore is complete.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : The restore will have a number of parts that you will have to download one after the other.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I am clicking Refresh in my browser&lt;/div&gt;&lt;div&gt;Peter Morris: requested 1 hour and 15 minutes ago&lt;/div&gt;&lt;div&gt;Peter Morris: Status = In_Progress&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Well, that will only refresh your browser.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : You will have to be patient since you have almost 54 GB to download that might have taken weeks to upload.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I am not downloading it yet&lt;/div&gt;&lt;div&gt;Peter Morris: it is "preparing" it for me so I can download it when it has finished&lt;/div&gt;&lt;div&gt;Peter Morris: It has taken 75 minutes and there isn't anything for me to download yet&lt;/div&gt;&lt;div&gt;Peter Morris: I know it will take days to download&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Mozy is preparing your download and will subsequently make a number of parts that you can download easily.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: but 75 minutes to prepare the files for me to download?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter... It does take that much time.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;NOTE: With hind-sight, yes, it is probably zipping up my files or something so 54GB should take a long time. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Still no email from you, has it been sent?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Email is being drafted. You will be receiving it soon.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : By the way, what  time is it now there?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 20:36&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : AM ?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;Okay, NOW I am getting worried!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 20 = 8pm&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Sorry about that. am not quite good in timings.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, mail has been sent. Please check and reply with the attachments.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Why is the email from Vinod and not you?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, That won't make any difference, I will put my transcripts and will work accordingly.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, are you with me ?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: yes&lt;/div&gt;&lt;div&gt;Peter Morris: just sent your email&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : thanks, let me check your mail.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, did you use the same subject line while replying?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: yes&lt;/div&gt;&lt;div&gt;Peter Morris: I just clicked reply, attached files....send&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok, I haven't received yet. need to check...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: got it?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : It has not yet come. Please check if you have received any undeliverable message..&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: no bounce&lt;/div&gt;&lt;div&gt;Peter Morris: would you like me to put the files on the web?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : If you don't mind let's wait for some more time.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: (Pasted 2 urls)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I have checked the log and there seems to be a database issue along with the manifest. &lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Go to C:\Program Files\MozyHome\Data using Windows Explorer.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : 2. Remove / delete the file named "cache.dat" and restart the computer.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : 3. Now try to perform a manual backup.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: If I reconnec to support will I get you or someone else?&lt;/div&gt;&lt;div&gt;Peter Morris: I don't want Vinod back&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : You can connect back to me.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 153, 153);"&gt;Computer is rebooted.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 153, 0);"&gt;Please wait for a site operator to respond.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 153, 0);"&gt;You are now chatting with 'Chandra '&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: hi&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Hi Peter.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : What is the status now ?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: Now I see a restore button&lt;/div&gt;&lt;div&gt;Peter Morris: but I am VERY concerned&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Great !!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I have logged into the web&lt;/div&gt;&lt;div&gt;Peter Morris: and it now says I am using 16GB&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: and not 54GB&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok wait a moment..&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I am looking into your account...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, I would suggest you to perform a web restore and while doing so you will find an option to select the date of restore. Please browse through the dates and you should find your data. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: when I log in&lt;/div&gt;&lt;div&gt;Peter Morris: to the website&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Yes&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: before I did this 1 file backup it said "In use, 54GB"&lt;/div&gt;&lt;div&gt;Peter Morris: then I did this 1 file backup&lt;/div&gt;&lt;div&gt;Peter Morris: and now it says "In use, 16GB"&lt;/div&gt;&lt;div&gt;Peter Morris: that's 38GB of data missing!&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok just login and click on web restore and browse through the dates.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I have already done so, I am just waiting for it&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 53.82GB that says&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: So I have 16GB on the main site, 53.82GB on the restore page, and neither are right&lt;/div&gt;&lt;div&gt;Peter Morris: it sould be 54.2GB if I recall&lt;/div&gt;&lt;div&gt;Peter Morris: or 52.4&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Anyway, we will look into it later, I would suggest you to start restoring your data. I may have to escalate the data mismatch happening.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: ok&lt;/div&gt;&lt;div&gt;Peter Morris: thanks&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Is there anything else I can assist you with?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I don't think so no&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Thank you for choosing Mozy, Have a nice day !&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: yes&lt;/div&gt;&lt;div&gt;Peter Morris: there is&lt;/div&gt;&lt;div&gt;Peter Morris: I can see a missing file&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: 2009-01-15 Baby Scan 2.wmv&lt;/div&gt;&lt;div&gt;Peter Morris: Slightly important, just a video scan of my baby&lt;/div&gt;&lt;div&gt;Peter Morris: I want this escalated immediately&lt;/div&gt;&lt;div&gt;Peter Morris: and I want someone to phone me&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, my recommendation would be to start restoring your data right away, I will put across this issue and get it resolved.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: There is a problem with that&lt;/div&gt;&lt;div&gt;Peter Morris: if the data is incorrect&lt;/div&gt;&lt;div&gt;Peter Morris: I may end up with old or corrupted data&lt;/div&gt;&lt;div&gt;Peter Morris: and not realise a file was wrong/corrupt until 12 months from now&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : yes.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: and by then it will be too late&lt;/div&gt;&lt;div&gt;Peter Morris: It needs to be fixed BEFORE I can trust the data that is sent to me&lt;/div&gt;&lt;div&gt;Peter Morris: I have wasted enough time on this now&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I will right away escalate this issue and they will get back to you.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: I want a phone call&lt;/div&gt;&lt;div&gt;Peter Morris: (My telephone number)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, MozyHome customers do not have the telephone facility. The escalation team will do whatever possible from their end to resolve your issue.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: tough&lt;/div&gt;&lt;div&gt;Peter Morris: Mozy has messed up&lt;/div&gt;&lt;div&gt;Peter Morris: my baby scan video is missing&lt;/div&gt;&lt;div&gt;Peter Morris: do you realise how important that is to me?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I will put across your request and they will take it to a conclusion.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: These are exceptional circumstances, make an exception&lt;/div&gt;&lt;div&gt;Peter Morris: Please talk to them now before I disconnect&lt;/div&gt;&lt;div&gt;Peter Morris: then tell me what they say&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : We don't have direct interaction with them, all we can do is to forward your ticket with all our transcripts and they will come back to you at the earliest.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: There is a number that mozy-pro users can phone right?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : yes, but MozyHome customers will be re-directed to chat support and will land back to us.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: then you please phone that number, explain who you are, explain that mozy has messed up and lost some of my very important data&lt;/div&gt;&lt;div&gt;Peter Morris: and tell them that to resolve the issue you would like them to treat me as a pro user&lt;/div&gt;&lt;div&gt;Peter Morris: to fix the error that has taken place&lt;/div&gt;&lt;div&gt;Peter Morris: When a company does something wrong, it should do its best to put it right&lt;/div&gt;&lt;div&gt;Peter Morris: I am not after pro support because I do not understand something. I understand how to use your application fully&lt;/div&gt;&lt;div&gt;Peter Morris: Your company has lost some vital data&lt;/div&gt;&lt;div&gt;Peter Morris: which was there only minutes ago&lt;/div&gt;&lt;div&gt;Peter Morris: so now it needs to take exceptional steps to rectify this mistake&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : I would suggest you to wait until the escalation team responds back to you. I have put your issue on high priority and they will get your issue fixed without any concerns.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: and how will they contact me?&lt;/div&gt;&lt;div&gt;Peter Morris: by phone?&lt;/div&gt;&lt;div&gt;Peter Morris: and how long will I wait?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : No, by mail and it will be done soon&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: So I have to sit here refreshing my email until I get a reply&lt;/div&gt;&lt;div&gt;Peter Morris: How long is "soon"?&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 153, 102);"&gt;Chandra : Peter, you have to allow me to escalate the issue. They are our next level of support. They will get back to you at the earliest on priority basis. You can rely on these statements.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Peter Morris: please escalate it&lt;/div&gt;&lt;div&gt;Peter Morris: I am on the phone to mozy-pro support now&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I explained the story to the guy on the phone, he agreed to give me telephone support (first positive action of the night).  We restarted my machine, tried doing a restore again and the missing file was there!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This whole episode took over 4 hours, that's 4 hours JUST TO START doing my restore.  Shocking!  I have no confidence in Mozy at all after this, they write software which hides user interface elements instead of disabling them, the app wont show you restore history unless you do a backup first (which is pointless after a fresh installation as there is nothing to restore), and then they let staff lose on their support system who obviously do not understand the product fully.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6659769405997093649?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6659769405997093649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6659769405997093649' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6659769405997093649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6659769405997093649'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/mozy-backup.html' title='MOZY backup'/><author><name>Peter Morris</name><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><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1003103608052619538</id><published>2009-05-08T14:34:00.002+01:00</published><updated>2009-05-08T14:40:22.808+01:00</updated><title type='text'>Locking in ECO 5</title><content type='html'>Jonas has just added the following feature to ECO 5 which should turn up in the next build.&lt;br /&gt;&lt;br /&gt;Let's say you have a package with a single class LockObject in it.  This LockObject &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;must &lt;/span&gt;have TimeStamp locking, but you use this package in many applications.  In ECO 5 you can set the default locking mode for a package.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now let's say you have multiple packages that you use in many applications and the locking type is different per app.  App 1 uses AllMembers locking, App 2 uses no locking at all; how can you specify the locking type?  Now you can add a .NET attribute to the top of your application specific EcoSpace class....&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[UmlTaggedValue("Eco.OptimisticLocking", "AllMembers")]&lt;br /&gt;public class Application1EcoSpace : DefaultEcoSpace&lt;br /&gt;{&lt;br /&gt;  etc....&lt;br /&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Locking is determined like so:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;The kind specified on the class itself.&lt;/li&gt;&lt;li&gt;The kind specified on any super class.&lt;/li&gt;&lt;li&gt;The kind specified on the package.&lt;/li&gt;&lt;li&gt;The kind specified on the EcoSpace.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1003103608052619538?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1003103608052619538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1003103608052619538' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1003103608052619538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1003103608052619538'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/locking-in-eco-5.html' title='Locking in ECO 5'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2533175927233667972</id><published>2009-05-08T13:47:00.000+01:00</published><updated>2009-05-08T13:49:11.849+01:00</updated><title type='text'></title><content type='html'>Someone I know recently got stung by this, so I thought I&amp;#8217;d mention it...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;int a = 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;int b = a;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Console.WriteLine(AreSame(a, b).ToString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;Console.ReadLine();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static bool AreSame(object a, object b)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;return a == b;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What&amp;#8217;s the output to the console?  Does a equal b?  Even if you didn&amp;#8217;t know the answer you will have guessed it is False otherwise this blog would be totally pointless!&lt;br /&gt;&lt;br /&gt;It&amp;#8217;s false because the parameter types of AreSame are both &amp;quot;object&amp;quot;.  For a value type such as &amp;quot;int&amp;quot; to be passed as an object it needs to be boxed, so a new object instance is created which stores the value &amp;quot;1&amp;quot;, but this is done for both parameters so we end up with 2 new instances both holding the value 1.&lt;br /&gt;&lt;br /&gt;1 equals 1 for value types but the default comparison for System.Object is to compare references.  If they are not the exact same object (and in this case they are not) then the result is false.  Now if we typecast both a and b back to integers this would pass, but the fact that we are using System.Object parameters suggests we don&amp;#8217;t know the true type.  Instead we need to use Object.Equals, because no matter what the original type is it should have a correct implementation of Equals().&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;static bool AreSame(object a, object b)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (a == null &amp;amp;&amp;amp; b == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (a == null || b == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;return a.Equals(b);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the moral of the story is this.  If ever you are passed &amp;quot;object&amp;quot; references make sure you check for equality using Equals!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2533175927233667972?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2533175927233667972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2533175927233667972' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2533175927233667972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2533175927233667972'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/someone-i-know-recently-got-stung-by.html' title=''/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2147473294702211675</id><published>2009-05-07T20:43:00.003+01:00</published><updated>2009-05-07T20:47:37.629+01:00</updated><title type='text'>I don't like AccuRev</title><content type='html'>A company I have been contracting for decided to use AccuRev as its source control solution. I've not liked it from the start because it is too much work, the terminology in it is quite frankly stupid, and it is far too "chatty" when you work remotely.  Anyway, for some time now I have suspected it has been losing source code.  On a few occasions I have found myself looking at source code and thinking "I could swear I have already done this!".&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Well, last week I wrote some pretty nice code which used multiple threads in a test case to ensure I experienced expected behavior when multiple users update the same objects in a database.  Today one of the other programmers said to me "Didn't you write more tests than this?" and showed me the tests.  My multi-threaded tests were gone!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I knew it all along, but now it is indesputible, AccuRev has been losing my work!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2147473294702211675?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2147473294702211675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2147473294702211675' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2147473294702211675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2147473294702211675'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/i-dont-like-accurev.html' title='I don&apos;t like AccuRev'/><author><name>Peter Morris</name><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><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4116549702260201123</id><published>2009-05-06T10:52:00.002+01:00</published><updated>2009-05-06T10:52:52.891+01:00</updated><title type='text'>The Pragmatic Programmer</title><content type='html'>My copy of &lt;a href="http://www.pragprog.com/the-pragmatic-programmer"&gt;this book&lt;/a&gt; has just turned up.  I am book hungry these days, can't wait to read it :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4116549702260201123?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4116549702260201123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4116549702260201123' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4116549702260201123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4116549702260201123'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/pragmatic-programmer.html' title='The Pragmatic Programmer'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4696930768106366165</id><published>2009-05-04T19:16:00.002+01:00</published><updated>2009-05-04T19:20:44.994+01:00</updated><title type='text'>Connascence</title><content type='html'>I&amp;#8217;ve just finished reading &lt;a href="http://www.amazon.com/Every-Programmer-Should-Object-Oriented-Design/dp/0932633315"&gt;What every programmer should know about object oriented design&lt;/a&gt;.  The first couple of sections are a bit useless really, but the 3rd section is very good.&lt;br /&gt;&lt;br /&gt;I particularly liked the section on the difference types of connascence in software.  There&amp;#8217;s some basic information about it &lt;a href="http://docs.rubyrake.org/articles/connascence/index.html"&gt;here&lt;/a&gt; if you fancy a brief overview of what it is about.&lt;br /&gt;&lt;br /&gt;It&amp;#8217;s a really good book, I recommend you read it!  Should only take a day if you only read the 3rd section.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4696930768106366165?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4696930768106366165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4696930768106366165' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4696930768106366165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4696930768106366165'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/connascence.html' title='Connascence'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3955436219498350033</id><published>2009-05-04T11:41:00.001+01:00</published><updated>2009-05-04T11:43:35.977+01:00</updated><title type='text'>I've just ordered a Java book</title><content type='html'>http://java.sun.com/blueprints/corej2eepatterns/index.html&lt;br /&gt;&lt;br /&gt;It was only $0.41 used in the USA, came to about £6 with posting to the UK.  I don't write Java and at the moment have no interest in learning it, but I expect I will be able to read it without problems (never really looked at it).  &lt;br /&gt;&lt;br /&gt;So why did I buy this book?  Because it is about this...&lt;br /&gt;&lt;br /&gt;&lt;img src="http://java.sun.com/blueprints/corej2eepatterns/images06/figure06_02.gif" alt="Patterns"/&gt;&lt;br /&gt;&lt;br /&gt;That's why :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3955436219498350033?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3955436219498350033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3955436219498350033' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3955436219498350033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3955436219498350033'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/ive-just-ordered-java-book.html' title='I&apos;ve just ordered a Java book'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4657048859877734510</id><published>2009-05-04T10:28:00.004+01:00</published><updated>2009-05-04T10:32:11.730+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Prism'/><title type='text'>Prism - Invariants, and pre/post conditions - update</title><content type='html'>Just found out that I can do this too&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public invariants&lt;br /&gt;&amp;nbsp;&amp;nbsp;Overdraft &amp;gt;= 0 : &amp;#8217;OverdraftGreaterThanOrEqualToZero&amp;#8217;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance + Overdraft &amp;gt;= 0 : &amp;#8217;BalancePlusOverDraftGreaterThanOrEqualToZero&amp;#8217;;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then in my handler routine I get that message!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;if not IsValid then&lt;br /&gt;&amp;nbsp;&amp;nbsp;throw new Exception(ErrorMessage);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Excellent :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4657048859877734510?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4657048859877734510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4657048859877734510' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4657048859877734510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4657048859877734510'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/prism-invariants-and-prepost-conditions_04.html' title='Prism - Invariants, and pre/post conditions - update'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5766645168594573589</id><published>2009-05-04T09:11:00.001+01:00</published><updated>2009-05-04T09:51:18.277+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Prism'/><title type='text'>Prism - Invariants, and pre/post conditions</title><content type='html'>I&amp;#8217;ve been looking at &lt;a href="http://remobjects.com/prism"&gt;Prism&lt;/a&gt; some more recently.  I&amp;#8217;m a bit annoyed with myself really because someone has been telling me to look at it for years but I wanted to concentrate on C#.  Now that I am looking at &lt;br /&gt;&lt;br /&gt;it I see things in there which I really like.&lt;br /&gt;&lt;br /&gt;Recently I am looking at invariants, pre-conditions, and post-conditions.  First let me show you the invariant support.  An invariant is like a class constraint, it specifies a condition which must be true &lt;br /&gt;&lt;br /&gt;but when must it be true?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BankAccount = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property Balance : Decimal read write;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public invariants&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Balance &amp;gt;= 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The invariant is enforced each time a public method exits.  Actually it is enforced after the post-conditions of a public method exits but we don&amp;#8217;t have any post conditions yet.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class method ConsoleApp.Main;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;var account : BankAccount := new BankAccount();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The next line throws an assertion error&lt;br /&gt;&amp;nbsp;&amp;nbsp;account.Balance := -1;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Property getters and setters are considered methods, so as long as you aren&amp;#8217;t publically exposing a field (which you should never do) then you are protected.  In the following example the setter for Balance is Private but it is set via a public constructor, so the assertion is checked when the public constructor exits.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BankAccount = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;constructor (OpeningBalance : Decimal);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property Balance : Decimal read private write;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public invariants&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Balance &amp;gt;= 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;constructor BankAccount(OpeningBalance : Decimal);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := OpeningBalance;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class method ConsoleApp.Main;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;var account : BankAccount := new BankAccount(-1);&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Obviously we can&amp;#8217;t set a balance for an account directly because its setter is private, so let&amp;#8217;s add another feature we could use instead.  Next I will add a Withdraw method with overdraft facility.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BankAccount = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;constructor (OpeningBalance : Decimal);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property Balance : Decimal read private write;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method Withdraw(Amount : Decimal);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property Overdraft : Decimal read write;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public invariants&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Balance + Overdraft &amp;gt;= 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Overdraft &amp;gt;= 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;constructor BankAccount(OpeningBalance : Decimal);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := OpeningBalance;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BankAccount.Withdraw(Amount : Decimal);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := Balance - Amount;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see the invariants of this class are that the Overdraft must be &amp;gt;= 0 and the Balance plus the Overdraft must be &amp;gt;= 0.  The following program illustrates use of this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class method ConsoleApp.Main;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;var account : BankAccount := new BankAccount(10);&lt;br /&gt;&amp;nbsp;&amp;nbsp;account.Overdraft := 10;&lt;br /&gt;&amp;nbsp;&amp;nbsp;account.Withdraw(20);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Outputs -10&lt;br /&gt;&amp;nbsp;&amp;nbsp;Console.WriteLine(&amp;quot;Balance : &amp;quot; + account.Balance.ToString);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The next line throws an assertion error&lt;br /&gt;&amp;nbsp;&amp;nbsp;account.Withdraw(1);&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So that&amp;#8217;s an invariant now let&amp;#8217;s look at pre-conditions.  The Withdraw method would currently accept an Amount of zero which obviously makes no sense, what&amp;#8217;s worse though is that it could accept a negative number and actually increment the balance so let&amp;#8217;s add a pre-condition to prevent this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;constructor BankAccount(OpeningBalance : Decimal);&lt;br /&gt;require&lt;br /&gt;&amp;nbsp;&amp;nbsp;OpeningBalance &amp;gt;= 0;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := OpeningBalance;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BankAccount.Withdraw(Amount : Decimal);&lt;br /&gt;require&lt;br /&gt;&amp;nbsp;&amp;nbsp;Amount &amp;gt; 0;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := Balance - Amount;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now if I try to create the bank account with a negative opening balance, or if I try to withdraw an amount of money that is not greater than zero I will experience an assertion.  It&amp;#8217;s possible also to add post-conditions by using the &amp;quot;ensure&amp;quot; keyword.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;method BankAccount.Withdraw(Amount : Decimal);&lt;br /&gt;require&lt;br /&gt;&amp;nbsp;&amp;nbsp;Amount &amp;gt; 0;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance := Balance - Amount;&lt;br /&gt;ensure&lt;br /&gt;&amp;nbsp;&amp;nbsp;Balance = old Balance - Amount;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note here that I use &amp;quot;old Balance&amp;quot;, which is the value that Balance was when the method was called.  The combination of pre/post conditions say that &amp;quot;If you pass me a positive amount of money I will decrement the Balance by that exact amount&amp;quot;.  &lt;br /&gt;&lt;br /&gt;I haven&amp;#8217;t finished yet!  The default behaviour is to use Debug.Assert whenever an invariant is broken, a pre-condition is not met, or a post-condition is not satisfied.  It is possible to change this behaviour for a Release build so that your invariants are not only checked during debugging.  This is particularly useful when you are persisting and retrieving objects&amp;#8217; state in a database, if another application bypasses your business classes and writes directly to the database your invariants ensure that your application fails immediately rather than working with an invalid state and possibly causing more invalid state elsewhere.  To do this create a static class like so&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;ClassContractChecker = public static class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class method Check(IsValid : Boolean; ErrorMessage : String);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;class method ClassContractChecker.Check(IsValid : Boolean; ErrorMessage : String);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;if not IsValid then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise new Exception(&amp;#8217;Unexpected class contract violation &amp;#8217; + ErrorMessage);&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then in the project&amp;#8217;s properties look on the DEBUG tab and enter&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;Assertion class name : MyNameSpace.ClassContractChecker&lt;br /&gt;&amp;nbsp;&amp;nbsp;Assertion method : Check&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;NOTE: This is a separate setting for each build configuration, so set it to RELEASE&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Now instead of using Debug.Assert any time something goes wrong the Prism compiler will execute this static method so that we can throw an exception instead (currently this doesn&amp;#8217;t work for RELEASE builds, I have reported it as a bug so hopefully it will be fixed).&lt;br /&gt;&lt;br /&gt;This kind of programming allows us to identify errors in our objects&amp;#8217; states early on.  Using this approach will cause an app to &amp;quot;fail hard&amp;quot; at the first sign of something unexpected happening.  This may sound harsh at first but the app failing immediately and prominently is better than it being wrong for a long period of time and causing weird side effects in your logic which could end up with nonesense data - that would be a much worse situation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5766645168594573589?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5766645168594573589/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5766645168594573589' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5766645168594573589'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5766645168594573589'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/05/prism-invariants-and-prepost-conditions.html' title='Prism - Invariants, and pre/post conditions'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1522749702921964605</id><published>2009-04-30T13:46:00.002+01:00</published><updated>2009-04-30T14:05:22.049+01:00</updated><title type='text'>Injecting into the ECO cache</title><content type='html'>In my previous post I showed an interface ILockable.  The actual interface is&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface ILockable&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;Guid GetLockID();&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type GetLockObjectType();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Any lockable resource implements this interface.  In its constructor it creates an instance of a class which descends from LockObject, a Document class for example would create an instance of DocumentLockObject which descends from LockObject.  The &amp;quot;Document&amp;quot; class would store away the ID of the DocumentLockObject in a private attribute.  Now when my LockingService is asked to lock multiple (un-related) instances which implement ILockable I want to do this&lt;br /&gt;&lt;br /&gt;1: Create a new EcoSpace&lt;br /&gt;2: Load objects efficiently&lt;br /&gt;3: Get locks&lt;br /&gt;4: Update the DB&lt;br /&gt;5: Dispose of the EcoSpace&lt;br /&gt;&lt;br /&gt;If I experience an OptimisticLockingException then loop and try again.  The item I would like to discuss is #2, &amp;quot;Load objects efficiently&amp;quot;.  In my model a LockObject has a *--1 association to Session identifying which session has locked it, I want to ensure that the LockObject + LockObject.ExclusiveLockedSession instances are loaded as quickly as possible.  If I have a collection of LockObject I can easily use the following code to efficiently load all relevant sessions&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;objectSpace.EnsureRelatedObjects(result, x =&amp;gt; x.ExclusiveLockedSession);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This extension method assumes that the association name is the same as the class&amp;#8217;s property name and is implemented like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public static class PersistenceServiceExtender&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;public static void EnsureRelatedObjects&amp;lt;T&amp;gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this IEcoServiceProvider instance, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IEnumerable&amp;lt;T&amp;gt; objects, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt; member&lt;br /&gt;&amp;nbsp;&amp;nbsp;) where T : IEcoObject&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MemberExpression memberExpression = (MemberExpression)member.Body;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ps = instance.GetEcoService&amp;lt;IPersistenceService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ps.EnsureRelatedObjects(objects, memberExpression.Member.Name);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is possible because I have a list of ECO class instances as my basis and can therefore rely on the ExclusiveLockedSession association to get ECO to quickly load the related objects.  The problem I need to solve is this: given an interface (ILockable) how do I get all LockObjects?  Remember that these lockable objects (Document, Image, Video, etc) have no common superclass and therefore have no common association to their LockObject, and this is exactly why I wanted to know the LockObject&amp;#8217;s primary key in my last post.&lt;br /&gt;&lt;br /&gt;Given a collection of ILockable I am able to determine 2 things.&lt;br /&gt;1: The primary key of its LockObject&lt;br /&gt;2: The exact type of the LockObject it refers to (DocumentLockObject, VideoLockObject, etc).&lt;br /&gt;&lt;br /&gt;In my new EcoSpace instance I can now inject locators for these objects directly into the cache.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;private IEnumerable&amp;lt;LockObject&amp;gt; GetLockObjects(IEcoServiceProvider objectSpace, IEnumerable&amp;lt;ILockable&amp;gt; subjects)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Get a reference to the cache content service so that we can inject&lt;br /&gt;&amp;nbsp;&amp;nbsp;var cache = objectSpace.GetEcoService&amp;lt;ICacheContentService&amp;gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Create an IObjectList, a list of object locators of type LockObjecct&lt;br /&gt;&amp;nbsp;&amp;nbsp;var variableFactoryService = objectSpace.GetEcoService&amp;lt;IVariableFactoryService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;IObjectList locators = variableFactoryService.CreateTypedObjectList(typeof(LockObject), false);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Inject each locator directly into the cache, avoiding the DB completely&lt;br /&gt;&amp;nbsp;&amp;nbsp;foreach (var subject in subjects)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Guid lockObjectID = subject.GetLockObjectID();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Type lockObjectType = subject.GetLockObjectType();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IObject lockObjectLocator = cache.GetObject(lockObjectID, lockObjectType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;locators.Add(lockObjectLocator);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Now I have a list of locators I can fetch the instances from&lt;br /&gt;&amp;nbsp;&amp;nbsp;//the DB in an efficient way by converting them to IList&amp;lt;LockObject&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var result = locators.GetAsIList&amp;lt;LockObject&amp;gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//And finally I ensure related objects&lt;br /&gt;&amp;nbsp;&amp;nbsp;objectSpace.EnsureRelatedObjects(result, x =&amp;gt; x.ExclusiveLockedSession);&lt;br /&gt;&amp;nbsp;&amp;nbsp;return result;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1522749702921964605?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1522749702921964605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1522749702921964605' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1522749702921964605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1522749702921964605'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/injecting-into-eco-cache.html' title='Injecting into the ECO cache'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1531018398220557557</id><published>2009-04-27T17:01:00.002+01:00</published><updated>2009-04-27T17:20:24.871+01:00</updated><title type='text'>Setting the ID of an ECO object before it is saved</title><content type='html'>This might seem like a bit of an odd requirement.  I am currently implementing an offline pessimistic locking service in an ECO app.  This will mean that I can use a LockingService to place read/write locks on objects.  As usual I have gone for an interface approach (ILockable) rather than making all my lockable classes descend from a base Lockable class.  Remember, it's what I &lt;strong&gt;DO&lt;/strong&gt;, not what I &lt;strong&gt;AM&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;The idea is that I have a Lockable object which holds a list of Sessions.  These Sessions indicate who has a read lock, and who has a write lock on the object.  Each ILockable business entity will create its own private instance of Lockable when it is created.&lt;br /&gt;&lt;br /&gt;The thing is, this application will at times need to lock thousands of objects in as little time as possible.  As there is no common ancester for these ILockable classes I can't just use EnsureRelatedObjects to traverse an association name and load all LockObject instances in a single select.&lt;br /&gt;&lt;br /&gt;I decided that what I needed was to be able to get the ID of the privately owned LockObject and store it in the ILockable entity.  This way ILockable only needs to look like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface ILockable&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;Guid GetLockID();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I can then&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Make a list of lock ID's I need to lock.&lt;/li&gt;&lt;li&gt;Inject each into the EcoSpace cache directly, because I know that each ID is always a LockObject.&lt;/li&gt;&lt;li&gt;Add the resulting IObject to an IObjectList.&lt;/li&gt;&lt;li&gt;Call IPersistenceService.EnsureRange on the IObjectList to fetch them all at once.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The key to this approach is to know the ID of the LockObject from the owning ILockable without having to navigate the association self.LockObject in order to retrieve it.  Now this isn't really that difficult if the object has already been saved because you can use the IExternalIdService to get the object's primary key.&lt;/p&gt;&lt;p&gt;However, the LockObject is created &lt;strong&gt;WITH&lt;/strong&gt; the ILockable object, we can't get the LockObjects key yet because it hasn't been saved.  We can't save the LockObject first because the app might never save the ILockable entity and we'd get an orphaned LockObject.&lt;/p&gt;&lt;p&gt;So, here is a little trick to set the primary key of a new ECO object instance before you have saved it.  This way we know what the primary key will be before we even save it.  First I created an interface like so:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface IObjectIdentityService&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;void SetIdentity(IObjectProvider instance, Guid identity);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Then in the LockObject class I added the following code to the IEcoServiceProvider (new object instance) constructor.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ID = Guid.NewGuid(); //Just a GUID attribute on the class&lt;br /&gt;serviceProvider.GetEcoService&amp;lt;IObjectIdentityService&amp;gt;()&lt;br /&gt;&amp;nbsp;&amp;nbsp;.SetIdentity(this, ID);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;That's all there is to it.  Obviously I need the key on my objects to be a GUID because there is no way of getting an integer value you can be sure is unique.  Finally here is the implementation of the IObjectIdentityService, I implemented it within my EcoSpace to gain access to FrontsidePolicy.ObjectRepresentationProvider, but I could have easily passed this in a constructor:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;void IObjectIdentityService.SetIdentity(IObjectProvider instance, Guid identity)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (!instance.AsIObject().ServiceProvider.GetEcoService&amp;lt;IStateService&amp;gt;().IsNew(instance))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new InvalidOperationException(&amp;quot;Instance must be new&amp;quot;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Locator locator = FrontsidePolicy.ObjectRepresentationProvider.LocatorForIObject(instance.AsIObject().ObjectInstance);&lt;br /&gt;&amp;nbsp;&amp;nbsp;ObjectId newID = new DefaultId(identity, locator.Id.ClassId);&lt;br /&gt;&amp;nbsp;&amp;nbsp;FrontsidePolicy.Cache.SetObjectId(locator, newID);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1531018398220557557?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1531018398220557557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1531018398220557557' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1531018398220557557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1531018398220557557'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/setting-id-of-eco-object-before-it-is.html' title='Setting the ID of an ECO object before it is saved'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1536184028037419911</id><published>2009-04-27T14:41:00.002+01:00</published><updated>2009-04-27T14:45:43.980+01:00</updated><title type='text'>Using a GUID as the key in ECO</title><content type='html'>&lt;ol&gt;&lt;li&gt;Drop a DefaultORMappingBuilder component next to your persistence mapper.&lt;/li&gt;&lt;li&gt;Point PersistenceMapperxxxxx1.NewMappingProvider and RuntimeMappingProvider to point to this new component.&lt;/li&gt;&lt;li&gt;On your PersistenceMapper component expand SqlDatabaseConfig and click the [...] next to KeyMappers, ensure that you have an item named "GuidMapper" with the MapperTypeName set to "Eco.Persistence.GuidKeyMapper".  If not then add it.&lt;/li&gt;&lt;li&gt;On your DefaultORMapperBuilder set IdKeySignature to System.Guid (case sensitive).&lt;/li&gt;&lt;li&gt;Set its IdMapperName to GuidMapper.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Now regenerate your DB.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1536184028037419911?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1536184028037419911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1536184028037419911' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1536184028037419911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1536184028037419911'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/using-guid-as-key-in-eco.html' title='Using a GUID as the key in ECO'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4147026806295361008</id><published>2009-04-23T12:33:00.001+01:00</published><updated>2009-04-23T12:35:36.306+01:00</updated><title type='text'>Prism AOP - 4</title><content type='html'>I&amp;#8217;ve played a little more with Prism.  I find it a little difficult to mentally code on two levels.  Level one being the code I am writing for the aspect, and level two being the code I am writing which will executed by the target.  Having said that, as soon as I ran my app and saw the output everything was worthwhile.&lt;br /&gt;&lt;br /&gt;Here is my Person class&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: EcoAspects.BusinessClass(&amp;#8217;DomainClasses.Package1&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FFirstName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FLastName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property FirstName : String read FFirstName write FFirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property LastName : String read FLastName write FLastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;here is the code which uses that class&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class method ConsoleApp.Main;&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;P: Person;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;P := new Person();&lt;br /&gt;&amp;nbsp;&amp;nbsp;for A in typeOf(Package1).GetCustomAttributes(true) do&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Console.WriteLine(a.ToString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;P.FirstName := &amp;#8217;Peter&amp;#8217;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;P.LastName := &amp;#8217;Morris&amp;#8217;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ShowGetValueByIndexResult(P as ILoopBack2, 0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;ShowGetValueByIndexResult(P as ILoopBack2, 1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;DoSetValueByIndex(P as ILoopBack2, 0, &amp;#8217;Hello&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;DoSetValueByIndex(P as ILoopBack2, 1, &amp;#8217;There&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;ShowGetValueByIndexResult(P as ILoopBack2, 0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;ShowGetValueByIndexResult(P as ILoopBack2, 1);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Console.ReadLine();&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;class method ConsoleApp.ShowGetValueByIndexResult(Obj: ILoopBack2; I: Integer);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Console.WriteLine(Obj.GetValueByIndex(I).ToString());&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;class method ConsoleApp.DoSetValueByIndex(Obj: ILoopBack2; I: Integer; Value: Object);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Obj.SetValueByIndex(I, Value);&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and finally, here is the output.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;Eco.UmlCodeAttributes.UmlMetaAttributeAttribute&lt;br /&gt;&amp;nbsp;&amp;nbsp;Peter&lt;br /&gt;&amp;nbsp;&amp;nbsp;Morris&lt;br /&gt;&amp;nbsp;&amp;nbsp;Hello&lt;br /&gt;&amp;nbsp;&amp;nbsp;There&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Fantastic!  Using a single line of code I am able to morph the Person class so that it acts as though I had written it like this (might not compile, I wrote this next source in notepad)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = public class(Object, ILoopBack2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FFirstName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FLastName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method GetValueByIndex(I: Integer): Object; virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method SetValueByIndex(I: Integer; Value: Object); virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property FirstName : String read FFirstName write FFirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property LastName : String read FLastName write FLastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;method Person.GetValueByIndex(I: Integer): Object;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;case I of&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0: exit FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1: exit LastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method Person.SetValueByIndex(I: Integer; Value: Object): Object;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;case I of&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0: FirstName := String(Value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1: LastName := String(Value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In addition my code will find a class DomainClasses.Package1 and add an attribute&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[Eco.UmlCodeAttributes.UmlMetaAttributeAttribute(&amp;quot;ownedElement&amp;quot;, typeof(Person)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Package1 = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see the implementation of the aspect &amp;quot;BusinessClass&amp;quot; is very specific to ECO and saves a lot of writing.  What is good too is that I could easily remove the project&amp;#8217;s reference to the BusinessClass aspect which generates ECO changes and replace it with a DLL which has a BusinessClass aspect for something else - maybe a blank one which does nothing so that you can use the same class definitions as data-transer-objects.&lt;br /&gt;&lt;br /&gt;But my interest in this technology goes far beyond easily implementing an object relational mapper in an abstract way, supporting the ORM is only the first step towards achieving what I really want - archetypes.  Once I have the experience to create all of the meta-information required for mapping I can start creating my models out of patterns.  I do this a lot at the moment, but all manually.  For example in one project I had to allow my Employee, Van, and VendingMachineColumn classes to all hold stock.  Each of these would need to hold stock, record stock adjustments such a stock found/lost during stock checks, and also due to stock transfers.  &lt;br /&gt;&lt;br /&gt;It would be a bad design to descend all of these classes from a StockHolder class.  Holding stock is something you DO, and not something you ARE, so inheritance here is wrong.  What I would typically do here is&lt;br /&gt;&lt;br /&gt;1: Create a StockHolder class which holds the stock + adjustment history.&lt;br /&gt;2: Employee, Van, and VendingMachineColumn would all own an instance of StockHolder.&lt;br /&gt;3: Each class would implement&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface IStockHolder&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;StockHolder GetStockHolder();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is a one-way relationship, if I needed for example to find all stock holders with X amount of a certain stock item so that I could request a transfer this would not be sufficient.  In which case I would introduce an abstract method to StockHolder&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;object GetOwner();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then I&amp;#8217;d have a descendant of StockHolder for each owner.  EmployeeStockHolder, VanStockHolder, VendingMachineColumnStockHolder; each would have an association back to their owning object (Employee, Van, VendingMachineColumn) which they would return by overriding GetOwner.  Now this is not a lot of work, but it is repetitive.  You see the pattern in the UML but do you instantly recognise it?  Is it really self-descriptive?&lt;br /&gt;&lt;br /&gt;My AOP goal is to be able to do something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: BusinessClass(&amp;#8217;MyNameSpace.Domain.MyPackageName&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: StockHolder]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Employee = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: BusinessClass(&amp;#8217;MyNameSpace.Domain.MyPackageName&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: StockHolder]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Van = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: BusinessClass(&amp;#8217;MyNameSpace.Domain.MyPackageName&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: StockHolder]&lt;br /&gt;&amp;nbsp;&amp;nbsp;VendingMachineColumn = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The StockHolder aspect would create the descendant (TargetClassName)StockHolder with an association back to the target, and override GetOwner.  The thing is, HOW the aspect is implemented is not relevant, all I am saying is &amp;quot;this holds stock&amp;quot;.  It is short, descriptive, and instantly understandable.  It&amp;#8217;s also only a few seconds of work to make a class hold stock.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: BusinessClass(&amp;#8217;MyNameSpace.Domain.MyPackageName&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: StockHolder] //Holds stock&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: ContactDetailsHolder] //Has personal contact information&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: Auditable] //Can make employees subject to an internal audit&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: TaskAssignable] //Can assign tasks to this employee&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: CustomerRole] //Employees can purchase goods&lt;br /&gt;&amp;nbsp;&amp;nbsp;Employee = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That&amp;#8217;s the kind of thing I&amp;#8217;d like to end up with.  Much more descriptive than UML I think :-)&lt;br /&gt;&lt;br /&gt;Here is the code.  It&amp;#8217;s just proof of concept code at the moment.  I&amp;#8217;ve decided to prefix Type_ Method_ Property_ etc to the start of types, methods, and property definitions where they are referring to values from the model the aspect is being applied to; this was something I decided to do to help me to mentally split the &amp;quot;this code&amp;quot; scenario and &amp;quot;target code&amp;quot; scenario.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;namespace EcoAspects;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Collections.Generic,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Linq,&lt;br /&gt;&amp;nbsp;&amp;nbsp;RemObjects.Oxygene.Cirrus,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Text;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FPackageName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method AddClassToPackage(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method AddILoopBack2GetValueByIndex(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method AddILoopBack2SetValueByIndex(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property PackageName: String read FPackageName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;constructor (PackageName: String);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method HandleInterface(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;uses &lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Windows.Forms,&lt;br /&gt;&amp;nbsp;&amp;nbsp;Eco.ObjectImplementation,&lt;br /&gt;&amp;nbsp;&amp;nbsp;RemObjects.Oxygene.Cirrus.Statements,&lt;br /&gt;&amp;nbsp;&amp;nbsp;RemObjects.Oxygene.Cirrus.Values;&lt;br /&gt;&lt;br /&gt;constructor BusinessClassAttribute(PackageName: String);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;FPackageName := PackageName;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BusinessClassAttribute.HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddClassToPackage(Services, aType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddILoopBack2Interface(Services, aType);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BusinessClassAttribute.AddClassToPackage(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_PackageReference: ITypeReference;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_PackageDefinition: ITypeDefinition;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_UmlMetaAttributeAttribute: IAttributeDefinition;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_PackageReference := Services.FindType(PackageName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (Type_PackageReference = nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Services.EmitError(&amp;#8217;Package class not found: &amp;#8217; + PackageName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//If it is an ITypeDefinition that means it is declared as source in the current&lt;br /&gt;&amp;nbsp;&amp;nbsp;//binary and we can therefore modify it - so we can attach .NET attributes.&lt;br /&gt;&amp;nbsp;&amp;nbsp;//If it isn&amp;#8217;t an ITypeDefinition but only an ITypeReference then it is immutible&lt;br /&gt;&amp;nbsp;&amp;nbsp;//and we cannot change it.&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_PackageDefinition := Type_PackageReference as ITypeDefinition;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (Type_PackageDefinition = nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Services.EmitError(&amp;#8217;Package class cannot be modified, it is not part of the same project: &amp;#8217; + PackageName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_UmlMetaAttributeAttribute := Type_PackageDefinition.AddAttribute();&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_UmlMetaAttributeAttribute.Type := Services.FindType(&amp;#8217;Eco.UmlCodeAttributes.UmlMetaAttributeAttribute&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_UmlMetaAttributeAttribute.AddParameter(&amp;#8217;ownedElement&amp;#8217;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The value we use for Type must be a TypeOfValue based on aType.&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_UmlMetaAttributeAttribute.AddParameter(new TypeOfValue(aType));&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BusinessClassAttribute.AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2: IType;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2 := Services.FindType(&amp;#8217;EcoSupport.ILoopBack2&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (Type_ILoopBack2 = nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Services.EmitError(&amp;#8217;EcoSupport.ILoopBack2 not found, are you missing an assembly reference?&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;aType.AddInterface(Type_ILoopBack2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddILoopBack2GetValueByIndex(Services, aType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddILoopBack2SetValueByIndex(Services, aType);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BusinessClassAttribute.AddILoopBack2GetValueByIndex(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2: IType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_ILoopBack2_GetValueByIndex: IMethod;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex: IMethodDefinition;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf: CaseStatement;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Find ILoopBack2 and ILoopBack2.GetValueByIndex&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2 := Services.FindType(&amp;#8217;EcoSupport.ILoopBack2&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_ILoopBack2_GetValueByIndex := Type_ILoopBack2.GetMethods(&amp;#8217;GetValueByIndex&amp;#8217;)[0];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Implement GetValueByIndex on the target&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex := aType.AddMethod(&amp;#8217;GetValueByIndex&amp;#8217;, Services.GetType(&amp;#8217;System.Object&amp;#8217;), false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex.AddParameter(&amp;#8217;I&amp;#8217;, ParameterModifier.In, Services.GetType(&amp;#8217;System.Int32&amp;#8217;));&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex.Virtual := VirtualMode.Virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex.Visibility := Visibility.Protected;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Explicitly tie our GetValueByIndex to ILoopBack2.GetValueByIndex.  This ensures they are linked&lt;br /&gt;&amp;nbsp;&amp;nbsp;//even though our method is protected.  This hides the method when using code-completion on the&lt;br /&gt;&amp;nbsp;&amp;nbsp;//target (because it is protected), but exposes it via the interface - much cleaner!&lt;br /&gt;&amp;nbsp;&amp;nbsp;aType.AddImplements(Method_GetValueByIndex, Type_ILoopBack2, Method_ILoopBack2_GetValueByIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Build case statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf := new CaseStatement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf.What := Method_GetValueByIndex.GetParameter(&amp;#8217;I&amp;#8217;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//As a CaseIndex for each property on the class&lt;br /&gt;&amp;nbsp;&amp;nbsp;var CaseIndex: Integer := 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Prop : IProperty := aType.GetProperty(PropertyIndex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Ignore properties which cannot be read&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Ignore properties which take parameters (properties with indexers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (Prop.ParameterCount = 0) and (Prop.ReadMethod &amp;lt;&amp;gt; nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create an expression which is basically Self.Property.Read&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Property_Read : ProcValue := new ProcValue(new SelfValue(), Prop.ReadMethod);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create an exit statement which is basically - exit Self.Property.Read&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//So that we exit the method, returning the result of reading the property value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Statement_Exit : ExitStatement := new ExitStatement(Property_Read);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create the CaseIndex which consists merely of the Statement_Exit&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var CaseItem_Index : CaseItem := new CaseItem(Statement_Exit, CaseIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Add the CaseIndex to the Statement_CaseIndexOf, and increment the case index&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf.Items.Add(CaseItem_Index);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CaseIndex := CaseIndex + 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Set the body of the GetValueByIndex method we created.  Normally we can just write the exact&lt;br /&gt;&amp;nbsp;&amp;nbsp;//code we need between the begin/end identifiers, but in this case we have generated the statements&lt;br /&gt;&amp;nbsp;&amp;nbsp;//to execute dynamically, so we need to &amp;quot;unquote&amp;quot; them - which basically means &amp;quot;expand&amp;quot; or &amp;quot;compile&amp;quot;.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_GetValueByIndex.SetBody(Services, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unquote(Statement_CaseIndexOf);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;//This method is very similar to GetValueByIndex, so I will only describe the setter&lt;br /&gt;method BusinessClassAttribute.AddILoopBack2SetValueByIndex(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2: IType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_ILoopBack2_SetValueByIndex: IMethod;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex: IMethodDefinition;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf: CaseStatement;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Type_ILoopBack2 := Services.FindType(&amp;#8217;EcoSupport.ILoopBack2&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_ILoopBack2_SetValueByIndex := Type_ILoopBack2.GetMethods(&amp;#8217;SetValueByIndex&amp;#8217;)[0];&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//SetValueByIndex&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex := aType.AddMethod(&amp;#8217;SetValueByIndex&amp;#8217;, nil, false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex.AddParameter(&amp;#8217;I&amp;#8217;, ParameterModifier.In, Services.GetType(&amp;#8217;System.Int32&amp;#8217;));&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex.AddParameter(&amp;#8217;Value&amp;#8217;, ParameterModifier.In, Services.GetType(&amp;#8217;System.Object&amp;#8217;));&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex.Virtual := VirtualMode.Virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex.Visibility := Visibility.Protected;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Make explicit interface&lt;br /&gt;&amp;nbsp;&amp;nbsp;aType.AddImplements(Method_SetValueByIndex, Type_ILoopBack2, Method_ILoopBack2_SetValueByIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Build case statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf := new CaseStatement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf.What := Method_SetValueByIndex.GetParameter(&amp;#8217;I&amp;#8217;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var CaseIndex: Integer := 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Prop : IProperty := aType.GetProperty(PropertyIndex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (Prop.ParameterCount = 0) and (Prop.WriteMethod &amp;lt;&amp;gt; nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Here we need 2 statements for every CaseItem.  So we need a BeginStatement&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//which is basically a begin/end block&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Statement_CaseItemBegin : BeginStatement := new BeginStatement();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create an expression which is equivalent to Self.Property.Set(Value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Property_Write : ProcValue := new ProcValue(new SelfValue(), Prop.WriteMethod, Method_SetValueByIndex.GetParameter(&amp;#8217;Value&amp;#8217;));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create a statement based on this expression.  We can use AssignementStatement without passing a value because we have&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//already specified the value in the previous expression.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Statement_SetPropertyValue : AssignmentStatement := new AssignmentStatement(Property_Write);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Add this assignment to the Begin/End block statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Statement_CaseItemBegin.Add(Statement_SetPropertyValue);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//And add a plain &amp;quot;Exit&amp;quot; after it within the Begin/End block.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ExitMethod : ExitStatement := new ExitStatement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Statement_CaseItemBegin.Add(ExitMethod);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Craete a CaseItem for the current property index which will execute our Begin/End block.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var CaseItem_Index : CaseItem := new CaseItem(Statement_CaseItemBegin, CaseIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Add the Begin/End block statement to the Case statement.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Statement_CaseIndexOf.Items.Add(CaseItem_Index);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CaseIndex := CaseIndex + 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Set the &amp;quot;unquoted&amp;quot; statement block as the method&amp;#8217;s body.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Method_SetValueByIndex.SetBody(Services, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unquote(Statement_CaseIndexOf);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end);&lt;br /&gt;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;end.&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4147026806295361008?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4147026806295361008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4147026806295361008' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4147026806295361008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4147026806295361008'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/prism-aop-4.html' title='Prism AOP - 4'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5580759756339561875</id><published>2009-04-21T16:52:00.002+01:00</published><updated>2009-04-21T16:58:33.896+01:00</updated><title type='text'>ECO – Ensure related objects</title><content type='html'>&lt;span xmlns=""&gt;&lt;p&gt;Sometimes you have a list of objects (say List&amp;lt;PurchaseOrder&amp;gt;) and you want to get all associated objects (say OrderLines) for all the orders in the list. There are 2 ways of doing this in ECO&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You call a PersistenceService method which takes the list of orders and a string identifying the association name. I don't like this approach because the name of the association may change during modelling and this wont be picked up as an error until runtime.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You use the overloaded method which takes an IAssociationEnd. I don't like this either because you have to find the IAssociationEnd instance from the meta-model at runtime, and to do this you'd probably use a name anyway.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So, what's the alternative?&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public static void EnsureRelatedObjects&amp;lt;T&amp;gt;(this IPersistenceService instance, IEnumerable&amp;lt;T&amp;gt; objects, Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt; member) where T : IEcoObject&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MemberExpression memberExpression = (MemberExpression)member.Body;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instance.EnsureRelatedObjects(objects, memberExpression.Member.Name);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;Now you can write code like this&lt;br /&gt;&lt;/p&gt;&lt;p&gt;EcoSpace.Persistence.EnsureRelatedObjects(orders, o =&amp;gt; o.Lines);&lt;br /&gt;EcoSpace.Persistence.EnsureRelatedObjects(orders, o =&amp;gt; o.Customer);&lt;br /&gt;&lt;/p&gt;&lt;p&gt;And it will be checked at compile time too.&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5580759756339561875?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5580759756339561875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5580759756339561875' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5580759756339561875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5580759756339561875'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/eco-ensure-related-objects.html' title='ECO – Ensure related objects'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8510792834691666790</id><published>2009-04-20T13:23:00.002+01:00</published><updated>2009-04-20T13:24:18.934+01:00</updated><title type='text'>Now PayPal is robbing us!</title><content type='html'>Here is a copy of a complaint I just filed with PayPal.&lt;br /&gt;&lt;br /&gt;On April 4th someone made a fraudulent transaction on my account for $431.23 / £298.59 - This was reconciled as a refund and I received £298.59 back.&lt;br /&gt;&lt;br /&gt;On Match 20th someone had made a fraudulent transaction on my account for $321.57 / £226.69 - After an investigation PayPal decided to give me my money back&lt;br /&gt;&lt;br /&gt;*However* instead of a refund you have performed a "Correction" for $321.57&lt;br /&gt;&lt;br /&gt;Today I went to WITHDRAW money back into my bank account from where it was stolen, but the amount you will let me withdraw is $307.60 / £204.47 - and this is £21.82 short of the amount stolen.&lt;br /&gt;&lt;br /&gt;Are you really trying to tell me that you are going to charge me commission for getting back money which was stolen from me using your service?&lt;br /&gt;&lt;br /&gt;I want the balance of £21.82 returned to my account please. At least if I am robbed in the street I don't pay commission!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8510792834691666790?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8510792834691666790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8510792834691666790' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8510792834691666790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8510792834691666790'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/now-paypal-is-robbing-us.html' title='Now PayPal is robbing us!'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7594255099670319696</id><published>2009-04-10T13:50:00.003+01:00</published><updated>2009-04-10T14:31:22.612+01:00</updated><title type='text'>Prism AOP - 3</title><content type='html'>I am playing with Prism&amp;#8217;s new Aspect Oriented Programming feature.  As a learning exercise I am implementing some of the features ECO requires on a class.  If all goes well I will end up with aspects I can apply to plain classes and have them run in ECO.  One of those features is the ILoopBack2 interface.&lt;br /&gt;&lt;br /&gt;In order to tackle this interface a small piece at a time I have created my own ILoopBack2 interface, so far with only one method.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ILoopBack2 = public interface&lt;br /&gt;&amp;nbsp;&amp;nbsp;function GetValueByIndex(I: Integer): System.Object;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This method allows the ECO to evaluate OCL (Object Constraint Language) expressions such as &amp;quot;self.FirstName&amp;quot; and have them route via the class&amp;#8217;s property, just in case there is any logic in the getter.  By implementing an interface which uses an index to identify the property to read it is possible to avoid reflection.&lt;br /&gt;&lt;br /&gt;So, getting on with it, here is my class definition&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: EcoAspects.BusinessClass(&amp;#8217;DomainClasses.Package1&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FFirstName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FLastName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property FirstName : String read FFirstName write FFirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property LastName : String read FLastName write FLastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see this is a very plain class, no magic at all except for the aspect I have applied to it.  And now some code which uses the class&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;P := new Person();&lt;br /&gt;P.FirstName := &amp;#8217;Peter&amp;#8217;;&lt;br /&gt;P.LastName := &amp;#8217;Morris&amp;#8217;;&lt;br /&gt;Console.WriteLine((P as ILoopBack2).GetValueByIndex(0).ToString());&lt;br /&gt;Console.WriteLine((P as ILoopBack2).GetValueByIndex(1).ToString());&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;Output&lt;br /&gt;&amp;nbsp;&amp;nbsp;Peter&lt;br /&gt;&amp;nbsp;&amp;nbsp;Morris&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;From the example code it is evident that an instance of the Person class is castable to ILoopBack2 and also implements the GetValueByIndex method.&lt;br /&gt;&lt;br /&gt;To achieve this the my needs to&lt;br /&gt;&lt;br /&gt;1: Add the interface to the class&lt;br /&gt;2: Generate a method&lt;br /&gt;3: Implement the method.&lt;br /&gt;&lt;br /&gt;The aspect class is a System.Attribute which implements ITypeInterfaceDecorator.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;procedure BusinessClassAttribute.HandleInterface(&lt;br /&gt;&amp;nbsp;&amp;nbsp;Services: RemObjects.Oxygene.Cirrus.IServices; &lt;br /&gt;&amp;nbsp;&amp;nbsp;aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddClassToPackage(Services, aType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddILoopBack2Interface(Services, aType);&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The implementation for AddILoopBack2Interface consists of the following&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;procedure BusinessClassAttribute.AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The type which represents ILoopBack2&lt;br /&gt;&amp;nbsp;&amp;nbsp;ILoopBack2Type: IType;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The type which represents the method GetValueByIndex on ILoopBack2&lt;br /&gt;&amp;nbsp;&amp;nbsp;ILoopBack2_GetValueByIndex: IMethod;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//The new method I will create on the Person class&lt;br /&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod: IMethodDefinition;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//A case statement I will use in the method&lt;br /&gt;&amp;nbsp;&amp;nbsp;IndexCaseStatement: CaseStatement;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Find the type for ILoopBack2&lt;br /&gt;&amp;nbsp;&amp;nbsp;ILoopBack2Type := Services.FindType(&amp;#8217;EcoSupport.ILoopBack2&amp;#8217;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//If not found then we are missing a reference, report a compiler error&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (ILoopBack2Type = nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Services.EmitError(&amp;#8217;EcoSupport.ILoopBack2 not found, are you missing an assembly reference?&amp;#8217;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Get a method reference to ILoopBack2.GetValueByIndex&lt;br /&gt;&amp;nbsp;&amp;nbsp;ILoopBack2_GetValueByIndex := ILoopBack2Type.GetMethods(&amp;#8217;GetValueByIndex&amp;#8217;)[0];&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This initial code just does some basic checking to ensure the assembly with the interface is referenced by the target assembly (the assembly which owns the Person class).  To hide the implementation of GetValueByIndex I want to make the implementing method protected, In Prism you have to explicitly link the interface method to the implementation method if it is not public.  For example&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;IMyInterface = public interface&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;procedure Method1();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;procedure Method2();&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;SomeClass = class(System.Object, IMyInterface)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Private, so much be explicit about what it implements&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;procedure Method1; implements IMyInterface.Method1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Public, the link to the interface method is inferred&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;procedure Method2;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Anyway, back to the important code.  Here is the next part of the AddILoopBack2Interface method.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;//Add the interface to Person&lt;br /&gt;&amp;nbsp;&amp;nbsp;aType.AddInterface(ILoopBack2Type);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Create a method which returns a System.Object&lt;br /&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod := aType.AddMethod(&amp;#8217;GetValueByIndex&amp;#8217;, Services.GetType(&amp;#8217;System.Object&amp;#8217;), false);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Add a parameter &amp;quot;I: Integer&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod.AddParameter(&amp;#8217;I&amp;#8217;, ParameterModifier.In, Services.GetType(&amp;#8217;System.Int32&amp;#8217;));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Make the method protected + virtual&lt;br /&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod.Virtual := VirtualMode.Virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod.Visibility := Visibility.Protected;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Explicitly link the method to ILoopBack2.GetValueByIndex, because the method is not public&lt;br /&gt;&amp;nbsp;&amp;nbsp;aType.AddImplements(GetValueByIndexMethod, ILoopBack2Type, ILoopBack2_GetValueByIndex);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The implementation of this method will basically be&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;case I of&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0: exit FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1: exit LastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To do this we need a case statement.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;//Build case statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;IndexCaseStatement := new CaseStatement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;IndexCaseStatement.What := GetValueByIndexMethod.GetParameter(&amp;#8217;I&amp;#8217;);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Next we need to loop through each property on Person and create the case item for it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;//The case index.  We wont include array properties so this keeps&lt;br /&gt;&amp;nbsp;&amp;nbsp;//track of how many properties we have added to the case statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;var CaseIndex: Integer := 0;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Loop through each property declared on the type Person&lt;br /&gt;&amp;nbsp;&amp;nbsp;for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do&lt;br /&gt;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get a reference to the property&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var Prop : IProperty := aType.GetProperty(PropertyIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Only continue if the property has no parameters (is not an array property)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//and only if it is possible to read this property&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (Prop.ParameterCount = 0) and (Prop.ReadMethod &amp;lt;&amp;gt; nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get the method which reads the property value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ReadProperty : ProcValue := new ProcValue(new SelfValue(), Prop.ReadMethod);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Construct an &amp;quot;exit&amp;quot; statement which returns the value of the property&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ExitMethod : ExitStatement := new ExitStatement(ReadProperty);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Construct a &amp;quot;case item&amp;quot; which executes the exit statement for the&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//given index&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var IndexCaseItem : CaseItem := new CaseItem(ExitMethod, CaseIndex);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Add the case item to the case statement&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IndexCaseStatement.Items.Add(IndexCaseItem);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Increment the case index for the next property added&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CaseIndex := CaseIndex + 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I have a case statement which I have constructed dynamically I need to assign it as the body of the method which implements ILoopBack2.GetValueByIndex&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;GetValueByIndexMethod.SetBody(Services, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unquote(IndexCaseStatement);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &amp;quot;unquote&amp;quot; statement tells the compiler to expand the statement into &amp;quot;real&amp;quot; code.&lt;br /&gt;&lt;br /&gt;That&amp;#8217;s the lot.  This effectively take the following code&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: EcoAspects.BusinessClass(&amp;#8217;DomainClasses.Package1&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FFirstName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FLastName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property FirstName : String read FFirstName write FFirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property LastName : String read FLastName write FLastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and compiles it as if I had written&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = public class(System.Object, ILoopBack2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FFirstName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FLastName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function GetValueByIndex(I: Integer) : System.Object; virtual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property FirstName : String read FFirstName write FFirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property LastName : String read FLastName write FLastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;function Person.GetValueByIndex(I: Integer) : System.Object;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;case Index of&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 : exit FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1 : exit LastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;end;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I hope like me you see the benefits of this!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7594255099670319696?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7594255099670319696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7594255099670319696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7594255099670319696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7594255099670319696'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/prism-aop-3.html' title='Prism AOP - 3'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8070961490835805764</id><published>2009-04-08T23:36:00.004+01:00</published><updated>2009-04-08T23:40:53.283+01:00</updated><title type='text'>We was robbed</title><content type='html'>I have no idea why this grammatically incorrect statement caused me great amusement in the past when I first read it, but since reading it I have always wanted to use it.&lt;br /&gt;&lt;br /&gt;Well, now that it is time for me to use it I find it is nowhere near as amusing as it was the first time around.&lt;br /&gt;&lt;br /&gt;Some how, and I have no idea how, someone has managed to spend nearly £600 on my PayPal account.  How on Earth have they managed to do it?  I don't know!  All I know is that I am hoping the PayPal resolution services is both swift and fair.  It's not nice to budget yourself very carefully and yet find yourself overdrawn only 8 days after receiving your monthly wages!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8070961490835805764?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8070961490835805764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8070961490835805764' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8070961490835805764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8070961490835805764'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/we-was-robbed.html' title='We was robbed'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6611370007003594091</id><published>2009-04-08T16:19:00.001+01:00</published><updated>2009-04-08T16:28:32.500+01:00</updated><title type='text'>AOP 2</title><content type='html'>This is fun :-)  I&amp;#8217;ve created an aspect like so&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FPackageName: String;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;property PackageName: String read FPackageName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;constructor (PackageName: String);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I can now decorator my class like so&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[aspect: BusinessClass(&amp;#8217;DomainClasses.Package1&amp;#8217;)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;Person = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;I can ensure this class&amp;nbsp;&amp;nbsp;exists in the aspect implementation method.&lt;br /&gt;&lt;br /&gt;constructor BusinessClassAttribute(PackageName: String);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;FPackageName := PackageName;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;method BusinessClassAttribute.HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;PackageType: ITypeReference;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;PackageType := Services.FindType(PackageName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (PackageType = nil) then&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Services.EmitError(&amp;#8217;Package class not found: &amp;#8217; + PackageName);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now when I compile my code I get the following compiler error:&lt;br /&gt;&lt;br /&gt;Error&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;(ASPE) Package class not found: DomainClasses.Package1&lt;br /&gt;&lt;br /&gt;Adding a class named Package1 to the source makes the error go away.  Okay, maybe I am getting a little too excited about this, I am a nerd :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6611370007003594091?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6611370007003594091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6611370007003594091' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6611370007003594091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6611370007003594091'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/aop-2.html' title='AOP 2'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8124699614630262341</id><published>2009-04-08T15:03:00.001+01:00</published><updated>2009-04-08T15:09:22.721+01:00</updated><title type='text'>My first AOP</title><content type='html'>Here’s what I did.&lt;br /&gt;&lt;br /&gt;01: I created a support project which just has a logger class in it.&lt;br /&gt;&lt;br /&gt;namespace EcoSupport.pas;&lt;br /&gt;interface&lt;br /&gt;uses&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Collections.Generic,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Linq,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Text;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;Logger = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class procedure Log(Message : String);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;class procedure Logger.Log(Message : String);&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Diagnostics.Debug.WriteLine(&amp;#8217;Log: &amp;#8217; + Message);&lt;br /&gt;end;&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;02: I created an Aspect which decorates methods, giving me the opportunity to intercept all method calls on the class it decorates.&lt;br /&gt;&lt;br /&gt;namespace EcoAspects;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Collections.Generic,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Linq,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Text,&lt;br /&gt;&amp;nbsp;&amp;nbsp;RemObjects.Oxygene.Cirrus,&lt;br /&gt;&amp;nbsp;&amp;nbsp;EcoSupport;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[AttributeUsage(AttributeTargets.Class)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;LogAspect = public class(System.Attribute, RemObjects.Oxygene.Cirrus.IMethodImplementationDecorator)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method HandleImplementation(Services: IServices; aMethod: IMethodDefinition);&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;method LogAspect.HandleImplementation(Services: IServices; aMethod: IMethodDefinition);&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;Name : String;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;Name := aMethod.Name;&lt;br /&gt;&amp;nbsp;&amp;nbsp;aMethod.SetBody(Services, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EcoSupport.pas.Logger.Log(&amp;#8217;Entering &amp;#8217; + Aspects.MethodName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Aspects.OriginalBody; //Call the original method body&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EcoSupport.pas.Logger.Log(&amp;#8217;Leaving &amp;#8217; + Aspects.MethodName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;end;&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;03: Now my aspect is ready I can apply it to a class.&lt;br /&gt;&lt;br /&gt;namespace PrismConsoleApplication1;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Collections.Generic,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Linq,&lt;br /&gt;&amp;nbsp;&amp;nbsp;EcoSupport,&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Text;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[Aspect:EcoAspects.LogAspect]&lt;br /&gt;&amp;nbsp;&amp;nbsp;TestSubject = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method DoSomething();&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;  &lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;method TestSubject.DoSomething();&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Diagnostics.Debug.WriteLine("DoSomething");&lt;br /&gt;end;&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;04: Finally a test&lt;br /&gt;&lt;br /&gt;namespace PrismConsoleApplication1;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;uses&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.Linq;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;ConsoleApp = class&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class method Main;&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;class method ConsoleApp.Main;&lt;br /&gt;var&lt;br /&gt;&amp;nbsp;&amp;nbsp;TS: TestSubject;&lt;br /&gt;begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;TS := new TestSubject();&lt;br /&gt;&amp;nbsp;&amp;nbsp;TS.DoSomething();&lt;br /&gt;end;&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The output from this is.....&lt;br /&gt;&lt;br /&gt;Log: Entering .ctor&lt;br /&gt;Log: Leaving .ctor&lt;br /&gt;Log: Entering .ctor&lt;br /&gt;Log: Leaving .ctor&lt;br /&gt;Log: Entering DoSomething&lt;br /&gt;DoSomething&lt;br /&gt;Log: Leaving DoSomething&lt;br /&gt;&lt;br /&gt;Now so far this is all stuff you can do with tools such as PostSharp.  The problem with PostSharp is that it is a post-compile processor so the aspects are applied after compilation is complete, which means that we can’t use any of the aspect introduced features within the same library without comprimising compile-time checking.  For example in PostSharp if an aspect introduces an interface with a LogMessage method we can’t do this...&lt;br /&gt;&lt;br /&gt;[Logger]&lt;br /&gt;public class Person&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var p = new Person();&lt;br /&gt;p.LogMessage(“Hello”);&lt;br /&gt;&lt;br /&gt;Because the Person class doesn’t have a LogMessage method until after it has been compiled.  To get around this PostSharp users will typically do something like this&lt;br /&gt;&lt;br /&gt;var p = new Person();&lt;br /&gt;((object)p as ILogger).LogMessage(“Hello”);&lt;br /&gt;&lt;br /&gt;What I don’t like is&lt;br /&gt;1.&amp;nbsp;&amp;nbsp;More typing&lt;br /&gt;2.&amp;nbsp;&amp;nbsp;It’s not compile-safe.  If I remove the [Logger] attribute from Person it will still compile and I wont know about the problem until runtime.&lt;br /&gt;&lt;br /&gt;This is exactly why I like the look of Prism, take a look at this.&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[AttributeUsage(AttributeTargets.Class)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;LogAspect = public class(System.Attribute, RemObjects.Oxygene.Cirrus.IMethodImplementationDecorator)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Aspect:AutoInjectIntoTargetAttribute]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class method LogMessage(aName: String); &lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;The AutoInjectIntoTargetAttribute tells the compiler to add that method to the target.&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;[Aspect:EcoAspects.LogAspect]&lt;br /&gt;&amp;nbsp;&amp;nbsp;TestSubject = public class&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected&lt;br /&gt;&amp;nbsp;&amp;nbsp;public&lt;br /&gt;&amp;nbsp;&amp;nbsp;end;&lt;br /&gt;&lt;br /&gt;p := new TestSubject();&lt;br /&gt;p.LogMessage(‘Hello’);&lt;br /&gt;&lt;br /&gt;This compiles even in the same library, because the aspects are able to modify the project structure before the compile process completes, so TestSubject really does have the LogMessage method.  Pre-compile AOP, how cool is that?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8124699614630262341?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8124699614630262341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8124699614630262341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8124699614630262341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8124699614630262341'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/04/my-first-aop_08.html' title='My first AOP'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6894912546570081646</id><published>2009-03-31T21:46:00.001+01:00</published><updated>2009-03-31T21:46:53.489+01:00</updated><title type='text'>My next new best friend</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;&lt;a href='http://blogs.remobjects.com/blogs/ck/2009/02/06/p251'&gt;http://blogs.remobjects.com/blogs/ck/2009/02/06/p251&lt;/a&gt;&lt;br /&gt;			&lt;/p&gt;&lt;p&gt;Read the comments I posted to see why I like it!&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6894912546570081646?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6894912546570081646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6894912546570081646' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6894912546570081646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6894912546570081646'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/my-next-new-best-friend.html' title='My next new best friend'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5245458889256638068</id><published>2009-03-31T17:04:00.001+01:00</published><updated>2009-03-31T17:04:48.997+01:00</updated><title type='text'>Unemployed</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;My employer's support contract has finally come to an end which means I have to look for another job, haven't had to do that in a while!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Drop me an email if you have any positions vacant, in the meantime it's time to do a little "networking".&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5245458889256638068?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5245458889256638068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5245458889256638068' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5245458889256638068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5245458889256638068'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/unemployed.html' title='Unemployed'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5945689309265728707</id><published>2009-03-26T15:07:00.001Z</published><updated>2009-03-26T15:07:02.521Z</updated><title type='text'>Hitler and the BNP</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;&lt;a href='http://www.youtube.com/watch?v=BUNUuqlG1a0'&gt;This&lt;/a&gt; is the funniest thing I have seen in a long time.  It's a Nazi film that's been fraudulently subtitled to depict Hitler finding out that his name is on the &lt;a href='http://www.theregister.co.uk/2008/11/18/bnp_loses_list/'&gt;leaked BNP membership list&lt;/a&gt;.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5945689309265728707?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5945689309265728707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5945689309265728707' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5945689309265728707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5945689309265728707'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/hitler-and-bnp.html' title='Hitler and the BNP'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5064824554593869508</id><published>2009-03-24T18:08:00.001Z</published><updated>2009-03-24T18:08:46.155Z</updated><title type='text'>Unity - contexts</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;The outcome of &lt;a href='http://unity.codeplex.com/Thread/View.aspx?ThreadId=46415'&gt;this fairly long exchange&lt;/a&gt; on codeplex has made me very happy!&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5064824554593869508?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5064824554593869508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5064824554593869508' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5064824554593869508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5064824554593869508'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/unity-contexts.html' title='Unity - contexts'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5495269398808804201</id><published>2009-03-24T14:40:00.001Z</published><updated>2009-03-24T14:40:11.943Z</updated><title type='text'>The BNP need my help</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;It seems they are unable to spell the word "money", because every time they send me their drivel of a newsletter they always want money.  Usually it is for their "Van of truth" (which amuses me immensely) but this time it is to help them to become "the most technically advanced political party in the UK".&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So, what are they going to do to earn this esteemed title?&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Set up our own BNP call centre&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Set up the party campaign central office&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Purchase 18 computers/screens/software&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Activate our brand new central database platform&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Recruit more staff to cope&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Train all staff in new systems&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Set up central mailing and telephone points for party&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Open our new logistical distribution warehouse&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Riiight.&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Call centre – Surely other parties have these?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Central office – I am pretty sure other parties have these!&lt;br /&gt;&lt;/li&gt;&lt;li&gt;18 computers / screens / software – Screens?  Are they going to watch TV on them?  MONITORS!  At least we are talking I.T. now though.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Central database platform – Most likely a list of names and addresses in MS Access, which is probably a nice upgrade from an Excel spreadsheet.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Recruit staff – Not I.T. related.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Train staff – Surely it is assumed that recruiting staff entails training them to do their jobs?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mailing / telephone points – Get an address and a telephone?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Warehouse – Bob's garage is too small for their Winston Churchill leaflets then?&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If they think that is technologically advanced I had better not start my own political party, because my mobile phone is more technologically advanced than that, and it's broken!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I love the part where he says "This is a task of almost biblical proportions", brilliant!  Nick Griffin has obviously not read The Bible!  I can imagine Moses now.  "Let my people go, or I will set up a small call centre with a telephone line, 8 computers with "Screens" and empty Bob's garage!"&lt;br /&gt;&lt;/p&gt;&lt;p&gt;As usual the entire thing is full of rubbish.  Don't people realise that this party will never get into power, all they are doing is electing people into a system which will give them money for doing nothing at all?&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Now what they should &lt;strong&gt;really&lt;/strong&gt; do is to &lt;a href='http://www.wheniamtheprimeminister.co.uk'&gt;vote for this guy&lt;/a&gt;!&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5495269398808804201?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5495269398808804201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5495269398808804201' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5495269398808804201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5495269398808804201'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/bnp-need-my-help.html' title='The BNP need my help'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4483875274753394711</id><published>2009-03-09T14:36:00.001Z</published><updated>2009-03-09T14:36:51.772Z</updated><title type='text'>My new favourite website</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I don't mind people asking me questions, I enjoy helping people.  What I don't like is when people ask me questions as a substitute for using their own brains.  If the answer isn't obvious there is always Google!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;That's why I love &lt;a href='http://www.lmgtfy.com'&gt;this website&lt;/a&gt;.  When someone is asking me really basic questions that would easily be answered by Google I pop along to the site and enter the search phrase.  I think it's a great way of getting a message across.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4483875274753394711?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4483875274753394711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4483875274753394711' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4483875274753394711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4483875274753394711'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/my-new-favourite-website.html' title='My new favourite website'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3817202524086434241</id><published>2009-03-08T12:23:00.001Z</published><updated>2009-03-08T12:23:29.416Z</updated><title type='text'>No Silverlight?</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I'm trying to view &lt;a href='http://www.bluerosegames.com/farseersilverlightdemos/'&gt;this&lt;/a&gt; page in Firefox.  When it loads the browser tells me I need Silverlight, so I download and install it and then restart my browser only to be told I need to install Silverlight.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;That's not impressive at all!&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3817202524086434241?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3817202524086434241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3817202524086434241' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3817202524086434241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3817202524086434241'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/no-silverlight.html' title='No Silverlight?'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6824675665025421974</id><published>2009-03-04T17:52:00.001Z</published><updated>2009-03-04T17:52:47.999Z</updated><title type='text'>Delphi flickers</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;My app has a wizard approach, the current control appears within a panel with Align set to fill the hosting panel.  My first wizard step control merely had an image on it which resizes with its parent control.  When I resize the form the host panel resizes, which resizes the wizard control, which resizes the image; and boy does it flicker!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I just had to spend some time upgrading my old Delphi DIB controls to D2009 just to get an image that doesn't flicker, crazy!  By the way, I tried the same thing in VS2008 and there was no flicker at all, and I thought Delphi was supposed to be the tool that produced fast UI apps.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6824675665025421974?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6824675665025421974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6824675665025421974' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6824675665025421974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6824675665025421974'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/delphi-flickers.html' title='Delphi flickers'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1872363511747598548</id><published>2009-03-03T09:18:00.001Z</published><updated>2009-03-03T09:18:58.496Z</updated><title type='text'>Synchronising downloads and updates (2)</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;There was a potential deadlock in the last piece of code.  When you obtain any kind of lock you will mostly use a try..finally block to ensure the lock is released when you have finished with it.  The using() command expands to a try..finally block so the original code might look okay, however if you mentally expand the code you will see the problem.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;using (FileLockingService.FileReadLock(product1.ID))&lt;br/&gt;{&lt;br/&gt;    //Some code here&lt;br/&gt;}&lt;br /&gt;&lt;/p&gt;&lt;p&gt;This becomes&lt;br /&gt;&lt;/p&gt;&lt;p&gt;var disposable = FileLockingService.FileReadLock(product1.ID);&lt;br/&gt;try&lt;br/&gt;{&lt;br/&gt;    //Some code here&lt;br/&gt;}&lt;br/&gt;finally&lt;br/&gt;{&lt;br/&gt;    disposable.Dispose();&lt;br/&gt;}&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So why is this a problem?  In a single threaded application it isn't, but this is a website so there are multiple threads running at the same time.  Now take into account the fact that a web request has a timeout, what if the thread is aborted after the lock is obtained?  Ordinarily this isn't a problem because the acquisition is followed immediately by a "try", but if we expand this code further it is evident that this isn't the case for the code I originally wrote.  The code below is indented per method rather than the normal logical indendation&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;var disposable =&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;//Code inside FileLockingService&lt;br /&gt;&lt;/li&gt;&lt;li&gt;var lockHandler = GetLockHandler(int id);&lt;br /&gt;&lt;/li&gt;&lt;li&gt;lockHandler.EnterWriteLock();&lt;br/&gt;//The next command &lt;strong&gt;must&lt;/strong&gt; be a try block&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;div&gt;var enterWriteLockResult = new DisposableAction(..............); //Oops!&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;if (action == null) throw new ArgumentNullException("Action");&lt;br /&gt;&lt;/li&gt;&lt;li&gt;this.Action = action;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;this.Context = context;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;return enterWriteLockResult;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;try&lt;br/&gt;{&lt;br/&gt;    //Some code here&lt;br/&gt;}&lt;br/&gt;finally&lt;br/&gt;{&lt;br/&gt;  disposable.Dispose();&lt;br/&gt;}&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So here you see that rather than there being a TRY block immediately after the EnterWriteLock the code above would create a DisposableAction, check a parameter, assign two instance members, and then return a result.  If anything went wrong at any of these points (OutOfMemoryException, ThreadAbortException, etc) then we'd end up with a lock that would not get removed.  The code has been changed as follows.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;    public class FileLockingService : IFileLockingService&lt;br/&gt;    {&lt;br/&gt;        readonly object SyncRoot = new object();&lt;br/&gt;        readonly Dictionary&amp;lt;int, WeakReference&amp;gt; Locks = new Dictionary&amp;lt;int, WeakReference&amp;gt;();&lt;br/&gt;&lt;br/&gt;        public ReaderWriterLockSlim GetLockHandler(int productId)&lt;br/&gt;        {&lt;br/&gt;            WeakReference resultReference;&lt;br/&gt;            ReaderWriterLockSlim result;&lt;br/&gt;            lock (SyncRoot)&lt;br/&gt;            {&lt;br/&gt;                if (Locks.TryGetValue(productId, out resultReference))&lt;br/&gt;                {&lt;br/&gt;                    result = (ReaderWriterLockSlim)resultReference.Target;&lt;br/&gt;                    if (result != null)&lt;br/&gt;                        return result;&lt;br/&gt;                }&lt;br/&gt;                result = new ReaderWriterLockSlim();&lt;br/&gt;                Locks[productId] = new WeakReference(result);&lt;br/&gt;            }&lt;br/&gt;            return result;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br /&gt;&lt;/p&gt;&lt;p&gt;and when I use this service:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;    FileLockingService.GetLockHandler(product.ID).EnterReadLock();&lt;br/&gt;    //.NET wont allow any exceptions until flow passes into the try block&lt;br/&gt;    try&lt;br/&gt;    {&lt;br/&gt;        //This is the earliest point that an exception can occur after acquiring the lock&lt;br/&gt;        using (var sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))&lt;br/&gt;        {&lt;br/&gt;            Response.ClearHeaders();&lt;br/&gt;            Response.AddHeader("Content-Type", "Application/Binary");&lt;br/&gt;            Response.AddHeader("Content-Disposition", "attachment; filename=" + product.ClientFileName);&lt;br/&gt;            using (var resultStream = DownloadService.Process(currentUser, new List&amp;lt;string&amp;gt;(), product.ClientFileName, sourceStream))&lt;br/&gt;            {&lt;br/&gt;                int bytesRead;&lt;br/&gt;                byte[] data = new byte[1024];&lt;br/&gt;                while ((bytesRead = resultStream.Read(data, 0, data.Length)) &amp;gt; 0)&lt;br/&gt;                    Response.OutputStream.Write(data, 0, bytesRead);&lt;br/&gt;                Response.End();&lt;br/&gt;            }//using&lt;br/&gt;        }//filestream&lt;br/&gt;    }&lt;br/&gt;    finally&lt;br/&gt;    {&lt;br/&gt;        FileLockingService.GetLockHandler(product.ID).ExitReadLock();&lt;br/&gt;    }&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Lesson learned: When it comes to thread synchronisation don't do anything fancy!&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1872363511747598548?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1872363511747598548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1872363511747598548' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1872363511747598548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1872363511747598548'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/synchronising-downloads-and-updates-2.html' title='Synchronising downloads and updates (2)'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7575784930760800056</id><published>2009-03-02T19:11:00.001Z</published><updated>2009-03-02T19:11:09.975Z</updated><title type='text'>Synchronising downloads and updates</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;Apparently my boss used to be annoyed with something on his old website. When he wanted to upload an updated binary he often couldn't because his product was so popular that someone was always downloading it at the same time. He'd have to continuously hit Delete in his FTP client until eventually he'd slip in and manage to delete it before updating the new version.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;This obviously isn't an ideal solution because:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;He constantly had to sit there for around an hour trying to delete it.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;Once deleted from the disk any user attempting to download would get a 404 – Not Found error.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;During upload any user attempting to download would get an Access Denied error of some kind.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;For this website I have decided first of all to only update the files via a HTTP form post. So firstly any long running upload will not deny access to the currently available file, this eliminates problem 3. Additionally, using a form post means I can overwrite the existing file, so this eliminates problem 2.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;The potential problems I have now though are:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;If he uploads but the file is in use the process will fail, causing him to have to upload again. From his upload use case this is worse than before because it takes much longer to discover your upload hasn't worked.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;If a user attempts to access the file during the process of copying from memory to disk they will still get an access error. Although this is now much less likely it could still happen.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;My solution was to implement an IFileLockingService. Downloading a file should place a read lock, uploading the file should place a write lock. Multiple read locks may exist at once, but a write lock is exclusive. Any conflicts will result in the lock request waiting until it may proceed.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;//Downloading&lt;br/&gt;using (FileLockingService.FileReadLock(Product.Id))&lt;br/&gt;{&lt;br/&gt;    //Write a binary response based on the file&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;//Uploading&lt;br/&gt;using (FileLockingService.FileWriteLock(Product.Id))&lt;br/&gt;{&lt;br/&gt;    //Write the posted file to disk&lt;br/&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;To implement the read/write lock I used a ReaderWriterLockSlim instance per Product.ID. To keep memory usage down I ensured that I used a WeakReference to hold the ReaderWriterLockSlim reference so that it may be collected once no longer referenced.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;public class FileLockingService : IFileLockingService&lt;br/&gt;{&lt;br/&gt;    readonly object SyncRoot = new object();&lt;br/&gt;    readonly Dictionary&amp;lt;int, WeakReference&amp;gt; Locks = new Dictionary&amp;lt;int, WeakReference&amp;gt;();&lt;br/&gt;&lt;br/&gt;ReaderWriterLockSlim GetLockHandler(int id)&lt;br/&gt;{&lt;br/&gt;    WeakReference resultReference;&lt;br/&gt;    ReaderWriterLockSlim result;&lt;br/&gt;    lock (SyncRoot)&lt;br/&gt;    {&lt;br/&gt;        if (Locks.TryGetValue(id, out resultReference))&lt;br/&gt;        {&lt;br/&gt;            result = (ReaderWriterLockSlim)resultReference.Target;&lt;br/&gt;            if (result != null)&lt;br/&gt;                return result;&lt;br/&gt;        }&lt;br/&gt;        result = new ReaderWriterLockSlim();&lt;br/&gt;        Locks[id] = new WeakReference(result);&lt;br/&gt;    }    &lt;br/&gt;    return result;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;public IDisposable FileReadLock(int productId)&lt;br/&gt;{&lt;br/&gt;    var lockHandler = GetLockHandler(productId);&lt;br/&gt;    lockHandler.EnterReadLock();&lt;br/&gt;    return new DisposableAction(context =&amp;gt; lockHandler.ExitReadLock(), null);&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;public IDisposable FileWriteLock(int productId)&lt;br/&gt;{&lt;br/&gt;    var lockHandler = GetLockHandler(productId);&lt;br/&gt;    lockHandler.EnterWriteLock();&lt;br/&gt;    return new DisposableAction(context =&amp;gt; lockHandler.ExitWriteLock(), null);&lt;br/&gt;}&lt;br/&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;DisposableAction is a class I have mentioned &lt;a href='http://mrpmorris.blogspot.com/2008/09/unit-testing-security.html'&gt;&lt;span style='color:blue; text-decoration:underline'&gt;here&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7575784930760800056?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7575784930760800056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7575784930760800056' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7575784930760800056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7575784930760800056'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/synchronising-downloads-and-updates_02.html' title='Synchronising downloads and updates'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5954594062265322781</id><published>2009-03-01T18:33:00.001Z</published><updated>2009-03-01T18:33:18.162Z</updated><title type='text'>The best tool for the job</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;If I had to write a large business app which accessed a DB, or a website, I would use .NET without hesitation.  When it comes to small apps (less than 10MB) that will be downloaded by home users what should I use?  I have such a project coming up soon, my boss has bought D2009 and wants me to use that because he is paranoid about people reverse engineering the binaries.  I'm a little hesitant but I must admit that I think in this case Delphi 2009 probably is the best tool for the job, and I like to use the best tool for &lt;strong&gt;the&lt;/strong&gt; job rather than the tool I like to use best for &lt;strong&gt;my&lt;/strong&gt; job.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Hopefully things will go smoothly, it will be nice writing apps in two different languages.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5954594062265322781?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5954594062265322781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5954594062265322781' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5954594062265322781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5954594062265322781'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/best-tool-for-job.html' title='The best tool for the job'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4099198166282059863</id><published>2009-03-01T18:25:00.001Z</published><updated>2009-03-01T18:25:38.676Z</updated><title type='text'>Music to fight to</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;An odd title for a coding blog I know, but I love martial arts too, especially competitive ones.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I'm just putting together a CD for my car.  The tunes on the CD music really make my adrenaline pump, so I am putting together a compilation of tunes which make me want to win a fight...with style!&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Nirvana – Smells like teen spirit.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Fat boy slim – Right here right now.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;William Orbit – Adagio for strings (Ferry Corsten Remix with first 2 mins cut off).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The Prodigy – Breathe.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The Prodidy – Firestarter.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Chemical Brothers – Block rockin' beat.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Chemical Brothers – Setting Sun.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4099198166282059863?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4099198166282059863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4099198166282059863' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4099198166282059863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4099198166282059863'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/03/music-to-fight-to.html' title='Music to fight to'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3171609373696362649</id><published>2009-02-25T14:40:00.001Z</published><updated>2009-02-25T14:40:04.760Z</updated><title type='text'>The amazing “Mr Thumb”</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;Yes, this is a very silly link but yes, this man really does look like he consists entirely of thumb.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;http://www.facebook.com/group.php?gid=47403524050&amp;amp;ref=share&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3171609373696362649?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3171609373696362649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3171609373696362649' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3171609373696362649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3171609373696362649'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/amazing-mr-thumb.html' title='The amazing “Mr Thumb”'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6426899541692040451</id><published>2009-02-25T14:06:00.001Z</published><updated>2009-02-25T14:06:37.691Z</updated><title type='text'>PayPal doesn’t do discounts</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I can't believe it, I really can't!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I'm writing a shopping cart for a website.  One of the features required is to give the user an X amount discount off their next order when they buy a specific product.  So let's say the user has a £20 discount on their next order, how does PayPal let me apply that?  Simple answer, it doesn't!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;You can't send a cart line with a negative price "Discount for 20 GBP", nor can you send a cart line with a positive price but a negative quantity (that was desperation).  The way to do this apparently is to send a cart total.  YES!  By sending the entire order aggregated into a single order line.  NOOOOOOO!!!!!!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So PayPal wants my user to order £100 of goods, click Check-out, and then be presented with "Total for your order £100 (£20 discount)", that's it?  In my opinion, that's not very good.  In fact, considering PayPal is probably the largest Internet payment organisation in the world I consider this to be atrocious!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Oh well, back to it...&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6426899541692040451?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6426899541692040451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6426899541692040451' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6426899541692040451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6426899541692040451'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/paypal-doesnt-do-discounts.html' title='PayPal doesn’t do discounts'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-769481161581517043</id><published>2009-02-24T14:54:00.002Z</published><updated>2009-02-24T14:56:01.040Z</updated><title type='text'>More Balsamiq madness!</title><content type='html'>&lt;span xmlns=""&gt;&lt;p&gt;Someone pointed this link out to me today:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.screensketcher.com/examples.html"&gt;http://www.screensketcher.com/examples.html&lt;/a&gt;&lt;br /&gt;   &lt;/p&gt;&lt;p&gt;It's another application along the lines of Balsamiq.  This app does seem very "sharp", it has very snappy response times etc, which is nice and everything &lt;strong&gt;BUT&lt;/strong&gt; I still don't like these kinds of apps!&lt;br /&gt;   &lt;/p&gt;&lt;p&gt;I do prefer the name though, it suggests "I sketch screens" rather than "I am a &lt;a href="http://en.wikipedia.org/wiki/Balsamic_vinegar"&gt;bottle of vinegar&lt;/a&gt;".&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-769481161581517043?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/769481161581517043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=769481161581517043' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/769481161581517043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/769481161581517043'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/more-balsamiq-madness.html' title='More Balsamiq madness!'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6958564489405490995</id><published>2009-02-17T15:15:00.001Z</published><updated>2009-02-17T15:33:01.443Z</updated><title type='text'>This is just a test</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I've just spotted a "Publish to Blog" option in Word 2007.  So I thought I'd give it a spin and see what happens.  If it works then Blogging is going to be a much more pleasurable experience.&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;My spelling is atrocious.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I find complex formatting on BlogSpot is terrible.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I will no longer have to edit HTML just to get my code to look right.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Talking of code, I had better try that out too...&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;    public class PreSaveConstraint&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;    {&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        public readonly IObject Instance;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        public readonly string Name;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        public readonly Func&amp;lt;bool&amp;gt; CheckIsValid;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        public PreSaveConstraint(IObject instance, string name, Func&amp;lt;bool&amp;gt; checkIsValid)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        {&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            if (instance == null)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;                throw new ArgumentNullException("Instance");&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            if (string.IsNullOrEmpty(name))&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;                throw new ArgumentNullException("Name");&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            if (checkIsValid == null)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;                throw new ArgumentNullException("CheckIsValid");&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            Instance = instance;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            Name = name;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;            CheckIsValid = checkIsValid;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;        }&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Courier New; font-size:10pt'&gt;    }&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;Well, here goes.  If you can see this then it worked!&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt='' src='http://www.peterlesliemorris.com/BlogFiles/021709_1515_Thisisjusta1.jpg'/&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6958564489405490995?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6958564489405490995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6958564489405490995' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6958564489405490995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6958564489405490995'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/this-is-just-test.html' title='This is just a test'/><author><name>Peter Morris</name><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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8177859136728373473</id><published>2009-02-13T09:59:00.002Z</published><updated>2009-02-13T10:12:29.876Z</updated><title type='text'>Proof of the existence of God</title><content type='html'>Finally there is proof that God exists.  The proof has come in the guise of a product named &lt;a href="http://www.balsamiq.com/"&gt;Balsamiq&lt;/a&gt;.  Balsamiq allows you to create mock GUI when specifying an application.  Rather than having graphics that look like real forms etc it uses a pencil drawing lookalike approach.&lt;br /&gt;&lt;br /&gt;Here is my reasoning.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It does nothing you can't do for free in the IDE you undoubtedly already have.&lt;/li&gt;&lt;li&gt;The result looks far worse than a mock up you can create in your IDE.&lt;/li&gt;&lt;li&gt;Once you have the mock up you can't do anything useful with it except export it as an image or print it.  You can't, for example, actually use that for a real form later in the project's life cycle.&lt;/li&gt;&lt;li&gt;The result looks crap.  Not poor, crap!  It looks like a 7 year old has drawn it with a wax crayon....a blunt one.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Balsamiq brought in revenue of over 100,000 US Dollars in its first 5 months.&lt;/li&gt;&lt;/ol&gt;Now the way I see it is this.  Lots of people are sending money to a guy to buy software that does something they can already do much better for nothing.  The only possible conclusion I can reach is that the developer must have sold his soul to the devil.  Taking into account that we now have conclusive proof that Satan exists it is logical to conclude therefore that it is an absolute certainty that God also exists.&lt;br /&gt;&lt;br /&gt;I rest my case.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8177859136728373473?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8177859136728373473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8177859136728373473' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8177859136728373473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8177859136728373473'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/proof-of-existence-of-god.html' title='Proof of the existence of God'/><author><name>Peter Morris</name><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><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1450972978725726855</id><published>2009-02-12T09:32:00.002Z</published><updated>2009-02-12T09:39:11.286Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><title type='text'>ASP.NET Development server and PayPal instant payment notification</title><content type='html'>I am currently developing part of a website where I pass cart information to PayPal to accept payment.  PayPal will call back a "secret URL" on my website to allow me to confirm the cart details haven't been tampered with.&lt;br /&gt;&lt;br /&gt;I hate installing software I don't need.  This is especially the case with software like IIS which just feels so intrusive, which is why I use the ASP.NET Development Server when creating websites.  The problem with this server is that it only accepts connections from the local machine.  My computer is behind a hardware firewall and my web server wont accept remote connections, so how can I test my PayPal IPN call back?&lt;br /&gt;&lt;br /&gt;Simple.  Obviously I have to open a port on my firewall and direct it to my machine.  So I opened port 80.  Then I used &lt;a href="http://www.quantumg.net/portforward.php"&gt;this tool&lt;/a&gt; to list on port 80 and redirect all traffic to port 10101 (the port my ASP.NET development server was listening on).  The result is that a remote computer can now make a HTTP request to my computer, and I can receive it + debug it in Visual Studio without having to install IIS.&lt;br /&gt;&lt;br /&gt;The great thing about this port forwarder is that it is only 44KB in size and doesn't require any installation!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1450972978725726855?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1450972978725726855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1450972978725726855' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1450972978725726855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1450972978725726855'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/aspnet-development-server-and-paypal.html' title='ASP.NET Development server and PayPal instant payment notification'/><author><name>Peter Morris</name><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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4489718140367205136</id><published>2009-02-10T16:51:00.002Z</published><updated>2009-02-10T16:54:53.460Z</updated><title type='text'>Ultimate integration testing</title><content type='html'>I am just creating some classes that allow the user of an app to specify date/time validity.  As I am modeling them it reminded me of some recurring event classes I once wrote to schedule payments between bank accounts.&lt;br /&gt;&lt;br /&gt;One day my customer phoned me up.  "Has your invoice been paid this month?" he asked.&lt;br /&gt;"Yes" I said.&lt;br /&gt;"Ah good, your code works then!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4489718140367205136?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4489718140367205136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4489718140367205136' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4489718140367205136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4489718140367205136'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/ultimate-integration-testing.html' title='Ultimate integration testing'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7837197680739261322</id><published>2009-02-09T11:16:00.002Z</published><updated>2009-02-09T11:18:04.473Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Onion'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><title type='text'>Onion architecture</title><content type='html'>Jeff seems to have put it very well in his blog.  Much better than I did myself back in December 2006 when I blogged about my "Onion" idea.&lt;br /&gt;&lt;br /&gt;My mistakes were&lt;br /&gt;01: I was missing a domain services layer.&lt;br /&gt;02: I tried to explain it using a specific example (wizard app with a "process stack" aka "task stack"), and later decided I didn't like the example :-)&lt;br /&gt;03: When I drew my diagrams I showed them as a typical stacked diagram, because my graphics skills are crap :-)&lt;br /&gt;&lt;br /&gt;Here's my badly put idea of onion layered applications:&lt;br /&gt;http://mrpmorris.blogspot.com/2006/12/net-onion-part-1.html&lt;br /&gt;&lt;br /&gt;Here's Jeffrey's&lt;br /&gt;http://jeffreypalermo.com/blog/the-onion-architecture-part-1/&lt;br /&gt;&lt;br /&gt;Well done Jeff, very nicely put!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7837197680739261322?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7837197680739261322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7837197680739261322' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7837197680739261322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7837197680739261322'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/onion-architecture.html' title='Onion architecture'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7838236540471288350</id><published>2009-02-09T10:35:00.002Z</published><updated>2009-02-09T10:50:00.685Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rhino-Mocks'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Rhino Mocks, returning a different result every time</title><content type='html'>&lt;pre&gt;&lt;code&gt;[TestMethod]&lt;br /&gt;public void Meh()&lt;br /&gt;{&lt;br /&gt;  var mockFileSystem = MockRepository.GenerateMock&amp;lt;IFileSystemService&amp;gt;();&lt;br /&gt;  mockFileSystem.Stub(fs =&amp;gt; fs.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None))&lt;br /&gt;    .IgnoreArguments()&lt;br /&gt;    .Return(new MemoryStream());&lt;br /&gt;&lt;br /&gt;  var result1 = mockFileSystem.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None);&lt;br /&gt;  var result2 = mockFileSystem.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None);&lt;br /&gt;  Assert.AreNotSame(result1, result2);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This test case shows a problem I was having.  The return value of the stubbed CreateFileStream method isn't calculated each time it is called, it is calculated once at the point you defined the stub method and then returned for every subsequent call.  The problem with this is that my real test needed to call CreateFileStream twice and get two different streams, the test was failing because the method being tested disposes of the stream it uses; this was resulting in an ObjectDisposedException in my test.&lt;br /&gt;&lt;br /&gt;The correct way to implement this is to override the return value using WhenExecuted()&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[TestMethod]&lt;br /&gt;public void Meh()&lt;br /&gt;{&lt;br /&gt;  var mockFileSystem = MockRepository.GenerateMock&amp;lt;IFileSystemService&amp;gt;();&lt;br /&gt;  mockFileSystem.Stub(fs =&amp;gt; fs.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None))&lt;br /&gt;    .IgnoreArguments()&lt;br /&gt;    .Return(null)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;  //*****The return value is replaced in the next line!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;  .WhenCalled(invocation =&amp;gt; invocation.ReturnValue = new MemoryStream());&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  var result1 = mockFileSystem.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None);&lt;br /&gt;  var result2 = mockFileSystem.CreateFileStream(null, FileMode.Append, FileAccess.Write, FileShare.None);&lt;br /&gt;  Assert.AreNotSame(result1, result2);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7838236540471288350?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7838236540471288350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7838236540471288350' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7838236540471288350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7838236540471288350'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/rhino-mocks-returning-different-result.html' title='Rhino Mocks, returning a different result every time'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8235116306525472828</id><published>2009-02-08T13:09:00.001Z</published><updated>2009-02-09T10:33:47.153Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Why are all my Visual Studio unit test results "Not executed"</title><content type='html'>When I run my unit tests in my project I am seeing a result "Not executed" for every one. I have restarted my computer so I doubt this is some kind of hung process issue.&lt;br /&gt;&lt;br /&gt;Google has revealed nothing that is not related to load balancing, and I am not load balancing!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Solved&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to determine the error you have to do this&lt;br /&gt;&lt;br /&gt;&lt;div class="post-text"&gt;  &lt;ol&gt;&lt;li&gt;Open the Visual Studio command prompt&lt;/li&gt;&lt;li&gt;Change to the directory where the binary output of your test project is.&lt;/li&gt;&lt;li&gt;Type mstest /testcontainer:The.Name.Of.Your.Test.Assembly.dll&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;At the bottom of the output you will see the following text&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Run has the following issue(s):&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In my case it was the following:&lt;/p&gt;  &lt;p&gt;Failed to queue test run 'Peter Morris@PETERMORRIS-PC 2009-02-09 10:00:37': Test Run deployment issue: The location of the file or directory 'C:\SomePath\SomeProject.Tests\bin\Debug\Rhino.Mocks.dll' is not trusted.&lt;/p&gt;  &lt;p&gt;Now if VS had told me this in the IDE I could have fixed it in minutes! All you have to do is open Windows Explorer and find that DLL. Right-click on it and go to Properties. Then click the "Unblock" button.&lt;/p&gt;&lt;/div&gt;If the IDE had told me the reason then it would have taken me 5 minutes to fix (as it did once I knew what it was)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8235116306525472828?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8235116306525472828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8235116306525472828' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8235116306525472828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8235116306525472828'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/why-are-all-my-visual-studio-unit-test.html' title='Why are all my Visual Studio unit test results &quot;Not executed&quot;'/><author><name>Peter Morris</name><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><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8123311639721630690</id><published>2009-02-06T08:52:00.002Z</published><updated>2009-02-06T09:04:08.448Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Silent errors</title><content type='html'>I'm working on an app which uses a 3rd party library for producing SWF and FLV files.  For some reason the trial worked perfectly but when I switched my app to the full version there was no audio output.&lt;br /&gt;&lt;br /&gt;We'd been looking at this problem for a while, emailing support etc, but just couldn't see what was wrong.  It wasn't until I went back to my proof of concept app and ran it that we realised the full version &lt;span style="font-weight: bold;"&gt;did&lt;/span&gt; produce audio, it was just my main app that wouldn't work properly.  Then I spotted the error...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;var compressor = new TVE4();&lt;br /&gt;compressor.LoadSettings(SettingsPath);&lt;br /&gt;compressor.SetOutputFile(outputFileName);&lt;br /&gt;compressor.EncodeSequenceAudio(Composition.EffectiveProductionAudioFileName);&lt;br /&gt;compressor.Key1 = 12345;&lt;br /&gt;compressor.Key2 = 54321;&lt;br /&gt;(loop to encode frames)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Do you see the error?  It was only as I switched between the proof of concept code and my app code in the IDE that I noticed the two following lines moving up and down...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;compressor.Key1 = 12345;&lt;br /&gt;compressor.Key2 = 54321;&lt;/code&gt;&lt;/pre&gt;Once I spotted it the problem was obvious!  If I don't set my license key before encoding anything (including audio) it is not going to work.  Moving the key up a couple of lines fixed the problem.  It was a simple absent minded mistake to have made, but why did it take over a day to solve?&lt;br /&gt;&lt;br /&gt;There were so many factors involved.  We aren't using the "full edition" of the tool we are using a feature restricted version so we thought it might be that for a while.  Then we thought it might be our settings files.  Then their support department kept talking about missing codecs (which made no sense to be honest.)  Then there was the fact that in the app it runs in a thread.  All sorts of variables that seemed much more likely than a simple 2 line coding error.&lt;br /&gt;&lt;br /&gt;The thing is, the EncodeSequenceAudio method returns a bool to indicate success or failure.  I hadn't even spotted this.  I am so accustomed to experiencing exceptions that I didn't even think to expect a boolean return type.  In addition to this the examples that ship with the product don't check for a result either.&lt;br /&gt;&lt;br /&gt;Silent errors are evil!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8123311639721630690?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8123311639721630690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8123311639721630690' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8123311639721630690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8123311639721630690'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/02/silent-errors.html' title='Silent errors'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2481218420951033909</id><published>2009-01-28T18:50:00.003Z</published><updated>2009-01-28T19:03:16.409Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Perceived speed</title><content type='html'>I'm writing an app which basically performs the following steps in a wizard-like interface:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Select an audio file of someone speaking + loads a text script in.&lt;/li&gt;&lt;li&gt;Process the audio file and the script, can take about 5 seconds for a minute of audio.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Select a character to use in the animation.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Select some additional graphics and scene settings.&lt;/li&gt;&lt;li&gt;Use the data generated in step #2.&lt;/li&gt;&lt;/ol&gt;Sitting there for 5 seconds wasn't really a problem, not to process a 1 minute audio file.  A longer audio file would take a little longer, but 10-15 seconds is okay, isn't it?  Well, what if I could make it take no time at all?&lt;br /&gt;&lt;br /&gt;Obviously I can't, but I can make it look like it takes no time at all.  The fact is that I don't need the processed data until Step 5.  The user will probably spend at least 30 seconds twiddling settings in each Step 3 and 4.  How much processing power does a bit of GUI interaction take?  Hardly any at all!&lt;br /&gt;&lt;br /&gt;So I made Step 2 run in a separate thread.  As soon as the user clicks "Next" on step 1 they instantly see Step 3.  They spend some time there and move onto Step 4.  By this point the processing is probably already complete, if however it isn't I simply disable Step #4's "Next" button with a flashing label at the top of the form "* Processing Lip Sync".  A 0.5 second timer keeps checking for the completed flag to be set, when it is the label disappears and the button enables.&lt;br /&gt;&lt;br /&gt;Unless the user is rushing through the steps they are unlikely to get held up at all.  The processing takes just as long as before (maybe a tad longer), but from the user's perspective it takes no time at all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2481218420951033909?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2481218420951033909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2481218420951033909' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2481218420951033909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2481218420951033909'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/01/perceived-speed.html' title='Perceived speed'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3945538641273092819</id><published>2009-01-27T20:47:00.004Z</published><updated>2009-01-28T09:01:51.022Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Data Transfer Objects</title><content type='html'>My observations on data transfer objects&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;They should not be a one to one representation of your domain classes.&lt;/span&gt;&lt;br /&gt;Sometimes you want a subset of the information of a single instance, sometimes your DTO will collect data from various instances (starting with an &lt;a href="http://domaindrivendesign.org/discussion/messageboardarchive/Aggregates.html"&gt;aggregate root&lt;/a&gt;).  You might create different types of DTO from the same domain classes based on what the user needs to do.  Sometimes the user only wants OrderNumber + DateRaised + TotalValue (to show as a list of orders), sometimes they want the entire order details (including lines) for editing.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;The domain classes should have no knowledge of your DTO classes.&lt;/span&gt;&lt;br /&gt;So you shouldn't have any methods such as&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;pubic PersonDto CreateDto();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;public UpdateFromDto(personDto);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;DTO's are not part of the business domain, so you should never do this!&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;The DTO you send out might not be the same type as you get back.  &lt;/span&gt;&lt;br /&gt;For example you don't want the user to edit the order number or the date raised.  If there are only a couple of properties like this you might opt to use the same DTO class but just ignore those properties when updating the domain instances, or you might decide on Request/Response DTOs&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;The DTOs should have no knowledge of the domain classes.  &lt;/span&gt;&lt;br /&gt;This is because the DTO classes will be shared between the client and the server, and the client will have no domain class knowledge.  If you have domain classes on the client then you probably don't need DTOs.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;There is only one way to update domain instances from a specific DTO.&lt;/span&gt;&lt;br /&gt;A DTO doesn't always update domain classes, but when you receive a specific kind of DTO and need it to update domain instances it will always update in the same fasion.&lt;/li&gt;&lt;/ol&gt;So, how do you create DTOs from domain instances and how do you update domain instances from DTOs?  The first thing to be aware of is that this code belongs in a layer above the domain layer, in a services layer or the app layer itself.  Then we need some way of saying&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;em&gt;"I want to convert this Order to an OrderSummaryDto"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;em&gt;"I want to convert this Order to an OrderDto, which includes its order lines"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;and then finally&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;em&gt;"I have received a DTO, I need to update the relevant domain instances"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;We need to do this in a reusable way, because the same DTOs may be reused in various parts of the application layer.  To achieve this I used the Dependency Injection Container from Microsoft named "Unity".&lt;br /&gt;&lt;br /&gt;Naming conventions I used are&lt;br /&gt;&lt;ul&gt;&lt;li&gt;xxxDtoFactory - Creates a DTO of a specific type from a specific domain instance.&lt;/li&gt;&lt;li&gt;xxxDtoTranslater - Takes a DTO of a specific type and translates its values back into the domain instances (updating existing instances, creating new instances, or whatever is required.)&lt;/li&gt;&lt;/ul&gt;The example model has a class named "Template" which has only a "string Name" property.  It is an aggregate root so it owns multiple TemplateProperty instances.  TemplateProperty is an abstract class with two concrete descendants; TemplateStringProperty and TemplateBooleanProperty.&lt;br /&gt;&lt;br /&gt;When I create a DTO I want a TemplateDto and added to its Properties I want TemplateStringPropertyDto and TemplateBooleanPropertyDto.  When I translate TemplateDto I want to update the existing object.&lt;br /&gt;&lt;br /&gt;To register a factory&lt;br /&gt;&lt;pre&gt;&lt;code&gt;container.RegisterType&amp;lt;IDtoFactory&amp;lt;Template, TemplateDto&amp;gt;, TemplateDtoFactory&amp;gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new ContainerControlledLifetimeManager()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The structure is IDtoFactory&amp;lt;TDomainClass, TDataTransferObject&amp;gt; to identify the domain class and desired DTO class, followed by the type that implements the interface to perform this DTO creation.  This code registers a factory that takes a Template domain class and creates a TemplateDto data transfer object.&lt;br /&gt;&lt;br /&gt;To register a translater&lt;br /&gt;&lt;pre&gt;&lt;code&gt;container.RegisterType&amp;lt;IDtoTranslater&amp;lt;TemplateDto&amp;gt;, TemplateDtoTranslater&amp;gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new ContainerControlledLifetimeManager()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The structure is more simple.  A DTO can only be translated in one way, so the generic IDtoTranslater interface only requires a single type, TDataTransferObject.  This code registers a translater that takes a data transfer object and maps it to the domain.&lt;br /&gt;&lt;br /&gt;Once registered the services are used like so:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;//Create a template&lt;br /&gt;Template template1 = new Template();&lt;br /&gt;&lt;br /&gt;//Create a DTO from the template&lt;br /&gt;TemplateDto template1Dto = container.Resolve&amp;lt;IDtoFactory&amp;lt;Template, TemplateDto&amp;gt;&amp;gt;().CreateDto(template1);&lt;br /&gt;&lt;br /&gt;//Update the template from the DTO&lt;br /&gt;container.Resolve&amp;lt;IDtoTranslater&amp;lt;TemplateDto&amp;gt;&amp;gt;().UpdateBusinessObjects(null, null, template1Dto);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that the "container.Resolve" code wouldn't really be there, you would use dependency injection.  I have used it in this example to avoid turning it into a dependency-injection-container blog :-)&lt;br /&gt;&lt;br /&gt;The "null, null" here are&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The object space that the app layer has created and is using for your domain instances to work inside.  Also known as your unit of work, transaction, or other.&lt;/li&gt;&lt;li&gt;An addition context.  For example when updating a Template the TemplateDtoTranslater will resolve services to translate each PropertyDto it encounters, and pass the Template as the context so that we know which aggregate root we are working with.&lt;/li&gt;&lt;/ul&gt;For now I will link to the &lt;a href="http://www.peterlesliemorris.com/blogfiles/DtoExample.zip"&gt;full source code&lt;/a&gt; for the example.  If I get time I will blog some more explaining how the code works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3945538641273092819?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3945538641273092819/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3945538641273092819' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3945538641273092819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3945538641273092819'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/01/data-transfer-objects.html' title='Data Transfer Objects'/><author><name>Peter Morris</name><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><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8504319424898705661</id><published>2009-01-20T10:52:00.004Z</published><updated>2009-01-20T10:59:12.120Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>How often should I test?</title><content type='html'>&lt;p&gt;I am lazy.  I don't mean that I don't work, I mean that I like to get my work done with as little effort as possible.  Writing tests before my code used to look like too much extra work, but I've realised just how much time they actually save me.&lt;/p&gt;&lt;p&gt;When you make a small change to something it's very easy to think to yourself "That's such a small change, I can't see how it can possibly fail", what I have also realised is this really means "Despite this being a small change, it will fail and I can't possibly see how".&lt;/p&gt;&lt;p&gt;I recently observed a change to some code that introduced a simple if statement, and all existing tests passed.  The problem is that the existing tests only checked the expected behaviour worked (which it still did), but by introducing the "if" statement (and an additional parameter on the method) the developer had changed the expected behaviour &lt;strong&gt;under certain circumstances&lt;/strong&gt;.  Thinking it was so simple it couldn't possibly fail he checked in his changes.  I happened to be changing something in the same code and spotted the changes, and realised immediately that his changes would actually result in files being deleted that are still required.&lt;/p&gt;&lt;p&gt;So, how often &lt;em&gt;should&lt;/em&gt; you write tests?  I think this site sums it up very well&lt;br /&gt;http://howoftenshoulditest.com&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8504319424898705661?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8504319424898705661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8504319424898705661' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8504319424898705661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8504319424898705661'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/01/how-often-should-i-test.html' title='How often should I test?'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-456151500952260126</id><published>2009-01-09T13:45:00.001Z</published><updated>2009-01-09T13:46:24.479Z</updated><title type='text'>InstallShield</title><content type='html'>Hmmm&lt;br /&gt;&lt;br /&gt;Considering their product is an installer I'd expect it to install okay.  It's been sitting here now for about 30 minutes with the progress bar at 100% and only the Cancel bar enabled.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-456151500952260126?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/456151500952260126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=456151500952260126' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/456151500952260126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/456151500952260126'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/01/installshield.html' title='InstallShield'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8671232936049976759</id><published>2009-01-09T10:53:00.003Z</published><updated>2009-01-09T11:12:24.401Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><title type='text'>Domain Driven Design by Eric Evans - my book review</title><content type='html'>To me this book has been a huge disappointment. Someone told me "Pete, there's a name for what you do!" and pointed me to this book.&lt;br /&gt;&lt;br /&gt;Now personally I prefer technical books, as I read the Ubiquitous Language part at the start I thought to myself "Not technical, but fair enough some people will get value from being taught how to ask questions" and I stuck with it.&lt;br /&gt;&lt;br /&gt;The first thing I must say that I cannot stand and which happens a &lt;strong&gt;lot&lt;/strong&gt; in this book is over emphasis. When you want to emphasise something it needs to stand out. This book not only emphasises whole paragraphs but does it far too often too. Being an Internet user for some time now when I read upper case letters the imagined vocalisation actually SHOUTS at me, when I read bold my brain vocalises it as a loud and punctuated word, so when I read a whole paragraph in bold &lt;strong&gt;I, READ, EACH, WORD, LIKE, THIS&lt;/strong&gt;; it makes it difficult to read.&lt;br /&gt;&lt;br /&gt;Another thing I don't like when reading something is reading it for the Nth time. I don't mind a bit of reiteration in the way of reading something and then at the end telling me it is another example of "X" but only if the example is so different that it probably didn't occur to me. When I am on page 400+ I really don't want to be reading more examples of what I was reading in the first chapter. It really switches my brain off when so far into the book I am reading yet another example of "The Ubiquitous Language" that was covered at the start of the book. To be honest I am finding it very difficult to motivate myself to read the remainder of the book.&lt;br /&gt;&lt;br /&gt;So, what have I learned from the book? I would say I have learned 1 valuable thing. Many times in the past I have modeled a parent/child association such as PurchaseOrder, and then modeled a kin-like parent/child such as Invoice/InvoiceLine, where the invoice is always for a single order and each invoice line is for a specific order line. In these circumstances I would have both Invoice.Order and InvoiceLine.OrderLine, everytime I did this I would cringe, it just felt "wrong" or "messy". Enforcing the idea that I should have no direct associations to the aggregated parts of the Order means that now I merely have Invoice.Order, it's easy to see which order line the invoice line is for because they are ordered the same and both the order and invoice are immutible. Now, I don't agree with the idea of never referencing an aggregated part, but at least now I consider the option. It certainly cleaned up a 3 kin-like aggregate part of the model I am currently working on.&lt;br /&gt;&lt;br /&gt;For someone who doesn't know how to talk the "language of the current domain" with a customer I can see that this book could be useful, and also for people who need some pointers on how to segment their apps a bit.&lt;br /&gt;&lt;br /&gt;When I read about people mentioning the "map of the world" example being such an eye opener (or whatever other way they express their positive experience) it honestly amazes me. The idea that a "Customer" to company A is completely different from how company B sees one as a break though just makes me shake my head in disbelief. In some businesses a customer is a company, in others a person, in others it could be either, and in one domain I worked a customer could have been either&lt;br /&gt;&lt;br /&gt;A: A company&lt;br /&gt;B: A department&lt;br /&gt;C: An individual&lt;br /&gt;D: A non physical entity such as a business process&lt;br /&gt;&lt;br /&gt;All of which could also be a "Supplier". This is because they saw their Customers and Suppliers as things that consume and things that produce.&lt;br /&gt;&lt;br /&gt;On the whole I personally found the book to be "a whole lot of nothing much at all", some of the personal stories were interesting but I also mainly found it repetitive and boring; a very difficult read. Unless the last 100 pages or so have something knock-out in them I expect I will remain very disappointed with it, if I ever manage to read the end that is.&lt;br /&gt;&lt;br /&gt;Some people seem to be quite religious about this book, referring to it as "The book" and I feel by posting this negative review I might be opening myself up to attacks from DDD-extremists :-)&lt;br /&gt;&lt;br /&gt;The fact is that I use parts of the DDD approach (now I at least know by what name to refer to it), it's just the book...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8671232936049976759?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8671232936049976759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8671232936049976759' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8671232936049976759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8671232936049976759'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2009/01/domain-driven-design-by-eric-evans-my.html' title='Domain Driven Design by Eric Evans - my book review'/><author><name>Peter Morris</name><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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6822561437154474966</id><published>2008-12-30T12:47:00.003Z</published><updated>2008-12-30T12:50:10.968Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Domain driven design, Test driven design</title><content type='html'>I was just reading through the VBUG events list when I came across an event entitled "Domain driven design approach, using unit testing".&lt;br /&gt;&lt;br /&gt;"Sounds interesting!" I thought to myself, I hope I can make it!  As I started to read it I thought it looked familiar.  At that point I realised it was me doing the talk!  So hopefully I will be able to make it :-)&lt;br /&gt;&lt;br /&gt;The posting is &lt;a href="http://www.vbug.co.uk/Events/March-2009/VBUG-MidlandsDomain-driven-design-approach-with-unit-testing.aspx"&gt;here&lt;/a&gt;.  If you come along make sure you say "Hello".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6822561437154474966?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6822561437154474966/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6822561437154474966' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6822561437154474966'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6822561437154474966'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/domain-driven-design-test-driven-design.html' title='Domain driven design, Test driven design'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5459619238224882242</id><published>2008-12-16T11:10:00.000Z</published><updated>2008-12-16T11:16:24.129Z</updated><title type='text'>Computer music</title><content type='html'>You may already know that I used to own a Commodore 64, and (as is nearly everyone else who ever owned one) I am still very passionate about the music people used to produce on it.&lt;br /&gt;&lt;br /&gt;The question "Which was your favourite Commodore 64 tune" is a very hard one to answer as there are so many good ones out there, and people tend to go for the same short list "Delta", "Sanxion", etc.&lt;br /&gt;&lt;br /&gt;However, recently I was creating some ambient background music for a presentation video.  It started off a bit like a Jarre tune, but as I started to add in the percussion (for which I used samples of car doors slamming) it started to remind me of a tune from an old C64 game; "Tetris".&lt;br /&gt;&lt;br /&gt;After downloading it and listening to it endlessly over and over as I write code I have come to a conclusion; TETRIS is my favourite C64 tune!  It is just so unique, and unlike many other C64 tunes it hasn't aged, and doesn't actually sound that much like a C64 tune at all!  Whenever I listen to it I feel like I am in the former USSR, building something out of strangely shaped bricks :-)&lt;br /&gt;&lt;br /&gt;There's a (large) Mp3 of the tune &lt;a href="http://www.se2a1.net:81/soasc/MOS6581R2/MUSICIANS/M/Moon/Tetris_intro_T01.sid.mp3"&gt;here &lt;/a&gt;if you are interested.&lt;br /&gt;&lt;br /&gt;Well done Wally Beben, you have in my opinion created a classic, and I will undoubtedly continue to enjoy it for many years to come.  Actually, I am going to do a remix of it too, I hope he wont mind :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5459619238224882242?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5459619238224882242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5459619238224882242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5459619238224882242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5459619238224882242'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/computer-music.html' title='Computer music'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5913029829346722572</id><published>2008-12-15T11:09:00.000Z</published><updated>2008-12-15T11:13:15.659Z</updated><title type='text'>FireBird - Did it burn me?</title><content type='html'>This morning I spent an hour on the phone to Microsoft support.  I tried to log into my machine only to see the error message&lt;br /&gt;&lt;br /&gt;&lt;em&gt;The user profile service failed the logon.&lt;/em&gt;&lt;br /&gt;&lt;em&gt;User profile cannot be loaded.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;The guy on the phone got me to put my Vista install DVD into the drive, boot from that, go to system-recovery, and revert to a restore point.  Worked nicely.&lt;br /&gt;&lt;br /&gt;The thing is, why did this happen?  The &lt;strong&gt;only&lt;/strong&gt; thing I can think of is Firebird server.  I installed it on Sunday to get an old app to run (installed as app, not service).  I didn't install the control panel plugin due to a warning on the site telling me not to install it on Vista or it will trash my Control Panel.&lt;br /&gt;&lt;br /&gt;I suppose the warning about the control panel should have been enough to put me off.  Still, it works now.  I just wont be installing it again, at least for some time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5913029829346722572?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5913029829346722572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5913029829346722572' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5913029829346722572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5913029829346722572'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/firebird-did-it-burn-me.html' title='FireBird - Did it burn me?'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6995813300027426986</id><published>2008-12-05T18:05:00.004Z</published><updated>2009-05-19T14:03:18.575+01:00</updated><title type='text'>The importance of clarity</title><content type='html'>I wrote and now maintain a Pocket PC app for Imperial Tobacco (yes, despite supporting the ban on smoking in public places).  It is important that the date/time on the PPC is accurate so part of the business flow is to get the user to check the date/time immediately after they log in.&lt;br /&gt;&lt;br /&gt;Despite this step we were getting invalid dates back from their collected data, in all cases it was always one day ahead of the correct time.  We have recently introduced various additional common sense checks such as "You have missed a working day, are you sure?" etc, but the change that will have the biggest affect is the one that was the smallest to change.&lt;br /&gt;&lt;br /&gt;When the user enters the current date/time if it is less than the last known date/time on the server (sent in their database of work to do) I showed a message saying it was incorrect.  The message read&lt;br /&gt;&lt;br /&gt;"The date you have entered is invalid"&lt;br /&gt;&lt;br /&gt;At this point the user looks at the date they have entered, thinks "but the date is right!", they click it forward a day and the PPC accepts it, so they continue with their work.  Because I used the word "date" the user was blinkered to the value in the date, and didn't even bother looking at the time they had entered.  The message now reads&lt;br /&gt;&lt;br /&gt;"The date/time you have entered is invalid"&lt;br /&gt;&lt;br /&gt;Hopefully this will be a bigger clue :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6995813300027426986?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6995813300027426986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6995813300027426986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6995813300027426986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6995813300027426986'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/importance-of-clarity.html' title='The importance of clarity'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3932181149356901284</id><published>2008-12-05T17:54:00.000Z</published><updated>2008-12-05T18:01:02.594Z</updated><title type='text'>Northern lights</title><content type='html'>For my 35th birthday my wife bought us two tickets to take a flight up north to see the aurora borealis with &lt;a href="http://www.auroraflights.co.uk/"&gt;Aurora Flights&lt;/a&gt;.  &lt;a href="http://www.peterlesliemorris.com/blogfiles/northernlights.jpg"&gt;Here is a photo &lt;/a&gt;of the view from our window.  As you can see we didn't manage to see much at all.  In fact, despite sitting at the window directly over the wing we couldn't actually see the wing (except for the flashing light on its tip).&lt;br /&gt;&lt;br /&gt;There was a small clear area at the top, but due to the thickness of the window frame we couldn't get our heads high enough to look down at the lights, only up at the stars (which were great).  As a consequence our entire view of the northern lights was merely a dull band of murky green fog.  At one point I got excited because I thought I could see some vertical structure in the lights, unfortunately I soon realised it was merely the dirty window as it moved whenever I moved my head.&lt;br /&gt;&lt;br /&gt;I strongly suspect that the &lt;strong&gt;only&lt;/strong&gt; good way to see the lights is to stand on the floor and look up.  Most definitely one of the most disappointing experiences of my entire life.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3932181149356901284?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3932181149356901284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3932181149356901284' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3932181149356901284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3932181149356901284'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/northern-lights.html' title='Northern lights'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2919628568413750239</id><published>2008-12-01T09:45:00.001Z</published><updated>2008-12-01T13:12:23.139Z</updated><title type='text'>The ubiquitous language</title><content type='html'>One of the most important things to do when designing a system for a customer is to to try create a common business language between you and the people who understand the problem domain. A lack of what is known as "the ubiquitous language" can result in misunderstandings and a failed project.&lt;br /&gt;&lt;br /&gt;I had a real life experience of a ubiquitious language failure yesterday. My brother went to the shop to buy some credit for his phone and offered to take my children with him to buy sweets. When they got back my son asked me&lt;br /&gt;&lt;br /&gt;"How come uncle Stephen was able to buy credit for his phone?"&lt;br /&gt;&lt;br /&gt;When I asked what he was talking about he described the sign on the wall in the shop, it read&lt;br /&gt;&lt;br /&gt;"Please do not ask for credit as refusal often offends"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2919628568413750239?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2919628568413750239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2919628568413750239' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2919628568413750239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2919628568413750239'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/12/ubiquitous-language.html' title='The ubiquitous language'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6193318806074032735</id><published>2008-11-25T09:41:00.000Z</published><updated>2008-11-25T09:43:17.102Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><title type='text'>Fancy a cup of tea?</title><content type='html'>Okay, this is just a silly thing.  It's a remix of the childrens' nursery song "Polly put the kettle on".  My remix is actually a remix of an old Commodore 64 remix from the 80's.&lt;br /&gt;&lt;br /&gt;http://remix.kwed.org/download.php/3981/Cubud%20-%20Kettle%20%28Fancy%20a%20cuppa%20remix%29.mp3&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6193318806074032735?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6193318806074032735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6193318806074032735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6193318806074032735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6193318806074032735'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/11/fancy-cup-of-tea.html' title='Fancy a cup of tea?'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1894631385366445038</id><published>2008-11-19T14:06:00.000Z</published><updated>2008-11-19T14:09:01.674Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Politics'/><title type='text'>BNP member list released</title><content type='html'>With a bit of looking around I managed to find it + download it.  I wanted to make sure I wasn't on it :-) I wrote to the BNP some time ago to tell them what idiots I think they are.  I blogged here &lt;p&gt;&lt;a href="http://mrpmorris.blogspot.com/2008/04/politics.html" rel="nofollow"&gt;http://mrpmorris.blogspot.com/2008/04/politics.html&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Since then they have sent me emails asking for money to support their “Van of truth” or whatever it is they call the van they drive around spreading “the truth”. They also wrote and asked if I wanted to join a trade union “for people like us”.&lt;/p&gt; &lt;p&gt;I haven’t signed up as a member with them so hopefully I am not on their list, but what if their email DB got out too? I’m certainly on there and might get harassed as a consequence.  Imagine how many Viagra spams I'd get in a day then!&lt;/p&gt;&lt;p&gt;I didn't expect to be on the list because I am not a member, you have to pay to be a member apparently.  Yeah, right.  You never know though, they may have added just about anyone to that list, you know, like how people on Facebook add people they don't know to make it look like they have more friends than they do :-)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1894631385366445038?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1894631385366445038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1894631385366445038' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1894631385366445038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1894631385366445038'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/11/bnp-member-list-released.html' title='BNP member list released'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8846087347847193374</id><published>2008-11-11T10:48:00.000Z</published><updated>2008-11-11T10:50:10.196Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Humour'/><title type='text'>Spiders in lieu of payment</title><content type='html'>Very funny :-)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.quoted4truth.com/articles/I-do-not-have-any-money-so-am-sending-you-this-drawing-I-did-of-a-spider-instead"&gt;http://www.quoted4truth.com/articles/I-do-not-have-any-money-so-am-sending-you-this-drawing-I-did-of-a-spider-instead&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and you can bid for the picture here&lt;br /&gt;&lt;br /&gt;&lt;a href="http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&amp;amp;item=190265903424"&gt;http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&amp;amp;item=190265903424&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I wish I had thought of ebay'ing it :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8846087347847193374?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8846087347847193374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8846087347847193374' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8846087347847193374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8846087347847193374'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/11/spiders-in-lieu-of-payment.html' title='Spiders in lieu of payment'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8603664912769889405</id><published>2008-10-24T11:50:00.002+01:00</published><updated>2008-10-24T13:42:21.450+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>How I would like to write code</title><content type='html'>Aspect Oriented Programming looks great. It's something I have always wanted to use, but I avoid it because it doesn't work exactly how I would like it to. The first thing I want to avoid is lots of reflection at runtime (compile time is fine), it is for this reason I have mainly been interested in &lt;a href="http://www.postsharp.org/"&gt;PostSharp&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;PostSharp looks great! You decorate your class with attributes that you create yourself. After compiling your assembly PostSharp inspects the result for Attributes that are descended from one of its own special PostSharp AOP classes. When it sees these attributes it modifys your code in a specific way.&lt;br /&gt;&lt;br /&gt;To use the same example as everyone else in the world (yawn) you could create an attribute from the method-boundard attribute. Override the methods declared on that attribute for entry/exit of the method, and write some code in there to write to the IDE's output window using System.Diagnostics.Debug.WriteLine(). Now when I add that attribute to a method on one of my classes I get that code injected into my method automatically&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[Logging]&lt;br /&gt;public void DoSomething()&lt;br /&gt;{&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;would be the same as&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void DoSomething()&lt;br /&gt;{&lt;br /&gt; System.Diagnostics.Debug.WriteLine("Enter");&lt;br /&gt; try&lt;br /&gt; {&lt;br /&gt; }&lt;br /&gt; finally&lt;br /&gt; {&lt;br /&gt;  System.Diagnostics.Debug.WriteLine("Exit");&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;So, this looks great, but what's so good / bad about it?&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Good&lt;/h4&gt;&lt;br /&gt;On the good side it lets me describe code functionality rather than writing it.  Instead of looking at loads of lines of code to see what something does we can look at a .NET attribute which simply tells us what generic task the class/method does.  Take Assets as an example. An Asset is worth money so its location and movement history must always be tracked. To achieve this I would have assets always linked to an AssetHolder class...&lt;br /&gt;&lt;br /&gt;&lt;div align="center"&gt;&lt;img alt="Asset holder" src="http://www.peterlesliemorris.com/blogfiles/assetholder.gif" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, what qualifies as an asset holder?  You could say that a person has an asset, a department could be using it, it could be in a stock room, or it could be out on hire to a customer.  &lt;br /&gt;&lt;br /&gt;&lt;em&gt;Digressing slightly...Don't descend these classes from AssetHolder!  Holding assets is something a person DOES, not something a person IS!&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;div align="center"&gt;&lt;img alt="Person with asset holder" src="http://www.peterlesliemorris.com/blogfiles/personwithassetholder.gif" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now my Person class can hold assets.  I can obtain its AssetHolder instance via its IAssetHolder interface, from there I can create AssetTransfer instances and so on.  Now I have to implement the same thing for Department, Room, and Customer!  Wouldn't it be nice if I could just do this?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[AssetHolder]&lt;br /&gt;public class Person { }&lt;br /&gt;&lt;br /&gt;[AssetHolder]&lt;br /&gt;public class Department { }&lt;br /&gt;&lt;br /&gt;[AssetHolder]&lt;br /&gt;public class Room { }&lt;br /&gt;&lt;br /&gt;[AssetHolder]&lt;br /&gt;public class Customer { }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That's the good side of PostSharp.  I could create an AOP attribute called &amp;quot;AssetHolderAttribute&amp;quot; and then build my business classes with that attribute adorning them.  After a successful compilation PostSharp would discover these attributes and do the following to each of my classes&lt;br /&gt;&lt;br /&gt;01: Add a member AssetHolder to the class.&lt;br /&gt;02: Ensure that member is created within the constructor.&lt;br /&gt;03: Implement IAssetHolder, returning this new member from the GetAssetHolder method&lt;br /&gt;&lt;br /&gt;What we have is a very small amount of code (two words stuck together, wrapped in a pair of square brackets) which not only clearly identifies a feature of these classes, but even implements it&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Bad&lt;/h4&gt;&lt;br /&gt;This is the only thing that is stopping me from using this approach to write applications.  PostSharp cannot add the code until after the assembly has finished compiling.  This means that an assembly consuming the business classes can easily write code such as&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;IAssetHolder holder = (IAssetHolder)person;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;but code within the same assembly as the Person class cannot, because Person does not implement IAssetHolder until &lt;strong&gt;after&lt;/strong&gt; the assembly has successfully compiled.  A &lt;a href="http://www.straightdope.com/columns/read/2093/whats-the-origin-of-catch-22"&gt;catch-22&lt;/a&gt; situation!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;How I would like to write code&lt;/h4&gt;&lt;br /&gt;I would love to see pre-compile time support for AOP, so that it feels like part of the development experience rather than like something that has been bolted onto my binary afterwards.  Instead of a UML diagramming tool to help me to design my complex models, what I would like to do is to use the diagramming tool to design patterns that occur frequently in applications (archetypes), and then in simple code decorate my classes to identify what they are capable of...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[BusinessObject] //Adds persistence capability using your favourite framework&lt;br /&gt;[AssetHolder] //Responsible for holding assets&lt;br /&gt;[StockHolder] //Responsible for holding stock&lt;br /&gt;[RoleHolder] //Can be assigned roles, such as Customer, Supplier, etc&lt;br /&gt;public class Person&lt;br /&gt;{&lt;br /&gt; [DataMember] //This property is persistent&lt;br /&gt; public string FirstName { get; set; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//I could now write code like this&lt;br /&gt;Person p = new Person(EcoSpace);&lt;br /&gt;decimal heldValue = p.AssetHolder.GetHeldAssetValue();&lt;br /&gt;heldValue += p.StockHolder.GetHeldStockValue();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Or for defining the model itself using simple code...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[BusinessObject]&lt;br /&gt;public class PurchaseOrder&lt;br /&gt;{&lt;br /&gt; //Persistent property that is an AutoIncrementing column in the DB&lt;br /&gt; [DataMember(SaveAction = SaveActionKind.DbAssigned)]&lt;br /&gt; public int OrderNumber { get; private set; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[BusinessObject]&lt;br /&gt;public PurchaseOrderLine&lt;br /&gt;{&lt;br /&gt; //Also adds a property to PurchaseOrder called "Lines"&lt;br /&gt; [Association(Aggregation = AggregationKind.Composite, OtherEndName="Lines")]&lt;br /&gt; public PurchaseOrder Order {get; set; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//I could now write code like this&lt;br /&gt;var order = new PurchaseOrder(EcoSpace);&lt;br /&gt;var line = new PurchaseOrderLine(EcoSpace);&lt;br /&gt;line.Order = order;&lt;br /&gt;&lt;br /&gt;//AOP created property &amp;quot;PurchaseOrder.Lines&amp;quot;!&lt;br /&gt;if (order.Lines.Count != 1 || order.Lines[0] != line)&lt;br /&gt; throw new ........&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;The future&lt;/h4&gt;&lt;br /&gt;I really hope to see pre-compiler support for AOP in the future in C#.  I've been working on one of the guys at &lt;a href="http://www.remobjects.com"&gt;RemObjects&lt;/a&gt; recently who writes the &lt;a href="http://www.remobjects.com/product/?id={DC0A9947-5FED-4D34-8CC8-F2DCFA87A1FE}"&gt;Oxygene&lt;/a&gt; .NET language compiler (formerly known to me as &amp;quot;Chrome&amp;quot;).  I have always thought their language has some really nice features in it, but never started using it because of two reasons&lt;br /&gt;&lt;br /&gt;01: No ECO support.&lt;br /&gt;02: I wanted to stay in C# to keep my skills more &amp;quot;main-stream&amp;quot;.&lt;br /&gt;&lt;br /&gt;Oxygene support is being worked on in ECO 5, which only leaves the second point.  I really like some of their features as I said, but not quite enough to make me switch.  However, if you are going to gain massively from working in a less popular language then switching to that language is the right thing to do, and this would be that feature for me!  If in the future I need a C# job I believe my skills will remain current enough to be able to not exclude me from this part of the job market.  &lt;br /&gt;&lt;br /&gt;In the meantime I will continue to use C# and hope that some relentless annoying nagging will get me what I want on the Oxygene front, and that my dream of writing solid reliable code with very little effort takes that big leap towards becoming a reality! :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8603664912769889405?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8603664912769889405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8603664912769889405' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8603664912769889405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8603664912769889405'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/how-i-would-like-to-write-code.html' title='How I would like to write code'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8412697434208347010</id><published>2008-10-17T12:27:00.001+01:00</published><updated>2008-10-17T12:41:57.640+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Sufficient architecture</title><content type='html'>I have a friend who once decided to teach himself VB.  We had a mutual friend who was the manager of a video shop so he decided to write some new software for him.  He started off by creating an MS Access database to hold the data for his application.  Each week I'd pop around to his house and he'd have MS Access open, reworking his tables etc.  After a few months I asked "Have you started to write the app yet?".&lt;br /&gt;&lt;br /&gt;"No, not yet."  He replied, "I am trying to get the DB right first, then I will get onto writing the application".  I asked what he meant by getting the DB right.  He explained that he wanted to make this the best video-hire software ever, and that I should hear about some of the features that are going into it as a result of the DB structure he has made.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Record the cast of the film.&lt;/li&gt;&lt;li&gt;Record the director, producer etc.&lt;/li&gt;&lt;li&gt;Record the genre; horror, comedy, etc.&lt;/li&gt;&lt;li&gt;Record film information for films due to be released in the future.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;All sorts of stuff.  His idea was that he could analyse rental history and match up upcoming releases with people who might be interested in them.  Then the computer could do a mailshot to the relevant people, enticing them to come back and rent out a film or two.&lt;/p&gt;&lt;p&gt;"Great ideas!" I said, "but.....Can members rent out films?"&lt;/p&gt;&lt;p&gt;"No, " he replied, "I haven't done that bit yet!"&lt;/p&gt;&lt;p&gt;This is the message I always try to get across when I am helping to design a new system. I either meet people who want their app to be a Swiss army knife application (who inevitably deliver late, and deliver rubbish) or those who want to write any old rubbish to get the job done and get it out of the door (which inevitably falls apart whenever a change is required). Someone once actually said to me "The problem with doing things properly is that it takes too long!". &lt;/p&gt;&lt;p&gt;Anyway, I read someone else's post on this subject this morning and just wanted to say&lt;/p&gt;&lt;p&gt;&lt;a href="http://blogs.msdn.com/simonince/archive/2008/10/16/sufficient-architecture.aspx"&gt;HEAR HEAR!&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Don't go designing apps with features nobody has asked for, if there is something the customer wants in the future they are quick enough to ask for it, and if they &lt;strong&gt;really&lt;/strong&gt; want it they will pay for it too!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8412697434208347010?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8412697434208347010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8412697434208347010' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8412697434208347010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8412697434208347010'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/sufficient-architecture-hear-hear.html' title='Sufficient architecture'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1029521838869886937</id><published>2008-10-12T13:28:00.000+01:00</published><updated>2008-10-12T13:35:26.442+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Accommodation manager - runtime error</title><content type='html'>I've just spotted something I omitted before zipping up my AccommodationManager app and making it available.&lt;br /&gt;&lt;br /&gt;When you run the app in Release mode you will experience SQL errors.  These errors are intermittent, and a query that worked only seconds ago might not always work.  The reason for this is that ECO executes all queries within a transaction; SQLite creates a journal file for every transaction and then deletes it when done; and my anti-virus decides it wants to take a look at this new journal file to see what's inside it; resulting in SQLite not being able to open its own journal file exclusively.&lt;br /&gt;&lt;br /&gt;This was something I noticed a while ago in another ECO+SQLite app of mine and the guy who writes the library spent a couple of hours with me on MSN trying to work out what the problem was (he had a fix to me by the next morning!).  Anyway, I have updated the project so that the connection string tells SQLite not to delete the journal file when it has finished with it, as a consequence there is no conflict with Avast! anti-virus.&lt;br /&gt;&lt;br /&gt;In case you don't want to download the example project again, here is the change you need to make inside AccommodationManager\AccommodationManagerPMP.cs&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;private void ConfigureConnectionString()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;#if !DEBUG&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqLiteConnection1.ConnectionString = string.Format(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Data Source={0};Version=3;Fail If Missing=True&lt;strong&gt;;Journal Mode=Persist&lt;/strong&gt;&amp;quot;, Settings.DataBaseFileName);&lt;br /&gt;#endif&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1029521838869886937?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1029521838869886937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1029521838869886937' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1029521838869886937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1029521838869886937'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/accommodation-manager-runtime-error.html' title='Accommodation manager - runtime error'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5702067191479771174</id><published>2008-10-08T16:53:00.002+01:00</published><updated>2008-10-11T19:40:03.408+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='WCF'/><title type='text'>ECO, Winforms, ASP.NET, and WCF</title><content type='html'>The technologies I used in an app I wrote for friends recently.  The app manages properties at different locations, bookings, and tariffs.  In addition to this the application (which uses SQLite) connects to their website using WCF and updates their database so that people can check prices and availability.&lt;br /&gt;&lt;br /&gt;I need to get them using it now so that there is data available by the time I put up the website, but I thought I'd share the app source anyway.  Don't be afraid to run it and click Publish, it will only publish to your local machine, I'm not &lt;em&gt;that&lt;/em&gt; stupid :-)&lt;br /&gt;&lt;br /&gt;There source is available &lt;a href="http://www.peterlesliemorris.com/blogfiles/trentinney.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5702067191479771174?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5702067191479771174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5702067191479771174' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5702067191479771174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5702067191479771174'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/eco-winforms-aspnet-and-wcf.html' title='ECO, Winforms, ASP.NET, and WCF'/><author><name>Peter Morris</name><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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6138368025657196282</id><published>2008-10-08T16:49:00.001+01:00</published><updated>2008-10-08T19:10:58.138+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Implementing complex unit testing with IoC</title><content type='html'>I have a method that looks something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void DoSomething(SomeClass someInstance, User user)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;var persistence = someInstance.AsIObject.GetEcoService&amp;lt;IPersistenceService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;persistence.Unload(someInstance.AsIObject());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (someInstance.CurrentUser != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new ..........;&lt;br /&gt;&amp;nbsp;&amp;nbsp;someInstance.CurrentUser = user;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;persistence.UpdateDatabase(someInstance);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This unloads the local cache before ensuring someInstance.CurrentUser == null, it then sets someInstance.CurrentUser and updates the DB.  The unit test I wanted would check what happens when two users try to perform this at the same time.  What I wanted was&lt;br /&gt;&lt;br /&gt;User A: Unload&lt;br /&gt;User B: Unload&lt;br /&gt;User A: Check == null, it is&lt;br /&gt;User B: Check == null, it is&lt;br /&gt;User A: Change + update DB&lt;br /&gt;User B: Change + update DB + experience a lock exception&lt;br /&gt;&lt;br /&gt;What I didn't want was&lt;br /&gt;&lt;br /&gt;User A: Unload&lt;br /&gt;User B: Unload&lt;br /&gt;User A: Check == null, it is&lt;br /&gt;User A: Change + update DB&lt;br /&gt;User B: Check == null, it isn't&lt;br /&gt;&lt;br /&gt;To achieve two things running at once I need to either &lt;br /&gt;&lt;br /&gt;A: Execute a line of code at a time for each user within the unit test instead of executing the method.&lt;br /&gt;B: Execute the same method from two different threads.&lt;br /&gt;&lt;br /&gt;Option A is easy to follow but is rubbish because it involves copying the method source out into the test method, no thanks!  Option B is good but harder because I need to ensure that the two threads execute the lines of code in sync.  What I really could do with is sync code inside the method, but there is no way I want to add additional sync logic because it is only needed for testing!  However, there is already an opportunity to inject some code into the method; take a look here&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;var persistence = someInstance.AsIObject.GetEcoService&amp;lt;IPersistenceService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;persistence.UpdateDatabase(someInstance);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Because we are using Inversion of Control it means that the method will call UpdateDatabase() on the reference we pass rather than a hard-coded reference.  This means that we could quite easily replace the IPersistenceService with our own implementor and intercept the call to UpdateDatabase.  &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public class PersistenceServiceWithEvents : IPersistenceService&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//An event to call back&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public event EventHandler BeforeUpdateDatabase;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//A reference to the original persistence service&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private readonly IPersistenceService PersistenceService;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Constructor&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public PersistenceServiceWithEvents(IEcoServiceProvider serviceProvider)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PersistenceService = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;serviceProvider.GetEcoService&amp;lt;IPersistenceService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Interface implementation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void UpdateDatabase(IObjectProvider obj)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EventHandler handler = BeforeUpdateDatabase;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (handler != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this, EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Other methods omitted&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I have the opportunity to call back some code just before the real UpdateDatabase is executed, which gives me the chance to insert some thread sync' code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;[TestMethod]&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void HandlesConcurrency()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create a shared object to work with&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ecoSpace = TestHelper.EcoSpace.Create();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var someInstance = new SomeClass(ecoSpace);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.UpdateDatabase();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get it's external ID&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string someInstanceID = ecoSpace.ExternalIds.IdForObject(someInstance);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create a thread sync object which both threads will wait for before&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//passing on their UpdateDatabase call to the original persistence servce&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var startUpdateDB = new ManualResetEvent(false);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create a thread sync object which tells the test when this thread has called&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//PersistenceService.UpdateDatabase&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user1ReadyToUpdateDB = new AutoResetEvent(false);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool user1Conflict = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user1ThreadStart = new ThreadStart(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delegate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandlesConcurrency_UserEmulation(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.PersistenceMapper, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;someInstanceID, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user1ReadyToUpdateDB, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startUpdateDB, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out user1Conflict);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create a thread sync object which tells the test when this thread has called&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//PersistenceService.UpdateDatabase&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user2ReadyToUpdateDB = new AutoResetEvent(false);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool user2Conflict = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user2StartUpdateDB = new AutoResetEvent(false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user2ThreadStart = new ThreadStart(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delegate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HandlesConcurrency_UserEmulation(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.PersistenceMapper, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;someInstanceID, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user2ReadyToUpdateDB, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startUpdateDB, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out user2Conflict);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create and start both threads&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user1Thread = new Thread(user1ThreadStart);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var user2Thread = new Thread(user2ThreadStart);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user1Thread.Start();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user2Thread.Start();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Wait until they have both signalled that the call to UpdateDatabase has been reached&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user1ReadyToUpdateDB.WaitOne();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user2ReadyToUpdateDB.WaitOne();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Both threads have now executed DoSomething() as far as the call to&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//persistenceService.UpdateDatabase and are waiting for me to tell them&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//to continue.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startUpdateDB.Set();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Both threads will now wake up and call the original PersistenceService.UpdateDatabase.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Wait for both threads to finish so that the test does not end prematurely.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user1Thread.Join();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user2Thread.Join();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Check at least one experienced a conflict&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int lockCount = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (user1Conflict)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lockCount++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (user2Conflict)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lockCount++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1, lockCount, &amp;quot;One should experience a lock conflict&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private void HandlesConcurrency_UserActionEmulation(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PersistenceMapper persistenceMapper, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string someInstanceID, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AutoResetEvent readyToUpdateDB, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WaitHandle startUpdateDB,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out bool lockConflict)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//SETUP &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create secondary ecospaces with the same persistence mapper as the&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//original.  This is because my test EcoSpace uses a memory persistence mapper&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ecoSpace = TestHelper.EcoSpace.CreateWithSharedMapper(persistenceMapper);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create the persistence service with the BeforeUpdateDatabase event&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var callbackPersistenceService = new TestHelper.PersistenceServiceWithEvents(ecoSpace);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Register it&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.RegisterEcoService&amp;lt;IPersistenceService&amp;gt;(callbackPersistenceService);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Ensure update happens in sync, this occurs when we try to lock the object&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;callbackPersistenceService.BeforeDatabaseUpdate += (sender, args) =&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Notify we are ready to update&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;readyToUpdateDB.Set();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Now wait to be told to complete the update&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startUpdateDB.WaitOne();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//ACT&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Now test our code&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var someServiceToTest = new SomeService(ecoSpace);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var someInstance = ecoSpace.ExternalIds.ObjectForId(someInstanceID).GetValue&amp;lt;SomeClass&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lockConflict = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;someServiceToTest.DoSomething(someInstance, new User(ecoSpace));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (OptimisticLockException)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lockConflict = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So there you have it.  An example of how Inversion of Control and the Service Provider pattern can enable you to redirect calls in parts of your code to enable complex testing scenarios without having to change your implementation!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6138368025657196282?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6138368025657196282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6138368025657196282' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6138368025657196282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6138368025657196282'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/implementing-complex-unit-testing-with.html' title='Implementing complex unit testing with IoC'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-7409870210939207782</id><published>2008-10-03T22:38:00.000+01:00</published><updated>2008-10-03T23:35:01.629+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Modeling ValueTypes</title><content type='html'>There are (apparently) two types of classes you can model.  These are Entities and ValueTypes.  An Entity would be something like a Company or a Person, where even two instances with the same data are not considered the same; whereas a ValueType would be something like a colour which would be considered the same no matter how many instances you had representing the value Red.&lt;br /&gt;&lt;br /&gt;ValueType classes are usually modeled for things like an address.  You might decide that whenever someone enters an address that already exists you should reference an existing instance rather than create a new instance with the same data, this could be used for example to see all deliveries made to a single address regardless of to whom the package was addressed.&lt;br /&gt;&lt;br /&gt;The problem with ValueTypes is that they are shared across so many different classes.  An address for example could be attached to a Person, a Delivery, a PurchaseOrder, all sorts.  This is really only a problem because&lt;br /&gt;&lt;br /&gt;A: How do you know when the ValueType is no longer referenced and can be deleted.&lt;br /&gt;B: If you don&amp;apos;t delete them then your DB will become full of data that is no longer needed.&lt;br /&gt;&lt;br /&gt;The first point (A) was that we don&amp;apos;t know when this ValueType is no longer referenced.  The solution to this is very simple.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;[Address] (Address) 1 &amp;lt;&amp;gt;----0..* (References)[AddressReference]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Address class has an aggregated association to the class AddressReference, meaning that if Address.References is not empty the Address cannot be deleted.  The rule to obey is that we should never have any persistent associations to the Address class, the only associations to Address should always be instances of AddressReference, this our scheduled app can find all addresses to delete&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;Address.allInstances-&amp;gt;select(references-&amp;gt;isEmpty)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Person (for example) would instead of having a reference to Address would have a privately owned AddressReference which it creates in its constructor&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public Person(IEcoServiceProvider serviceProvider)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.CurrentAddressReference = new AddressReference(serviceProvider);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The problem is that this code is not &amp;quot;very nice&amp;quot; to write:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;Person.CurrentAddressReference.Address = someAddress;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;when what we really want is&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;Person.Addres = someAddress;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So here&amp;apos;s a comprimise.  You make the association Person.AddressReference private, so it is not accessible via code at all.  Then you create a transient association from Person to Address and mark it HasUserCode&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;[Person] 0..* ------&amp;gt; 1 (CurrentAddress) [Address]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public Address CurrentAddress&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return CurrentAddressReference.Address; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set { CurrentAddressReference.Address = value; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So now we have a transient association with user code so ECO will always get/set via our property accessor methods, this propery is just an indirection for our private CurrentAddressReference.  As a consequence we have the ability to use code and in-memory OCL such as&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;somePerson.CurrentAddress = someAddress;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;but the persistent association is actually made via an AddressReference class, so Address still only ever has one association end and is therefore easy to check if it is being associated from any class in the model.&lt;br /&gt;&lt;br /&gt;Note:  To do an OclPs evaluation you would need to use person.CurrentAddressReference.Address, as Person.Address is transient.&lt;br /&gt;&lt;br /&gt;So, that&amp;apos;s how we achieve the ability to reference a class and know its current reference count, but how do we go about deleting non-referenced values?  The first thing to accept is that an individual client should not attempt to delete value types.  Two clients could attempt to delete the ValueType at the same time, fail, and then no instance would ever try again, so your DB would still fill up.  What you need is a server application scheduled to run regularly, maybe once per day (at a time when nobody is using it preferably, but this is not essential).&lt;br /&gt;&lt;br /&gt;The final challenge is to determine which classes are ValueTypes, and what the name of their single association is.  At this point I could suggest you use a tagged value on the class to identify it as a ValueType, and then use the runtime model information to find its only association, but I feel this is not expressive enough and the data is a bit hidden away.  What I prefer is a .NET attribute identifying the class as a ValueType and also identifying the association member.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class ValueTypeAttribute : Attribute&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public readonly int MemberLoopbackIndex;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public ValueTypeAttribute(int memberLoopbackIndex)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MemberLoopbackIndex = memberLoopbackIndex;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The member is identified using its member index, this way if you rename the association end your app will not compile until you fix your ValueType declarations.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&lt;b&gt;[ValueType(Address.Eco_LoopbackIndices.References_MemberIndex)]&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class Address&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Your application can now use reflection to find all classes which define the ValueType attribute:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;var valueTypeClasses =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from c in typeof(MyPackage).Assembly.GetTypes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;where c.GetCustomAttributes(typeof(ValueTypeAttribute), true).Length &amp;gt; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;select c;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you can loop through each class and delete non-referenced instances&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;//Find all ValueType classes&lt;br /&gt;&amp;nbsp;&amp;nbsp;var valueTypeClasses =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from c in typeof(MyPackage).Assembly.GetTypes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;where c.GetCustomAttributes(typeof(ValueTypeAttribute), true).Length &amp;gt; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;select c;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Process each class in turn&lt;br /&gt;&amp;nbsp;&amp;nbsp;foreach (Type currentClass in valueTypeClasses)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get the ValueTypeAttribute for this class&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var attribute = (ValueTypeAttribute)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentClass.GetCustomAttributes(typeof(ValueTypeAttribute), true)[0];&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get a list of non-referenced instances&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (var ecoSpace = new MyEcoSpace())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.Active = true;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get the model information for this type&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IClass ecoClass = ecoSpace.TypeSystem.GetClassByType(currentClass);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Convert the loopback index to a feature index&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int featureIndex =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoClass.GetStructuralFeatureIndexByLoopbackIndex(attribute.MemberLoopbackIndex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Now get the name of the association&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string associationName = ecoClass.AllStructuralFeatures[featureIndex].Name;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Create our OCL expression&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string ocl =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string.Format(&amp;quot;{0}.allInstances-&amp;gt;select({1}-&amp;gt;isEmpty)&amp;quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoClass.Name,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;associationName);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//Get the non-referenced ValueType instances&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IObjectList objectsToDelete = ecoSpace.OclPs.Execute();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach (IObject currentLocator in objectsToDelete)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentLocator.Delete();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (OptimisticLockException)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//After selecting someone has referenced the object, so ignore this exception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//without deleting the instance&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}//using ecoSpace&lt;br /&gt;&amp;nbsp;&amp;nbsp;}//foreach type&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-7409870210939207782?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/7409870210939207782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=7409870210939207782' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7409870210939207782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/7409870210939207782'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/10/modeling-valuetypes.html' title='Modeling ValueTypes'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1791878516972292288</id><published>2008-09-30T12:58:00.000+01:00</published><updated>2008-09-30T13:41:34.579+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Unit testing security</title><content type='html'>Following on from my previous post about using(Tricks) here is an example which makes writing test cases easier rather than just for making your code nicely formatted.  Take a look at the following test which ensures Article.Publish sets the PublishedDate correctly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[TestMethod]&lt;br /&gt;public void PublishedDateIsSet()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Create the EcoSpace, set its PMapper to a memory mapper&lt;br /&gt;&amp;nbsp;&amp;nbsp;var ecoSpace = TestHelper.EcoSpace.Create();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Creat an article&lt;br /&gt;&amp;nbsp;&amp;nbsp;var article = new Article(ecoSpace);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Create our Rhino Mocks repository&lt;br /&gt;&amp;nbsp;&amp;nbsp;var mocks = new MockRepository();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Mock the date/time to give us a predictable value&lt;br /&gt;&amp;nbsp;&amp;nbsp;var mockDateTimeService = mocks.StrictMock&amp;lt;IDateTimeService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;ecoSpace.RegisterEcoService(typeof(IDateTimeService), mockDateTimeService);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Get a date/time to return from the mock DateTimeService&lt;br /&gt;&amp;nbsp;&amp;nbsp;var now = DateTime.Now;&lt;br /&gt;&amp;nbsp;&amp;nbsp;using (mocks.Record())&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//When asked, return the value we recorded earlier&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(mockDateTimeService.Now).Return(now);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Check mockDateTimeService.Now is called from Article.Publish&lt;br /&gt;&amp;nbsp;&amp;nbsp;using (mocks.Playback())&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;article.Publish();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//Check the date/time from IDateTimeService is stored in PublishedDate&lt;br /&gt;&amp;nbsp;&amp;nbsp;Assert.AreEqual(now, article.PublishedDate);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The method it is testing&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void Publish()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var dateTimeService = serviceProvider.GetEcoService&amp;lt;IDateTimeService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;PublishedDate = dateTimeService.Now;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now at some point in the future you decide to enforce some security within your business objects.  You decide that only certain people can publish your article, such as the author or an administrator.  This will now break every test you have which assumes article.Publish will just work.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void Publish()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var currentUserService = serviceProvider.GetEcoService&amp;lt;ICurrentUserService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;var currentUser = currentUserService.CurrentUser;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (currentUser != this.Author &amp;amp;&amp;amp; !currentUser.HasRole&amp;lt;SystemAdministratorRole&amp;gt;())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new SecurityException(&amp;quot;Cannot publish this article&amp;quot;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var dateTimeService = serviceProvider.GetEcoService&amp;lt;IDateTimeService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;PublishedDate = dateTimeService.Now;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This would dramatically complicated any test which relied on having a published article.  Each time you would additionally have to:&lt;br /&gt;01: Create a user.&lt;br /&gt;02: Mock ICurrentUserService to return that user.&lt;br /&gt;03: Ensure the article.Author is set to that user, or the user owns a SystemAdministratorRole.&lt;br /&gt;&lt;br /&gt;If you have 20 tests requiring a published article this is going to cause you a lot of work!  The first mistake here is that the article is testing for permissions; permission granting should be a service.  I would separate the service out like so...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface IPermissionService&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;bool MayPublishArticle(Article article);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Your EcoSpace would have a class implementing this interface and return the appropriate result, the EcoSpace would register this default service.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class PermissionService : IPermissionService&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;IEcoServiceProvider ServiceProvider;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public PermissionService(IEcoServiceProvider serviceProvider)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServiceProvider = serviceProvider;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public bool MayPublishArticle(Article article)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var currentUserService = serviceProvider.GetEcoService&amp;lt;ICurrentUserService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var currentUser = currentUserService.CurrentUser;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return currentUser == article.Author&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| currentUser.HasRole&amp;lt;SystemAdministratorRole&amp;gt;());&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the EcoSpace:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public override bool Active&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;get { return base.Active; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (value &amp;amp;&amp;amp; !Active)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RegisterDefaultServices();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base.Active = value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void RegisterDefaultServices()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;RegisterEcoService(typeof(IPermissionService), new PermissionService(this));&lt;br /&gt;&amp;nbsp;&amp;nbsp;RegisterEcoService(typeof(ICurrentUserService), new CurrentUserService());&lt;br /&gt;&amp;nbsp;&amp;nbsp;RegisterEcoService(typeof(IDateTimeService), new DateTimeService());&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now Article.Publish looks like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void Publish()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var permissionService = serviceProvider.GetEcoService&amp;lt;IPermissionService&amp;gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (!permissionService.MayPublishArticle(this))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new SecurityException(&amp;quot;Cannot publish this article&amp;quot;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;var dateTimeService = serviceProvider.GetEcoService&amp;lt;IDateTimeService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;PublishedDate = dateTimeService.Now;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But how does this help?  The first advtange is that we have separated the PermissionService so that we can test it in isolation, but we would still need to mock the IPermissionService wouldn&amp;apos;t we?  Yes we would! But how does this look?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;using (TestHelper.Permissions.PermitAll(ecoSpace))&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;article.Publish();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Much more simple eh?  To achieve this I have a static class named Permissions in my test project&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public static class Permissions&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;private class TestPermissionService : IPermissionService&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(simple code omitted)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Accept a boolean in the constructor, and return&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;it for every method call.&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public static IDisposable Allow(MyEcoSpaceType ecoSpace)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var originalService = ecoSpace.GetEcoService&amp;lt;IPermissionService&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var newService = new TestPermissionService(true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ecoSpace.RegisterEcoService(typeof(IPermissionService), newService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return DisposableAction( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;() =&amp;gt; ecoSpace.RegisterEcoService(typeof(IPermissionService), originalService)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All this does is to record the current IPermissionService, register a new one which always returns True/False (depending on what we pass to its constructor), and then return an instance of DisposableAction.  To this instance we pass an Action which re-registers the original service.  The action is called when IDisposable.Dispose is called:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class DisposableAction : IDisposable&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;Action Action;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public DisposableAction(Action action)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Action = action&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;void IDisposable.Dispose()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Action();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So the following line&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;using (TestHelper.Permissions.PermitAll(ecoSpace))&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;article.Publish();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;will &lt;br /&gt;01: Record the original IPermissionService.&lt;br /&gt;02: Register a new one which always returns True.&lt;br /&gt;03: Execute the code within the Using { } block.&lt;br /&gt;04: IDisposable.Dispose will be called on my DisposableAction.&lt;br /&gt;05: The previous service will be restored.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1791878516972292288?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1791878516972292288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1791878516972292288' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1791878516972292288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1791878516972292288'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/09/unit-testing-security.html' title='Unit testing security'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5888692785263239643</id><published>2008-09-30T08:30:00.000+01:00</published><updated>2008-09-30T09:25:51.134+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Single instance application - revisited</title><content type='html'>Not so long ago I posted a solution to having a single-instance application.  Rather than just preventing secondary instances from running the requirement was to have the 2nd instance pass its runtime parameters onto the 1st instance so that it can process them.  My solution used remoting on the local machine.  This appeared to work very well until recently when I needed an OpenFileDialog.  Attempting to show the dialog resulted in an error about COM thread apartments.  So, it wasn&amp;apos;t THE solution.&lt;br /&gt;&lt;br /&gt;After a bit of research I decided to use named pipes instead.  This meant I had to upgrade my app from .NET 2 to 3.5, but I think it is worth it.  To implement the feature in an app you need to do 2 things.  First you need to realize the interface ISingleInstanceApplicationMainForm on your app&amp;apos;s main form in order to accept command line arguments from any subsequently started instances.  Next you need to change your Program.Main method like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[STAThread]&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;Application.EnableVisualStyles();&lt;br /&gt;&amp;nbsp;&amp;nbsp;Application.SetCompatibleTextRenderingDefault(false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;new SingleInstanceApplication&amp;lt;MainForm&amp;gt;(&amp;quot;CompanyName.ApplicationName&amp;quot;, args);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The source code for SingeInstanceApplication is an adaptation of some code I read &lt;a href="http://www.flawlesscode.com/post/2008/02/Enforcing-single-instance-with-argument-passing.aspx"&gt;here&lt;/a&gt;; and here it is:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface ISingleInstanceApplicationMainForm&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;void AcceptCommandLineArguments(string[] args);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class SingleInstanceApplication&amp;lt;TForm&amp;gt; : IDisposable&lt;br /&gt;&amp;nbsp;&amp;nbsp;where TForm : ISingleInstanceApplicationMainForm&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;Mutex Mutex;&lt;br /&gt;&amp;nbsp;&amp;nbsp;bool IsFirstInstance;&lt;br /&gt;&amp;nbsp;&amp;nbsp;string ApplicationUniqueID;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected TForm MainForm { get; private set; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public SingleInstanceApplication(string applicationUniqueID, string[] args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ApplicationUniqueID = applicationUniqueID;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mutex = new Mutex(true, ApplicationUniqueID, out IsFirstInstance);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (IsFirstInstance)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MainForm = (TForm)Activator.CreateInstance(typeof(TForm));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(MainForm as ISingleInstanceApplicationMainForm).AcceptCommandLineArguments(args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ThreadPool.QueueUserWorkItem(new WaitCallback(ListenForArguments));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Application.Run((Form)(object)MainForm);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PassArgumentsToFirstInstance(args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private void ListenForArguments(Object state)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (var server = new NamedPipeServerStream(ApplicationUniqueID))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (var reader = new StreamReader(server))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;server.WaitForConnection();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string[] args = reader.ReadLine().Split(new char[] {&amp;apos;\t&amp;apos;}, StringSplitOptions.RemoveEmptyEntries);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveArgumentsFromNewInstance), args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}//using reader&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}//using server&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (IOException) { }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ListenForArguments(null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private void ReceiveArgumentsFromNewInstance(Object state)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string[] args = (string[])state;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(MainForm as Form).Invoke(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new ThreadStart(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delegate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MainForm.AcceptCommandLineArguments(args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private void PassArgumentsToFirstInstance(string[] args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var builder = new StringBuilder();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach (var arg in args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;builder.AppendFormat(&amp;quot;{0}\t&amp;quot;, arg);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (var client = new NamedPipeClientStream(ApplicationUniqueID.ToString()))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (var writer = new StreamWriter(client))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;client.Connect(500); // 0.5 seconds timeout&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.WriteLine(builder.ToString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}//using writer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}//using client&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (TimeoutException) { }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (IOException) { }&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;void IDisposable.Dispose()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (IsFirstInstance &amp;amp;&amp;amp; Mutex != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mutex.ReleaseMutex();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5888692785263239643?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5888692785263239643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5888692785263239643' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5888692785263239643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5888692785263239643'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/09/single-instance-application-revisited.html' title='Single instance application - revisited'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2769465666497130062</id><published>2008-09-24T08:43:00.000+01:00</published><updated>2008-09-24T08:44:00.956+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>using(TricksToFormatYourCodeNicely)</title><content type='html'>I&amp;apos;ve been writing a data importer which takes a specific data input format and outputs XML, this XML is then imported within my application.  What annoyed me was the way in which the source code was formatted....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;writer.WriteStartElement(&amp;quot;data&amp;quot;);&lt;br /&gt;writer.WriteAttributeString(&amp;quot;1&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;writer.WriteAttributeString(&amp;quot;2&amp;quot;, &amp;quot;2&amp;quot;);&lt;br /&gt;writer.WriteAttributeString(&amp;quot;3&amp;quot;, &amp;quot;3&amp;quot;);&lt;br /&gt;&lt;br /&gt;writer.WriteStartElement(&amp;quot;systemData&amp;quot;);&lt;br /&gt;writer.WriteAttributeString(&amp;quot;a&amp;quot;, &amp;quot;a&amp;quot;);&lt;br /&gt;writer.WriteAttributeString(&amp;quot;b&amp;quot;, &amp;quot;b&amp;quot;);&lt;br /&gt;writer.WriteEndElement();//systemData&lt;br /&gt;&lt;br /&gt;writer.WriteEndElement();//data&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It just didn&amp;apos;t look nice.  I thought about splitting it into separate methods, but most of the time this would have been overkill as the methods would have been very short.  Instead I wrote an extension method on XmlWriter:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public static class XmlWriterHelper&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;public static IDisposable StartElement(this XmlWriter writer, string elementName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new DisposableElementWriter(writer, elementName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private class DisposableElementWriter : IDisposable&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private XmlWriter Writer;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public DisposableElementWriter(XmlWriter writer, string elementName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Writer = writer;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Writer.WriteStartElement(elementName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void Dispose()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Writer.WriteEndElement();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now I can write code like this instead:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;using (writer.StartElement(&amp;quot;data&amp;quot;))&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;writer.WriteAttributeString(&amp;quot;1&amp;quot;, &amp;quot;1&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;writer.WriteAttributeString(&amp;quot;2&amp;quot;, &amp;quot;2&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;writer.WriteAttributeString(&amp;quot;3&amp;quot;, &amp;quot;3&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;using (writer.StartElement(&amp;quot;systemData&amp;quot;))&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.WriteAttributeString(&amp;quot;a&amp;quot;, &amp;quot;a&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.WriteAttributeString(&amp;quot;b&amp;quot;, &amp;quot;b&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}//systemData&lt;br /&gt;}//data&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Less code AND easier to read.  What a bonus!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2769465666497130062?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2769465666497130062/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2769465666497130062' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2769465666497130062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2769465666497130062'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/09/usingtrickstoformatyourcodenicely.html' title='using(TricksToFormatYourCodeNicely)'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2614554310719334829</id><published>2008-09-17T10:47:00.001+01:00</published><updated>2008-09-17T10:57:14.524+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><title type='text'>Parameterised queries in ECO</title><content type='html'>Whenever I generate OCL queries in code I find myself having to escape user input in order to avoid making the query invalid, or allowing malicious input.&lt;br /&gt;&lt;br /&gt;I&amp;apos;ve decided instead to use the ECO equivalent of parameterised queries (variables in ECO) and here is the result.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public static string CreateParameterisedQuery(&lt;br /&gt;&amp;nbsp;&amp;nbsp;this IEcoServiceProvider serviceProvider, &lt;br /&gt;&amp;nbsp;&amp;nbsp;string query, &lt;br /&gt;&amp;nbsp;&amp;nbsp;out IModifiableVariableList vars,&lt;br /&gt;&amp;nbsp;&amp;nbsp;params object[] args)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;vars = serviceProvider.GetEcoService&amp;lt;IVariableFactoryService&gt;().CreateVariableList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;for (int varIndex = 0; varIndex &amp;lt; args.Length; varIndex++)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string variableName = &amp;quot;autoVar_&amp;quot; + varIndex.ToString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;query = query.Replace(&amp;quot;{&amp;quot; + varIndex.ToString() + &amp;quot;}&amp;quot;, variableName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vars.AddConstant(variableName, args[varIndex]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;return query;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To use this code you would do something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;//1: Create the OCL with string.format style parameters&lt;br /&gt;string query = &amp;quot;Person.allInstances&amp;quot; +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;quot;-&amp;gt;select(name.sqlLikeCaseInsensitive({0}))&amp;quot; +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;quot;-&amp;gt;select(gender = {1}&amp;quot;;&lt;br /&gt;&lt;br /&gt;//2: Parse the query and build the variable list&lt;br /&gt;IModifyableVariableList vars;&lt;br /&gt;query = self.AsIObject().ServiceProvider.CreateParameterisedQuery(query,&lt;br /&gt;&amp;nbsp;&amp;nbsp;out vars,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;quot;Peter Morris&amp;quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;Gender.Male);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you can use IOclPsService or IOclService to execute the new query passing the variables.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2614554310719334829?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2614554310719334829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2614554310719334829' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2614554310719334829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2614554310719334829'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/09/parameterised-queries-in-eco.html' title='Parameterised queries in ECO'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2299901956511613185</id><published>2008-09-08T22:20:00.000+01:00</published><updated>2008-09-08T22:24:29.873+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Silverlight'/><title type='text'>User authentication in SilverLight</title><content type='html'>I wanted to know how to authenticate users in a SilverLight app using their Windows login info.&lt;br /&gt;&lt;br /&gt;01: Set the authentication mode to Windows and &amp;lt;deny users=&amp;quot;?&amp;quot;/&amp;gt; in &amp;lt;system.web&amp;gt; within web.config&lt;br /&gt;02: Move the silverlight control to Default.aspx and set that as your start page&lt;br /&gt;03: Add the following Page_Load code&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;  protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;  {&lt;br /&gt;    IPrincipal p = HttpContext.Current.User;&lt;br /&gt;    if (p == null)&lt;br /&gt;      throw new SecurityException(&amp;quot;No current user&amp;quot;);&lt;br /&gt;    if (!(p is WindowsPrincipal))&lt;br /&gt;      throw new SecurityException(&amp;quot;Not a windows user&amp;quot;);&lt;br /&gt;    if (!p.Identity.IsAuthenticated)&lt;br /&gt;      throw new SecurityException(&amp;quot;Not authenticated&amp;quot;);&lt;br /&gt;    Xaml1.InitParameters =&lt;br /&gt;      string.Format(&amp;quot;user={0},session={1}&amp;quot;, p.Identity.Name, Session.SessionID);&lt;br /&gt;  }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;04: In app.xaml.cs you can now read the InitParameters using e.InitParameters in the Application_Startup method.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2299901956511613185?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2299901956511613185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2299901956511613185' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2299901956511613185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2299901956511613185'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/09/user-authentication-in-silverlight.html' title='User authentication in SilverLight'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-2310613139477541563</id><published>2008-08-27T23:37:00.000+01:00</published><updated>2008-08-27T23:42:23.113+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><title type='text'>No more free disk space - Vista</title><content type='html'>My computer was recently running out of disk space.  I couldn't believe it, I have 175GB on my partition, how could I fill it so quickly?  I was considering buying a 500GB external drive to store my home videos etc on (which is where I assumed all the space was being taken) and earlier today I nearly went out and bought one too!&lt;br /&gt;&lt;br /&gt;I just decided to check where all that space was being taken up.  Here are the folders on C and their sizes in GB&lt;br /&gt;&lt;br /&gt;apps        2.76&lt;br /&gt;data        40.7&lt;br /&gt;msocache    0.7&lt;br /&gt;otherdata    0.8&lt;br /&gt;program files    9.16&lt;br /&gt;program data    0.9&lt;br /&gt;users        1.64&lt;br /&gt;windows        13.8&lt;br /&gt;&lt;br /&gt;That's a total of 70.46GB, but Windows was reporting 173GB in use!  I ran scan disk etc, no luck!&lt;br /&gt;&lt;br /&gt;Where was all the space being used?  The answer was System Restore!  After reading &lt;a href="http://www.howtogeek.com/howto/windows-vista/reduce-system-restores-disk-usage-in-vista/"&gt;this article&lt;/a&gt; I realised my restore data was using 90GB of data!  What a waste!  I have now limited it to 10GB maximum.  Can you believe that?  It's ridiculous!  Luckily I have just saved myself £70!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-2310613139477541563?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/2310613139477541563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=2310613139477541563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2310613139477541563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/2310613139477541563'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/08/no-more-free-disk-space-vista.html' title='No more free disk space - Vista'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-1816507240247488744</id><published>2008-08-14T16:25:00.001+01:00</published><updated>2008-09-10T13:18:38.423+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WCF'/><title type='text'>The contract name could not be found in the list of contracts implemented by the service</title><content type='html'>When trying to add a service reference to my SilverLight project I kept getting this error message&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;The contract name xxxxxxx could not be found in the list of contracts implemented by the service&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I looked around the web and couldn't find anything of any use (yes, I had added ServiceContract to my interface). The solution was really simple!  In my Web.Config I had the wrong interface defined.  &lt;endpoint address="" binding="basicHttpBinding" contract="IApplicationService"&gt;IApplicationService was defined in another assembly, one I had added a namespace to, so I additionally needed to add the namespace before the interface name.&lt;br /&gt;&lt;br /&gt;&lt;endpoint address="" binding="basicHttpBinding" contract="AirSoftware.ApplicationFramework.IApplicationService"&gt;All I then had to do was to add the following attribute to the service implementing the interface and it seemed to import fine.&lt;br /&gt;&lt;br /&gt;[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]&lt;/endpoint&gt;&lt;/endpoint&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-1816507240247488744?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/1816507240247488744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=1816507240247488744' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1816507240247488744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/1816507240247488744'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/08/contract-name-could-not-be-found-in.html' title='The contract name could not be found in the list of contracts implemented by the service'/><author><name>Peter Morris</name><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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5957564605152160400</id><published>2008-07-19T09:20:00.002+01:00</published><updated>2008-07-25T09:46:40.135+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='CF'/><title type='text'>Validating NumericUpDown on compact framework</title><content type='html'>A customer requested that instead of my NumericUpDown controls silently capping the input value within the Minimum..Maximum range it instead showed an error message telling the user their input is incorrect and that they need to alter it.&lt;br /&gt;&lt;br /&gt;I was a bit annoyed to see that NumericUpDown.Validating is never called on the compact framework, in addition there was no way to get the input value and either accept or reject it before it is applied to its data bindings.&lt;br /&gt;&lt;br /&gt;There's an article &lt;a href="http://blog.markarteaga.com/CommentView,guid,85271d7d-14a1-41dd-8469-d230a4e609cf.aspx"&gt;here&lt;/a&gt; which shows how to implement auto-select text when the NumericUpDown receives focus and I have been using it since Feb 2006.  I decided to extend upon the techniques within it to implement the Validating event.  My goal was to fire the Validating event before the value is applied to all data-bindings, but also to allow the programmer to read NumericUpDown.Value in order to determine the new value.  To do this I had to replace the WndProc of the control so that I could handle the&lt;br /&gt;WM_UPDOWN_NOTIFYVALUECHANGED message, parse the value, validate it, and then either accept it (call the original WndProc) or restore the value to the current value.&lt;br /&gt;&lt;br /&gt;Rather than teach how this is done I thought I would just include the source code here.  One point to note though is that I had to have a "bool IsInternalCall" wrapped around my handler otherwise I would have re-entrant problems and experience a stack overflow.  Here is the source, it includes the auto-select code by &lt;a href="http://blog.markarteaga.com"&gt;Mark Arteaga&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using System.Windows.Forms;&lt;br /&gt;using System.Runtime.InteropServices;&lt;br /&gt;using System.Threading;&lt;br /&gt;using System.ComponentModel;&lt;br /&gt;&lt;br /&gt;namespace Mycompany.Windows.Forms&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class NumericUpDownWithSelect : NumericUpDown, ISupportInitialize&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region API&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const int GWL_WNDPROC = -4;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const int WM_UPDOWN_NOTIFYVALUECHANGED = 13;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public const int WM_GETTEXTLENGTH = 0x000E;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public const int WM_GETTEXT = 0x000D;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const int WM_GETSELECTION = 0x00B0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const int WM_SETSELECTION = 0x00B1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private WndProcHandler NewWndProc = null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private IntPtr OldWndProc = IntPtr.Zero;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public delegate IntPtr WndProcHandler(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, SetLastError = true)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, SetLastError = true)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, StringBuilder buffer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, CharSet = CharSet.Auto)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, WndProcHandler wndproc);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, CharSet = CharSet.Auto)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, CharSet = CharSet.Auto)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[DllImport(&amp;quot;coredll.dll&amp;quot;, CharSet = CharSet.Auto)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private bool ControlDisposed = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private bool IsValidating = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private decimal ValueToValidate;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public NumericUpDownWithSelect()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public new event CancelEventHandler Validating;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected virtual void OnValidating(out bool cancel, decimal newValue)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cancel = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CancelEventHandler validating = Validating;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (validating == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cancel = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CancelEventArgs args = new CancelEventArgs(false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IsValidating = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ValueToValidate = newValue;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Validating(this, args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IsValidating = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cancel = args.Cancel;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private decimal currentValue = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public new decimal Value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (IsValidating)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ValueToValidate;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return base.Value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool cancel;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnValidating(out cancel, value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!cancel)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base.Value = value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentValue = value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region Validation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected override void OnHandleCreated(EventArgs e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base.OnHandleCreated(e);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (this.Site == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NewWndProc = new WndProcHandler(ReplacementWndProcImpl);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OldWndProc = SetWindowLong(this.Handle, GWL_WNDPROC, NewWndProc);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static bool IsInternalCall = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private IntPtr ReplacementWndProcImpl(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool cancelled = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (msg == WM_UPDOWN_NOTIFYVALUECHANGED &amp;amp;&amp;amp; !IsInternalCall)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IsInternalCall = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int length = CallWindowProc(OldWndProc, this.Handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero).ToInt32();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder buffer = new StringBuilder(length + 1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SendMessage(this.Handle, WM_GETTEXT, length + 1, buffer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;decimal newValue = decimal.Parse(buffer.ToString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnValidating(out cancelled, newValue);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (cancelled)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Value = currentValue;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;catch (FormatException)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cancelled = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IsInternalCall = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (cancelled)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Focus();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SelectAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return CallWindowProc(OldWndProc, hWnd, msg, wParam, lParam);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region AutoSelect&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private delegate void SelectAllInvoke();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private bool suppressOnGotFocus = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected override void OnGotFocus(EventArgs e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base.OnGotFocus(e);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!this.suppressOnGotFocus)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SelectAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void SelectAll()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.SelectInternal(0, this.Value.ToString().Length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void Select(int start, int length)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.SelectInternal(start, length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private void SelectInternal(int start, int length)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!ControlDisposed)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.suppressOnGotFocus = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!this.Focused)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.Focus();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IntPtr ret = SendMessage(this.Handle, WM_SETSELECTION, start, length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.suppressOnGotFocus = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected override void Dispose(bool disposing)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ControlDisposed = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base.Dispose(disposing);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region ISupportInitialize Members&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//This region is here simply because the WinForm designer insists on casting this control&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//to ISupportInitialize&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void ISupportInitialize.BeginInit()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void ISupportInitialize.EndInit()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And how might you use it?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;private void numericUpDownWithSelect1_Validating_1(object sender, CancelEventArgs e)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (numericUpDownWithSelect1.Value &amp;lt; numericUpDownWithSelect1.Minimum&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| numericUpDownWithSelect1.Value &amp;gt; numericUpDownWithSelect1.Maximum)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//No need to cancel, the new value will be rejected&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MessageBox.Show(&amp;quot;Warning, value is about to be capped&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (numericUpDownWithSelect1.Value &amp;gt; 5)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MessageBoxIcon icon = new MessageBoxIcon();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;e.Cancel = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MessageBox.Show(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Is it really greater than 5?&amp;quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Are you sure?&amp;quot;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MessageBoxButtons.YesNo, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;icon, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MessageBoxDefaultButton.Button1) != DialogResult.Yes;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5957564605152160400?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5957564605152160400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5957564605152160400' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5957564605152160400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5957564605152160400'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/07/validating-numericupdown-on-compact.html' title='Validating NumericUpDown on compact framework'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5270703366957652176</id><published>2008-07-15T16:37:00.001+01:00</published><updated>2008-07-15T17:14:33.919+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><title type='text'>AccommodationManager</title><content type='html'>I've decided to upload a project I have been working on over the weekend. So far it's taken about 5 hours, this includes me deciding exactly what it was I was going to write plus hunting for pretty graphics :-)&lt;br /&gt;&lt;br /&gt;The requirement came about because I was asked if I could display booking availability on &lt;a href="http://www.trentinney.co.uk/"&gt;www.trentinney.co.uk&lt;/a&gt; - in addition they wanted to show availability for a number of other sites + the cost to stay during certain periods.  I decided to write a single-user ECO app using SQLite as the database.  This app will track customer information, bookings, properties, and prices; whenever the user is happy they will click a Publish button and it will communicate with a webservice to publish the data (this part is not yet written).&lt;br /&gt;&lt;br /&gt;The app uses some practises I would recommend, such as&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Having one EcoSpace per form where possible in order to ensure your memory is freed when no longer needed.&lt;/li&gt;&lt;li&gt;Using OCL constraints to validate user input - using my DroopyEyes extensions.&lt;/li&gt;&lt;li&gt;Replacing the IPersistenceService to perform double-check no constraints are broken, and to allow classes to define PreSave validation for more complicated validation (such as doing in-DB searches for clashing bookings so that you don't have to load in all bookings into memory).&lt;/li&gt;&lt;li&gt;Adding a non-ECO DBStructure table to the DB so that you can detect the current version of the DB and apply relevant SQL upgrade instructions when needed (no upgrade code in there yet).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To run the project you will need to download &lt;a href="http://sqlite.phxsoftware.com/"&gt;SQLite&lt;/a&gt;.  You will also need to remove the AirSoftware.Common and Eco.Persistence.SQLite projects and re-add them (I have copied them into the zip file).&lt;/p&gt;&lt;p&gt;The file is available &lt;a href="http://www.peterlesliemorris.com/blogfiles/AccommodationManager.zip"&gt;here&lt;/a&gt;.  The file uses only a few classes so it will work in the trial too.  The project is a Visual Studio 2008 one.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5270703366957652176?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5270703366957652176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5270703366957652176' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5270703366957652176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5270703366957652176'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/07/accommodationmanager.html' title='AccommodationManager'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8588874433291212001</id><published>2008-07-14T11:38:00.001+01:00</published><updated>2008-07-14T11:55:50.264+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Politics'/><title type='text'>Global warming and the price of oil</title><content type='html'>I'm sure not everyone will agree with me here, but I am in a mood to moan and thought I would just say what is on my mind.  I just don't beleive in it, sorry.&lt;br /&gt;&lt;br /&gt;In the past the Romans used to grow grapes near Scotland, does that sound like a time that was warmer or colder than now?  Are we to believe that the sun doesn't go through changes which over time change the average temperature of the world?  Are we to believe that over a relatively small number of years the human race is capable of changing the temperature of our oceans, a process which apparently normally takes thousands of years?  Is it the case that CO2 is causing the oceans to warm up, or that the sun warming up the oceans is causing more CO2 release?  &lt;br /&gt;&lt;br /&gt;I am also tired of the news showing glaciers collapsing into the ocean.  This hasn't been going on every year as the seasons change from winter to spring?  I remember watching nature documentaries back in 1980 about animals that live half the year on grass and half the year on snow.  What happened to those glaciers each year?  Fell into the sea I suppose.&lt;br /&gt;&lt;br /&gt;So, what's the point of saying it exists?  In my opinion it is all about money.  We are approaching a point in time where there wont be enough oil left to use it as our main energy source.  The sellers of the oil can put the price up, sure they can, but only so high.  To put it up unreasonably high they can only do it in the name of saving the planet, but how exactly will higher oil prices save the planet?  We will use less oil and pay more for it.  All this will do is to make it last longer so that the sellers can make more money for a longer period of time.  &lt;br /&gt;&lt;br /&gt;The fact is there is only so much oil in the world and we are going to use the same amount no matter how quickly we use it, all of that oil is going to get processed at some point.  So how exactly is poisoning the atmosphere slowly going to stop us doing the same amount of damage as if we had done it quickly?&lt;br /&gt;&lt;br /&gt;Personally I agree with producing less waste and poluting the atmosphere less.  I agree because the air should be nice to breath, and how can we throw away the amount of food we do when we could help people who have no food?  But this global warming thing is just a scam to make the poor poorer and the rich richer.  Just like recessions, but maybe I will rant about those another time!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8588874433291212001?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8588874433291212001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8588874433291212001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8588874433291212001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8588874433291212001'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/07/global-warming-and-price-of-oil.html' title='Global warming and the price of oil'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6682058722237415318</id><published>2008-07-08T18:00:00.000+01:00</published><updated>2008-07-08T18:09:49.492+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Single instance application</title><content type='html'>An app I am working on needs to be a single instance.  It is associated with certain file extensions so that when I select a character or license file it will be imported automatically.  When the user buys a character or license (etc) from the website it will be downloaded and opened, and then imported. &lt;br /&gt;&lt;br /&gt;Obviously it is a pretty poor user experience if they have to close the app, download, close the app, download...  So what I really needed was a way to have the 2nd instance of the application to invoke the first instance and pass the command line parameters.  Here is a simple solution I implemented using remoting.&lt;br /&gt;&lt;br /&gt;01: An interface&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public interface ISingleInstance&lt;br /&gt;{&lt;br /&gt;  void Execute(string[] args);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;02: A class that implements the interface&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class SingleInstance : MarshalByRefObject, ISingleInstance&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;private static object SyncRoot = new object();&lt;br /&gt;&amp;nbsp;&amp;nbsp;private static Form1 MainForm;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;static SingleInstance()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Application.EnableVisualStyles();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Application.SetCompatibleTextRenderingDefault(false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void Execute(string[] args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool isNew = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lock (SyncRoot)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isNew = (MainForm == null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (isNew)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MainForm = new Form1();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MainForm.AcceptCommandLineArguments(args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (isNew)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Application.Run(MainForm);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;03: And finally the remoting code in the Program.cs file itself:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;static class Program&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;private static IpcChannel IpcChannel;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;[STAThread]&lt;br /&gt;&amp;nbsp;&amp;nbsp;static void Main(string[] args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool isNew;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using (Mutex mutex = new Mutex(true, &amp;quot;TheCatSatOnTheMat&amp;quot;, out isNew))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (isNew)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RegisterServer();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IpcChannel = new IpcChannel(&amp;quot;Client&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ChannelServices.RegisterChannel(IpcChannel, false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ISingleInstance app = (ISingleInstance)Activator.GetObject(typeof(ISingleInstance), &amp;quot;ipc://Server/RemotingServer&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;app.Execute(args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private static void RegisterServer()&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IpcChannel = new IpcChannel(&amp;quot;Server&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ChannelServices.RegisterChannel(IpcChannel, false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RemotingConfiguration.RegisterWellKnownServiceType(typeof(SingleInstance), &amp;quot;RemotingServer&amp;quot;, WellKnownObjectMode.Singleton);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is more of a note to myself in case I lose my small test app before I come around to implementing it into my main application :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6682058722237415318?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6682058722237415318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6682058722237415318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6682058722237415318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6682058722237415318'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/07/single-instance-application.html' title='Single instance application'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3786339078135124030</id><published>2008-07-08T17:49:00.000+01:00</published><updated>2008-07-08T18:00:10.220+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>More leak fixes</title><content type='html'>I have changed the DirtyObjectCatcher so that it initially only hooks&lt;br /&gt;&lt;br /&gt;Form.Disposed - Automatically disposes the DirtyObjectCatcher&lt;br /&gt;Form.Closed - Unhooks all additional form events (below)&lt;br /&gt;Form.Shown - To hook additional form events (below)&lt;br /&gt;&lt;br /&gt;==Additional form events==&lt;br /&gt;Form.Activated&lt;br /&gt;Form.MdiParent.Activated&lt;br /&gt;Form.MdiChildActivate&lt;br /&gt;&lt;br /&gt;The additional events are to ensure that the DirtyObjectCatcher's undo block is moved to the top.  The reason that these events are now unhooked is so that there is no strong event references from the application's main form (Form.MdiParent) to this component, keeping it alive.  Now we only have long-term event references from the owning form itself.   Really though this is just an added precaution against something I may not have thought of :-) &lt;br /&gt;&lt;br /&gt;The true memory saver comes from only holding a WeakReference to the owning form.  Otherwise  in an MDI application we have the following&lt;br /&gt;&lt;br /&gt;MainForm.MdiChildActivate-&gt;DirtyObjectCatcher-&gt;Form&lt;br /&gt;&lt;br /&gt;In such a case closing the MDI child form will not be collected it because it is referenced by the DirtyObjectCatcher, which cannot be collected because it is referenced by the application's main form.  Unhooking these events and holding only a WeakReference prevents the leakage.&lt;br /&gt;&lt;br /&gt;In addition I have hooked Form.Disposed so that this component is disposed along with its owner.  I have also hooked DirtyObjectCatcher.Disposed from ObjectValidator so that it may also auto-dispose itself and knows not to place any new subscriptions.&lt;br /&gt;&lt;br /&gt;Again the files are available &lt;a href="http://www.peterlesliemorris.com/blogfiles/ecoextensions.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In addition I was able to track down and reproduce a couple of leaks in ECO 4 which are now fixed internally.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Deactivating / Reactivating an EcoSpace would leak an object reference each time.  This is not the case if you allow the EcoSpace to be collected.&lt;/li&gt;&lt;li&gt;Using OclVariables with OclPsHandle and calling EcoSpace.Persistence.Refresh would leak a single object reference, also if OclPsHandle.Execute was executed.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I monitored my app with over 4,000 tasks today (normally there are about 200) for a few hours and the memory usage didn't budge!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3786339078135124030?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3786339078135124030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3786339078135124030' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3786339078135124030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3786339078135124030'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/07/more-leak-fixes.html' title='More leak fixes'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4105997535085973862</id><published>2008-06-27T18:50:00.000+01:00</published><updated>2008-06-27T19:13:29.133+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WinForms'/><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Memory leaks</title><content type='html'>As a follow up to yesterday's post here is a list of problems I found...&lt;br /&gt;&lt;br /&gt;01: The following code for some reason causes the form holding the DirtyObjectCatcher to remain referenced.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;if (Owner.MdiParent != null)&lt;br /&gt;{&lt;br /&gt;  Owner.MdiParent.Activated += new EventHandler(MdiParent_Activated);&lt;br /&gt;  Owner.MdiParent.MdiChildActivate += new EventHandler(MdiParent_MdiChildActivate);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;The odd thing about this &lt;em&gt;is&lt;/em&gt; that it really is the events that matter!  I subscribe Shown, Disposed, Activated on the Owner (which is a form) and that doesn't cause the same behaviour.  The reason is that the Owner normally gets disposed and takes out the DirtyObjectCatcher with it, however, if I have Owner.MdiParent events referencing DirtyObjectCatcher then the form will never get disposed because DirtyObjectCatcher has a strong reference to Owner.  Maybe I should change it, but for now the Shown and Activated events seem to be doing the trick.&lt;br /&gt;&lt;br /&gt;02: This one was a &lt;em&gt;real&lt;/em&gt; pain!  DirtyObjectCatcher creates its own UndoBlock and then subscribes to it, so that whenever the user modifies an object it goes into the UndoBlock and the catcher is notified.  At this point other components (such as ObjectValidator) can get a list of modified objects and do something (such as evaluate their OCL constraints). &lt;br /&gt;&lt;br /&gt;Unfortunately this event fires at some point between the user making the change and the ECO cache being ready for the new value to be read.  For this reason in the past I had to set&lt;br /&gt;&lt;br /&gt;Application.Idle += CheckForModifiedObjects;&lt;br /&gt;&lt;br /&gt;CheckForModifiedObjects would then trigger the relevant events in Application's Idle event so that we know the changes to the ECO cache are ready to be read.  Unfortunately I made a silly mistake, I forgot to do this as the first line of CheckForModifiedObjects&lt;br /&gt;&lt;br /&gt;Application.Idle &lt;em&gt;-=&lt;/em&gt; CheckForModifiedObjects;&lt;br /&gt;&lt;br /&gt;As a result the static Application class had a reference to my DirtyObjectCatcher via an event.  DirtyObjectCatcher holds a reference to the form (Owner) and the EcoSpace (ServiceProvider).  So the form and the EcoSpace would remain in memory.&lt;br /&gt;&lt;br /&gt;03: In the past you could not subscribe to UndoBlock so I had to subscribe to DirtyListService.  Unfortunately this would not get triggered if you activated a DirtyObjectCatcher on an object that was already dirty, or transient.  To overcome this I added a CheckForUpdates method which would get every instance of DirtyObjectCatcher to check its UndoBlock for changes.&lt;br /&gt;&lt;br /&gt;The idea was that you plug into the ECO cache and call the static DirtyObjectCatcher.CheckForUpdates whenever something changed.  A pain in the ass.  I requested UndoBlock.Subscribe and it was implemented within days (thanks Jonas!) but I left that code in just in case anyone was using it.  Unfortunately this meant I had a static List&lt;dirtyobjectcatcher&gt; sitting around holding references to my DirtyObjectCatchers.  Again this referenced the EcoSpace and the Form, so the Form was never disposed in order to dispose of my DirtyObjectCatcher.&lt;br /&gt;&lt;br /&gt;This wasn't noticed for some time because I use the AutoFormService to handle the lifetime of my forms, but my current app has quite a few forms where I manage the lifetime manually so the form wasn't automatically being disposed as I had expected.  In addition the Form class does not call Dispose on any of its components when you call its &lt;em&gt;Dispose&lt;/em&gt; method, which I find very strange!&lt;br /&gt;&lt;br /&gt;Anyway, that's the lot.  What a hellish week!  I have uploaded the lastest version of EcoExtensions here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.peterlesliemorris.com/blogfiles/ecoextensions.zip"&gt;www.peterlesliemorris.com/blogfiles/ecoextensions.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4105997535085973862?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4105997535085973862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4105997535085973862' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4105997535085973862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4105997535085973862'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/memory-leaks.html' title='Memory leaks'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5234279368614153903</id><published>2008-06-27T00:44:00.001+01:00</published><updated>2008-06-27T04:13:38.875+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>DirtyObjectCatcher</title><content type='html'>Oh boy, what a nightmare! After days of messing around I finally found where the memory leak is in my app, it was in DirtyObjectCatcher!&lt;br /&gt;&lt;br /&gt;The DirtyObjectCatcher used to subscribe to the DirtyListService, so that it was notified whenever an object was made dirty. I experienced this problem...&lt;br /&gt;&lt;br /&gt;01: User creates a "Call" to a customer site.&lt;br /&gt;02: User edits a purchase order.&lt;br /&gt;03: Save purchase order (merges the undo block to the Call undo block and closes the form)&lt;br /&gt;04: Edit the purchase order again from the Call form&lt;br /&gt;&lt;br /&gt;The PurchaseOrder is already dirty so it wont get triggered again, this used to result in no constraints being checked etc and the possibility of entering dodgy data. The solution at the time was to have a static list in DirtyObjectCatcher&lt;br /&gt;&lt;br /&gt;private List&lt;dirtyobjectcatcher&gt; Instances;&lt;br /&gt;&lt;br /&gt;whenever a new instance was created it would be added, whenever Dispose was called it would be removed. I then hooked into the cache chain and whenever a value changed I would call a static method on DirtyObjectCatcher which iterated over Instances and told each to check if anything had changed. It worked fine enough, and I put in a request to add a Subscribe method to IUndoBlock.&lt;br /&gt;&lt;br /&gt;My request for IUndoBlock.Subscribe was soon added so I changed the code. Now it worked lovely! Unfortunately there was a problem! I had left the static code in the component just in case anyone was using it, I should really have just removed it as it is now obsolete. The really big problem however was that I had never added a Finalizer to the DirtyObjectCatcher class to call Dispose(). I had assumed the following in the Windows.Form default code template would call Dispose.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;private System.ComponentModel.IContainer components = null;&lt;br /&gt;protected override void Dispose(bool disposing)&lt;br /&gt;{&lt;br /&gt;  if (disposing &amp;amp;&amp;amp; (components != null))&lt;br /&gt;  {&lt;br /&gt;    components.Dispose();&lt;br /&gt;  }&lt;br /&gt;  base.Dispose(disposing);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;However I was wrong. The components on the form are never added to this "components" member. As a result the Instances list grew and grew, and was never emptied. DirtyObjectCatcher holds a reference to the EcoSpace, so this is why the memory usage got bigger and bigger! I was going to add the Finalizer and remove the instance from Instances, but I have decided to remove Instances completely!&lt;br /&gt;&lt;br /&gt;Update: There were some other odd issues too which I will blog about tomorrow after I have released an update!&lt;code&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5234279368614153903?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5234279368614153903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5234279368614153903' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5234279368614153903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5234279368614153903'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/dirtyobjectcatcher.html' title='DirtyObjectCatcher'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8398117976502369891</id><published>2008-06-25T12:44:00.000+01:00</published><updated>2008-06-25T12:52:21.684+01:00</updated><title type='text'>TeamCoherence - disaster!</title><content type='html'>That's it, my short evaluation of TC is over!&lt;br /&gt;&lt;br /&gt;Problem 1: I emailed support with a question weeks ago, didn't get a response. Not impressed.&lt;br /&gt;&lt;br /&gt;Problem 2: I reinstalled my O/S recently so had to restore my version control folder. When I try to check files out I now get an "Object not found" error, whatever that means? Some searching reveals it is a bug that has been fixed in the version I have, I beg to differ.&lt;br /&gt;&lt;br /&gt;Problem 3: This one was the worst! I use TC client for a customer already which is why I decided to try out the server. I connected to the customer server, checked out loads of files, upgraded my VS2005 project to VS2008 and then checked everything back in. What a disaster! TC had replaced the source in the customer files with source from my private local server!&lt;br /&gt;&lt;br /&gt;I couldn't believe it! As I looked through Assembly.cs files I could see WinForm code from the last project I worked on locally! This is obviously bad because I had exposed source code from one concerned party to another. Luckily it was my own source code and I trust the person I exposed it to, but that could have landed me in legal trouble if there had been NDA's involved!&lt;br /&gt;&lt;br /&gt;Also, there didn't seem to be a way to undo all changes in check-in number X. I had to find each file with a change containing the text "VS2008" and delete the revision manually. This was made worse by the fact that the F3 search in TC doesn't take you to the next found item but instead takes you to the first found item after the current folder! So, I spent quite a bit of my day yesterday scanning through hundreds of files and manually deleting revisions from a single check-in.&lt;br /&gt;&lt;br /&gt;I am actually shaking my head as I write this, I just can't believe it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8398117976502369891?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8398117976502369891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8398117976502369891' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8398117976502369891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8398117976502369891'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/teamcoherence-disaster.html' title='TeamCoherence - disaster!'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-526030050206276293</id><published>2008-06-16T20:03:00.000+01:00</published><updated>2008-06-16T20:14:01.853+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><title type='text'>TimeBasedSyncHandler</title><content type='html'>I recently had to tweak my remote persistence server settings.  I noticed that I had leaft the SyncHandler.HistoryLength at the default value of 10,000 items.  This was overkill because my clients sync every 3 seconds.  I thought maybe I should drop this down to about 100, that should be okay?  Each client only does about one update every few minutes so I it should, right?&lt;br /&gt;&lt;br /&gt;Problem is not all of my users are people.  One user is a messenger service which looks for unsent messages, sends them one at a time, marking each in turn as sent and updating the database.  This gets run every five minutes so probably sends about fifty messages at the most, but what if someone decides to change it to ten minutes, or thirty?  So maybe I should increase the HistoryLength to about 200?&lt;br /&gt;&lt;br /&gt;Then there is the other client, this one syncs with an external database.  This could perform hundreds of updates every minute.  If the messenger is set to run every thirty minutes...I'm not sure what a good HistoryLength would be!  If I set it too low I fill it up quickly and my clients will have to deactivate/reactivate their EcoSpace instances.  If I set it too high I might use up too much memory, especially if the messenger runs infrequently and the history gets filled up with "human" client updates which are much larger.&lt;br /&gt;&lt;br /&gt;So, I wrote my own SyncHandler.  This one accepts a TimeSpan property, which by default is five minutes but on my server I dropped it to two minutes (forty times longer than my clients' sync intervals.)  The idea is that I keep changes for the specified period of time no matter how irregularly the updates occur.  This way the server uses more memory during heavy usage but&lt;br /&gt;&lt;br /&gt;A: Usage drops during lower activity, in fact it goes right down if there are no updates for the specified TimeSpan.&lt;br /&gt;B: It wont run out of history length too quickly for the client.&lt;br /&gt;&lt;br /&gt;If you'd like to try it for yourself take a look &lt;a href="http://www.peterlesliemorris.com/blogfiles/timebasedsynchandler.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-526030050206276293?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/526030050206276293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=526030050206276293' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/526030050206276293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/526030050206276293'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/timebasedsynchandler.html' title='TimeBasedSyncHandler'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4199929468349473108</id><published>2008-06-11T18:41:00.004+01:00</published><updated>2008-06-12T08:39:49.481+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Making a generic type from a Type</title><content type='html'>List&amp;lt;Person&amp;gt; p = new List&amp;lt;Person&amp;gt;();&lt;br /&gt;&lt;br /&gt;This works fine, but what about this?&lt;br /&gt;&lt;br /&gt;Type someType = typeof(Person);&lt;br /&gt;List&amp;lt;someType&amp;gt; p = new List&amp;lt;someType&amp;gt;();&lt;br /&gt;&lt;br /&gt;Nope!&lt;br /&gt;&lt;br /&gt;But you can do this...&lt;br /&gt;&lt;br /&gt;Type genericType = typeof(List&amp;lt;&amp;gt;).MakeGenericType(someType);&lt;br /&gt;&lt;br /&gt;Why would I want to?  Because I am creating a tool that generates plain old .NET objects from an EcoSpace's model, and I wanted to implement multi-role association ends as&lt;br /&gt;&lt;br /&gt;public List&amp;lt;Building&amp;gt; RoleName;&lt;br /&gt;&lt;br /&gt;rather than just&lt;br /&gt;&lt;br /&gt;public Building[] RoleName;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4199929468349473108?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4199929468349473108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4199929468349473108' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4199929468349473108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4199929468349473108'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/making-generic-type-from-type.html' title='Making a generic type from a Type'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6027061449329954708</id><published>2008-06-11T18:36:00.001+01:00</published><updated>2008-06-11T18:44:40.145+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Returning a binary respose in ASP MVC RC3</title><content type='html'>In a &lt;a href="http://mrpmorris.blogspot.com/2008/04/binary-response-in-asp-mvc.html"&gt;previous post&lt;/a&gt; I showed how to return a binary file from a controller action, well, this no longer works in release candidate 3 of the framework.  Instead you have to create a new ActionResult descendant to do the job for you.  This is how I did it....&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;return new BinaryResult(data, Path.GetFileName(productFileName));&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and the class is implemented like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class BinaryResult : ActionResult&lt;br /&gt;{&lt;br /&gt;  private string ClientFileName;&lt;br /&gt;  private byte[] Data;&lt;br /&gt;  private string VirtualFileName;&lt;br /&gt;&lt;br /&gt;  public BinaryResult(string virtualFileName, string clientFileName)&lt;br /&gt;  {&lt;br /&gt;    if (string.IsNullOrEmpty(virtualFileName))&lt;br /&gt;      throw new ArgumentNullException("VirtualFileName");&lt;br /&gt;    if (string.IsNullOrEmpty(clientFileName))&lt;br /&gt;      throw new ArgumentNullException("ClientFileName");&lt;br /&gt;&lt;br /&gt;    ClientFileName = clientFileName;&lt;br /&gt;    VirtualFileName = virtualFileName;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public BinaryResult(byte[] data, string clientFileName)&lt;br /&gt;  {&lt;br /&gt;    if (data == null)&lt;br /&gt;      throw new ArgumentNullException("Data");&lt;br /&gt;    if (string.IsNullOrEmpty(clientFileName))&lt;br /&gt;      throw new ArgumentNullException("ClientFileName");&lt;br /&gt;&lt;br /&gt;    ClientFileName = clientFileName;&lt;br /&gt;    Data = data;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public override void ExecuteResult(ControllerContext context)&lt;br /&gt;  {&lt;br /&gt;    if (!string.IsNullOrEmpty(VirtualFileName))&lt;br /&gt;    {&lt;br /&gt;      string localFileName = context.HttpContext.Server.MapPath(VirtualFileName);&lt;br /&gt;      FileStream fileStream = new FileStream(localFileName, FileMode.Open, FileAccess.Read, FileShare.Read);&lt;br /&gt;      using (fileStream)&lt;br /&gt;      {&lt;br /&gt;        Data = new byte[fileStream.Length];&lt;br /&gt;        fileStream.Read(Data, 0, (int)fileStream.Length);&lt;br /&gt;      }//using fileStream&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + ClientFileName);&lt;br /&gt;    context.HttpContext.Response.BinaryWrite(Data);&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6027061449329954708?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6027061449329954708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6027061449329954708' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6027061449329954708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6027061449329954708'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/returning-binary-respose-in-asp-mvc-rc3.html' title='Returning a binary respose in ASP MVC RC3'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-8662890056010939881</id><published>2008-06-10T10:18:00.000+01:00</published><updated>2008-06-10T10:25:18.265+01:00</updated><title type='text'>Obama is not black!</title><content type='html'>Okay, it's petty, but I find the repetitive use of the word "Black" to describe Obama annoying!&lt;br /&gt;&lt;br /&gt;If he were to be described as America's first "non-white" presidential candidate that would be fine, because it is probably historically accurate and certain genetically accurate, but he simply is not black!  His father was apparently black and his mother white, how exactly does that make him black?  It would be equally as accurate to call him "white"!&lt;br /&gt;&lt;br /&gt;I remember a documentary where a mixed race guy stood in the street and asked passers by what colour he was.  White people said he was black, black people said he was white, I think out of the many people he asked maybe one said he was mixed race.  I also remember another documentary where a Sikh guy stood in the street with a world map asking people to stick a pin where they think he was "from".  Not one of them stuck a pin in the north of England, where he was in fact born :-)&lt;br /&gt;&lt;br /&gt;If people are going to make his ancestry such a big deal I think they should be accurate and either call him "non-white" or "mixed race".  Personally I am over the moon that "the land of the free" is willing to consider such a candidate for president, even if it is years after "the land of apartheid", but most importantly I just hope he is good at the job and has a much better sense for World affairs than Bush has.&lt;br /&gt;&lt;br /&gt;End of rant :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-8662890056010939881?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/8662890056010939881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=8662890056010939881' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8662890056010939881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/8662890056010939881'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/obama-is-not-black.html' title='Obama is not black!'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4575944678062123497</id><published>2008-06-07T22:25:00.000+01:00</published><updated>2008-06-07T22:29:09.363+01:00</updated><title type='text'>A letter to Prof Stephen Hawking</title><content type='html'>Watching a documentary about supernovas understandably made me think of Stephen Hawking.  No idea why the youtube video "&lt;a href="http://youtube.com/watch?v=cykJoGcNnk4"&gt;WII hacks&lt;/a&gt;" came to me at the same time, but it did!  So with those two subjects in my head at the same time I came up with a simple idea which I have just written to Prof with, and here it is:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Hi&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;A simple idea I had which should allow you to communicate more quickly.  I  believe that you have full control over your eye movement and can twitch your  cheek, is that correct?&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;01: First you have an infrared bulb shining at your face from on top of  your screen.&lt;/div&gt; &lt;div&gt;02: You wear contact lenses, each lense has a circular area on or outside  of the iris area which reflects infra red light.  &lt;/div&gt; &lt;div&gt;03: Next to the IR bulb there is an IR camera.&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;First you calibrate the device:&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;01: A cross appears at the top left of the screen.&lt;/div&gt; &lt;div&gt;02: You look at it and twitch your cheek.&lt;/div&gt; &lt;div&gt;03: The computer looks at the image from the IR camera and works out the  location of the centre of each of the two circles.  (Might be possible to locate  the pupil, so no lenses are required).&lt;/div&gt; &lt;div&gt;04: This process is repeated for top-right, bottom-left,  bottom-right.&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;Now each time you twitch your cheek the computer looks at the image from  the IR camera to calculate the centres of the reflective circles in your  lenses.  From this point onwards it should be possible with a bit of mathematics  (not my department) to work out the X/Y location of the screen you are looking  at.  I believe this would be a much faster way of choosing on-screen options.  I  think this very simple system would only work so well for you because of the  fact that you do not move your head.&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;What the interface looks like I have no idea :-)  Maybe the mobile phone  system of spelling words using only 12 buttons, I expect you've played with that  though.&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;I hope this is of some use and I haven't wasted your time :-)&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;Regards&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;Pete&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4575944678062123497?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4575944678062123497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4575944678062123497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4575944678062123497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4575944678062123497'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/06/letter-to-prof-stephen-hawking.html' title='A letter to Prof Stephen Hawking'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6078006745348503828</id><published>2008-05-29T17:07:00.001+01:00</published><updated>2008-05-29T17:10:44.088+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><title type='text'>ASP MVC preview 3 released</title><content type='html'>I'm trying to upgrade from Preview 2 to Preview 3.  I &lt;span style="font-style: italic;"&gt;think &lt;/span&gt;the idea of having each action return an ActionResult was a good one, so far it has actually made my code slightly smaller.&lt;br /&gt;&lt;br /&gt;What I don't understand though is why Html.Select seems to have disappeared...&lt;br /&gt;&lt;br /&gt;Compilation Error&lt;br /&gt;Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.&lt;br /&gt;&lt;br /&gt;Compiler Error Message: CS1501: No overload for method 'Select' takes '5' arguments&lt;br /&gt;&lt;br /&gt;Source Error:&lt;br /&gt;&lt;br /&gt;Line 8:                      Product&lt;br /&gt;Line 9:                     &lt;br /&gt;Line 10:                         &lt;%= Html.Select("SoftwareID", (object)ViewData["SoftwareList"], "Name", "ID", (object)ViewData["SoftwareID"]) %&gt;&lt;br /&gt;Line 11:                    &lt;br /&gt;Line 12:                &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When I go into the APX and type Html. there is no Select method listed along with the other options!  Where is it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6078006745348503828?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6078006745348503828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6078006745348503828' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6078006745348503828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6078006745348503828'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/05/asp-mvc-preview-3-released.html' title='ASP MVC preview 3 released'/><author><name>Peter Morris</name><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><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-6268830691843104065</id><published>2008-05-28T15:52:00.002+01:00</published><updated>2008-05-29T18:54:12.627+01:00</updated><title type='text'>Annoying!</title><content type='html'>Two things that have annoyed me today:&lt;br /&gt;&lt;br /&gt;01: AVG - The free anti-virus.&lt;br /&gt;&lt;br /&gt;It's been nagging at me for weeks to upgrade to the next version.  Seeing as support for the current version finishes in 3 days I thought I would do as I was asked (nagged).&lt;br /&gt;&lt;br /&gt;I opened FireFox after installation and saw all I needed to see.  Yahoo! toolbar.  No thankyou!  I uninstalled it.  If I wanted that crap on my computer I would have installed it.  The number of times I have thought to myself "This AVG product is great, I think I will buy it", well now I am glad I never quite got around to it because I would be &lt;span style="font-weight: bold;"&gt;really &lt;/span&gt;annoyed with myself!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;02: Team Coherence.&lt;br /&gt;&lt;br /&gt;I have just tried installing the server on a new machine at work.  It asks me for my user name and password.  Quite helpfully the login box informs me that the password for new users is "password".  Unhelpfully it doesn't tell me what the default administrator user name is.  Guess what, nor does the help file!  Nor do the docs on their website!  Nor does any webpage I can find.&lt;br /&gt;&lt;br /&gt;Very impressed!&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;I wrote to QSC and got a reply within the hour, in short it said&lt;br /&gt;&lt;br /&gt;"I take your point and will update the server help file to make this more obvious......We're doing a new release over the weekend and I'll make sure that things are a bit clearer."&lt;br /&gt;&lt;br /&gt;That's good support isn't it? :-)&lt;br /&gt;&lt;br /&gt;PS: The user name is "supervisor".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-6268830691843104065?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/6268830691843104065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=6268830691843104065' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6268830691843104065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/6268830691843104065'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/05/annoying.html' title='Annoying!'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-3655654339296771360</id><published>2008-05-28T10:41:00.000+01:00</published><updated>2008-05-28T10:55:27.653+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ECO'/><title type='text'>Hooking into ECO multi-association events</title><content type='html'>Although I have not (yet) needed this myself I can see myself needing it in the future and the question has been asked before.&lt;br /&gt;&lt;br /&gt;&amp;quot;Setting HasUserCode=True on a Child.Parent single role does what I want, but how do I handle the scenario where Parent.Children.Add(item) is called on a multirole?&amp;quot;&lt;br /&gt;&lt;br /&gt;By default you can&amp;#8217;t, but with the addition of a single class and a small amount of tweaking you can get it to do what you want!  Here is how to do it:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;01: Mark Parent.Children&amp;#8217;s association end with HasUserCode=True in the modeler and then generate code.&lt;br /&gt;02: In the source code of your class (not within an ECO region) add the following&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;private EcoMultiAssociation&amp;lt;Child&amp;gt; m_Children;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is a class that does not yet exist, I will show the source code for it later.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;02: In the source code locate the &amp;quot;Children&amp;quot; property and change it like so&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public IEcoList&amp;lt;Child&amp;gt; Children&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (m_Children == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m_Children= new EcoMultiAssociation&amp;lt;Child&amp;gt;((IList)(this.eco_Content.get_MemberByIndex(Eco_LoopbackIndices.Children_MemberIndex)));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m_Children+= new AssociationItemChangedEventHandler&amp;lt;Child&amp;gt;(result_ItemChanged);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m_Children+= new AssociationChangedEventHandler&amp;lt;Child&amp;gt;(result_ItemInserted);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m_Children+= new AssociationChangedEventHandler&amp;lt;Child&amp;gt;(result_ItemRemoved);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return m_Children&lt;br /&gt;&lt;br /&gt;#if NeverDoThis&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region MM_ECO_Generated&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new ObjectListAdapter&amp;lt;Child&amp;gt;((IList) (this.eco_Content.get_MemberByIndex(Eco_LoopbackIndices.Children_MemberIndex)));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;#endif&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that I have put an #if around the original code that will never be true.  You cannot remove the MM_ECO_Generated section due to the source code generator expecting to find it, but you can make sure it is never even compiled!  The parameters for the EcoMultiAssociation&amp;lt;T&amp;gt; constructor were just copied directly from the ObjectListAdapter constructor below.&lt;br /&gt;&lt;br /&gt;(Just a small note, I don&amp;#8217;t usually name my private members m_Name, I just did it in this case to make it easier to spot the difference between the Children property and the m_Children private member).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;03: The event handlers are implemented like so&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void result_ItemRemoved(object sender, AssociationChangedEventArgs&amp;lt;Child&amp;gt; args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Diagnostics.Debug.WriteLine(string.Format(&amp;quot;Removed index {0}&amp;quot;, args.Index));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void result_ItemInserted(object sender, AssociationChangedEventArgs&amp;lt;Child&amp;gt; args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Diagnostics.Debug.WriteLine(string.Format(&amp;quot;Inserted at index {0}&amp;quot;, args.Index));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void result_ItemChanged(object sender, AssociationItemChangedEventArgs&amp;lt;Child&amp;gt; args)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Diagnostics.Debug.WriteLine(string.Format(&amp;quot;Changed object at index {0}&amp;quot;, args.Index));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The args parameter has a reference to the old object and also the new object in the case of ItemChanged which is executed when you do this...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;Association[x] = y;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally here is the source code for the EcoMultiAssociation&amp;lt;T&amp;gt; class.  There&amp;#8217;s quite a bit here, but that&amp;#8217;s really because I have to implement so many interfaces, the actual code is very small.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;public delegate void AssociationChangedEventHandler&amp;lt;T&amp;gt;(object sender, AssociationChangedEventArgs&amp;lt;T&amp;gt; args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class AssociationChangedEventArgs&amp;lt;T&amp;gt; : EventArgs&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public readonly T Item;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public readonly int Index;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public AssociationChangedEventArgs(int index, T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Item = item;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Index = index;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public delegate void AssociationItemChangedEventHandler&amp;lt;T&amp;gt;(object sender, AssociationItemChangedEventArgs&amp;lt;T&amp;gt; args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class AssociationItemChangedEventArgs&amp;lt;T&amp;gt; : AssociationChangedEventArgs&amp;lt;T&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public readonly T OriginalItem;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public AssociationItemChangedEventArgs(int index, T newItem, T originalItem)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: base(index, newItem)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OriginalItem = originalItem;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class EcoMultiAssociation&amp;lt;T&amp;gt; : IEcoList&amp;lt;T&amp;gt;, IList&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private readonly IList Adaptee;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public EcoMultiAssociation(IList adaptee)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (adaptee == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw new ArgumentNullException(&amp;quot;Adaptee&amp;quot;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee = adaptee;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void Add(T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (Adaptee.IndexOf(item) == -1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.Add(item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnItemInserted(Count - 1, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void Clear()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;T&amp;gt; originals = new List&amp;lt;T&amp;gt;(this);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.Clear();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int index = 0; index &amp;lt; originals.Count; index++)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnItemRemoved(index, originals[index]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public bool Contains(T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Adaptee.Contains(item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void CopyTo(T[] array, int arrayIndex)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.CopyTo(array, arrayIndex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public int Count&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return Adaptee.Count; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public IEnumerator&amp;lt;T&amp;gt; GetEnumerator()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new ObjectEnumeratorAdapter&amp;lt;T&amp;gt;(Adaptee.GetEnumerator());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public int IndexOf(T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Adaptee.IndexOf(item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void Insert(int index, T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (Adaptee.IndexOf(item) == -1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.Insert(index, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnItemInserted(index, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public bool IsReadOnly&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return Adaptee.IsReadOnly; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public bool Remove(T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int index = Adaptee.IndexOf(item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (index == -1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.RemoveAt(index);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void RemoveAt(int index)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;T item = this[index];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.RemoveAt(index);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnItemRemoved(index, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public T this[int index]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (T)Adaptee[index];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;T originalItem = (T)Adaptee[index];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee[index] = value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnItemChanged(index, originalItem, value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public event AssociationChangedEventHandler&amp;lt;T&amp;gt; ItemInserted;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected void OnItemInserted(int index, T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationChangedEventHandler&amp;lt;T&amp;gt; handler = ItemInserted;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (handler != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationChangedEventArgs&amp;lt;T&amp;gt; args = new AssociationChangedEventArgs&amp;lt;T&amp;gt;(index, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this, args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public event AssociationChangedEventHandler&amp;lt;T&amp;gt; ItemRemoved;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected void OnItemRemoved(int index, T item)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationChangedEventHandler&amp;lt;T&amp;gt; handler = ItemRemoved;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (handler != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationChangedEventArgs&amp;lt;T&amp;gt; args = new AssociationChangedEventArgs&amp;lt;T&amp;gt;(index, item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this, args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public event AssociationItemChangedEventHandler&amp;lt;T&amp;gt; ItemChanged;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected void OnItemChanged(int index, T originalItem, T newItem)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationItemChangedEventHandler&amp;lt;T&amp;gt; handler = ItemChanged;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (handler != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AssociationItemChangedEventArgs&amp;lt;T&amp;gt; args = new AssociationItemChangedEventArgs&amp;lt;T&amp;gt;(index, newItem, originalItem);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this, args);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region IEnumerable Members&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IEnumerator IEnumerable.GetEnumerator()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return GetEnumerator();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region IList Members&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int IList.Add(object value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Add((T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return IndexOf((T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool IList.Contains(object value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Contains((T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int IList.IndexOf(object value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return IndexOf((T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void IList.Insert(int index, object value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Insert(index, (T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool IList.IsFixedSize&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return Adaptee.IsFixedSize; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void IList.Remove(object value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Remove((T)value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;object IList.this[int index]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return this[index];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this[index] = (T)value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#region ICollection Members&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void ICollection.CopyTo(Array array, int index)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Adaptee.CopyTo(array, index);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool ICollection.IsSynchronized&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return Adaptee.IsSynchronized; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;object ICollection.SyncRoot&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { return Adaptee.SyncRoot; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endregion&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Note that these events will not be executed if you do Child.Parent = p;  For this case you have to set HasUserCode=True on Child.Parent as normal and react accordingly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-3655654339296771360?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/3655654339296771360/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=3655654339296771360' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3655654339296771360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/3655654339296771360'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/05/hooking-into-eco-multi-association.html' title='Hooking into ECO multi-association events'/><author><name>Peter Morris</name><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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-5721421227663969548</id><published>2008-05-08T12:52:00.000+01:00</published><updated>2008-05-08T12:58:52.034+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WPF'/><title type='text'>Using Vista Aero theme in XP WPF apps</title><content type='html'>I found &lt;a href="http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!199.entry"&gt;this article&lt;/a&gt; on the web recently which shows how to use the Vista Aero theme on XP in your WPF apps.&lt;br /&gt;&lt;br /&gt;I found two things:&lt;br /&gt;01: It is less complicated that the article states.&lt;br /&gt;02: It is a bit different if you already have stuff in your app resources, such as styles or control templates etc.&lt;br /&gt;&lt;br /&gt;So here are my steps&lt;br /&gt;01: Add PresentationFramework.Aero to your applications References list.  It is listed in the [.NET] tab.&lt;br /&gt;02: Edit your App.xaml and change it from this&lt;br /&gt;&lt;br /&gt;&amp;lt;Application.Resources&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;!-- Your stuff here --&amp;gt;&lt;br /&gt;&amp;lt;/Application.Resources&amp;gt;&lt;br /&gt;&lt;br /&gt;to this&lt;br /&gt;&lt;br /&gt;&amp;lt;Application.Resources&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;ResourceDictionary&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!-- Put your stuff here instead --&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ResourceDictionary Source="/PresentationFramework.Aero;component/themes/aero.normalcolor.xaml"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/ResourceDictionary&amp;gt;&lt;br /&gt;&amp;lt;/Application.Resources&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-5721421227663969548?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/5721421227663969548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=5721421227663969548' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5721421227663969548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/5721421227663969548'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/05/using-vista-aero-theme-in-xp-wpf-apps.html' title='Using Vista Aero theme in XP WPF apps'/><author><name>Peter Morris</name><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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1688176320920284646.post-4084090849224503296</id><published>2008-05-04T13:23:00.000+01:00</published><updated>2008-05-04T13:31:43.796+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WPF'/><title type='text'>Selecting WPF ListView row in code</title><content type='html'>An app I am considering migrating to WPF uses a DevExpress grid to show a list of events, as I animate my composition I highlight the current event in the grid.  Things I have learned about WPF:&lt;br /&gt;&lt;br /&gt;01: You can create a data grid type view like so:&lt;br /&gt;&amp;lt;ListView Name=&amp;quot;ListView&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;ListView.View&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;GridView AllowsColumnReorder=&amp;quot;False&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;GridViewColumn DisplayMemberBinding=&amp;quot;{Binding Path=Title}&amp;quot; Header=&amp;quot;Title&amp;quot;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;GridViewColumn DisplayMemberBinding=&amp;quot;{Binding Path=FirstName}&amp;quot; Header=&amp;quot;FirstName&amp;quot;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;GridViewColumn DisplayMemberBinding=&amp;quot;{Binding Path=LastName}&amp;quot; Header=&amp;quot;LastName&amp;quot;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/GridView&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/ListView.View&amp;gt;&lt;br /&gt;&amp;lt;/ListView&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;02: Don&amp;#8217;t put a ListView in a &amp;lt;StackPanel&amp;gt;!  It renders all of the data at once.  When you have 6K rows of data for example it takes about 1 second to select the next row.  Instead you should put it in  a grid&lt;br /&gt;&lt;br /&gt;&amp;lt;Grid&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;Grid.RowDefinitions&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;RowDefinition Height=&amp;quot;50&amp;quot;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;RowDefinition Height=&amp;quot;*&amp;quot;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/Grid.RowDefinitions&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;Button Grid.Row=&amp;quot;0&amp;quot;...../&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;ListView Grid.Row=&amp;quot;1&amp;quot;...../&amp;gt;&lt;br /&gt;&amp;lt;/Grid&amp;gt;&lt;br /&gt;&lt;br /&gt;03: When selecting a row in code the row does not become visible.  To automatically make the row visible you need to use ScrollIntoView()&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ListView.SelectedIndex++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ListView.ScrollIntoView(ListView.SelectedItem);&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1688176320920284646-4084090849224503296?l=mrpmorris.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mrpmorris.blogspot.com/feeds/4084090849224503296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1688176320920284646&amp;postID=4084090849224503296' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4084090849224503296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1688176320920284646/posts/default/4084090849224503296'/><link rel='alternate' type='text/html' href='http://mrpmorris.blogspot.com/2008/05/selecting-wpf-listview-row-in-code.html' title='Selecting WPF ListView row in code'/><author><name>Peter Morris</name><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><thr:total>2</thr:total></entry></feed>
