<?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-20196010</id><updated>2012-02-16T19:33:11.849+01:00</updated><category term='C#'/><category term='CruiseControl.NET'/><category term='miscellaneous'/><category term='Domain Driven Design'/><category term='Test Driven Development'/><category term='Agile'/><category term='books'/><category term='Visual Studio.NET'/><category term='SQL Server'/><category term='NHibernate'/><category term='Photography'/><category term='.NET 3.0'/><category term='MSBuild'/><category term='Aspect Oriented Programming'/><category term='OO'/><category term='Unit Testing'/><category term='.NET'/><title type='text'>Frederik Gheysels' DevLog</title><subtitle type='html'>Weblog on C# and software development</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>80</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-20196010.post-291863205443499109</id><published>2009-06-19T10:18:00.002+02:00</published><updated>2009-06-19T10:44:22.900+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>WinForms: DataBinding on a cancellable Dialog Form</title><content type='html'>&lt;p&gt;On some occasions, you might want to create a dialog window that enables a user to modify data.&lt;/p&gt;&lt;p&gt;In almost all situations, you will want to make use of the databinding capabilities in the .NET Framework, so that you do not have to write the tedious synchronization-code yourself.&lt;/p&gt;&lt;p&gt;However, when you want to databind a custom object to the controls on the dialog-form, databinding might get in the way:&lt;br /&gt;When you change the Text in a TextBox that is databound on a property of your object, the property of your object will be updated as soon as the databound TextBox has been validated (default behaviour).&lt;br /&gt;This is not a problem, until the user decides that he does not want to keep the changes he has made, and clicks the Cancel button of the dialog form. &lt;br /&gt;In such a case, you want to revert back to the original state of the object.&lt;/p&gt;&lt;p&gt;There are several solutions to handle this problem.  &lt;br /&gt;&lt;br /&gt;One solution is to implement some kind of &lt;a href="http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-1_08.html"&gt;Undo-behaviour&lt;/a&gt; in your custom object, but this might be a little bit overkill for the situation at hand.&lt;/p&gt;&lt;p&gt;A more simple solution, is to disable the 2-way databinding when the form is displayed, and explicitly update the databound object when the user clicks the Ok button of the dialog.&lt;br/&gt;&lt;br /&gt;This can be done like this:&lt;/p&gt;&lt;pre class="code"&gt;public class MyEditForm : Form&lt;br /&gt;{&lt;br /&gt;    public MyEditForm( MyClass objectToDisplay )&lt;br /&gt;    {&lt;br /&gt;        // Suppose that you have defined databindings via the&lt;br /&gt;        // designer&lt;br /&gt;        this.MyBindingSource.DataSource = objectToDisplay;&lt;br /&gt;&lt;br /&gt;        DataBindingUtils.SuspendTwoWayBinding(this.BindingContext[MyBindingSource]);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void btnOk_Click( object sender, EventArgs e )&lt;br /&gt;    {&lt;br /&gt;        DataBindingUtils.UpdateDataBoundObject(this.BindingContext[MyBindingSource]);&lt;br /&gt;        this.DialogResult = DialogResult.Ok;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void btnCancel_Click( object sender, EventArgs e )&lt;br /&gt;    {&lt;br /&gt;        this.DialogResult = DialogResult.Cancel;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The concept is very simple; when the form is displayed, we suspend the databindings, so that changes that are made to the controls on the form, are not directly persisted in the underlying object.&lt;br /&gt;When the user confirms the changes he has made by clicking the OK-button, we need to make sure that the contents of the controls are persisted in the underlying object.&lt;/p&gt;&lt;p&gt;The two methods that are of interest look like this:&lt;/p&gt;&lt;pre class="code"&gt;public static class DataBindingUtils&lt;br /&gt;{&lt;br /&gt;    public static void SuspendTwoWayBinding( BindingManagerBase bindingManager )&lt;br /&gt;    {&lt;br /&gt;        if( bindingManager == null )&lt;br /&gt;        {&lt;br /&gt;           throw new ArgumentNullException ("bindingManager");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        foreach( Binding b in bindingManager.Bindings )&lt;br /&gt;        {&lt;br /&gt;            b.DataSourceUpdateMode = DataSourceUpdateMode.Never;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void UpdateDataBoundObject( BindingManagerBase bindingManager )&lt;br /&gt;    {&lt;br /&gt;        if( bindingManager == null )&lt;br /&gt;        {&lt;br /&gt;           throw new ArgumentNullException ("bindingManager");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        foreach( Binding b in bindingManager.Bindings )&lt;br /&gt;        {&lt;br /&gt;            b.WriteValue ();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-291863205443499109?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/291863205443499109/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=291863205443499109' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/291863205443499109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/291863205443499109'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2009/06/winforms-databinding-on-cancellable.html' title='WinForms: DataBinding on a cancellable Dialog Form'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-966406646574184293</id><published>2009-05-15T11:49:00.004+02:00</published><updated>2009-05-15T12:30:07.889+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Forcing NHibernate to cascade deletes before updates</title><content type='html'>&lt;p&gt;At work, I've ran into a particular problem; let me describe the situation at hand.&lt;/p&gt;&lt;p&gt;Suppose I have the following DB schema:&lt;/p&gt;&lt;br /&gt;&lt;div style="text-align:center"&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibcascade/db_schema.jpg" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;In the &lt;code&gt;OrderLines&lt;/code&gt; table, there exists a UNIQUE CONSTRAINT on ( OrderId, SequenceNumber)&lt;/p&gt;&lt;p&gt;Next to this DB schema, I have an &lt;code&gt;Order&lt;/code&gt; and an &lt;code&gt;OrderLine&lt;/code&gt; class.   &lt;br /&gt;The &lt;code&gt;Order&lt;/code&gt; class has a collection of &lt;code&gt;OrderLines&lt;/code&gt;:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Order&lt;br /&gt;{&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;       get;&lt;br /&gt;       private set;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ISet&lt;OrderLine&gt; OrderLines = new HashedSet&lt;OrderLine&gt;();&lt;br /&gt;&lt;br /&gt;    public void AddOrderLine( OrderLine ol )&lt;br /&gt;    {&lt;br /&gt;       ol.Order = this;&lt;br /&gt;       OrderLines.Add (ol);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I've them mapped using NHibernate so that I can save them in the above DB schema.&lt;br /&gt;In the mapping, I've specified that the OrderLines collection should be cascaded when the Order is saved:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;&amp;lt;set name="OrderLines" cascade="all-delete-orphan"&amp;gt;&lt;br /&gt;   &amp;lt;key column="OrderId" /&amp;gt;&lt;br /&gt;   &amp;lt;one-to-many class="OrderLine" /&amp;gt;&lt;br /&gt;&amp;lt;/set&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Now, all goes well until you remove an OrderLine, add a new OrderLine with the same sequencenumber as the one that you've just removed.&lt;/p&gt;&lt;p&gt;Instead of first removing the OrderLine that you want to remove, and inserting the new OrderLine afterwards, NHibernate will perform these actions just the other way around:&lt;br /&gt;&lt;br /&gt;It will first try to insert the new OrderLine, and then it will remove the existing Orderline.  &lt;br /&gt;Now, since we have a unique constraint in the DB, this will fail offcourse.&lt;/p&gt;&lt;p&gt;I've found a way to work around this problem.  Although I find it not optimal (rather an ugly hack), it kinda works, so I'll stick with it for now.&lt;br /&gt;This is how I've done it:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Instead of specifying 'all-delete-orphan' as the cascade option for the collection, I've modified it to 'delete-orphan' instead.  &lt;br /&gt;This means that deletes will be cascaded, but inserts &amp; updates are not&lt;/li&gt;&lt;br /&gt;&lt;li&gt;All DB access goes through 'repositories' in my application.  This means I have an &lt;code&gt;OrderRepository&lt;/code&gt; which has a Save method.&lt;br /&gt;I have modified my &lt;code&gt;Save&lt;/code&gt; method so it looks like this:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public void Save( Order o )&lt;br /&gt;{&lt;br /&gt;    // With.Transaction only starts a transaction when the given session&lt;br /&gt;    // is not in a transaction yet.&lt;br /&gt;    // session is an ISession and is a member variable of my Repository.&lt;br /&gt;    With.Transaction (session, new delegate()&lt;br /&gt;    {&lt;br /&gt;       session.SaveOrUpdate (o);&lt;br /&gt;&lt;br /&gt;       session.Flush();&lt;br /&gt;&lt;br /&gt;       foreach( OrderLine ol in o.OrderLines )  &lt;br /&gt;       {&lt;br /&gt;          session.SaveOrUpdate (ol);&lt;br /&gt;       }&lt;br /&gt;    };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;What I do here, is just calling &lt;code&gt;SaveOrUpdate&lt;/code&gt; on the given &lt;code&gt;Order&lt;/code&gt;.  Because of the cascading setting, only the &lt;code&gt;OrderLines&lt;/code&gt; that are to be deleted, will be cascaded.&lt;br /&gt;Afterwards, I call the &lt;code&gt;Flush&lt;/code&gt; method of the session to make sure that the DELETE statements are actually send to the database.&lt;br /&gt;&lt;br /&gt;We're now left with the &lt;code&gt;OrderLine&lt;/code&gt; entities that are new or modified.  To make sure that they get persisted as well, I loop over the &lt;code&gt;OrderLines&lt;/code&gt; collection and call SaveOrUpdate for every &lt;code&gt;OrderLine&lt;/code&gt; instance.&lt;br /&gt;This will make sure that new &lt;code&gt;OrderLines&lt;/code&gt; get inserted and modified ones, are updated.  &lt;br /&gt;NHibernate will not update those &lt;code&gt;OrderLines&lt;/code&gt; that are not changed.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Although this 'hack' is nicely hidden / abstracted by the Repository, I still find it a bit ugly, but at this very moment I see no better way to handle this kind of issue ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-966406646574184293?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/966406646574184293/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=966406646574184293' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/966406646574184293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/966406646574184293'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2009/05/forcing-nhibernate-to-cascade-deletes.html' title='Forcing NHibernate to cascade deletes before updates'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1200655561337519453</id><published>2008-09-02T21:42:00.002+02:00</published><updated>2008-09-02T21:45:36.370+02:00</updated><title type='text'>Google Chrome</title><content type='html'>The beta-version of Google's Chrome browser is available for &lt;a href="http://www.google.com/chrome/index.html?hl=nl&amp;brand=CHMG&amp;utm_source=nl-hpp&amp;utm_medium=hpp&amp;utm_campaign=nl"&gt;download&lt;/a&gt;.  I've just installed it, and I like it. &lt;br /&gt;I love their new 'start-page' concept, for instance.&lt;br /&gt;&lt;br /&gt;They've also created a &lt;a href="http://www.google.com/chrome/index.html?hl=nl&amp;brand=CHMG&amp;utm_source=nl-hpp&amp;utm_medium=hpp&amp;utm_campaign=nl"&gt;comic&lt;/a&gt; where they explain the concepts and techniques of Chrome.  Very interesting as well. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1200655561337519453?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1200655561337519453/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1200655561337519453' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1200655561337519453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1200655561337519453'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/09/google-chrome.html' title='Google Chrome'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-8892130766508627695</id><published>2008-08-25T21:23:00.004+02:00</published><updated>2008-08-26T21:59:06.599+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>On reading books ....</title><content type='html'>&lt;p&gt;Davy Brion has made a &lt;a href="http://davybrion.com/blog/2008/08/software-development-books-investing-in-yourself/"&gt;statement&lt;/a&gt;  on his blogs in where he states that reading certain development books should be considered as an investment for a software developer.&lt;/p&gt;&lt;p&gt;I fully support that statement.&lt;br /&gt;I have a few books on my shelf that I consider as 'my Software Development Bibles'. These books have -imho- sharpened my skills, broadened my view and helped me to be a better developer.&lt;br /&gt;These books -which I consider to be my bibles- are, in no particular order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215"&gt;Domain Driven Design: Tackling Complexity in the Heart of Software&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1219692863&amp;amp;sr=8-1"&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/a&gt;&lt;/li&gt;&lt;a href="http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1219692863&amp;amp;sr=8-1"&gt;&lt;br /&gt;&lt;/a&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Enterprise-Application-Architecture-Addison-Wesley-Signature/dp/0321127420/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1219692907&amp;amp;sr=1-1"&gt;Patters of Enterprise Application Architecture&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1219692950&amp;amp;sr=1-1"&gt;Refactoring: Improving the design of existing code&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0321247140/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1219692983&amp;amp;sr=1-1"&gt;Design Patterns Explained&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1219693145&amp;sr=1-1"&gt;Agile Software Development&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;There are lots of other books on software development on my shelf, but I consider the ones above as the ones that have influenced me most.&lt;br /&gt;Books, you can't get enough of them (you still have to read them as well offcourse).      There are still some books regarding software-development on my &lt;a href="http://www.amazon.com/gp/registry/registry.html?ie=UTF8&amp;type=wishlist&amp;id=34KVAI4BDKYYU"&gt;whishlist&lt;/a&gt;, and I'm sure that every now and then, another book will be added to it...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-8892130766508627695?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/8892130766508627695/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=8892130766508627695' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8892130766508627695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8892130766508627695'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/08/on-reading-books.html' title='On reading books ....'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-5007798590241539658</id><published>2008-08-19T21:14:00.014+02:00</published><updated>2008-08-25T21:45:34.162+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Aspect Oriented Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Locking system with aspect oriented programming</title><content type='html'>&lt;h2&gt;Intro&lt;/h2&gt;&lt;p&gt;A few months ago, I had to implement a 'locking system' at work.  &lt;br /&gt;I will not elaborate to much on this system, but it's intention is that users can prevent that certain properties of certain entities are updated automatically;&lt;br /&gt;The software-system in where I had to implement this functionality, keeps a large database up-to-date by processing and importing lots of data-files that we receive from external sources.&lt;br /&gt;Because of that, in certain circumstances, users want to avoid that data that they've manually changed or corrected, gets overwritten with wrong information next time a file is processed.&lt;p&gt;&lt;p&gt;The application where I'm talking about, makes heavy use of DataSets and I've been able to create a rather elegant solution for it.&lt;br /&gt;At the same time, I've also been thinking on how I could solve this same problem in a system that is built around &lt;a href="http://en.wikipedia.org/wiki/POCO"&gt;POCO's&lt;/a&gt; instead of Datasets, and that's what this post will be all about. :)&lt;/p&gt;&lt;h2&gt;Enter Aspects&lt;/h2&gt;&lt;p&gt;When the idea of implementing such a system first crossed my mind, I already realized that Aspects Oriented Programming could be very helpfull to solve this problem.&lt;/p&gt;&lt;p&gt;A while ago, I already &lt;a href="http://fgheysels.blogspot.com/2006/11/aspect-oriented-programming-in-net.html"&gt;played&lt;/a&gt; with Aspect Oriented Programming using Spring.NET.  &lt;br /&gt;AOP was very nice and interesing, but I found the runtime-weaving a big drawback.  Making use of runtime weaving meant that you could not directly create an instance using it's constructor.&lt;br /&gt;&lt;br /&gt;So, instead of:&lt;pre class='code-content'&gt;MyClass c = new MyClass();&lt;/pre&gt;you had to instantiate instances via a proxyfactory:&lt;pre class='code-content'&gt;ProxyFactory f = new ProxyFactory (new TestClass());&lt;br /&gt;&lt;br /&gt;f.AddAdvice (new MethodInvocationLoggingAdvice());&lt;br /&gt;&lt;br /&gt;ITest t = (ITest)f.GetProxy();&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I am sure that you agree that this is quite a hassle, just to create a simple instance.  (Yes, I know, offcourse you can make abstraction of this by making use of a Factory...).&lt;/p&gt;&lt;p&gt;Recently however, I bumped at an article on &lt;a href="http://patrickdeboeck.blogspot.com/"&gt;Patrick De Boeck's weblog&lt;/a&gt;, where he was talking about &lt;a href="http://www.postsharp.org/"&gt;PostSharp&lt;/a&gt;.&lt;br /&gt;PostSharp is an aspect weaver for .NET which weaves at compile-time!&lt;br /&gt;This means that the drawback that I just described when you make use of runtime-weaving has disappeared.  &lt;br /&gt;So, I no longer had excuses to start implementing a similar locking system for POCO's.&lt;/p&gt;&lt;h2&gt;Bring it on&lt;/h2&gt;&lt;p&gt;I like the idea of &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test-Driven-Development&lt;/a&gt;, so I started out with writing a first simple test:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/firsttest.PNG" /&gt;&lt;p&gt;The advantage of writing your test first, is that you start thinking on how the interface of our class should look like.&lt;/p&gt;&lt;p&gt;This first test tells us that our class should have a &lt;code&gt;Lock&lt;/code&gt; and an &lt;code&gt;IsLocked&lt;/code&gt; method.&lt;br /&gt;The purpose of the &lt;code&gt;Lock&lt;/code&gt; method is to put a 'lock' on a certain property, so that we can avoid that this property is modified at run-time.&lt;br /&gt;The &lt;code&gt;IsLocked&lt;/code&gt; method is there to inform us whether a property is locked or not.&lt;/p&gt;&lt;p&gt;To define this contract, I've created an interface &lt;code&gt;ILockable&lt;/code&gt; which contains these 2 methods. &lt;br /&gt;In order to get this first test working, I've created an abstract class &lt;code&gt;LockableEntity&lt;/code&gt; which inherits from one of my base entity-classes implements this interface.&lt;br /&gt;This &lt;code&gt;LockableEntity&lt;/code&gt; class looks like this:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/firstlockableentity.PNG" /&gt;&lt;br /&gt;&lt;p&gt;This is not sufficient to get a green bar on my first test, since I still need an &lt;code&gt;AuditablePerson&lt;/code&gt; class:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/auditpersonforfirsttest.PNG" /&gt;&lt;p&gt;These pieces of code are sufficient to make my first test pass, so I continued with writing a second test:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/secondtest.PNG" /&gt;&lt;p&gt;As you can see, in this test-case I define that it should be possible to unlock a property.  Unlocking a property means that the value of that property can be modified by the user at runtime.&lt;br /&gt;To implement this simple functionality, it was sufficient to just add an &lt;code&gt;UnLock&lt;/code&gt; method to the &lt;code&gt;LockableEntity&lt;/code&gt; class:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/unlockable.PNG" /&gt;.&lt;p&gt;Simple, but now, a more challenging feature is coming up.&lt;br /&gt;&lt;br /&gt;Now, we can already 'lock' and 'unlock' properties, but there is nothing that really prevents us from changing a locked property.&lt;br /&gt;It's about time to tackle this problem and therefore, I've written the following test:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/thirdtest.PNG" /&gt;&lt;p&gt;Running this test obviously gives a red bar, since we haven't implemented any logic yet.&lt;br /&gt;The most simple way to implement this functionality, would be to check in the setter of the &lt;code&gt;Name&lt;/code&gt; property whether there exists a lock on this property or not.  &lt;br /&gt;If a lock exists, we should not change the value of the property, otherwise we allow the change.  &lt;br /&gt;I think that this is a fine opportunity to use aspects.&lt;/p&gt;&lt;h3&gt;Creating the Lockable Aspect&lt;/h3&gt;&lt;p&gt;As I've mentionned earlier, I have used &lt;a href="http://www.postsharp.org/"&gt;PostSharp&lt;/a&gt; to create the aspects.  Once you've downloaded and installed PostSharp, you can create an aspect rather easy.&lt;/p&gt;&lt;p&gt;There is plenty of &lt;a href="http://www.postsharp.org/about/documentation/"&gt;documentation to be found on the PostSharp site&lt;/a&gt;, so I'm not going to elaborate here on the 'getting started' aspect (no pun intended).&lt;/p&gt;&lt;p&gt;Instead, I'll directly dive into the &lt;code&gt;Lockable&lt;/code&gt; aspect that I've created.&lt;br /&gt;&lt;br /&gt;This is how the definition of the class that defines the aspect looks like:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/lockaspectdef.PNG" /&gt;&lt;p&gt;Perhaps I should first elaborate a bit on how I would like to use this &lt;code&gt;Lockable&lt;/code&gt; aspect.&lt;br /&gt;&lt;br /&gt;I'd like to be able to decorate the properties of a class that should be 'lockable' with an attribute.  Like this:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/lockableperson.PNG" /&gt;&lt;p&gt;Decorating a property with the &lt;code&gt;Lockable&lt;/code&gt; attribute, means that the user should be able to 'lock' this property.  That is, prevent that it gets changed after it has been locked.&lt;br /&gt;To be able to implement this, I've created a class which inherits from the OnMethodInvocationAspect class (which eventually inherits from &lt;code&gt;Attribute&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Why did I choose this class to inherit from?  &lt;br /&gt;Well, because there exists no &lt;code&gt;OnPropertyInvocation&lt;/code&gt; class or whatsoever.&lt;br /&gt;&lt;br /&gt;As you probably know, the getters and setters of a property are actually implemented as &lt;code&gt;get_&lt;/code&gt; and &lt;code&gt;set_&lt;/code&gt; methods, so it is perfectly possible to use the &lt;code&gt;OnMethodInvocationAspect&lt;/code&gt; class to add extra 'concerns' to the property.&lt;/p&gt;&lt;p&gt;This extra functionality is written in the &lt;code&gt;OnInvocation&lt;/code&gt; method that I've overriden in the &lt;code&gt;LockableAttribute&lt;/code&gt; class.&lt;br /&gt;&lt;br /&gt;In fact, it does nothing more then checking whether we're in the setter method of the property, and if we are, check whether there exists a lock on the property.&lt;br /&gt;If there exists a lock, we won't allow the property-value to be changed.  Otherwise, we just make sure that the implementation of the property itself is called.&lt;br /&gt;The implementation looks like this:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/oninvocation.PNG" /&gt;&lt;p&gt;Here, you can see that we use reflection to determine whether we're in the setter-method or in the getter-method of the property; we're only interested if this property is locked if we're about to change the value of the property.&lt;/p&gt;&lt;p&gt;Next, we need to get the name of the property for which we're entering the setter method.  This is done via the &lt;code&gt;GetPropertyForSetterMethod&lt;/code&gt; method which uses reflection as well to get the &lt;code&gt;PropertyInfo&lt;/code&gt; object for the given setter-method.&lt;/p&gt;&lt;p&gt;Once this has been done, I can use the &lt;code&gt;IsLocked&lt;/code&gt; method to check whether this property is locked or not.&lt;br /&gt;&lt;br /&gt;Note that I haven't checked whether the conversion from &lt;code&gt;eventArgs.Delegate.Target&lt;/code&gt; to &lt;code&gt;ILockable&lt;/code&gt; has succeeded or not.  More on that later ...&lt;/p&gt;&lt;p&gt;When the property is locked, I call the &lt;code&gt;OnAttemptToModifyLockedProperty&lt;/code&gt; method (which is declared in &lt;code&gt;ILockable&lt;/code&gt;), and which just raises the &lt;code&gt;LockedPropertyChangeAttempt&lt;/code&gt; event (also declared in the &lt;code&gt;ILockable&lt;/code&gt; interface).  By doing so, the programmer can decide what should happen when someone / something attempts to change a locked property.  This gives a bit more control to the programmer and is much more flexible then throwing an exception.&lt;/p&gt;&lt;p&gt;When the property is not locked, we let the setter-method execute.&lt;/p&gt;&lt;p&gt;With the creation of this aspect, our third test finally gives a green bar.&lt;/p&gt;&lt;h3&gt;Compile time Validation&lt;/h3&gt;&lt;p&gt;As I've said a bit earlier, I haven't checked in the &lt;code&gt;OnInvocation&lt;/code&gt; method whether the Target really implemented the &lt;code&gt;ILockable&lt;/code&gt; interface before I called methods of the &lt;code&gt;ILockable&lt;/code&gt; type.&lt;/p&gt;&lt;p&gt;The reason for this , is quite simple: the &lt;code&gt;OnMethodInvocationAspect&lt;/code&gt; class has a method &lt;code&gt;CompileTimeValidate&lt;/code&gt; which you can override to add compile-time validation logic (hm, obvious).&lt;/p&gt;&lt;p&gt;I made use of this to check whether the types where I've applied the &lt;code&gt;Lockable&lt;/code&gt; attribute really are &lt;code&gt;ILockable&lt;/code&gt; types:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/compiletimevalidate.PNG" /&gt;&lt;br /&gt;&lt;small&gt;Note that it should be possible to make this code more concise, but I could not just call &lt;code&gt;method.DeclaringType.GetInterface("ILockable")&lt;/code&gt; since that gave a &lt;code&gt;NotImplementedException&lt;/code&gt; while compiling.  Strange, but true&lt;/small&gt;&lt;p&gt;Now, when I use the &lt;code&gt;Lockable&lt;/code&gt; attribute on a type which is not &lt;code&gt;ILockable&lt;/code&gt;, I'll get the following compiler errors:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/lockingsystem/compileerror.PNG" /&gt;&lt;p&gt;Pretty neat, huh ?&lt;br /&gt;Now, what's left is a way to persist the locks in a datastore, but that will be a story for some other time ... &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-5007798590241539658?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/5007798590241539658/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=5007798590241539658' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5007798590241539658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5007798590241539658'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/08/locking-system-with-aspect-oriented.html' title='Locking system with aspect oriented programming'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-772817336885647281</id><published>2008-07-28T21:39:00.003+02:00</published><updated>2008-07-28T22:48:58.488+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET 3.0'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>NHibernate in a remoting / WCF scenario</title><content type='html'>I am thinking on how I could use NHibernate in a remoting scenario (using &lt;a href="http://en.wikipedia.org/wiki/.NET_Remoting"&gt;.NET remoting&lt;/a&gt;, webservices, &lt;a href="http://en.wikipedia.org/wiki/Windows_Communication_Foundation"&gt;WCF&lt;/a&gt; ... ), but I can already see some problems which I will likely encounter on my path.&lt;br /&gt;&lt;br /&gt;This is how I see the big picture of the application:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibremoting/bigpict.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;Let me explain it in short:&lt;br /&gt;The client application (a rich Windows client for instance) communicates via some kind of technique, be it WCF or the old .NET remoting, with the Service Layer.&lt;br /&gt;This means that the client application calls a (remote) method on the Service Layer to retrieve a Customer for instance.  The client can make some changes to that object and later, the client can call the remote 'SaveCustomer' method so that the Service Layer can persist the changes back to the datastore.&lt;br /&gt;In order to do this, the Service Layer uses a Repository that uses NHibernate to retrieve or persist objects.&lt;br /&gt;Note that the Client Application and the Remote service layer use the same Domain Entities.  This means that the domain classes need to be [Serializable].&lt;br /&gt;&lt;br /&gt;The problem that I will be facing is this:&lt;br /&gt;- Since (N)Hibernate uses its ISession as a UnitOfWork, which keeps track of the objects that have been created, deleted, inserted, the Client Application doesn't know whether it is necessary to perform a remote call to save the entity or not.&lt;br /&gt;(The client application doesn't know anything of some thing called an 'NHibernate Session', and my business object (entity) has no state tracking as well.  (In other words: my entity itself doesn't know whether it has been created, changed or deleted).&lt;br /&gt;&lt;br /&gt;- The remote method which will save my entity, will use another ISession then the method that has retrieved it.  (Remote methods should be stateless, since multiple callers can call the same method.  Client x should not know anything of client Y).&lt;br /&gt;The fact that the 'SaveCustomer' method will use another ISession, means that it is possible that NHibernate will perform unnecessary UPDATE statements.  This could be problematic if you use an &lt;a href="http://fgheysels.blogspot.com/2008/07/nhibernate-iinterceptor.html"&gt;AuditInterceptor&lt;/a&gt;, since this Interceptor will update the LastUpdated, Version, etc... columns in the DB, while this was not necessary.  In other words: this leads to wrong information in the database.&lt;br /&gt;&lt;br /&gt;How could these problems be tackled:&lt;br /&gt;- For the first problem, you could implement some kind of 'state tracking' in your entities, and add a property which tells you whether the entity has modified , etc...&lt;br /&gt;&lt;br /&gt;- Implementing state-tracking in your domain entities  may also solve the 2nd problem; in your repository you can check whether you've to Update or Save (for new entities) your entity.  However, I don't know yet how this will behave in situations where an entity contains a collection of other entities ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I'd like to know from other people how they have tackled these kind of problems ? Did you implement some kind of state tracking in your business entities ? &lt;br /&gt;Or, did you choose not to expose your business entities to the client application, and use Data Transfer Objects instead ?  If so, how did you map these DTO's to your business classes ?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-772817336885647281?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/772817336885647281/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=772817336885647281' title='6 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/772817336885647281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/772817336885647281'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/07/nhibernate-in-remoting-wcf-scenario.html' title='NHibernate in a remoting / WCF scenario'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-8494760539850004937</id><published>2008-07-05T20:51:00.007+02:00</published><updated>2008-07-05T21:53:16.703+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>NHibernate IInterceptor: an AuditInterceptor</title><content type='html'>&lt;p&gt;As I was playing around with NHibernate today, I came accross a rather inconvenient problem. :).&lt;br /&gt;&lt;br /&gt;Let me first explain what I wanted to achieve:&lt;br /&gt;For every domain object that I save, I want to persist in the database when the entity has been created, when it has been last updated and by whom.  Nothing special, just regular audit-information.&lt;br /&gt;&lt;br /&gt;To make this all possible, I've created the following classes / interfaces:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;h3&gt;&lt;code&gt;IAuditable&lt;/code&gt; interface&lt;/h3&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/iauditable.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;h3&gt;&lt;code&gt;AuditableEntity&lt;/code&gt; interface&lt;/h3&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/auditableentity.PNG" /&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;I think this is pretty straightforward and doesn't require any further explanation.&lt;br /&gt;Then, I continued with creating an NHibernate interceptor which would set the Created and Updated dates.  (I could also used the &lt;code&gt;ILifecycle&lt;/code&gt; interface instead, but this meant that I would have a dependency to the NHibernate assembly in my 'domain classes assembly', and I don't like that.  In fact, the &lt;code&gt;ILifecycle&lt;/code&gt; interface has been deprecated for exactly that reason).&lt;br /&gt;&lt;br /&gt;This is an extract from my &lt;code&gt;AuditInterceptor&lt;/code&gt; which would perform the task I wanted (at least, I thought so ... ).&lt;br /&gt;(Note that my &lt;code&gt;AuditInterceptor&lt;/code&gt; is NOT in the same assembly where the &lt;code&gt;IAuditable&lt;/code&gt;, &lt;code&gt;AuditableEntity&lt;/code&gt; and other domain base class reside in.  This would create a dependency from my base classes to NHibernate and again, I hate this :) ).&lt;br /&gt;&lt;br /&gt;The AuditInterceptor (snippet):&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/interceptor1.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;As you can see, it is very simple: I only had to implement 2 methods of the IInterceptor interface:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;OnSave&lt;/code&gt;, which is called when an entity is saved for the first time in the database (INSERT)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;OnFlushDirty&lt;/code&gt;, which is called when an existing entity is dirty and has to be updated&lt;/li&gt;&lt;/ul&gt;What I do, is check whether the entity that is to be saved implements the &lt;code&gt;IAuditable&lt;/code&gt; interface, and if so, I just set the necessary properties (&lt;code&gt;Created&lt;/code&gt; and &lt;code&gt;Updated&lt;/code&gt;) to the appropriate values (the current DateTime).&lt;/p&gt;&lt;p&gt;Easy enough, simple, understandable and clean... If only this would work...&lt;br /&gt;During testing, I got the following exception:&lt;pre class='code-content'&gt;  ----&gt; System.Data.SqlTypes.SqlTypeException : SqlDateTime overflow. &lt;br /&gt;Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.&lt;br /&gt;   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object[] fields, &lt;br /&gt;Boolean[] notNull, SqlCommandInfo sql, Object obj, ISessionImplementor session)&lt;/pre&gt;&lt;p&gt;As it turns out, NHibernate doesn't 'see' the changes you make to the entity parameter that is passed to the Interceptor methods:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/parameterbinding.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;You can however, change the values that are in the &lt;code&gt;state&lt;/code&gt; array parameter. Then NHibernate will correctly persist the changes.&lt;br /&gt;&lt;br /&gt;But, I do not like to 'hard-code' property names as strings for obvious reasons (if you change a property, the compiler will not detect that you should change your 'hardcoded property name string', etc...).&lt;br /&gt;&lt;br /&gt;Anyway, in order to get my interceptor to work, I have no other choice then messing around with the &lt;code&gt;propertyNames[]&lt;/code&gt; and &lt;code&gt;state[]&lt;/code&gt; parameters.&lt;br /&gt;In order to get rid of the 'weak-typing', I added a little bit more code.&lt;br /&gt;So, now my classes look like this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;h3&gt;&lt;code&gt;IAuditable&lt;/code&gt; interface&lt;/h3&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/iauditable2.PNG" /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;h3&gt;&lt;code&gt;AuditableEntity&lt;/code&gt; class&lt;/h3&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/auditableentity2.PNG" /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;h3&gt;&lt;code&gt;AuditInterceptor&lt;/code&gt;&lt;/h3&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibintercept/interceptor2.PNG" /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This solution is, IMHO, elegant enough to live with, and it works. &lt;br /&gt;&lt;br /&gt;However, maybe someone else has a better, more elegant solution for this ?  If so, I'd like to hear from you ...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-8494760539850004937?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/8494760539850004937/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=8494760539850004937' title='7 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8494760539850004937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8494760539850004937'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/07/nhibernate-iinterceptor.html' title='NHibernate IInterceptor: an AuditInterceptor'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-2760368230197385453</id><published>2008-07-01T22:21:00.005+02:00</published><updated>2008-07-01T23:53:44.715+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>NHibernate Session Management</title><content type='html'>&lt;p&gt;I know that there has been written a lot about this topic, but somehow, I haven't found the 'sweet spot' concerning NHibernate Session Management in WinForms applications yet.&lt;/p&gt;&lt;p&gt;Some time ago, I've created a simple abstraction around the NHibernate ISession which would make it easier to use the ISession in my Winforms application.&lt;/p&gt;&lt;P&gt;Why do I want to clutter my presentation layer with NHibernate stuff, you ask ? Because Context is King.  &lt;br /&gt;The &lt;a href="http://www.martinfowler.com/eaaCatalog/repository.html"&gt;Repository&lt;/a&gt; has  no notion of transactions, since the Repository doesn't know the context in where it's used.&lt;br /&gt;Therefore, I like to start my Transaction in my WinForm app for instance, and pass the 'Transaction' to my repository, like this:&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibsession/uow1.PNG" /&gt;&lt;br /&gt;In the code above, the &lt;code&gt;UnitOfWork&lt;/code&gt; class is just a simple wrapper around the NHibernate ISession which allows me to start and commit or rollback a transaction, disconnect the ISession from the Database, etc... with a minimum amount of code.&lt;/p&gt;&lt;p&gt;The UnitOfWork class looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/nhibsession/uow2.png" /&gt;&lt;/p&gt;&lt;p&gt;This approach also allows me to have multiple NHibernate ISessions opened in one application instance. &lt;br /&gt;This approach also gives me full control about when to start a new UnitOfWork, and when to close a UnitOfWork.&lt;/p&gt;&lt;p&gt;I've been convinced that this was the way to go.  Especially because I thought that you had to commit the changes you've made to an object using the same ISession as the ISession which you've used to retrieve the object if you want to avoid unnecessary &lt;code&gt;SELECT&lt;/code&gt; statements. &lt;/p&gt;&lt;p&gt;But, thanks to my collegue Thierry (who's starting to use NHibernate as well, and acted as some kind of catalysator to me so that I picked up my NHibernate quest again), it seems that my assumptions where not true:&lt;br /&gt;I thought that, when you save an object to the datastore, using another ISession then the ISession you've used to retrieve the object, NHibernate would first perform a &lt;code&gt;SELECT&lt;/code&gt; query in order to find out whether an &lt;code&gt;INSERT&lt;/code&gt; or an &lt;code&gt;UPDATE&lt;/code&gt; statement should be executed.&lt;br /&gt;This seems to be false if you do not use the 'assigned' generator class for your Id property.&lt;/p&gt;&lt;p&gt;So, now I'm in doubt:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;do I really need to be able to have concurrent ISessions in the same application instance ?  Until now, I haven't needed it yet (so, yes, that makes it a &lt;a href="http://en.wikipedia.org/wiki/You_Ain't_Gonna_Need_It"&gt;YAGNI&lt;/a&gt; in fact).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I haven't seen anyone on the net using a similar approach.  I see that everyone uses some kind of 'SessionManager' like the one Billy McCafferty has written &lt;a href="http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx"&gt;here&lt;/a&gt;, so this makes me doubt as well ...&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;This last point is also the reason for this blogpost: I'm in doubt :)&lt;br /&gt;Using some kind of 'SessionManager' class allows me to do the transaction demarcation  where ever I want as well.  Next to that, I also do not have to pass my UnitOfWork to the repository, since the repository has access to the current Session via the SessionManager as well ...&lt;br /&gt;&lt;br /&gt;I know that, maybe, I should just give it a try.  However, I'd like to hear experiences and thoughts of other people who are using (N)Hibernate in a Rich Client environment as well.&lt;br /&gt;How are you dealing with those (session management) issues ?  What difficulties did you encounter ?&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Note: another post of me regarding this subject can be found &lt;a href="http://fgheysels.blogspot.com/2007/04/nhibernate-session-management-in-domain.html"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;assumptions are the mother of all fuckups.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-2760368230197385453?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/2760368230197385453/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=2760368230197385453' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2760368230197385453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2760368230197385453'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/07/nhibernate-session-management.html' title='NHibernate Session Management'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-4823876954647060536</id><published>2008-06-30T21:01:00.002+02:00</published><updated>2008-06-30T21:03:09.901+02:00</updated><title type='text'>New Layout</title><content type='html'>&lt;p&gt;I've changed the layout of my weblog, I hope you like it.&lt;/p&gt;&lt;p&gt;If you have any remarks regarding the layout, if you don't find it readable, if you miss something, please let me know.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-4823876954647060536?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/4823876954647060536/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=4823876954647060536' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4823876954647060536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4823876954647060536'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/06/new-layout.html' title='New Layout'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-909752963358279644</id><published>2008-06-13T18:38:00.008+02:00</published><updated>2008-06-21T13:15:45.807+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CruiseControl.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Setting Up Continuous IntegrationPart II: configuring CruiseControl.NET</title><content type='html'>&lt;p&gt;Now that we've created our &lt;a href="http://fgheysels.blogspot.com/2008/06/setting-up-continuous-integration.html"&gt;buildscript in part I&lt;/a&gt;, it's time to set up the configuration file for &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET"&gt;CruiseControl.NET&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;The ccnet.config file and multiple project-configurations&lt;/h2&gt;&lt;p&gt;The tasks that CruiseControl.NET should execute for your project, are configured in the &lt;a href="http://ccnet.sourceforge.net/CCNET/Configuring%20the%20Server.html"&gt;ccnet.config&lt;/a&gt; file.&lt;br /&gt;The ccnet.config file can contain multiple &lt;a href="http://ccnet.sourceforge.net/CCNET/Project%20Configuration%20Block.html"&gt;project configuration blocks&lt;/a&gt;.  However, I like to have each project-configuration in it's own, separate file.   In my opinion, this is much more manageable.&lt;/p&gt;&lt;p&gt;In order to put each project-configuration in its own XML file and import it in the ccnet.config file, you can make use of &lt;a href="http://www.w3schools.com/dtd/dtd_entities.asp"&gt;DTD entities&lt;/a&gt; to substitute constants with the contents of other XML files.&lt;br /&gt;This is how I've done it:&lt;/p&gt;&lt;pre class="code-content"&gt;&amp;lt!DOCTYPE cruisecontrol [&lt;br /&gt;    &amp;lt;!ENTITY project1  SYSTEM "file:D:\folder\project1_ccnet.xml.config"&amp;gt;&lt;br /&gt;    &amp;lt;!ENTITY project2 SYSTEM "file:D:\folder\project2_ccnet.xml.config"&amp;gt;&lt;br /&gt;]&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;cruisecontrol&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;project1;&lt;br /&gt;  &amp;project2;&lt;br /&gt;&lt;br /&gt;&amp;lt/cruisecontrol&amp;gt;&lt;/pre&gt;&lt;p&gt;The above piece of code makes sure that the &amp;project1 and &amp;project2 'placeholders' are replaced with the content of the project1_ccnet.xml.config and project2_ccnet.xml.config files.&lt;/p&gt;&lt;p&gt;I just saw that CruiseControl.NET 1.4 has a &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Configuration+Preprocessor"&gt;new approach&lt;/a&gt; to accomplish this, however, I haven't tried it yet.&lt;/p&gt;&lt;h2&gt;The CC.NET config file&lt;/h2&gt;&lt;p&gt;The CC.NET config file is in fact very simple.  You just have to put the Tasks that you've defined in your MSBuild file in the CC.NET config file.&lt;br /&gt;Your CC.NET config file could look like this:&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/ccnetconfig.PNG" /&gt;&lt;p&gt;The above configuration file is by all means not complete; I've kept it simple, and left out some tasks.  However, you should have an idea :)&lt;/p&gt;&lt;h2&gt;MSBuild doesn't support my sln file format&lt;/h2&gt;&lt;p&gt;The reason why I specify which executable must be used by msbuild, is very simple:&lt;br /&gt;My project is written in VS.NET 2008, but targets the .NET 2.0 framework.  So, by default, CC.NET will use the MSBuild program that has been delivered with the .NET 2.0 framework.&lt;br /&gt;This results in an error: MSBuild doesn't recognize the VS.NET 2008 solution file format, and will stop with this error:&lt;/p&gt;&lt;code&gt;Solution file error MSB5014: File format version is not recognized. MSBuild can only read solution files between versions 7.0 and 9.0, inclusive.&lt;/code&gt;&lt;p&gt;This is offcourse due to the fact that the MSBuild that is used by VS.NET 2005 doesn't know anything about the solution file format that is used by VS.NET 2008.&lt;br /&gt;You can solve this issue by specifying that CC.NET should use the MSBuild executable that can be found in the directory of the .NET 3.5 framework.&lt;/p&gt;&lt;h2&gt;The MSBuild XmlLogger Issue&lt;/h2&gt;&lt;p&gt;It is possible that CruiseControl.NET will not be able to execute your project, because CC.NET can't find an appropriate XmlLogger.&lt;br /&gt;In this case, you'll find the following error in the CC.NET logfile:&lt;br /&gt;&lt;code&gt;Cannot create an instance of the logger. Could not load file or assembly 'ThoughtWorks.CruiseControl.MsBuild.dll' or one of its dependencies. The system cannot find the file specified.&lt;/code&gt;&lt;br /&gt;&lt;p&gt;You can solve this problem by placing the XmlLogger for MSBuild (you can find the dll &lt;a href="http://ccnetlive.thoughtworks.com/MSBuildXmlLogger-Builds/"&gt;here&lt;/a&gt; in your project working directory.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-909752963358279644?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/909752963358279644/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=909752963358279644' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/909752963358279644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/909752963358279644'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/06/setting-up-continuous-integration-part.html' title='Setting Up Continuous Integration&lt;/br&gt;Part II: configuring CruiseControl.NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-3067109602655719759</id><published>2008-06-07T17:37:00.016+02:00</published><updated>2008-06-07T18:45:26.683+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CruiseControl.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='MSBuild'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Setting Up a Continuous Integration process using CruiseControl.NET and MSBuild.Part I: creating the MSBuild build script</title><content type='html'>&lt;h2&gt;Intro&lt;/h2&gt;&lt;p&gt;I’ve been struggling lately to get a new project that I’ve started at work, under &lt;a href="http://en.wikipedia.org/wiki/Continuous_Integration"&gt;Continuous Integration&lt;/a&gt;.&lt;br /&gt;Although I’ve used &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET"&gt;CruiseControl.NET&lt;/a&gt; &amp; &lt;a href="http://nant.sourceforge.net/"&gt;NAnt&lt;/a&gt; in my previous project for CI purposes, things didn’t go so smooth now ...   &lt;br /&gt;&lt;br /&gt;In my current project, I’m using Visual Studio.NET 2008 and targetting .NET 2.0.  &lt;br /&gt;&lt;br /&gt;Now, I wanted to use &lt;a href="http://msdn.microsoft.com/en-us/library/wea2sca5.aspx"&gt;MSBuild&lt;/a&gt; for the build process and that’s where it all started.&lt;/p&gt;&lt;p&gt;I had to spent some time searching on the Net in order to get everything working like I wanted.  It seems that there’s no single source of documentation for &lt;a href="http://msdn.microsoft.com/en-us/library/wea2sca5.aspx"&gt;MSBuild&lt;/a&gt; &amp; &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET"&gt;CC.NET&lt;/a&gt; which addresses all the problems that I’ve encountered. &lt;br /&gt;So, the intention of this article, is to help other people setting up a CC.NET environment with MSBuild, and it will also serve as a reference for me, so that I can grab back to it when needed. :)&lt;/p&gt;&lt;h2&gt;Requirements&lt;/h2&gt;&lt;p&gt;What I wanted to achieve, is very simple:&lt;br /&gt;&lt;br /&gt;I have a build machine where CruiseControl.Net is installed.  This machine is already used for another project of mine for which I’m using NAnt for the build process.&lt;br /&gt;&lt;br /&gt;The new project that I’ve started, is being developped in VS.NET 2008, targets the .NET 2.0 framework and is under SourceControl via Visual SourceSafe.&lt;br /&gt;&lt;br /&gt;I wanted to have a CI process that regularly looks in VSS and, when something has changed, performs the following tasks:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Make sure that the latest buildscript will be used&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Clean the source directory&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Get the latest version of the codebase out of Visual SourceSafe&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Build the entire codebase&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Execute the unit tests that I have using &lt;a href="http://www.nunit.org/index.php"&gt;NUnit&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Perform a &lt;a href="http://en.wikipedia.org/wiki/Static_code_analysis"&gt;statical code analysis&lt;/a&gt; using &lt;a href="http://www.nunit.org/index.php"&gt;FxCop&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/p&gt;&lt;br /&gt;&lt;!--adsense--&gt;&lt;h2&gt;The MSBuild build-script&lt;/h2&gt;&lt;p&gt;In order to automate all the steps above, I needed to have a build-script first which I can execute using MSBuild.&lt;br /&gt;&lt;br /&gt;Such a script is also handy when the application you’re building consists of numerous VS.NET solutions; instead of opening each solution separately in Visual Studio, compiling it, opening the next solution ..., you can build the entire codebase using a single command line.  &lt;br /&gt;This is quite handy and productive, I can tell you :)&lt;/p&gt;&lt;p&gt;For every ‘task’ (clean source directory, get latest, build codebase, etc… ) that I want to execute, I’ve created a &lt;a href="http://www.nunit.org/index.php"&gt;Target&lt;/a&gt; in the build script.&lt;/p&gt;&lt;p&gt;A first little problem I encountered was that MSBuild doesn’t contain any tasks that would allow you to get a latest version out of VSS, run &lt;a href="http://www.nunit.org/index.php"&gt;NUnit&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Unit_test"&gt;unit-tests&lt;/a&gt; or perform a code analysis with FxCop out of the box.&lt;br /&gt;Fortunately, there exists an open source project called the &lt;a href="http://msbuildtasks.tigris.org/"&gt;'MSBuild Community Tasks Project'&lt;/a&gt; which contains additional tasks that can be executed by MSBuild.   This means that you don’t need to &lt;a href="http://community.bartdesmet.net/blogs/bart/archive/2008/02/15/the-custom-msbuild-task-cookbook.aspx"&gt;write your own MSBuild Tasks&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;Skeleton of the buildscript&lt;/h3&gt;&lt;p&gt;Before creating the Targets, I’ve defined a few properties which I will use in all the tasks:&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_skeleton.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;I define the working directory (where my source can be found) as the builddir, and a directory where the assemblies that have been build should be placed (outputdir).&lt;br /&gt;Next to that, I also have an artifactsdirectory where the results of the unittests and code analysis will be put.&lt;br /&gt;&lt;br /&gt;The last line in the above code is necessary so that we can use the additional MSBuild Tasks that can be found in the MSBuild Community Tasks Project.&lt;/p&gt;&lt;p&gt;Now, we can start creating our 'Targets'.&lt;/p&gt;&lt;h3&gt;Clean Target&lt;/h3&gt;&lt;p&gt;I want to have the possibility to start from a 'clean sheet', so I really need a Target which justs deletes everything that can be found in my builddir and outputdir.&lt;br /&gt;This Target is very simple; you just have to make use of the &lt;a href="http://msdn.microsoft.com/en-us/library/7wd15byf.aspx"&gt;Delete Task&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_cleantarget.PNG" /&gt;&lt;/p&gt;&lt;h3&gt;Getlatest Target&lt;/h3&gt;In order to get the latest version of the source out of SourceSafe, I’ve created the following step:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_getlatesttarget.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;Here, I just make use of the VssGet Task that is part of the &lt;a href="http://msbuildtasks.tigris.org/"&gt;MSBuild Community Tasks project&lt;/a&gt;.&lt;br /&gt;Also, notice that this Target depends on the createdirs Target; this means that, when you execute the getlatest Target, the createdirs Target will be executed before the getlatest Target is executed.&lt;/p&gt;&lt;p&gt;The createdirs tasks looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_createdirstask.PNG" /&gt;&lt;/p&gt;&lt;h3&gt;BuildAll Target&lt;/h3&gt;&lt;p&gt;This is the first target where I’ve had some issues, although it’s task is very trivial:&lt;br /&gt;Compile and build everything that can be found in the &lt;code&gt;$(builddir)&lt;/code&gt;, and make sure that the assemblies that have been built are placed in the &lt;code&gt;$(outputdir)&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;It seemed very easy to do, since I found out that MSBuild.exe (which is the program I use to compile the code) had a property &lt;code&gt;OutputDir&lt;/code&gt;.  So, it would be fairly easy to set this property to the &lt;code&gt;$(outputdir)&lt;/code&gt; variable.&lt;br /&gt;  &lt;br /&gt;Alas, to no avail.  My assemblies were never copied to the output-directory.  Eventually, I discovered that there also exists an &lt;code&gt;OutputPath&lt;/code&gt; property, so I tried it.  This seemed to work.&lt;br /&gt;So, the buildall Target looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_buildalltarget.PNG" /&gt;&lt;/p&gt;&lt;p&gt;Offcourse, you can put multiple solution files in the Projects attribute of the &lt;a href="http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx"&gt;MSBuild&lt;/a&gt; Task.  &lt;br /&gt;You’ll have to separate the &lt;a href="http://msdn.microsoft.com/en-us/library/bb165951(VS.80).aspx"&gt;sln files&lt;/a&gt; with a semicolon.&lt;/p&gt;&lt;h3&gt;NUnit Target&lt;/h3&gt;&lt;p&gt;This Target is quite simple:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_nunittask.PNG" /&gt;&lt;p&gt;With this Target, I run the NUnit tests that have been written in my test assembly.  (I tend to name all my Test-assemblies &lt;code&gt;&lt;projectname&gt;.Tests.dll&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;The results of the unit-tests procedure are placed in the artifacts directory as an XML file.  In this way, I can easily incorporate the test-results in my CC.NET report (more on this later).&lt;/p&gt;&lt;h3&gt;FxCop Target&lt;/h3&gt;&lt;p&gt;This one was a bit cumbersome.&lt;br /&gt;I started out writing this Target with the FxCop task that can be found in the MSBuild Community Project; it looked like this:&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_fxcop1.PNG" /&gt;&lt;p&gt;The reason why I do not apply the output XSL stylesheet, is very simple: I want CC.NET to display the results on the Dashboard, so CC.NET should read the XML file, and apply the XSL stylesheet.&lt;/p&gt;&lt;p&gt;Now, this Target just worked fine on my development box.  However, on my ‘build server’ *ahum* (my previous dev Workstation), I couldn’t get it working.&lt;br /&gt;On the build machine, I constantly kept getting errors. &lt;br /&gt;Apparently, msbuild was trying to locate FxCop in &lt;code&gt;C:\Program Files\Microsoft FxCop 1.32&lt;/code&gt;, but I don’t have this old version of FxCop installed.  &lt;br /&gt;I’m using FxCop 1.36 beta instead.&lt;/p&gt;&lt;p&gt;Therefore, I eventually opted to put the path where FxCop is installed in my %PATH% environment variable, and decided to use the &lt;a href="http://msdn.microsoft.com/en-us/library/x8zx72cd.aspx"&gt;&lt;code&gt;Exec&lt;/code&gt;&lt;/a&gt; Task so that I could call the &lt;a href="http://msdn.microsoft.com/en-us/library/x8zx72cd.aspx"&gt;fxcopcmd&lt;/a&gt; tool:&lt;/p&gt;&lt;img src="http://users.pandora.be/fgzone/blog/msbuild/msbuild_fxcop2.PNG" /&gt;&lt;p&gt;In order to keep my build script a bit readable, I’ve created an &lt;a href="http://msdn.microsoft.com/en-us/library/646dk05y.aspx"&gt;&lt;code&gt;ItemGroup&lt;/code&gt;&lt;/a&gt; in where I define all the command-line arguments that I want to pass to &lt;code&gt;FxCopCmd.exe&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;By default, the items that are defined in an &lt;a href="http://msdn.microsoft.com/en-us/library/646dk05y.aspx"&gt;&lt;code&gt;ItemGroup&lt;/code&gt;&lt;/a&gt; will be concatenated with a semicolon.   This is something I do not wanted offcourse, since command line arguments should be separated by a space.&lt;br /&gt;It is easy to define that the items should be separated by a space:&lt;/p&gt;&lt;pre class="code-content"&gt;@(Args, ' ')&lt;/pre&gt;&lt;p&gt;There’s a litle sublety with the Exec command however: it doesn’t work well when you have a commandline argument that is a path which contains a space.   You should escape such paths with quotes, but I haven’t succeeded in getting it to work with MSBuild yet ... &lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Executing Targets via MSBuild&lt;/h2&gt;&lt;p&gt;Now that we’ve defined all the Targets, we need to see if they work offcourse.&lt;br /&gt;Executing a Target is fairly easy:&lt;/p&gt;&lt;p&gt;Just open up a VS.NET command prompt (or open a regular command prompt and make sure that the path to the MSBuild.exe utility is in your path environment variable), and navigate to the location where your msbuild build-script is located.&lt;/p&gt;&lt;p&gt;Then, you just execute MSBuild, make sure that your build script is used, and tell MSBuild which target he should execute.  You can also override the default values of the parameters (like $(outputdir) ) that we’ve defined in our script.&lt;br /&gt;&lt;br /&gt;For instance:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;msbuild myproject.msbuild /t:buildall /p:outputdir=r:\myproject\release /p:buildmode=release&lt;/code&gt;&lt;/p&gt;&lt;p&gt;I think that this is enough text for today.   I will soon post a subsequent article in where I’ll explain how to use this script in CruiseControl.NET.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-3067109602655719759?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/3067109602655719759/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=3067109602655719759' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3067109602655719759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3067109602655719759'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/06/setting-up-continuous-integration.html' title='Setting Up a Continuous Integration process using CruiseControl.NET and MSBuild.&lt;br /&gt;Part I: creating the MSBuild build script'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-8566075739654925175</id><published>2008-04-20T20:09:00.002+02:00</published><updated>2008-04-20T20:22:27.853+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>using directives within namespaces</title><content type='html'>&lt;p&gt;Sometimes, I come across code examples where the programmer puts his using directives within the namespace declaration, like this:&lt;/p&gt;&lt;pre class='code-content'&gt;namespace MyNamespace&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using System.Data;&lt;br /&gt;&lt;br /&gt;    using SomeOtherNamespace;&lt;br /&gt;&lt;br /&gt;    public class MyClass&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I am used to put my using directives outside the namespace block (which is no surprise, since VS.NET places them by default outside the namespace declaration when you create a new class):&lt;/p&gt;&lt;pre class='code-content'&gt;using System;&lt;br /&gt;using System.Data;&lt;br /&gt;&lt;br /&gt;namespace MyNamespace&lt;br /&gt;{&lt;br /&gt;    public class MyClass&lt;br /&gt;    {  &lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;So, I'm wondering: what are the advantages of placing the using directives within the namespace declaration ?&lt;br /&gt;I've googled a little bit, but I haven't found any clue why I should do it as well.  Maybe you'll know a good reason, and can convice me to &lt;a href="http://fgheysels.blogspot.com/2006/09/changing-default-access-modifier-when.html"&gt;adapt my VS.NET templates&lt;/a&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/20196010-8566075739654925175?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/8566075739654925175/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=8566075739654925175' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8566075739654925175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8566075739654925175'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/04/using-directives-within-namespaces.html' title='using directives within namespaces'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-4930155251804969087</id><published>2008-03-12T17:31:00.004+01:00</published><updated>2008-03-12T17:48:59.646+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>VS.NET 2008: Form designer not working on Windows Vista</title><content type='html'>&lt;p&gt;I installed Visual Studio.NET 2008 on my Vista workstation at work.  I was keen to work with it, but apparently, my workstation had some issues.&lt;/p&gt;&lt;p&gt;When I started VS.NET 2008, and created a new WinForms project, I received the following error when I wanted to open a Form in the designer:&lt;/p&gt;&lt;quote&gt;The Service Microsoft.VisualStudio.Shell.Interop.ISelectionContainer already exists in the service container&lt;/quote&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/vsnet2008errorvista.png" alt="ISelectionContainer" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I've searched a bit on the Internet, and it seems that I was not the only person who was having this problem.&lt;br /&gt;However, nobody seemed to have a solution for this problem, and according to &lt;a href="https://connectbeta.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=311949"&gt;Microsoft, this problem was not reproducable&lt;/a&gt; ...&lt;br /&gt;&lt;br /&gt;Eventually, I found a website where someone said you had to install SP1 for .NET 2.0 and SP1 for .NET 3.0.&lt;br /&gt;Unfortunately, these service packs are not supported by Vista.&lt;br /&gt;&lt;br /&gt;I was however able to install the updates KB110806 and &lt;a href="http://support.microsoft.com/kb/929300"&gt;KB929300&lt;/a&gt; and installing these 2 updates, fixed my problem&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-4930155251804969087?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/4930155251804969087/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=4930155251804969087' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4930155251804969087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4930155251804969087'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/03/vsnet-2008-form-designer-not-working-on.html' title='VS.NET 2008: Form designer not working on Windows Vista'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-3970387348307213726</id><published>2008-01-28T11:31:00.000+01:00</published><updated>2008-01-28T11:41:09.499+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>Debugging the .NET framework</title><content type='html'>&lt;p&gt;As I've written &lt;a href="http://fgheysels.blogspot.com/2007/10/new-debugging-experience-in-vsnet-2008.html"&gt;earlier&lt;/a&gt;, Visual Studio.NET 2008 makes it possible to debug code that can be found on a source server.&lt;br /&gt;I think this can be very interesting and I can think of numerous situations where this can be very handy.&lt;br /&gt;Suppose you work in a company that uses an in-house developped framework, and you're building an application that uses this framework.&lt;br /&gt;If you experience some strange behaviour inside your application, you can debug your code and step into the code of the framework to see if the company's framework has a bug.&lt;/p&gt;&lt;p&gt;I've read that Microsoft has setup a source server which contains the debug-symbols of the .NET framework, so, as from this month, it is possible to step into the .NET framework source-code as well!&lt;br /&gt;Setting up VS.NET 2008 to enable this is very simple; you can find a step-by-step guide &lt;a href="http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The Mozilla team also have a symbol server.  You can read more about it &lt;a href="http://developer.mozilla.org/en/docs/Using_the_Mozilla_symbol_server"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-3970387348307213726?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/3970387348307213726/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=3970387348307213726' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3970387348307213726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3970387348307213726'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/01/debugging-net-framework.html' title='Debugging the .NET framework'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-2848736577643088087</id><published>2008-01-15T12:33:00.001+01:00</published><updated>2008-02-26T20:50:51.718+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>Cannot open log for source {0} on Windows 2003 Server</title><content type='html'>&lt;p&gt;I am writing an application which uses some .NET remote components that are hosted in IIS on a Windows 2003 Server.&lt;br /&gt;When the remote component throws an exception, the exception information should be written to the EventLog on the Windows 2003 Server; however, Win2k3 seems to be a bit restrictive when a component that is hosted in IIS wants to write to the eventlog.&lt;br /&gt;Although the component does not run under the IIS_WPG or ASPNET account (I am using Windows Impersonation), I always received the following exception when the .NET remote component wanted to write something to the eventlog:&lt;/p&gt;&lt;div style='code'&gt;Cannot open log for source {0}.  You may not have write access.  Access is denied&lt;/div&gt;&lt;p&gt;You can get rid of this behaviour and make sure that the error is indeed written to the EventLog by following the steps below:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Open the registry on the Win2k3 server using regedit&lt;/li&gt;&lt;li&gt;Locate the &lt;code&gt;HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\Application key&lt;/li&gt;&lt;li&gt;Find the CustomSD key and append the following string to the existing value: &lt;code&gt;(A;;0x0002;;;AU)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Now, the (impersonated) remote component should have rights to write to the EventLog.&lt;/p&gt;&lt;p&gt;Now, what is the meaning of the string you've just added to the CustomSD key ?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A stands for &lt;code&gt;SDDL_ACCESS_ALLOWED&lt;/code&gt;.  (See &lt;a href="http://msdn2.microsoft.com/en-us/library/aa374928.aspx"&gt;ACE strings&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;0x0002 stands for &lt;code&gt;ELF_LOGFILE_WRITE&lt;/code&gt;. (See also &lt;a href="http://msdn2.microsoft.com/en-us/library/aa363658.aspx"&gt;Event Logging Security&lt;/a&gt;&lt;/li&gt;&lt;li&gt;AU stands for Authenticated Users.  (See also &lt;a href="http://msdn2.microsoft.com/en-us/library/aa379602.aspx"&gt;SID strings&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-2848736577643088087?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/2848736577643088087/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=2848736577643088087' title='4 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2848736577643088087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2848736577643088087'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2008/01/cannot-open-log-for-source-0-on-windows.html' title='Cannot open log for source {0} on Windows 2003 Server'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-3571732097036673839</id><published>2007-11-03T16:10:00.000+01:00</published><updated>2007-11-03T16:25:33.354+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>Castle Windsor and ActiveRecord</title><content type='html'>&lt;p&gt;I sometimes read blogposts where people mention &lt;a href="http://www.castleproject.org/container/index.html"&gt;Castle Windsor&lt;/a&gt; and &lt;a href="http://www.castleproject.org/activerecord/index.html"&gt;Castle's ActiveRecord&lt;/a&gt;, so I decided to have a little peek at these two projects.&lt;/p&gt;&lt;p&gt;Castle Windsor is an &lt;a href="http://martinfowler.com/articles/injection.html"&gt;IoC container&lt;/a&gt; and ActiveRecord is an implementation of the &lt;a href="http://martinfowler.com/eaaCatalog/activeRecord.html"&gt;Active&lt;/a&gt; &lt;a href="http://davidhayden.com/blog/dave/archive/2006/06/10/2984.aspx"&gt;Record&lt;/a&gt; pattern which internally uses NHibernate.&lt;br /&gt;These projects look very interesting, and I'd like to make use of them in a little hobby-project before using it in a real-world project&lt;/p&gt;&lt;p&gt;Is there anybody who has used one (or both) of these projects in a real world scenario and who wants to share his (or her) findings ? &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-3571732097036673839?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/3571732097036673839/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=3571732097036673839' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3571732097036673839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3571732097036673839'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/11/castle-windsor-and-activerecord.html' title='Castle Windsor and ActiveRecord'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1337315018092736036</id><published>2007-10-24T22:07:00.000+02:00</published><updated>2007-10-24T22:27:09.405+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>New debugging experience in VS.NET 2008</title><content type='html'>&lt;p&gt;As I was reading a bit on &lt;a href="http://community.bartdesmet.net/blogs/bart/default.aspx"&gt;Bart De Smet's weblog&lt;/a&gt; today, I came accros an interesting post where Bart &lt;a href="http://community.bartdesmet.net/blogs/bart/archive/2007/10/03/arrived-in-redmond-some-hot-net-news.aspx"&gt;talks about a new 'debugging experience in VS.NET 2008&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;It seems that Microsoft will release the source code for the .NET Framework libraries, and that there will be an &lt;a href="http://weblogs.asp.net/scottgu/archive/2007/10/03/releasing-the-source-code-for-the-net-framework-libraries.aspx"&gt;integrated debugging support in VS.NET 2008&lt;/a&gt;.  &lt;br /&gt;This means: when debugging your code, it should be possible to 'step into' the code of the .NET framework classes.   &lt;br /&gt;I think this is great!&lt;/p&gt;&lt;p&gt;I've also read another interesting thing on Bart's weblog:   Bart will be working in the MS HQ's in Redmond, where he'll work on WPF.   That must be a great job, and I'd like to wish Bart a lot of succes!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1337315018092736036?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1337315018092736036/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1337315018092736036' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1337315018092736036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1337315018092736036'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/10/new-debugging-experience-in-vsnet-2008.html' title='New debugging experience in VS.NET 2008'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-7808910477170672780</id><published>2007-10-18T20:20:00.000+02:00</published><updated>2007-10-18T20:31:41.200+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>MSDN RampUp</title><content type='html'>&lt;p&gt;Microsoft has set up a programme in which you can take part to gain new or refresh your  existing .NET development skills.&lt;br /&gt;This programme is called 'MSDN Ramp Up'.  It is an online course with a lot of course material and online virtual labs.&lt;/p&gt;&lt;p&gt;The nice thing about this Ramp Up programme, is that Microsoft has made different 'tracks', which are tailored to the existing experience and knowledge of the student.&lt;br /&gt;There are four tracks available:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Aspiring Developer; which is suited for people who have no prior programming experience, but want to learn programming with MS technology&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Java Developer; this track is for Java developers who want to gain knowledge about the .NET framework and want to learn programming in C#&lt;/li&gt;&lt;br /&gt;&lt;li&gt;MS Visual Basic 6.0 developer; lets existing VB 6 developers become proficient in .NET and VB.NET 2005&lt;/li&gt;&lt;br /&gt;&lt;li&gt;MS VS.NET 2002/2003 developer; introduces the .NET 1.x developer with the improvements and changes in the .NET 2.0 framework&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;If you want to learn more about this programme, or want to take part in it, then visit &lt;a href="http://msdn2.microsoft.com/en-us/rampup/default.aspx"&gt;this website&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-7808910477170672780?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/7808910477170672780/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=7808910477170672780' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7808910477170672780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7808910477170672780'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/10/msdn-rampup.html' title='MSDN RampUp'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-577345825227262783</id><published>2007-09-28T18:51:00.000+02:00</published><updated>2007-09-28T18:55:24.423+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>MS LEAP Programme, yet another rant.</title><content type='html'>A while ago, I received an e-mail with an invitation to take part in the &lt;a href="http://www.microsoft.com/belux/msdn/nl/leap/default.mspx"&gt;MS LEAP programme&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This looked very interesting, so I asked my employer if I could take that course and if my employer could help me bear the costs.&lt;br /&gt;A few days later, I received the answer that I could and that they (the company where I work) would bear the majority of the cost.  \o/&lt;br /&gt;&lt;br /&gt;Now, today, I wanted to subscribe but unfortunately, the program is already fully booked. :(&lt;br /&gt;&lt;br /&gt;&lt;sub&gt;The &lt;a href="http://fgheysels.blogspot.com/2007/09/auto-formatting-in-vsnet-2005-does-not.html"&gt;ranting&lt;/a&gt; doesn't stop. :( &lt;/sub&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-577345825227262783?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/577345825227262783/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=577345825227262783' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/577345825227262783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/577345825227262783'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/09/ms-leap-programme-yet-another-rant.html' title='MS LEAP Programme, yet another rant.'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1372624998117639578</id><published>2007-09-27T22:09:00.000+02:00</published><updated>2007-09-27T22:17:09.096+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>Auto-Formatting in VS.NET 2005 does not work anymore</title><content type='html'>&lt;p&gt;After a harddisk-crash, I had to reinstall Visual Studio.NET 2005.&lt;br /&gt;As I started programming again after it was re-installed, I noticed that the 'auto-formatting' function of VS.NET didn't work anymore ...&lt;/p&gt;&lt;p&gt;This is very strange, and very annoying once you're used to it.  I've verified my options, and they're set the way I want too.   &lt;br /&gt;The options:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Automatically format completed statement on;&lt;/li&gt;&lt;li&gt;Automatically format completed block on }&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;are enabled.   However, when I type a ; or a }, the code is not being formatted at all...&lt;br /&gt;When I use the shortcut 'Ctrl + E, D (format document)', my current document is formatted.&lt;/p&gt;&lt;p&gt;I've no idea why this doesn't work anymore.  I've just made a fresh install of VS.NET.  &lt;br /&gt;Is there anybody who has experienced this 'annoyance' as well, and if so, how did you fix it ?&lt;br /&gt;In other words: if there's anybody out there who knows how to fix it, please let me know. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1372624998117639578?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1372624998117639578/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1372624998117639578' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1372624998117639578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1372624998117639578'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/09/auto-formatting-in-vsnet-2005-does-not.html' title='Auto-Formatting in VS.NET 2005 does not work anymore'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1389662496411222375</id><published>2007-08-13T20:30:00.001+02:00</published><updated>2007-08-13T20:49:31.984+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>Cleaning a Keyboard</title><content type='html'>&lt;p&gt;My keyboard is about 5 years old, and it has been used extensively.  During these years, it has become more and more filthy.&lt;br /&gt;Now, I thought the time has come to do something about it.  Basically, I had two options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Buy a new keyboard&lt;/li&gt;&lt;li&gt;Clean my keyboard&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I thought the second option was more of a challenge then just buying a new one.&lt;br /&gt;So, this is what I did.&lt;/p&gt;&lt;p&gt;To give you an idea of how filthy my keyboard actually was, I've made a picture of it:&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;img src="http://users.pandora.be/fgzone/blog/IMG_3915.jpg" alt="filthy keyboard" /&gt;&lt;/p&gt;&lt;p&gt;After I took a picture of the layout of my keyboard, I removed all keys from the board.  You can imagine that the picture I've taken comes in quite handy when I wanted to place the keys back.&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;img src="http://users.pandora.be/fgzone/blog/IMG_3918.jpg" alt="removing keys from keyboard" /&gt;&lt;/p&gt;&lt;p&gt;I've put all the keys in a bucket of water, and cleaned them.&lt;br /&gt;After removing all the keys, the keyboard looked like this:&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;img src="http://users.pandora.be/fgzone/blog/IMG_3923.jpg" alt="stripped keyboard" /&gt;&lt;/p&gt;&lt;p&gt;Hmm, this contains a lot of filth as well.  I made good use of the vacuum-cleaner to remove most of it.&lt;br /&gt;Then, when all the cleaned keys were dry again, I could put all the keys back to where they belong.  The result: a clean keyboard:&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;img src="http://users.pandora.be/fgzone/blog/IMG_3932.jpg" alt="clean keyboard" /&gt;&lt;/p&gt;&lt;p&gt;It was quite some work, but, devving with a clean keyboard is much nicer then using a dirty one. ;)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1389662496411222375?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1389662496411222375/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1389662496411222375' title='5 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1389662496411222375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1389662496411222375'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/08/cleaning-keyboard.html' title='Cleaning a Keyboard'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-7392965390654199031</id><published>2007-08-08T19:37:00.000+02:00</published><updated>2007-08-08T19:43:41.332+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>C# 3.0: new language features</title><content type='html'>&lt;p&gt;I've already written &lt;a href="http://fgheysels.blogspot.com/2006/01/c-30-orcas-playing-with-linq.html"&gt;a blogpost&lt;/a&gt; regarding LINQ and new language features that we may expect in C# 3.0 a while ago.&lt;br /&gt;&lt;a href="http://blogs.zdnet.com/microsoft/?p=564"&gt;Since the release of VS.NET 2008 and thereby C# 3.0 is coming closer and closer&lt;/a&gt;, I was planning to write an updated blogpost about the new language features in C# 3.0.&lt;/p&gt;&lt;p&gt;However, I've noticed that John Papa has already written an excellent article already concerning the language enhancements in .NET 3.5, so I've decided to just link to that article:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.simple-talk.com/dotnet/.net-framework/.net-3.5-language-enhancements/"&gt;.NET 3.5 Language Enhancements&lt;/a&gt;&lt;br /&gt;That's much easier for me, and it saves me some time. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-7392965390654199031?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/7392965390654199031/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=7392965390654199031' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7392965390654199031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7392965390654199031'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/08/c-30-new-language-features.html' title='C# 3.0: new language features'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-9189109606614875217</id><published>2007-08-04T10:59:00.000+02:00</published><updated>2007-08-04T11:11:04.843+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>XSD Schema for configuration of Log4Net</title><content type='html'>&lt;p&gt;A few days ago, I was playing around with &lt;a href="http://logging.apache.org/log4net/"&gt;log4net&lt;/a&gt;, the open source framework that allows you to easily log messages in an application.&lt;/p&gt;&lt;p&gt;Since log4net is very configurable, I wanted to have Intellisense in Visual Studio.NET when I'm editing the log4net configuration. &lt;br /&gt;Therefore, I need a schema definition for the log4net XML configuration file so that I can set up the Intellisense for the log4net configuration as I did &lt;a href="http://fgheysels.blogspot.com/2006/04/net-20-could-not-find-schema.html"&gt;here&lt;/a&gt; for NHibernate.&lt;/p&gt;&lt;p&gt;Unfortunately, it seems that the source of log4net doesn't contain such an XSD.&lt;br /&gt;So, before I take the time and effort to create such an XSD, I wonder if anyone has already made such an XSD for log4net and is willing to share it. :) &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-9189109606614875217?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/9189109606614875217/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=9189109606614875217' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/9189109606614875217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/9189109606614875217'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/08/xsd-schema-for-configuration-of-log4net.html' title='XSD Schema for configuration of Log4Net'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-3919256655587944349</id><published>2007-07-27T20:39:00.001+02:00</published><updated>2007-07-27T21:09:19.334+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>NHibernate &amp; MS Access: problems with autonumber fields</title><content type='html'>&lt;p&gt;I was playing around with NHibernate and MS Access a bit the other day, and I ran into an annoying problem.&lt;br /&gt;&lt;br /&gt;When I tried to save an object which maps to a table wich has an 'autonumber' field, I received this error:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;a different object with the same identifier value was already associated with the session: 0&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;It looked like NHibernate was unable to retrieve the ID that has been given to the new record in the database.   &lt;br /&gt;&lt;br /&gt;So, I decided to set up &lt;a href="http://logging.apache.org/log4net/"&gt;log4net&lt;/a&gt; to see if I could find any clue in the logs about the reason for this problem.&lt;/p&gt;&lt;p&gt;As it turned out, it seems that NHibernate was closing the database-connection between the &lt;code&gt;INSERT&lt;/code&gt; and the &lt;code&gt;SELECT @@identity&lt;/code&gt; statements.&lt;/p&gt;&lt;pre class="code-content"&gt;DEBUG NHibernate.SQL - INSERT INTO table ...&lt;br /&gt;DEBUG NHibernate.Connection.DriverConnectionProvider - Obtaining &lt;br /&gt;IDbConnection from Driver&lt;br /&gt;DEBUG NHibernate.Impl.BatcherImpl - Closed IDbCommand, open IDbCommands: 0&lt;br /&gt;DEBUG NHibernate.Impl.ConnectionManager - aggressively releasing &lt;br /&gt;database connection&lt;br /&gt;DEBUG NHibernate.Connection.ConnectionProvider - Closing connection&lt;br /&gt;DEBUG NHibernate.Impl.BatcherImpl - Opened new IDbCommand, open IDbCommands: 1&lt;br /&gt;DEBUG NHibernate.Impl.BatcherImpl - Building an IDbCommand object for &lt;br /&gt;the SqlString: select @@identity&lt;br /&gt;DEBUG NHibernate.SQL - select @@identity&lt;/pre&gt;&lt;p&gt;After some research in the Hibernate documentation and on the Internet, I discovered that this is caused by the 'connection release mode'.&lt;br /&gt;&lt;br /&gt;The default connection release mode seems to release the connection after each SQL statement, so obviously, Access is not able to determine the correct ID of the last inserted record, since the &lt;code&gt;select @@identity&lt;/code&gt; command must be executed on the same connection as its related &lt;code&gt;INSERT&lt;/code&gt; statement.&lt;/p&gt;&lt;p&gt;Now that we know what caused this problem, it is easy to fix it: just make sure that you do not use the default connection release mode when using NHibernate with MS Access.  &lt;br /&gt;&lt;br /&gt;To do so, you should specify the connection release mode in your NHibernate configuration file like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&amp;lt;hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"&amp;gt;&lt;br /&gt;  &amp;lt;session-factory&amp;gt;&lt;br /&gt;    &amp;lt;property name="hibernate.connection.release_mode"&amp;gt;on_close&amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;/session-factory&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;&lt;p&gt;More information regarding the different options / settings for the connection-release mode can be found &lt;a href="http://www.hibernate.org/hib_docs/reference/en/html/transactions.html"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-3919256655587944349?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/3919256655587944349/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=3919256655587944349' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3919256655587944349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/3919256655587944349'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/07/nhibernate-ms-access-problems-with.html' title='NHibernate &amp; MS Access: problems with autonumber fields'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-5134078156605231134</id><published>2007-07-18T19:08:00.000+02:00</published><updated>2007-07-18T19:23:16.644+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Counting in SQL Server: SELECT COUNT(*) != SELECT COUNT(columnname)</title><content type='html'>&lt;p&gt;Today, I've learned something new; or rather, I was being pointed out something that I didn't know about:&lt;br /&gt;It seems that, doing a &lt;code&gt;SELECT COUNT(columnname)&lt;/code&gt; does not always give you the same results as doing a &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;.   As it was pointed out to me, doing a &lt;code&gt;SELECT COUNT(columnname)&lt;/code&gt; returns the number of rows in the resultset in where the value of the field in 'columnname' is NOT NULL.&lt;/p&gt;&lt;p&gt;This means that, given the following set of data:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;id&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;NULL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Anothername&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;performing this statement:&lt;pre class="code-content"&gt;SELECT COUNT(*) FROM table&lt;/pre&gt;gives us 3 as a result, while doing this:&lt;pre class="code-content"&gt;SELECT COUNT(name) FROM table&lt;/pre&gt; returns 2 as result.&lt;/p&gt;&lt;p&gt;I really was ignorant to this behaviour, as I do (or did), most of the time a &lt;code&gt;SELECT COUNT(1) FROM table&lt;/code&gt; instead, since I thought that this was the most performant option, but this also seems to be not true in some circumstances; &lt;br /&gt;doing a SELECT COUNT(*) enables SQL Server to use indexes in calculating the number of results in a resultset.&lt;/p&gt;&lt;p&gt;For a more thourough article about this, I'd like to refer to &lt;a href="http://www.sqlservercentral.com/columnists/chedgate/adviceoncount_printversion.asp" target="_blank"&gt;this article on SQL Server Central&lt;/a&gt;.&lt;p&gt;&lt;small&gt;Thanks to Peter De Boer for pointing this out&lt;/small&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-5134078156605231134?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/5134078156605231134/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=5134078156605231134' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5134078156605231134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5134078156605231134'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/07/counting-in-sql-server-select-count.html' title='Counting in SQL Server: SELECT COUNT(*) != SELECT COUNT(columnname)'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-121450154349243613</id><published>2007-06-18T20:42:00.000+02:00</published><updated>2007-06-18T22:17:24.643+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Dependency Injection versus Service Locators</title><content type='html'>&lt;p&gt;When I was driving home from work, I have been thinking:  when an object (entity) needs a reference to a repository for instance, to get some work done, how do you get a reference to that dependency (in this case, the repository) ? &lt;br /&gt;Do you use dependency injection, or do you use a service locator ?   When to choose what ? What are the advantages / disadvantages over both approaches ?&lt;/p&gt;&lt;p&gt;For example, suppose you have a 'Customer' class; this class has a property 'Status' which says whether the Customer is a 'Gold Customer', a 'Badly Paying Customer' or a 'Normal Customer'.&lt;br /&gt;In order to determine to which 'state' a specific Customer belongs to, it might be necessary to retrieve some information out of a datastore.  In this case, it could be necessary for instance to retrieve some invoice information regarding this customer.&lt;br /&gt;(A Gold Customer for instance, is a Customer who has ordered for a specific amount of goods in a certain period, while a Bad Payer might be some-one who has overdue invoices).&lt;br /&gt;So, a reference to an instance of IInvoiceRepository must be given to the Customer object.&lt;br /&gt;The question now is, how do you give this dependency to the Customer object ? &lt;br /&gt;&lt;br /&gt;Do you use 'Dependency Injection', and give this dependency via a constructor or a property to the Customer instance, like this:&lt;/p&gt;&lt;pre class="code-content"&gt;Customer c = customerRepository.GetCustomer (5);&lt;br /&gt;c.InvoiceRepository = repositoryFactory.CreateInvoiceRepository();&lt;/pre&gt;&lt;p&gt;Or, should you leave this responsability to the Customer object ?   In other words: let the Customer instance search for the correct IInvoiceRepository using a service locator.  This could then be done in the &lt;code&gt;Status&lt;/code&gt; property of the Customer class:&lt;/p&gt;&lt;pre class="code-content"&gt;public CustomerStatus Status&lt;br /&gt;{&lt;br /&gt;   get&lt;br /&gt;   {&lt;br /&gt;      IInvoiceRepository ir = DomainSettings.Instance.RepositoryLocator.GetInvoiceRepository();&lt;br /&gt;      ...&lt;br /&gt;      return status;&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In this case, the 'RepositoryLocator' can decide which implementation of IInvoiceRepository it has to create and return based on a value in a config file for instance.&lt;/p&gt;&lt;p&gt;Anyway, when do you choose for which option ? What are the advantages  /disadvantages of both concepts ? In which situation do you favor the one over the other ? &lt;/p&gt;&lt;p&gt;I hope that some smart people can enlighten me about this matter. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-121450154349243613?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/121450154349243613/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=121450154349243613' title='11 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/121450154349243613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/121450154349243613'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/06/dependency-injection-versus-service.html' title='Dependency Injection versus Service Locators'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-4659016977934479391</id><published>2007-04-30T22:33:00.000+02:00</published><updated>2007-04-30T23:56:52.235+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>NHibernate Session Management in Domain Driven App's</title><content type='html'>&lt;p&gt;&lt;a href="http://fgheysels.blogspot.com/2007/04/article-nhibernate-best-practices-with.html"&gt;As  I've posted earlier&lt;/a&gt;, Billy McCafferty has written an excellent article on using NHibernate within ASP.NET applications.&lt;br /&gt;In this article, he describes how to use the 'Open Session in View' pattern.  This is perfectly useable and a very clean way of working with NHibernate in a DDD ASP.NET application, unfortunately, it is -imho- less (or not) useable in a WinForms application.&lt;br /&gt;The 'Open Session in View' pattern implies that a new NHibernate ISession object is created at each request, and closed at the end of each request.  (Changes are committed as well).&lt;/p&gt;&lt;p&gt;It is very important that the Repositories keep their hands off of Transaction Management.  In other words: a repository should not start, commit or rollback a transaction.  This is natural, since the repository doesn't know anything of the 'context' of the Unit of Work.  Therefore, transaction managment should be a responsability of the client (the UI, the Service layer, whatever).&lt;br /&gt;To achieve this, Billy stores the current ISession in the CallContext or HttpContext (he has made abstraction of which implementation is used offcourse.  CallContext is used in the case of a WinForms app or a Unit-Test project, HttpContext is used in case of an ASP.NET app; see the source-code that accompagnies the article for more details).&lt;br /&gt;In this way, the repositories can easily access the current ISession when they need it by just getting it out of the CallContext or HttpContext, and the ASP.NET application (the ASP.NET webpages) have access to the ISession as well, so they can start, commit and rollback the unit-of-work.&lt;br /&gt;This is the ideal solution in ASP.NET, since you only need one 'ISession' on each request.  At the end of the request, the Session can be closed (committed / rollbacked), so they're short-lived, and this is perfect.&lt;/p&gt;&lt;p&gt;However, in a WinForms application, you'll probably want more then one 'Unit of Work' at the same time, and therefore, have more then one open ISession at the same time.&lt;br /&gt;An example will clarify this a bit more:&lt;/p&gt;&lt;p&gt;Suppose you've a WinForms application which consists of a form which lists all Customers.  You can edit a customer by double-clicking it; a 'Customer-Detail' form will open up, where you can make changes to the Customer.&lt;/p&gt;&lt;p&gt;Now, what happens if you edit a Customer ? You'll retrieve the ISession from the CallContext, load the Customer from the DB, make some changes and confirm the changes; closing the form will close the ISession.&lt;br /&gt;So far, no problem.   However, what happens if you open another Customer-Detail form before you've committed the changes to the first Customer ? &lt;br /&gt;What if you have 2 Customer Detail Forms open, make changes to Customer 1, save the changes and close the form.  Afterwards, you want to save the changes to Customer 1... The problem now is that you've closed the ISession when you've closed the first Customer-Detail form.  Customer 2 was attached to the same session which is now closed.  If you want to save the changes to customer 2, you'll need an ISession, so you'll require a new ISEssion in this case.  Then, NHibernate will think that Customer 2 is a new Customer, and will try to insert a new record to the DB where in fact, the existing Customer needs to be updated.&lt;/p&gt;&lt;p&gt;In my opinion, it is better to have a seperate Unit Of Work (ISession) for each instance of the CustomerDetail form in this case.  &lt;br /&gt;This implies that you should create a new ISession for each instance of the CustomerDetail form: this means that the ISession cannot be kept in the CallContext since otherwise, each form would share the same ISession.&lt;br /&gt;Then... how should this be solved ? In my humble opinion, the best way to solve this is to create an ISession when an instance of a form is created, pass it to the repositories, and use it to handle the transaction(s) in the form:&lt;/p&gt;&lt;pre class="code-content"&gt;public class CustomerDetailForm : Form&lt;br /&gt;{&lt;br /&gt;   private ISession _unitOfWork;&lt;br /&gt;&lt;br /&gt;   private CustomerRepository _custRep;&lt;br /&gt;&lt;br /&gt;   private Customer _customer;&lt;br /&gt;&lt;br /&gt;   public void ShowCustomer( int customerId )&lt;br /&gt;   {&lt;br /&gt;       _unitOfWork = AppSettings.Instance.SessionFactoryObj.OpenSession();&lt;br /&gt;       &lt;br /&gt;       _custRep = new CustomerRepository(_unitOfWork);&lt;br /&gt;&lt;br /&gt;       _customer = _custRep.GetCustomer(customerId);&lt;br /&gt;&lt;br /&gt;       // Disconnect from the DB, since we do not need DB action at this time &lt;br /&gt;       // anymore.&lt;br /&gt;       _unitOfWork.Disconnect();&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void OKButton_Click(object sender, EventArgs e )&lt;br /&gt;   {&lt;br /&gt;      // Reconnect to the DB&lt;br /&gt;      _unitOfWork.Reconnect();&lt;br /&gt;&lt;br /&gt;      ITransaction tx = _unitOfWork.BeginTransaction();&lt;br /&gt;      _custRep.Save (_customer);&lt;br /&gt;      tx.Commit();&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I don't know if there's any better / cleaner way of doing this... If you know one, please let me know. :) It is offcourse cleaner if you could abstract the use of NHibernate's ISession by creating a class which encapsulates the ISession, but I wanted to keep things a bit sparse. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-4659016977934479391?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/4659016977934479391/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=4659016977934479391' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4659016977934479391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4659016977934479391'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/04/nhibernate-session-management-in-domain.html' title='NHibernate Session Management in Domain Driven App&apos;s'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-4446796392668297299</id><published>2007-04-27T19:20:00.000+02:00</published><updated>2007-04-27T19:27:59.544+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>Article: NHibernate best practices with ASP.NET</title><content type='html'>&lt;p&gt;&lt;a href="http://devlicio.us/blogs/billy_mccafferty/default.aspx"&gt;Billy McCafferty&lt;/a&gt; has written an excellent article on NHibernate and Domain Driven Design.  It can be found &lt;a href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp"&gt;here&lt;/a&gt; on the CodeProject.&lt;br /&gt;Although it is focused on using NHibernate in ASP.NET, it is still very interesting even if you do not develop in ASP.NET.  He talks about a few key concepts in DDD, and the example code is also very interesting.&lt;br /&gt;There's one little drawback however in his example code: although he mentions that the way of working he demonstrates should work for Winforms development as well, I do not completely agree on that.   However, I've to investigate it a bit more, which I'll do as soon as I find some time.   Once done, I'll post my findings offcourse.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-4446796392668297299?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/4446796392668297299/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=4446796392668297299' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4446796392668297299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/4446796392668297299'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/04/article-nhibernate-best-practices-with.html' title='Article: NHibernate best practices with ASP.NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-121557224585504107</id><published>2007-04-22T13:19:00.000+02:00</published><updated>2007-04-22T14:36:08.773+02:00</updated><title type='text'>Split</title><content type='html'>&lt;p&gt;When I started this weblog in december 2005, I decided to blog about software development and about photography.&lt;/p&gt;&lt;p&gt;Now, 1 and a half year later, it seems to me that this was not such a good idea; I feel that software engineering and photography do not share the same audience.  &lt;br /&gt;Now, you probably think, did it really take you that long to acknowledge this fact ?  Well, not really: during the first few months of blogging, the number of photography posts here was pretty low.  At this time, I've noticed that 3 out of the 5 last posts were tagged with 'photography', so that's why I've finally decided to split things up.&lt;/p&gt;&lt;p&gt;This will remain my weblog that is focussed on programming (notice that I've changed the name from 'weblog' to 'devlog' as well :) ).  Things that are focused on photography will appear on &lt;a href="http://frederikgheysels.wordpress.com"&gt;my photolog&lt;/a&gt;, so if you're interested, you can have a look there :).&lt;/p&gt;&lt;p&gt;I've choosen Wordpress to host my photo-blog, since Wordpress allows me to import posts from blogger, and that's what I've done: I've imported all the 'photography-tagged' posts from this weblog in my wordpress photo-blog, and translated them to dutch.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-121557224585504107?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/121557224585504107/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=121557224585504107' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/121557224585504107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/121557224585504107'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/04/split.html' title='Split'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-7508856415597441226</id><published>2007-04-13T22:33:00.000+02:00</published><updated>2007-04-13T23:37:00.356+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>.NET 1.1: Problems when selecting rows from a DataTable using an aggregate filter</title><content type='html'>&lt;p&gt;I came accross a rather weird error today when I was working in C# 1.1.  I was trying to select the records that exist in a DataTable that had more then one child in a related datatable.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To make things a little bit more clear, I've reproduced this by creating a Typed Dataset which contains 2 tables from the Northwind database:&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/northwind_ds.JPG" /&gt;&lt;br /&gt;&lt;p&gt;What I want to do, is to select the Categories for which there are multiple products classified into.&lt;br /&gt;So, this is what I've done; I created an instance of this dataset, and populated it with some data:&lt;/p&gt;&lt;pre class="code-content"&gt;NorthwindDS ds = new NorthwindDS();&lt;br /&gt;NorthwindDS.CategoriesRow c1 = ds.Categories.NewCategoriesRow();&lt;br /&gt;c1.CategoryID = 1;&lt;br /&gt;c1.CategoryName = "Category 1";&lt;br /&gt;ds.Categories.AddCategoriesRow (c1);&lt;br /&gt;&lt;br /&gt;NorthwindDS.CategoriesRow c2 = ds.Categories.NewCategoriesRow();&lt;br /&gt;c2.CategoryID = 2;&lt;br /&gt;c2.CategoryName = "Category 2";&lt;br /&gt;ds.Categories.AddCategoriesRow (c2);&lt;br /&gt;&lt;br /&gt;NorthwindDS.ProductsRow p1 = ds.Products.NewProductsRow();&lt;br /&gt;p1.ProductID = 1;&lt;br /&gt;p1.CategoryID = 1;&lt;br /&gt;p1.ProductName = "Product 1";&lt;br /&gt;ds.Products.AddProductsRow (p1);&lt;br /&gt;&lt;br /&gt;NorthwindDS.ProductsRow p2 = ds.Products.NewProductsRow();&lt;br /&gt;p2.ProductID = 2;&lt;br /&gt;p2.CategoryID = 1;&lt;br /&gt;p2.ProductName = "Product 2";&lt;br /&gt;ds.Products.AddProductsRow (p2);&lt;br /&gt;&lt;br /&gt;// The following line is important to illustrate the problem :)&lt;br /&gt;ds.AcceptChanges();&lt;/pre&gt;&lt;p&gt;This is pretty simple, I've just populated the Dataset with 2 categories and 2 products, and the 2 products are both linked to Category 1.&lt;br /&gt;Now, the Query I've talked about earlier should thus return 1 Category, specifically, Category 1, since there's more then one product related to this category.&lt;br /&gt;I do this using the following code:&lt;/p&gt;&lt;pre class="code-content"&gt;string filterExpression = "COUNT (Child.CategoryID) &gt; 1";&lt;br /&gt;DataRow[] dr = ds.Categories.Select (filterExpression);&lt;br /&gt;&lt;br /&gt;foreach( NorthwindDS.Category cat in dr )&lt;br /&gt;{&lt;br /&gt;   Console.WriteLine (cat.CategoryName);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This gives me the expected result, it writes 'Category 1' to the output-window.&lt;br /&gt;However, suppose that I've populated this Dataset with data coming from the Database, and that I add a product - record to this Dataset &lt;i&gt;after&lt;/i&gt; the Dataset has been populated...  &lt;br /&gt;I can simulate this by adding a ProductRow to the DataSet after the 'AcceptChanges()' call:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;// previous lines which create the dataset, and add the categories&lt;br /&gt;// and the first 2 products are left out for brevity&lt;br /&gt;ds.AcceptChanges();&lt;br /&gt;&lt;br /&gt;NorthwindDS.ProductsRow p3 = ds.Products.NewProductsRow();&lt;br /&gt;p2.ProductID = 3;&lt;br /&gt;p2.CategoryID = 1;&lt;br /&gt;p2.ProductName = "Product 3";&lt;br /&gt;ds.Products.AddProductsRow (p3);&lt;br /&gt;&lt;br /&gt;filterExpression = "COUNT (Child.CategoryID) &gt; 1";&lt;br /&gt;DataRow[] dr = ds.Categories.Select (filterExpression);&lt;br /&gt;&lt;br /&gt;foreach( NorthwindDS.Category cat in dr )&lt;br /&gt;{&lt;br /&gt;   Console.WriteLine (cat.CategoryName);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now, I would expect that this gives me the exact same result as before, however, I get an exception:&lt;/p&gt;&lt;p&gt;&lt;quote&gt;An unhandled exception of type 'System.Data.VersionNotFoundException' occurred in system.data.dll&lt;br /&gt;&lt;br /&gt;Addition information: There is no original data to access.&lt;/quote&gt;&lt;p&gt;Probably, .NET wants to access the original contents of my new ProductRow, but since it is a new one, it has no Original DataViewRowState ...&lt;br /&gt;I've tried to work around this by specifying that the CurrentRows should be used:&lt;pre class="code-content"&gt;ds.Categories.Select (filterExpression, &lt;br /&gt;                      string.Empty, &lt;br /&gt;                      DataViewRowState.CurrentRows);&lt;/pre&gt;&lt;p&gt;But, to no avail. (Which didn't surprise me, because this line just says: return the Current DataViewRowState of the Categories and the problem seems to occur when the Product datarows are accessed).&lt;/p&gt;&lt;p&gt;So, this seems to be a bug in the .NET 1.1 (I've tried this in .NET 2.0 as well, and there, it works like a charm).&lt;/p&gt;&lt;p&gt;So, is there anybody who knows how to solve or work around this issue ?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-7508856415597441226?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/7508856415597441226/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=7508856415597441226' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7508856415597441226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/7508856415597441226'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/04/net-11-problems-when-selecting-rows.html' title='.NET 1.1: Problems when selecting rows from a DataTable using an aggregate filter'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-8559598721197201369</id><published>2007-04-01T12:37:00.001+02:00</published><updated>2007-04-01T12:41:43.748+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Tagliatelle - scampi</title><content type='html'>&lt;center&gt;&lt;img src="http://users.pandora.be/fgzone/blog/tagliatellescampi.jpg" alt="Tagliatelle - Scampi" /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-8559598721197201369?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/8559598721197201369/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=8559598721197201369' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8559598721197201369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/8559598721197201369'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/04/tagliatelle-scampi.html' title='Tagliatelle - scampi'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-5642111641251967169</id><published>2007-03-09T21:01:00.000+01:00</published><updated>2007-03-09T21:33:30.131+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Oudenaarde by Night</title><content type='html'>Yesterday evening, I've been walking around in Oudenaarde - the city were I'll be living in in the near future -, and I've taken these photographs.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH_3490.jpg" alt="oudenaarde stadhuis" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH_3496.jpg" alt="oudenaarde walburga carillon" /&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-5642111641251967169?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/5642111641251967169/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=5642111641251967169' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5642111641251967169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5642111641251967169'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/03/oudenaarde-by-night.html' title='Oudenaarde by Night'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-2502678195128959510</id><published>2007-02-25T12:02:00.001+01:00</published><updated>2007-02-25T12:03:08.321+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>DDD: implementing persistence using NHibernate</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;At last, I've found some time to sit down and begin writing what should be the last part of this 'Domain Driven Design Quickstart' series of articles.  In this final article, I will implement the persistence of the domain model that I’ve created in the previous parts of this series.  (&lt;a href="http://fgheysels.blogspot.com/2006/05/domain-driven-design-quickstart-part-1.html"&gt;part I&lt;/a&gt;, &lt;a href="http://fgheysels.blogspot.com/2006/07/ddd-quickstart-implementation-using.html"&gt;part II&lt;/a&gt; and &lt;a href="http://fgheysels.blogspot.com/2006/08/ddd-quickstart-part-iii_19.html"&gt;part III&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;To implement the persistence functionality of the domain model, I will use an &lt;a href="http://en.wikipedia.org/wiki/Object-relational_mapping"&gt;O/R mapper&lt;/a&gt;.  I’ve opted to use &lt;a href="http://www.nhibernate.org"&gt;NHibernate&lt;/a&gt; as O/R mapper for this project.&lt;br /&gt;I will not make an in-depth coverage of NHibernate in this article, since this is a bit beyond the scope of this article.  Besides, there are various excellent resources on (N)Hibernate available.  The book &lt;a href="http://www.amazon.com/gp/product/193239415X?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=193239415X"&gt;Hibernate in Action&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=193239415X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;, for instance, is an excellent resource to learn about (N)Hibernate, and it offers also some more insights in the O/R principle.&lt;/p&gt;&lt;br /&gt;&lt;!--adsense--&gt;&lt;h2&gt;The Database Schema&lt;/h2&gt;&lt;p&gt;To get the information in a database, we need a database first (clever :) ).  So, I’ve created this ERD:&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/ddd-erd.jpg" /&gt;&lt;br /&gt;&lt;p&gt;I’m not going to elaborate on this database-schema since I presume that you have enough knowledge of databases / database-modelling, etc…  So lets hop to the next phase.&lt;/p&gt;&lt;h2&gt;NHibernate configuration&lt;/h2&gt;&lt;p&gt;NHibernate is an O/R mapper which lets you persist your data to a number of supported databases.   To be able to use it however, it needs to be setup properly; in .NET this can be easily done in the configuration file of your project.&lt;br /&gt;For this project, I’ll create a WinForms application, and I’ll make some changes to the app.config file to configure NHibernate.   This is done like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;  &amp;lt;configSections&amp;gt;&lt;br /&gt;    &amp;lt;section name="nhibernate"&lt;br /&gt;             type="System.Configuration.NameValueSectionHandler, &lt;br /&gt;                   System, Version=1.0.5000.0,Culture=neutral, &lt;br /&gt;                   PublicKeyToken=b77a5c561934e089"/&amp;gt;&lt;br /&gt;  &amp;lt;/configSections&amp;gt;&lt;br /&gt;  &amp;lt;nhibernate&amp;gt;&lt;br /&gt;    &amp;lt;add key="hibernate.connection.provider"&lt;br /&gt;         value="NHibernate.Connection.DriverConnectionProvider"/&amp;gt;&lt;br /&gt;    &amp;lt;add key="hibernate.dialect"&lt;br /&gt;         value="NHibernate.Dialect.MsSql2000Dialect"/&amp;gt;&lt;br /&gt;    &amp;lt;add key="hibernate.connection.connection_string"&lt;br /&gt;         value="Data Source=localhost;&lt;br /&gt;                Initial Catalog=SomeShop;&lt;br /&gt;                Integrated Security=SSPI"/&amp;gt;&lt;br /&gt;  &amp;lt;/nhibernate&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;/pre&gt;&lt;p&gt;As you can see, I need to define the nhibernate configuration section first.  Once this is done, I specify to which type of database NHibernate will have to talk to (SQL Server 2000), and I also specify the connection string that must be used.&lt;/p&gt;&lt;p&gt;If you use Microsoft Visual Studio.NET 2005, you might come across some unwanted error/information messages concerning the nhibernate config section.  I’ve written another post about this problem, and how to solve it a while ago.  You can read it &lt;a href="http://fgheysels.blogspot.com/2006/04/net-20-could-not-find-schema.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;!--adsense--&gt;&lt;p&gt;Now we’ve configured our project to use NHibernate, but we also need to tell NHibernate how our classes should be mapped to our database-schema.  This can be done by creating NHibernate mapping files which describe how a class should be represented in the database.  Those mapping files are nothing more then XML files.  As I’ve said earlier, this is not an NHibernate tutorial, but merely an article on how NHibernate can be incorporated in a Domain Driven Design project.  Therefore, I will not post all the mapping files here or explain them in detail.  I will post 2 mapping files here however, and explain them a bit.&lt;/p&gt;&lt;h3&gt;NHibernate mapping files&lt;/h3&gt;&lt;p&gt;For each class in my domain model, I create an NHibernate mapping file in my Visual Studio.NET project which contains my business classes.&lt;br /&gt;In this specific case, I have a Class Library project which contains the ‘Customer’, ‘Order’, ‘OrderLine’, etc… business classes, so it is in this class library that I also create the NHibernate mapping files.&lt;/p&gt;&lt;p&gt;The mapping file for the Order class, for instance, looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&amp;lt;xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;br /&gt;&amp;lt;hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"&amp;gt;&lt;br /&gt;  &amp;lt;class name="SomeShopDomain.Order, SomeShopDomain" &lt;br /&gt;         table="[Order]" lazy="false"&amp;gt;&lt;br /&gt;    &amp;lt;id name="Id" column="OrderId" type="Int32" &lt;br /&gt;        unsaved-value="-1" &lt;br /&gt;        access="field.camelcase-underscore"&amp;gt;&lt;br /&gt;      &amp;lt;generator class="identity" /&amp;gt;&lt;br /&gt;    &amp;lt;/id&amp;gt;&lt;br /&gt;    &amp;lt;property name="OrderDate" column="OrderDate" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="Status" column="OrderStatus" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="Discount" column="Discount" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;many-to-one name="OwningCustomer" column="CustomerId" &lt;br /&gt;                 class="SomeShopDomain.Customer, SomeShopDomain"&lt;br /&gt;                 not-null="true" &lt;br /&gt;                 access="field.camelcase-underscore"&lt;br /&gt;                 /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;bag name="OrderLines"  cascade="save-update" &lt;br /&gt;                            inverse="true" &lt;br /&gt;                            access="field.camelcase-underscore"&amp;gt;&lt;br /&gt;      &amp;lt;key column="OrderId" /&amp;gt;&lt;br /&gt;      &amp;lt;one-to-many class="SomeShopDomain.OrderLine, SomeShopDomain"/&amp;gt;&lt;br /&gt;    &amp;lt;/bag&amp;gt;&lt;br /&gt;    &lt;br /&gt;  &amp;lt;/class&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;/pre&gt;&lt;p&gt;This is not the most simple mapping-file in the project, since the &lt;code&gt;Order&lt;/code&gt; class contains a collection of &lt;code&gt;OrderLines&lt;/code&gt;, so this relationship is reflected in the mapping file.&lt;/p&gt;&lt;p&gt;But, it is maybe better if I just explain the complete mapping file a bit .&lt;/p&gt;&lt;p&gt;As you can see at the beginning of the file, this file describes how the &lt;code&gt;Order&lt;/code&gt; class should be mapped to the Order table in the database.&lt;br /&gt;The 'id' element tells NHibernate that the Id property of the class should be used to identify each Order.  This element also defines that the &lt;code&gt;Id&lt;/code&gt; property of the class maps to the OrderId column in the Order table.  The data-type is also defined in there, and it is stated that instances of the &lt;code&gt;Order&lt;/code&gt; class that have a value of -1 for the Id property, are New/&lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html"&gt;transcient &lt;/a&gt;(unsaved).&lt;br /&gt;Now, NHibernate will use the &lt;code&gt;Id&lt;/code&gt; property of the &lt;code&gt;Order&lt;/code&gt; class to get or set the value of the Id.  &lt;br /&gt;This however, has some disadvantages in my opinion: what if you have some specific logic in the setter-part of the property, some validation-logic for instance ? When NHibernate loads an object out of the database, should this logic be executed ? In my humble opinion not.  When an object is loaded from a persistent state, the values of the object must be set, without performing any extra logic.&lt;br /&gt;Another problem pops up when the property is read-only.  In this case, &lt;code&gt;Id&lt;/code&gt; is an 'identification', and the programmer that uses the &lt;code&gt;Order&lt;/code&gt; class, shouldn't be able to change this property.  Therefore, this property has only a getter in the &lt;code&gt;Order&lt;/code&gt; class, and no setter.  NHibernate will have a problem with that:  it will throw an exception saying that it cannot set the value of the Id property since it has no setter.&lt;br /&gt;That's why I use the &lt;code&gt;access&lt;/code&gt; attribute.  With this attribute, I tell NHibernate that it should use the field instead of the property, and that it can find this field by taking the property-name that I've defined in the mapping file, prefix it with an underscore, and make the first character lowercase.  &lt;br /&gt;So, in this case, instead of setting the Id property, NHibernate will look for a field in the &lt;code&gt;Order&lt;/code&gt; class which is called &lt;code&gt;_id&lt;/code&gt; and give this field the value of the OrderId column.   By using the field instead of the property, I'm always sure that no extra logic will be executed, and I do not have to bother with getter-only properties.&lt;br /&gt;You can read more about this &lt;a href="http://geekswithblogs.net/opiesblog/archive/2006/09/01/90046.aspx#FeedBack"&gt;here&lt;/a&gt; and &lt;a href="http://fgheysels.blogspot.com/2006/04/net-20-could-not-find-schema.html"&gt;here&lt;/a&gt; &lt;small&gt;Particulary in the comments of these articles.&lt;/small&gt;&lt;br /&gt;The following three properties are quite straightforward.  These elements just say to which column each property maps, and I've also defined that NHibernate should use the field instead of the property to get / set these properties.&lt;/p&gt;&lt;p&gt;The next two elements are bit more interesting though.&lt;br /&gt;The &lt;code&gt;many-to-one&lt;/code&gt; element defines how the relationship between &lt;code&gt;Order&lt;/code&gt; and &lt;code&gt;Customer&lt;/code&gt; must be saved.   As you might remember, the &lt;code&gt;Order&lt;/code&gt; class holds a reference to the &lt;code&gt;Customer&lt;/code&gt; to which the order belongs.  To be able to populate the &lt;code&gt;OwningCustomer&lt;/code&gt; property which contains this reference, and to be able to save the link with the &lt;code&gt;Customer&lt;/code&gt; when the &lt;code&gt;Order&lt;/code&gt; is saved, the &lt;code&gt;many-to-one&lt;/code&gt; element is used.  A &lt;code&gt;Customer&lt;/code&gt; can have many &lt;code&gt;Orders&lt;/code&gt;, and an &lt;code&gt;Order&lt;/code&gt; always belongs to one &lt;code&gt;Customer&lt;/code&gt;.  The &lt;code&gt;many-to-one&lt;/code&gt; element just says that the &lt;code&gt;OwningCustomer&lt;/code&gt; property maps to the &lt;code&gt;CustomerId&lt;/code&gt; column in the Order table, and that the &lt;code&gt;OwningCustomer&lt;/code&gt; is of the type &lt;code&gt;SomeShopDomain.Customer&lt;/code&gt;.  By defining this type, NHibernate knows that it should look for a mapping file which should be called &lt;code&gt;SomeShopDomain.Customer.hbm.xml&lt;/code&gt; in where the mapping of the &lt;code&gt;Customer&lt;/code&gt; type is defined.  &lt;br /&gt;&lt;small&gt;Mapping files should be named after the class for which they contain the mapping, and should have the extension hbm.xml.  Next to that, you also have to specify 'embedded resource' as build action for the mapping files.&lt;/small&gt;&lt;br /&gt;The &lt;code&gt;&amp;lt;bag&amp;gt;&lt;/code&gt; element defines that the &lt;code&gt;Order&lt;/code&gt; contains a collection of &lt;code&gt;OrderLines&lt;/code&gt;, and that these objects must be of type &lt;code&gt;SomeShopDomain.OrderLine&lt;/code&gt;.  NHibernate will use the mapping-file of the &lt;code&gt;OrderLine&lt;/code&gt; class to determine in which table those &lt;code&gt;OrderLines&lt;/code&gt; must be saved.  &lt;br /&gt;The &lt;code&gt;&amp;lt;key&amp;gt;&lt;/code&gt; element states that the &lt;code&gt;Id&lt;/code&gt; of the current &lt;code&gt;Order&lt;/code&gt; must be saved in the OrderId column of the table where the &lt;code&gt;OrderLines&lt;/code&gt; will be saved.&lt;/p&gt;&lt;br /&gt;This concludes the quick tour of the NHibernate mapping file.  For more information about the possible elements, I'll refer to the &lt;a href="http://www.amazon.com/gp/product/193239415X?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=193239415X"&gt;Hibernate in Action&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=193239415X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt; book, or to the &lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/mapping.html#mapping-declaration"&gt;Hibernate&lt;/a&gt; documentation.&lt;/p&gt;&lt;p&gt;The mapping file of the &lt;code&gt;OrderLine&lt;/code&gt; class, looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;br /&gt;&amp;lt;hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"&amp;gt;&lt;br /&gt;  &amp;lt;class name="BlogShopDomain.OrderLine, BlogShopDomain" &lt;br /&gt;         table="OrderLine" lazy="false"&amp;gt;&lt;br /&gt;    &amp;lt;id name="Id" type="Int32" &lt;br /&gt;                  column="OrderLineId" &lt;br /&gt;                  unsaved-value="-1" &lt;br /&gt;                  access="field.camelcase-underscore"&amp;gt;&lt;br /&gt;      &amp;lt;generator class="identity" /&amp;gt;&lt;br /&gt;    &amp;lt;/id&amp;gt;&lt;br /&gt;    &amp;lt;property name="ArticleName" column="ArticleName" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="ArticlePrice" column="ArticlePrice" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="ArticleId" column="ArticleId" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="NumberOfItems" column="NumberOfItems" &lt;br /&gt;              access="field.camelcase-underscore" /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;many-to-one name="OwningOrder" column="OrderId" &lt;br /&gt;                 class="BlogShopDomain.Order, BlogShopDomain" &lt;br /&gt;                 access="field.camelcase-underscore"&lt;br /&gt;                 not-null="true" /&amp;gt;&lt;br /&gt;    &lt;br /&gt;  &amp;lt;/class&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;/pre&gt;I'm not going to elaborate on this file, so lets get to the next part. :)&lt;/p&gt;&lt;h2&gt;Integrating NHibernate in the project&lt;/h2&gt;&lt;p&gt;At this point, I've done some necessary configurations, but some coding needs to be done as well.&lt;br /&gt;NHibernate uses the concept of Sessions as an 'interface' between your objects and the database.  A session can be considered as a &lt;a href="http://www.martinfowler.com/eaaCatalog/unitOfWork.html"&gt;unit of work&lt;/a&gt;.  This is a bit a simplistic definition, but you can find a better one in the &lt;a href="http://www.hibernate.org/hib_docs/nhibernate/html/architecture.html#architecture-overview"&gt;Hibernate documentation&lt;/a&gt; or in &lt;a href="http://www.amazon.com/gp/product/193239415X?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=193239415X"&gt;Hibernate in Action&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=193239415X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;.&lt;br /&gt;To be able to create Sessions, you'll need a &lt;code&gt;SessionFactory&lt;/code&gt;.  Unlike Hibernate Sessions, a SessionFactory is an expensive object to instantiate, so it's best practice to create this object only once.&lt;br /&gt;Therefore, I like to create a Singleton class which holds amongst some other stuff, the NHibernate &lt;code&gt;ISessionFactory&lt;/code&gt;:&lt;pre class="code-content"&gt;public class ApplicationSettings&lt;br /&gt;{&lt;br /&gt;  private static ApplicationSettings _instance;&lt;br /&gt;&lt;br /&gt;  private ApplicationSettings() {}&lt;br /&gt;&lt;br /&gt;  // The only access point to the ApplicationSettings instance.&lt;br /&gt;  public static ApplicationSettings Instance&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;       if( _instance == null )&lt;br /&gt;       {&lt;br /&gt;         _instance = new ApplicationSettings();&lt;br /&gt;       }&lt;br /&gt;       return _instance;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private ISessionFactory _sessionFactory = null;&lt;br /&gt;&lt;br /&gt;  public ISessionFactory SessionFactoryObj&lt;br /&gt;  {&lt;br /&gt;    if( _sessionFactory == null )&lt;br /&gt;    {&lt;br /&gt;      Configuration cfg = new Configuration();&lt;br /&gt;      cfg.AddAssembly (typeof(SomeShopDomain.Customer).Assembly);&lt;br /&gt;      _sessionFactory = cfg.BuildSessionFactory();&lt;br /&gt;    }&lt;br /&gt;    return _sessionFactory;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This structure guarantees that I'll have only one ISessionFactory in my project, and that this SessionFactory is created only once.&lt;br /&gt;The code to create the &lt;code&gt;ISessionFactory&lt;/code&gt; is quite simple; first of all a &lt;code&gt;Configuration&lt;/code&gt; object is instantiated, and the assembly which contains the business classes is added to the &lt;code&gt;Configuration&lt;/code&gt;.  As you can see, I use reflection to find that assembly, since I don't want to be bothered with assembly-names and locations.   After that, the &lt;code&gt;BuildSessionFactory&lt;/code&gt; method is called which creates the ISessionFactory.&lt;br /&gt;This &lt;code&gt;ApplicationSettings&lt;/code&gt; class is not part of my class library which contains the domain, but belongs to the project that contains my client application (in this case, a WinForms application).  More on that later.&lt;br /&gt;At this time, we can create &lt;code&gt;ISessions&lt;/code&gt;.&lt;pre class="code-content"&gt;ISession s = ApplicationSettings.Instance.&lt;br /&gt;                   SessionFactoryObj.OpenSession();&lt;/pre&gt;&lt;/p&gt;&lt;h2&gt;Creating Repositories for our Domain classes&lt;/h2&gt;&lt;p&gt;In Domain Driven Design, getting objects from a persistence store, and getting objects into a persistence store, is being abstracted by the use of repositories.&lt;br /&gt;As you might have seen in the previous articles of this serie, I've already created 'dummy' repositories for the unit-tests.&lt;br /&gt;Now, it is time to implement the 'real' repositories, which will actually do the work of loading / storing objects to our database.&lt;br /&gt;In the &lt;a href="http://fgheysels.blogspot.com/2006/07/ddd-quickstart-implementation-using.html"&gt;2nd part&lt;/a&gt; of this serie, I've already mentionned the existence of the &lt;code&gt;DomainSettings&lt;/code&gt; class and the &lt;code&gt;RepositoryFactory&lt;/code&gt; interface.&lt;br /&gt;At that point, I've only implemented the 'mock-repositories', but now, I want to implement repositories which use NHibernate to do some real work.  This means that I'll have to create an &lt;code&gt;NHibernateRepositoryFactory&lt;/code&gt; class.  I choose to put this class, along with the NHibernate-repositories in the same project / assembly where my business classes reside.&lt;br /&gt;The &lt;code&gt;NHibernateRepositoryFactory&lt;/code&gt; class must implement the &lt;code&gt;IRepositoryFactory&lt;/code&gt; interface, which looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;public interface IRepositoryFactory&lt;br /&gt;{&lt;br /&gt; ICustomerRepository  CreateCustomerRepository();&lt;br /&gt; IOrderRespository CreateOrderRepository();&lt;br /&gt; ...&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;There's one caveat here however...  When using databases you'll be faced with 'Transactions'.  A repository should not be the initiator nor the 'committer' of a transaction.  &lt;br /&gt;Why?  The repository has no knowledge of the 'context' of the situation.  The repository does not know when it should initiate and commit or rollback a unit-of-work.&lt;br /&gt;As I've told earlier in this article, the NHibernate Session object is the NHibernate object which 'controls' a unit-of-work.  Therefore, I'd like to extent the &lt;code&gt;IRepositoryFactory&lt;/code&gt; interface with methods which allow me to create a specific repository with an object which is responsible for transaction-handling injected into it:&lt;pre class="code-content"&gt;public interface IRepositoryFactory&lt;br /&gt;{&lt;br /&gt; ICustomerRepository  CreateCustomerRepository();&lt;br /&gt; IOrderRespository CreateOrderRepository();&lt;br /&gt; ...&lt;br /&gt; &lt;br /&gt; ICustomerRepository CreateCustomerRepository( object transHandler );&lt;br /&gt; IOrderRepository CreateOrderRepository( object transHandler );&lt;br /&gt;}&lt;/pre&gt;This allows me to create repositories which can take part in a specific transaction/unit-of-work, and it also allows me to create repositories without having to take care about transactions.  (This is also necessary; as you can see in the code in the &lt;code&gt;Customer&lt;/code&gt; class in &lt;a href="http://fgheysels.blogspot.com/search/label/Domain%20Driven%20Design"&gt;part 2&lt;/a&gt; of this serie.  There I need to retrieve a value, which is given to me by a repository.  In this case, I do not really need a transaction context).&lt;/p&gt;&lt;p&gt;I've chosen to define the &lt;code&gt;transHandler&lt;/code&gt; in my &lt;code&gt;CreateXXX&lt;/code&gt; methods in the &lt;code&gt;IRepositoryFactory&lt;/code&gt; as an object, since I do not want to couple the &lt;code&gt;IRepositoryFactory&lt;/code&gt; interface with NHibernate.&lt;/p&gt;&lt;p&gt;At this time, I can already start writing an implementation of a &lt;code&gt;NHibernateRepositoryFactory&lt;/code&gt;:&lt;pre class="code-content"&gt;public class NHibernateRepositoryFactory :&lt;br /&gt;    IRepositoryFactory&lt;br /&gt;{&lt;br /&gt;  public IOrderRepository CreateOrderRepository()&lt;br /&gt;  {&lt;br /&gt;    ISession s = DomainSettings.Instance.SessionFactoryObj.OpenSession();&lt;br /&gt;    return new NHibernateOrderRepository(s);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public IOrderRepository CreateOrderRepository( object transHandler )&lt;br /&gt;  {&lt;br /&gt;    // convert the transHandler to an NHibernat ISession object.&lt;br /&gt;    ISession s = transHandler as ISession;&lt;br /&gt;&lt;br /&gt;    if( s == null )&lt;br /&gt;    {&lt;br /&gt;      throw new ArgumentException ("object of type NHibernate.ISession expected.",&lt;br /&gt;                                   "transHandler");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return new NHibernateOrderRepository (s);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;The &lt;code&gt;NHibernateOrderRepository&lt;/code&gt; can look like this:&lt;pre class="code-content"&gt;public class NHibernateOrderRepository :&lt;br /&gt;  IOrderRepository&lt;br /&gt;{&lt;br /&gt;  private ISession _context;&lt;br /&gt;&lt;br /&gt;  public NHibernateOrderRepository( ISession s )&lt;br /&gt;  {&lt;br /&gt;    _context = s;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Order GetOrder( int id )&lt;br /&gt;  {&lt;br /&gt;    return _context.Get (typeof(Order), id) as Order;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public decimal GetOrderTotalForCustomerSinceDate( Customer c, DateTime date )&lt;br /&gt;  {&lt;br /&gt;    string hql = "select sum (ol.NumberOfItems * ol.ArticlePrice) " +&lt;br /&gt;                 "from Order o, " +&lt;br /&gt;                 "OrderLine ol, Customer c " +&lt;br /&gt;                 "where ol.OwningOrder = o and o.OwningCustomer = c " +&lt;br /&gt;                 " and c.Id = :custId and o.OrderDate &gt;= :orderDate";&lt;br /&gt;&lt;br /&gt;    IQuery q = _context.CreateQuery (hql);&lt;br /&gt;    q.SetInt32 ("custId", c.Id);&lt;br /&gt;    q.SetDateTime ("orderDate", date);&lt;br /&gt;&lt;br /&gt;    object o = q.UniqueResult ();&lt;br /&gt;&lt;br /&gt;    if( o == null || o == DBNull.Value )&lt;br /&gt;    {&lt;br /&gt;      return 0;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      return Convert.ToDecimal (o);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;I will not elaborate here on the &lt;acronym title="Hibernate Query Language"&gt;HQL&lt;/acronym&gt; and the specific &lt;code&gt;ISession&lt;/code&gt; methods which I've used in the code above, since this would be truly outside the scope of this article.  For more information, I'd refer -again- to the &lt;a href="http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html"&gt;Hibernate documentation&lt;/a&gt; or the &lt;a href="http://www.amazon.com/gp/product/193239415X?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=193239415X"&gt;Hibernate in Action&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=193239415X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt; book.&lt;br /&gt;I do like to mention though that the &lt;a href="http://jira.nhibernate.org/browse/NH-555"&gt;bug&lt;/a&gt; which I've mentionned &lt;a href="http://fgheysels.blogspot.com/2006/03/nhibernate-wheres-productivity.html"&gt;here&lt;/a&gt; is fixed in NHibernate 1.2.0 CR1. :)&lt;/p&gt;&lt;br /&gt;&lt;p&gt;As I'm typing this, I now also see that it would be better to let the repositories implement the &lt;code&gt;IDisposable&lt;/code&gt; interface.  In the &lt;code&gt;Dispose&lt;/code&gt; method.  In the Dispose method of the NHibernateRepositories, I can then check if my &lt;code&gt;_context&lt;/code&gt; is still open, and close it if it is.&lt;br /&gt;Then, I should also change the code in the &lt;code&gt;Status&lt;/code&gt; property of the &lt;code&gt;Customer&lt;/code&gt; class, so that it looks like this:&lt;pre class="code-content"&gt;public CustomerStatus Status&lt;br /&gt;{&lt;br /&gt;  get&lt;br /&gt;  {&lt;br /&gt;    CustomerStatus result = CustomerStatus.Normal;&lt;br /&gt;    &lt;br /&gt;    using( IOrderRepository or = &lt;br /&gt;             DomainSettings.Instance.&lt;br /&gt;                            RepositoryFactoryObj.&lt;br /&gt;                            CreateOrderRepository () )&lt;br /&gt;    {&lt;br /&gt;      decimal orderTotal = or.GetOrderTotalForCustomerSinceDate(this,  &lt;br /&gt;                                 DateTime.Now.AddMonths (-3));&lt;br /&gt;&lt;br /&gt;      if( orderTotal &gt; DomainSettings.GoldAmountTreshold )&lt;br /&gt;      {&lt;br /&gt;        result = CustomerStatus.Gold;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;    ...&lt;br /&gt;    return result;&lt;br /&gt;}&lt;/pre&gt;If I don't do this, the ISession which is used by the &lt;code&gt;OrderRepository&lt;/code&gt; would be kept open.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Using NHibernate in the client application&lt;/h2&gt;&lt;p&gt;Now that we've seen (albeit rather quickly) how we can use NHibernate, it is time to integrate it in the client application.&lt;br /&gt;Because of the way NHibernate works when it has to save changed objects back to the DB, I'd like to use long living sessions.&lt;br /&gt;When you retrieve an object from the database, NHibernate will keep this object in the Session.  When you save the object back to the database, NHibernate will try to find the object in the &lt;code&gt;ISession&lt;/code&gt; instance which is used to perform the save.   If the instance is found, NHibernate will update the corresponding record.  If the instance is not found, NHibernate will create a new record.&lt;br /&gt;If you use a separate &lt;code&gt;ISession&lt;/code&gt; to retrieve an object from the DB, and use another &lt;code&gt;ISession&lt;/code&gt; to persist this object back to the DB, NHibernate will asume that it has to insert a new record.&lt;br /&gt;Therefore, it is -imho- best to use the same &lt;code&gt;ISession&lt;/code&gt; instance for retrieving and saving the same business object instance.&lt;br /&gt;When you're using NHibernate in Windows applications, you can do this by using the &lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html"&gt;'long conversation'&lt;/a&gt; model. &lt;/p&gt;&lt;p&gt;In short, this means that you can keep your session open as long as the user is busy with a 'unit-of-work', but you can disconnect your session from the database when no database-access is needed for a while.&lt;/p&gt;&lt;p&gt;Although this article doesn't provide really in-depth information, I'd like to finish it here.  This is yet another article which is way to long (maybe I should stop writing such long posts), but if you've gotten to here, please feel free to post any comments, critics, remarks, ...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-2502678195128959510?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/2502678195128959510/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=2502678195128959510' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2502678195128959510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/2502678195128959510'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/02/ddd-implementing-persistence-using.html' title='DDD: implementing persistence using NHibernate'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-5743348076931260272</id><published>2007-01-31T23:46:00.000+01:00</published><updated>2007-02-11T12:23:25.382+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Meet My Mom</title><content type='html'>&lt;p&gt;In the past few weeks, I've been making some portraits.   Some of them in a studio, others 'on location'.&lt;br /&gt;I will post some of the results here and I'll start with a portrait of my mother.&lt;/p&gt;&lt;p&gt;These photographs are shot in her 'study room'; my mom loves painting and  calligraphy (she's pretty good at it) and I wanted to express this in my photographs.&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH_2423.jpg" alt="high iso portrait creative mother" /&gt;&lt;br /&gt;&lt;small&gt;Just a pity that she's holding the pen a little bit to high&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH_2427.jpg" alt="high iso portrait creative mother2" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;The photos have been taken with ambient light (I don't like to use flash for location-portraits).  I've used a Nikon D200 with a 50mm f/1.8 lens mounted on it.  Since there was not too much light available, I've choosen a small aparture number and a high ISO setting. (The EXIF information is available in the picture; you can easily view it using &lt;a href="http://www.opanda.com/en/iexif/index.html"&gt;Opanda IEXIF Viewer"&lt;/a&gt;).  Despite the high ISO setting, I've to admit that I'm pleasantly surprised with the noise levels in these photo's.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-5743348076931260272?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/5743348076931260272/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=5743348076931260272' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5743348076931260272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/5743348076931260272'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/01/meet-my-mom.html' title='Meet My Mom'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1721513087673245859</id><published>2007-01-20T11:41:00.001+01:00</published><updated>2007-01-20T11:41:55.877+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Lambda expressions in .NET</title><content type='html'>&lt;p&gt;In C# 3.0, there will be a new feature available: lambda expressions.   This new feature was necessary to create the &lt;a href="http://fgheysels.blogspot.com/2006/01/c-30-orcas-playing-with-linq.html"&gt;LINQ&lt;/a&gt; feature, which will also be introduced in C# 3.0&lt;/p&gt;&lt;p&gt;Today, I came across an interesting article written by Howard Dierking in where he explains what lambda expressions are, and how you can use them.  &lt;br /&gt;Very nice and clear &lt;a href="http://blogs.msdn.com/howard_dierking/archive/2007/01/18/lambda-lambda-lambda.aspx"&gt;article&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1721513087673245859?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1721513087673245859/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1721513087673245859' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1721513087673245859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1721513087673245859'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/01/lambda-expressions-in-net_20.html' title='Lambda expressions in .NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-1177474821253692504</id><published>2007-01-20T10:39:00.000+01:00</published><updated>2007-01-20T11:42:18.537+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>Tagged</title><content type='html'>&lt;p&gt;Seems that I've been tagged by &lt;a href="http://blogs.chayachronicles.com/sonofnun/Default.aspx"&gt;Mike Nichols&lt;/a&gt;, so I'm supposed to come up with 5 things you don't know about me; here goes: &lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;I've started playing music when I was about 8 or 9 years old.  I've played guitar until I was about 16 years old.  At that age, I stopped playing music.  I didn't wan't to do it anymore.  I was supposed to go to music classes each wednesday afternoon, and each saterday morning and I wanted to do other things during that time. :)  Sometimes, I regret it though that I didn't continue with that guitar-thing. (I still have my guitar though, and maybe I'll pick it up again in the future)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I don't like cheese.&lt;br /&gt;Well, that's not completely true; I don't eat cheese 'as is', but I do like food that has cheese as an ingredient (pizza, croque monsieur, tiramissu, heck I even like cheese-burgers).  I also like Cheetos.  Weird huh?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I started programming relatively late.  I was 17 years old when I wrote my first computer program.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I always wanted to become a pilot.  Not on airliners, but a jet pilot. :)  At age 16, I've sent a letter to the Belgian Air Force to enquire for more information about this.  A few days later, I've received an information bundle about it.  Unfortunately, I never took part in any of the 'promotions' of the Belgian Air Force.  Maybe I was already realistic about my chances: there are only very few positions (about 4 each year) and numerous candidates.  &lt;br /&gt;I haven't lost my interest in jets though; I still like to visit air-shows (although since a plane crashed at the Ostend Airshow in 1997, air-shows in Belgium aren't what they used to be...).&lt;br /&gt;If I would ever have the chance to be a passenger in a two-seater, I'd certainly do it!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I do like &lt;a href="http://en.wikipedia.org/wiki/Kart_racing"&gt;karting&lt;/a&gt;.  Until a few years ago, I used to do it at least once a week.  Unfortunately, I'm not able to keep up that frequency right now.  I'm already happy if I can go karting once a month.  &lt;br /&gt;&lt;small&gt;I also dream of driving an open-wheel formula race car once (Formula One, Formula Ford, F3000, ... whatever&lt;/small&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;The 5 persons I'm tagging are: &lt;a href="http://born2code.net/"&gt;PJ van de Sande&lt;/a&gt;, &lt;a href="http://pveentjer.wordpress.com/"&gt;Peter Veentjer&lt;/a&gt;, &lt;a href="http://www.jellejanvanveelen.nl/"&gt;Jelle-Jan Van Veelen&lt;/a&gt;, &lt;a href="http://www.blik-opener.be/"&gt;Valentijn&lt;/a&gt; and &lt;a href="http://www.evanhoff.com/"&gt;Evan Hoff&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-1177474821253692504?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/1177474821253692504/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=1177474821253692504' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1177474821253692504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/1177474821253692504'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/01/tagged.html' title='Tagged'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116845930761757525</id><published>2007-01-10T20:07:00.000+01:00</published><updated>2007-01-20T01:02:42.037+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Left- and RightPadding in SQL Server</title><content type='html'>&lt;p&gt;Although SQL Server contains a few &lt;a href="http://msdn2.microsoft.com/en-us/library/ms181984.aspx"&gt;string manipulation functions&lt;/a&gt;, there exists no function that allows you to left- or rightpad a string expression.&lt;br /&gt;This is a bit of a pity, since left- and rightpadding is sometimes necessary.&lt;/p&gt;&lt;p&gt;But, no one stops you from creating your own LPAD and RPAD function offcourse; in fact, it is rather very simple. :)&lt;br /&gt;Although SQL Server doesn't provide padding methods out of the box, it doesn't require a lot of work to pad a string.&lt;/p&gt;&lt;div style="clear:both"&gt;&lt;br /&gt;For instance: suppose you have a variable @number, which contains a number. If you need to leftpad this string with zeroes so that it always contains 8 characters, you simply have to do this:&lt;/p&gt;&lt;pre class="code-content"&gt;SELECT RIGHT ('00000000' + @number, 8)&lt;/pre&gt;&lt;p&gt;If @number contains '81337', this statement will return '00081337'.&lt;/p&gt;&lt;p&gt;With this knowledge, it is pretty simple to create your own LPAD method.  This is the code:&lt;/p&gt;&lt;pre class="code-content"&gt;CREATE FUNCTION [dbo].[LPAD] (@string VARCHAR(8000), &lt;br /&gt;                              @length INT, &lt;br /&gt;                              @paddingChar CHAR(1))&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;    RETURN RIGHT (REPLICATE (@paddingChar, @length) + @string, @length)&lt;br /&gt;END&lt;/pre&gt;&lt;p&gt;Creating an RPAD function is very similar:&lt;/p&gt;&lt;pre class="code-content"&gt;CREATE FUNCTION [dbo].[RPAD] (@string VARCHAR(8000),&lt;br /&gt;                              @length INT,&lt;br /&gt;                              @paddingChar CHAR(1))&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;    RETURN LEFT (@string + REPLICATE (@paddingChar, @length), @length)&lt;br /&gt;END&lt;/pre&gt;&lt;p&gt;&lt;/div&gt;&lt;p&gt;&lt;small&gt;As you might have noticed, REPLICATE is a SQL Server function which repeats a specified characters a number of times. :)&lt;/small&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116845930761757525?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116845930761757525/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116845930761757525' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116845930761757525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116845930761757525'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2007/01/left-and-rightpadding-in-sql-server.html' title='Left- and RightPadding in SQL Server'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116749217047905129</id><published>2006-12-30T16:15:00.000+01:00</published><updated>2007-01-20T11:42:28.355+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>Shelfari Bookshelf</title><content type='html'>&lt;p&gt;As a software developer, I reguraly buy (and read) books on software development.  I've found a website where you can create your &lt;a href="http://www.shelfari.com/"&gt;'virtual bookshelf'&lt;/a&gt;, and I've decided to create one.&lt;/p&gt;&lt;br /&gt;&lt;embed width="325" height="465" src="http://sws.shelfari.com/shelf.swf" wmode="transparent"FlashVars="UserName=fgheysels&amp;ShelfType=user&amp;verE=s1.1&amp;booksize=large&amp;AmazonAssociate=fredgheywebl-20&amp;Alpha=0&amp;BGColor=FFFFFF"&gt;&lt;/embed&gt;&lt;p&gt;On my virtual shelf, you will mostly find books on software development and photography.  Not all of my books are already listed, but I will complete my shelf in the near future.  &lt;br /&gt;For some books on my shelf, I've written a little opinion about it.  If you want to buy a book that I have on my shelf, you can directly go to the Amazon.com website by clicking on a particular book.&lt;br /&gt;If you want to know my opinion on a particular book for which I haven't supplied an opinion yet, do not hesitate to contact me. :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116749217047905129?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116749217047905129/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116749217047905129' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116749217047905129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116749217047905129'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/12/shelfari-bookshelf.html' title='Shelfari Bookshelf'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116725220219802738</id><published>2006-12-27T19:37:00.000+01:00</published><updated>2007-01-20T01:02:58.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>VS.NET 2005: XML View of a Typed DataSet</title><content type='html'>&lt;p&gt;Although I'm not a big proponent of (typed) DataSets ~ I rather use custom business classes ~ , I do have to use them every once in a while.&lt;/p&gt;&lt;p&gt;When I use a typed dataset in VS.NET 2003, I always use &lt;a href="http://www.ondotnet.com/pub/a/dotnet/2003/03/31/typeddatasetannotations.html"&gt;'typed DataSet Annotations'&lt;/a&gt;.  &lt;br /&gt;This is very easily done in VS.NET 2003: you'll have to switch from DataSet view to XML View, and this can easily be done by the handy buttons that you can see in the lower-left corner:&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/VSNET2k3DS.PNG" &gt; &lt;/img&gt;&lt;br /&gt;&lt;br /&gt;Clicking on 'XML' would give you the XML view of your typed Dataset; simple as that.&lt;/p&gt;&lt;p&gt;A few days ago, I was working in VS.NET 2005, and I wanted to create a typed DataSet ...  I wanted to use the 'annotations' like I was used to do in VS.NET 2003.&lt;/p&gt;&lt;p&gt;I was amazed to find out that those handy XML / DataSet View buttons that exist in VS.NET 2003 are gone in VS.NET 2005.   &lt;br /&gt;Apparently, Microsoft doesn't like the idea that developers sometimes want to tweak some settings via code instead of via the properties window ? At least, that's how I'm thinking about it.&lt;br/&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=visual%20studio%20tips&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;br/&gt;&lt;br /&gt;After some searching, I've found out that it is still possible to get the XML View of a DataSet in VS.NET 2005, but damn, it is very well hidden.&lt;br /&gt;Here's how you can see the XML definition of the DataSet:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Right click on the DataSet file&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You'll see the following context-menu:&lt;br /&gt;&lt;br/&gt;&lt;img src="http://users.pandora.be/fgzone/blog/VSNET2k5cm.PNG" /&gt;&lt;br /&gt;Select the 'Open With option'&lt;br/&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The following Dialog Box opens:&lt;br /&gt;&lt;br/&gt;&lt;img src="http://users.pandora.be/fgzone/blog/vsnet2k5openwith.PNG"/&gt;&lt;br/&gt;&lt;br /&gt;Select the 'XML Editor' option&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, you can see the XML View of the DataSet definition&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;As you can see, in VS.NET 2005, 3 user interactions are needed in order to go to your destination, instead of just a single click in VS.NET 2003.  &lt;br /&gt;Not very productive IMHO.&lt;br/&gt;&lt;br /&gt;I wonder why Microsoft has removed those buttons that existed in VS.NET 2003...&lt;/p&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=working%20with%20visual%20studio%202005&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116725220219802738?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116725220219802738/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116725220219802738' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116725220219802738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116725220219802738'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/12/vsnet-2005-xml-view-of-typed-dataset.html' title='VS.NET 2005: XML View of a Typed DataSet'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116565986740657351</id><published>2006-12-09T11:22:00.000+01:00</published><updated>2007-01-20T11:42:40.327+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miscellaneous'/><title type='text'>Official owners</title><content type='html'>Since yesterday, my girlfriend and I are the official and proud owners of this piece of Belgium:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH1487.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next step: building a house on it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116565986740657351?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116565986740657351/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116565986740657351' title='4 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116565986740657351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116565986740657351'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/12/official-owners.html' title='Official owners'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116499492045404491</id><published>2006-12-01T18:40:00.000+01:00</published><updated>2007-02-25T21:11:36.074+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>High Key</title><content type='html'>Yesterday, I've been playing around a bit and wanted to create a &lt;a href="http://en.wikipedia.org/wiki/High-key_lighting"&gt;high-key&lt;/a&gt; picture.  &lt;br /&gt;I think the result is quite ok. :)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH2011.jpg" /&gt;&lt;p&gt;Apart from some 'levels' adjustements in Photoshop, no other modifications have been made.&lt;br /&gt;I've put the lighters on a plexiglass (hence the reflection), and used two elinchrome strobes to make the picture like it is shown here.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116499492045404491?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116499492045404491/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116499492045404491' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116499492045404491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116499492045404491'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/12/high-key.html' title='High Key'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116323947086399264</id><published>2006-11-11T11:02:00.000+01:00</published><updated>2007-01-20T01:03:34.699+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Nested Transactions in SQL Server</title><content type='html'>&lt;p&gt;I've been wondering if it would be possible to use 'nested transactions' in SQL Server.  To test this, I've set up a little test database and executed a few &lt;br /&gt;T-SQL batches:&lt;/p&gt;&lt;pre class="code-content"&gt;USE testdb&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name1')&lt;br /&gt;&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name2')&lt;br /&gt;COMMIT TRAN&lt;br /&gt;&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name3')&lt;br /&gt;&lt;br /&gt;COMMIT TRAN&lt;/pre&gt;&lt;p&gt;This is trivial, and it works as expected: 3 records have been added to the table.  The next batch looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;USE testdb&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name1')&lt;br /&gt;&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name2')&lt;br /&gt;COMMIT TRAN&lt;br /&gt;&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name3')&lt;br /&gt;&lt;br /&gt;ROLLBACK TRAN&lt;/pre&gt;&lt;p&gt;This is no big deal either: as expected, no records have been added to the table.  Up to the next one:&lt;/p&gt;&lt;pre class="code-content"&gt;USE testdb&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name1')&lt;br /&gt;&lt;br /&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name2')&lt;br /&gt;ROLLBACK TRAN&lt;br /&gt;&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name3')&lt;br /&gt;&lt;br /&gt;COMMIT TRAN&lt;/pre&gt;&lt;p&gt;This batch fails with the following error message:&lt;/p&gt;&lt;code&gt;Server: Msg 3902, Level 16, State 1, Line 16&lt;br/&gt;&lt;br /&gt;The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.&lt;/code&gt;&lt;br /&gt;&lt;p&gt;It turns out that only the last record (Name3) is inserted into the database.  That's not what I expected.  &lt;br /&gt;Normally, one should expect that Name1 and Name3 are persisted in the database, and only Name2 gets rollbacked.&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=microsoft%20sql%20server&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;However, as my collegue Geert pointed out: the BEGIN TRANSACTION statement increments the @@TRANCOUNT Server Variable with 1, and the COMMIT TRANSACTION decrements the @@TRANCOUNT variable with 1. &lt;br /&gt;The ROLLBACK TRAN statement however, decrements the @@TRANCOUNT server variable to 0.  That's why the last COMMIT statement gives us the error message: there has been a rollback, and therefore the @@Trancount is set to zero.&lt;br /&gt;Apparently, the ROLLBACK TRANSACTION also rollbacks to the most outer &lt;code&gt;begin transaction&lt;/code&gt;, that's why the record 'Name1' is not persisted into the database.&lt;/p&gt;&lt;p&gt;As it turns out, it is not possible to use nested transactions in this way.  There is however a way to solve this 'problem':&lt;/p&gt;&lt;h2&gt;Savepoints to the rescue&lt;/h2&gt;&lt;p&gt;It is possible to use 'savepoints' to solve this problem.  As stated in the SQL Server books online:&lt;/p&gt;&lt;quote&gt;Savepoints offer a mechanism to roll back portions of transactions.&lt;br /&gt;&lt;/quote&gt;You use savepoints like this:&lt;pre class="code-content"&gt;BEGIN TRAN&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name1')&lt;br /&gt;&lt;br /&gt;SAVE TRANSACTION sp1&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name2')&lt;br /&gt;ROLLBACK TRAN sp1&lt;br /&gt;&lt;br /&gt;INSERT INTO tblTest (Name) VALUES ('Name3')&lt;br /&gt;&lt;br /&gt;COMMIT TRAN&lt;/pre&gt;&lt;p&gt;In this code example, you start a transaction, execute a statement, and save the transaction using the &lt;code&gt;SAVE TRANSACTION sp1&lt;/code&gt; statement.&lt;br /&gt;This statement sets a savepoint with the name 'sp1'.  You can then rollback to that savepoint using the &lt;code&gt;ROLLBACK TRAN &amp;lt;savepointname&amp;gt;&lt;/code&gt; command.&lt;br /&gt;The result of this batch is as expected: 2 records are inserted into the tblTest table: 'Name1' and 'Name3'&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116323947086399264?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116323947086399264/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116323947086399264' title='5 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116323947086399264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116323947086399264'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/11/nested-transactions-in-sql-server.html' title='Nested Transactions in SQL Server'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-116271996323432831</id><published>2006-11-05T10:37:00.000+01:00</published><updated>2007-01-20T01:03:57.864+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Aspect Oriented Programming in .NET</title><content type='html'>&lt;p&gt;A while ago, there was somebody who asked the question on a programming forum whether it was possible to retrieve the values of the arguments that are passed to a method in .NET.   The purpose was to create some kind of a 'logging' system so that he could log which methods have been called, and what values were passed to those methods.&lt;br /&gt;This person had already created a method that retrieved all kinds of information of a certain method, but getting the values of the parameters via reflection was not possible.&lt;/p&gt;&lt;p&gt;The disadvantage of this approach is that your methods are being polluted by this logging method.  You always have to add a call to this logging method in your 'business methods'.  &lt;br /&gt;For instance:&lt;/p&gt;&lt;pre class="code-content"&gt;public void SomeMethod()&lt;br /&gt;{&lt;br /&gt;    LogThisMethod (MethodBase.GetCurrentMethod());&lt;br /&gt;    &lt;br /&gt;    // Do the real work here.&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The call to the &lt;code&gt;LogThisMethod&lt;/code&gt; method is not likely a core concern in the application, yet, if you want to log calls to certain methods, you’ll have to write a call to this method in every method that you want to log.&lt;br /&gt;In other words: the logging is a cross-cutting concern because it is an aspect of our program that has nothing to do with the core-problem that is to be solved by our program and it appears in multiple parts of the program.&lt;/p&gt;&lt;p&gt;Luckily, there's a much cleaner approach to solve this problem.    &lt;a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming"&gt;Aspect Oriented Programming&lt;/a&gt; offers a way to separate cross-cutting concerns like logging in a much cleaner way.&lt;br /&gt;&lt;acronym title="Aspect Oriented Programming"&gt;AOP&lt;/acronym&gt; allows you to remove the cross-cutting concerns from your 'business code', and create an 'aspect' for it instead.  &lt;br /&gt;This ‘aspect’ will then be weaved into your code at runtime which means that you do not have to call it yourself in the core parts of the application.&lt;br /&gt;In this way, the cross-cutting concerns can be decomposed from the core logic of the application and this will result in more readable and better maintainable software.&lt;/p&gt;&lt;p&gt;In .NET, you can use the &lt;a href="http://www.springframework.net/"&gt;Spring.NET&lt;/a&gt; framework to apply Aspect Oriented Programming.&lt;br /&gt;In the examples that follow, I’ll be using the Spring.NET framework.&lt;/p&gt;&lt;p&gt;You can solve the logging-problem that I've mentioned earlier using AOP in C# in the following way:&lt;/p&gt;&lt;p&gt;Suppose we have a class 'TestClass' and we want to log every method that is being invoked in this class.   Our &lt;code&gt;TestClass&lt;/code&gt; looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;public interface ITest&lt;br /&gt;{&lt;br /&gt;  void SayHello( string name );&lt;br /&gt;  void Shout( string message );&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class TestClass : ITest&lt;br /&gt;{&lt;br /&gt;  public void Method1( string name )&lt;br /&gt;  {&lt;br /&gt;    Console.WriteLine ("Hello " + name + " ! ");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Shout( string message )&lt;br /&gt;  {&lt;br /&gt;    Console.WriteLine (message + "!!!!!!!");&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;These are the steps that have to be taken to create some kind of logging functionality using AOP:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Create an Advice that takes care of the logging.  An Advice describes a certain ‘procedure’ that must be executed at certain points (joinpoints) in the application.   For instance: an Advice can be executed at the entry point of a method.&lt;br /&gt;&lt;br /&gt;If you use Spring.NET, you can create a class which implements the &lt;code&gt;IMethodBeforeAdvice&lt;/code&gt;.  This will make sure that this Advice is called before a method-call.&lt;br /&gt;The Advice can look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;public class MethodInvocationLoggingAdvice : IMethodBeforeAdvice&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  public void Before( System.Reflection.MethodInfo method, &lt;br /&gt;    object[] args, object target )&lt;br /&gt;  {&lt;br /&gt;    string message = method.Name + " called with ";&lt;br /&gt;&lt;br /&gt;    string arguments = string.Empty;&lt;br /&gt;&lt;br /&gt;    for( int i = 0; i &lt; args.Length; i++ )&lt;br /&gt;    {&lt;br /&gt;      arguments += args[i] +", ";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Console.WriteLine (message + arguments.SubString (0, arguments.Length - 2));&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now, we have separated the logging logic in a separate class.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Tell our program to use the Advice&lt;br /&gt;In our program, we must indicate that our Advice has to be called when we invoke the methods of a certain class.&lt;br /&gt;Using Spring.NET, we can do this with only 3 lines of code:&lt;br /&gt;&lt;pre class="code-content"&gt;static void Main()&lt;br /&gt;{&lt;br /&gt;  ProxyFactory f = new ProxyFactory (new TestClass());&lt;br /&gt;&lt;br /&gt;  f.AddAdvice (new MethodInvocationLoggingAdvice());&lt;br /&gt;&lt;br /&gt;  ITest t = (ITest)f.GetProxy();&lt;br /&gt;&lt;br /&gt;  t.SayHello ("Frederik");&lt;br /&gt;&lt;br /&gt;  t.Shout ("Watch out");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The beautiful thing is, that we've kept our 'business methods' clean and every time we invoke a method, the logging functionality is called.   If we extend our &lt;code&gt;TestClass&lt;/code&gt; with a couple of new methods, we do not have to worry about this logging functionality, since those new methods will also call our MethodInvocationLoggingAdvice as well.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;But, what if you only want to log invocations of certain methods, instead of logging every method call? This can also be done rather easily by defining a &lt;a href="http://en.wikipedia.org/wiki/Pointcut"&gt;PointCut&lt;/a&gt;.   Everytime the pointcut is reached, our Advice will be executed.&lt;br /&gt;.NET attributes provide a great way to define PointCuts.&lt;br /&gt;&lt;br /&gt;Building on the previous example, we can extend our code so that the &lt;code&gt;MethodInvocationLoggingAdvice&lt;/code&gt; is only called when a method is decorated with a specific Attribute.   For instance: only invocations of methods that have the 'Log' attribute, must be logged.&lt;br /&gt;To do this, we must first create this &lt;code&gt;Log&lt;/code&gt; attribute:&lt;br /&gt;&lt;pre class="code-content"&gt;[AttributeUsage(AttributeTargets.Method)]&lt;br /&gt;public class LogAttribute : Attribute&lt;br /&gt;{&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;We can now change the &lt;code&gt;TestClass&lt;/code&gt; to indicate that only method-calls to the &lt;code&gt;Shout&lt;/code&gt; method should be logged:&lt;br /&gt;&lt;pre class="code-content"&gt;public class TestClass : ITest&lt;br /&gt;{&lt;br /&gt;  public void Method1( string name )&lt;br /&gt;  {&lt;br /&gt;    Console.WriteLine ("Hello " + name + " ! ");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  [Log]&lt;br /&gt;  public void Shout( string message )&lt;br /&gt;  {&lt;br /&gt;    Console.WriteLine (message + "!!!!!!!");&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;All what's left to do, is to make a change to the code that will be responsible of weaving the advice into our code.   We must now indicate that our Advice should only be executed on methods that have the &lt;code&gt;Log&lt;/code&gt; attribute.&lt;br /&gt;&lt;pre class="code-content"&gt;static void Main()&lt;br /&gt;{&lt;br /&gt;  ProxyFactory f = new ProxyFactory (new TestClass());&lt;br /&gt;&lt;br /&gt;  f.AddAdvisor (new DefaultPointCutAdvisor (&lt;br /&gt;    new AttributeMatchMethodPointcut (typeof(LogAttribute),  &lt;br /&gt;          new MethodInvocationLoggingAdvice()));&lt;br /&gt;&lt;br /&gt;  ITest t = (ITest)f.GetProxy();&lt;br /&gt;&lt;br /&gt;  t.SayHello ("Frederik");&lt;br /&gt;&lt;br /&gt;  t.Shout ("Watch out");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;When you execute this program, you'll see that only the method-call to 'Shout' is being logged.&lt;/p&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=8&amp;l=st1&amp;mode=books&amp;search=aspect%20oriented%20programming%20.NET&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="120" height="240" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-116271996323432831?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/116271996323432831/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=116271996323432831' title='6 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116271996323432831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/116271996323432831'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/11/aspect-oriented-programming-in-net.html' title='Aspect Oriented Programming in .NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115957726487074626</id><published>2006-09-30T02:19:00.000+02:00</published><updated>2007-01-20T01:04:13.146+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>Changing the default access modifier when adding a new class in VS.NET 2005</title><content type='html'>&lt;p&gt;When you add a new class or interface to an existing project in Visual Studio.NET 2005, VS.NET 2005 will not define this class (or interface) as &lt;code&gt;public&lt;/code&gt; by default.&lt;br /&gt;In Visual Studio.NET 2003 however, new classes and interfaces always received the &lt;code&gt;public&lt;/code&gt; access modifier by default, and I do like this VS.NET 2003 approach far better.&lt;/p&gt;&lt;p&gt;When I create a class-library, most of the classes and interfaces that are contained in this library are meant to be used outside the library itself.  This means that I have to explicitly add the &lt;code&gt;public&lt;/code&gt; access modifier to most of my classes, and this is a dreadfull job.&lt;br /&gt;Not only is it a boring job to manually define this access modifier for (almost) every new class that you create, it sometimes causes me loosing some time as well:&lt;br /&gt;Today, I created a new class in VS.NET 2005 in where I've written some unit-tests.  I'm using Testdriven.NET to execute my unit-tests in Visual Studio, and as long as I executed only one Test-method at a time, everything went fine.&lt;br /&gt;However, when I wanted to run all the test-methods that I've written in that class, Testdriven.NET didn't execute a single one of them.  I didn't get any error-message, I just received the message: '0 tests passed, 0 tests failed'.&lt;br /&gt;After some investigating, it turned out that my class didn't had the &lt;code&gt;public&lt;/code&gt; access modifier, and was therefore &lt;code&gt;internal&lt;/code&gt;.  Adding the &lt;code&gt;public&lt;/code&gt; access modifier fixed the problem, and all my tests ran smoothly.&lt;/p&gt;&lt;p&gt;After living with this little annoyance for a while, I thought that it should be possible to change this behaviour and make sure that every new class I create in Visual Studio.NET 2005 is marked as &lt;code&gt;public&lt;/code&gt;.  In the rare cases that I'll need an internal class, I'll just explicitly change the &lt;code&gt;public&lt;/code&gt; modifier to &lt;code&gt;internal&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Since every new item that you can create in VS.NET is based on a template (just like a Word document is based on the normal.dot Document Template), it should be rather easy to change this behaviour.&lt;br /&gt;After some digging in the directory-structure of VS.NET 2005, I've finally found where those templates are being kept.  They're in this directory:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;PF&amp;gt;\Microsoft Visual Studio 8\Common7\IDE\ItemTemplates\CSharp\1033&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;(where &lt;code&gt;&amp;lt;PF&amp;gt;&lt;/code&gt; is your Program Files directory, or the directory where you've installed VS.NET)&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;In this directory, you'll find a number of zip files.  I've just unzipped the Class.zip file, and editted the Class.cs file that was in it to this:&lt;br /&gt;&lt;pre class="code-content"&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;&lt;br /&gt;namespace $rootnamespace$&lt;br /&gt;{&lt;br /&gt; public class $safeitemrootname$&lt;br /&gt; {&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Then, I've just added the changed template-file back into the zip-file and thought the job was done, so I tried to create a new class in an existing Class Library...  Sadly, the new class was still not public by default...&lt;br /&gt;After some searching, it appeared that I had to execute a command which would load the Item Templates into Visual Studio.  The command to do so is:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;devenv /InstallVsTemplates&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This finally did the trick! When I add a new class in VS.NET 2005, this new class now has the public access modifier by default!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115957726487074626?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115957726487074626/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115957726487074626' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115957726487074626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115957726487074626'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/09/changing-default-access-modifier-when.html' title='Changing the default access modifier when adding a new class in VS.NET 2005'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115947257260740254</id><published>2006-09-28T21:38:00.000+02:00</published><updated>2007-01-20T01:04:26.644+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><title type='text'>Session on Unit Testing &amp; TDD</title><content type='html'>&lt;p&gt;Tomorrow, I'll be giving a talk on Unit Testing and Test Driven Development at work. I just hope that everything will go smoothly, and that I can encourage a few collegues to effectively use Unit Testing. :)&lt;/p&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=agile%20software%20development&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115947257260740254?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115947257260740254/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115947257260740254' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115947257260740254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115947257260740254'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/09/session-on-unit-testing-tdd.html' title='Session on Unit Testing &amp; TDD'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115947213894724610</id><published>2006-09-28T21:35:00.000+02:00</published><updated>2007-01-20T01:04:41.153+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>VS.NET 2003 not supported on Vista ?</title><content type='html'>&lt;p&gt;Today, I've encountered some articles on blogs that some unpleasant news.  &lt;br /&gt;It seems that Microsoft will not support Visual Studio .NET 2003 on Windows Vista; however, Visual Basic 6 will be supported.  I do not understand the logic behind this; &lt;br /&gt;I thought VB6 was deprecated ? &lt;br /&gt;Why is VS.NET 2003 not supported ? &lt;/p&gt;&lt;p&gt;I hope that these rumours will not turn into reality, since I'm still using VS.NET 2003 as my primary dev-tool at work.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/fbouma/archive/2006/09/27/So_2C00_-VB6-is-more-important-than-VS.NET-2003-I-suppose_3F00_.aspx"&gt;Frans Bouma: So, VB6 is more important then VS.NET 2003&lt;/a&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/bsimser/archive/2006/09/27/Join-the-Windows-XP-club_2E002E002E00_-for-5-more-years-or-so.aspx"&gt;Fear  and Loathing: Join the Windows XP club ... for 5 more years or so&lt;/a&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/pwilson/archive/2006/09/27/Vista-will-NOT-support-Developers.aspx"&gt;Paul  Wilson: Vista will NOT support developers&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115947213894724610?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115947213894724610/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115947213894724610' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115947213894724610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115947213894724610'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/09/vsnet-2003-not-supported-o_115947213894724610.html' title='VS.NET 2003 not supported on Vista ?'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115727773880561314</id><published>2006-09-03T12:02:00.000+02:00</published><updated>2007-01-20T11:45:03.085+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Collections in Business Entities</title><content type='html'>&lt;p&gt;A while ago, I've been thinking on what would be the best approach to work with collections inside business entities.&lt;/p&gt;&lt;p&gt;As an example, I'll refer back to the domain classes I'm using in another &lt;a href="http://fgheysels.blogspot.com/2006/07/ddd-quickstart-implementation-using.html"&gt;post&lt;/a&gt; of me.&lt;br /&gt;There, I have a class &lt;code&gt;Order&lt;/code&gt;, and an &lt;code&gt;OrderLine&lt;/code&gt; class.  The &lt;code&gt;Order&lt;/code&gt; class contains a collection of &lt;code&gt;OrderLines&lt;/code&gt;.&lt;br /&gt;In C# 2.0, this could look like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;OrderLine&amp;gt; orderLines &lt;br /&gt;    = new List&amp;lt;OrderLine&amp;gt;();&lt;br /&gt;}&lt;/pre&gt;The problem here, is that I want to have 'controlled access' to the &lt;code&gt;OrderLines&lt;/code&gt; collection in the &lt;code&gt;Order&lt;/code&gt; class. &lt;br /&gt;What I mean is, that a consumer of these classes should not be able to add an &lt;code&gt;OrderLine&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt; directly, since some additional action(s) need to be executed.&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=C%23%20generic%20programming&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;More precisely, if an &lt;code&gt;OrderLine&lt;/code&gt; is added to the &lt;code&gt;Order&lt;/code&gt;, I want to set a member of the &lt;code&gt;OrderLine&lt;/code&gt;, so that the &lt;code&gt;OrderLine&lt;/code&gt; knows to which &lt;code&gt;Order&lt;/code&gt; it belongs.&lt;br /&gt;Therefore, I create an &lt;code&gt;AddOrderLine&lt;/code&gt; method in the &lt;code&gt;Order&lt;/code&gt; class:&lt;pre class="code-content"&gt;public void AddOrderLine( OrderLine ol )&lt;br /&gt;{&lt;br /&gt;  ol.OwningOrder = this;&lt;br /&gt;  this.orderLines.Add (ol);&lt;br /&gt;}&lt;/pre&gt;A consumer of the code should always use the &lt;code&gt;AddOrderLine&lt;/code&gt; method if he wants to add an &lt;code&gt;OrderLine&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt;.&lt;br /&gt;You can enforce this easily by not making the &lt;code&gt;orderLines&lt;/code&gt; member public; easy enough.&lt;br /&gt;However, at one time, the consumer of your code (or you :) ) will want to iterate through the &lt;code&gt;OrderLines&lt;/code&gt; of the &lt;code&gt;Order&lt;/code&gt;, or he will want to know how many &lt;code&gt;OrderLines&lt;/code&gt; an &lt;code&gt;Order&lt;/code&gt; contains.&lt;/p&gt;&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;&lt;p&gt;Now, you'll have several options to achieve this:&lt;br /&gt;&lt;br /&gt;One solution is to expose the &lt;code&gt;orderLines&lt;/code&gt; collection to the public.  In other words: create a public property which exposes the collection to the outside:&lt;pre class="code-content"&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;OrderLine&amp;gt; orderLines&lt;br /&gt;    = new List&amp;lt;OrderLine&amp;gt;();&lt;br /&gt;&lt;br /&gt;  public List&amp;lt;OrderLine&amp;gt; OrderLines&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return orderLines;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Although this is a solution, I do not like it.&lt;br /&gt;Now, we're unable to force the user to use the &lt;code&gt;AddOrderLine&lt;/code&gt; method.  Since the &lt;code&gt;OrderLines&lt;/code&gt; collection is now publicly exposed, it is now possible to just use the &lt;code&gt;Add&lt;/code&gt; method of the &lt;code&gt;List&lt;/code&gt; to add &lt;code&gt;OrderLines&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt;.&lt;br /&gt;The documentation of the classes could of course mention that you should always use the &lt;code&gt;AddOrderLine&lt;/code&gt; method, but there's no real hard constraint here.  (If the classes are used inappropriatly, the program could of course crash, so then there is a constraint after all. ;)  However, then the programmer using those classes will maybe lose a lot of time to detect the error he made.&lt;/p&gt;&lt;p&gt;Another solution is to keep the &lt;code&gt;OrderLines&lt;/code&gt; collection private, and to add some extra members to the &lt;code&gt;Order&lt;/code&gt; class.  In this way, uncontrolled access to the &lt;code&gt;OrderLines&lt;/code&gt; is not possible.&lt;br /&gt;However, there are offcourse disadvantages to this approach as well.  First of all, you'll have to write some tedious code that just delegates the functionality to the Collection class, like this:&lt;pre class="code-content"&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;OrderLine&amp;gt; orderLines = ...&lt;br /&gt;&lt;br /&gt;  public void AddOrderLine( OrderLine ol )&lt;br /&gt;  {&lt;br /&gt;    ol.OwningOrder = this;&lt;br /&gt;    orderLines.Add (ol);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public int NumberOfOrderLines&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return orderLines.Count;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Unnecessary to say that this is just a boring task.&lt;br /&gt;Then, to be able to iterate through the &lt;code&gt;OrderLines&lt;/code&gt; of an &lt;code&gt;Order&lt;/code&gt;, you could write code like this:&lt;pre class="code-content"&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;OrderLine&amp;gt; orderLines = ...&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  public OrderLine[] GetOrderLines()&lt;br /&gt;  {&lt;br /&gt;    return orderLines.ToArray();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Actually, I think this is plain ugly.&lt;/p&gt;&lt;p&gt;I always have to make a choice between these 2 approaches, where each one has his disadvantages.  After doing this too much, I wanted a better solution for this problem, and after some reading and experimenting, I finally found one.  It is in fact such a simple solution that I can't imagine why I haven't been using this one much earlier...&lt;/p&gt;&lt;p&gt;It's just nothing more then this:&lt;br /&gt;Keep the collection private, create the necessary methods to provide the necessary controlled access to the collection (for instance the &lt;code&gt;AddOrderLine&lt;/code&gt; method, and create a public property which returns a read-only instance of the collection, so that you can iterate through the collection, change existing instances of objects within the collection (at least, this is only true if you have reference types in the collection; you will not be allowed to modify value types on a &lt;code&gt;ReadOnlyCollection&lt;/code&gt;, get the number of objects that are in the collection, ...&lt;br /&gt;&lt;br /&gt;In code, it looks like this:&lt;pre class="code-content"&gt;class Order&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;OrderLine&amp;gt; orderLines =&lt;br /&gt;    new List&amp;lt;OrderLine&amp;gt;();&lt;br /&gt;&lt;br /&gt;  public void AddOrderLine( OrderLine ol )&lt;br /&gt;  {&lt;br /&gt;    ol.Order = this;&lt;br /&gt;    orderLines.Add (ol);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public ReadOnlyCollection&amp;lt;OrderLine&amp;gt; OrderLines&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return orderLines.AsReadOnly();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;This is the C# 2.0 version.  &lt;br /&gt;Now, it is impossible to add OrderLines in an uncontrolled way, but it is possible to iterate the &lt;code&gt;OrderLines&lt;/code&gt; that are in the collection, and modify existing &lt;code&gt;OrderLine&lt;/code&gt; objects that are already in the collection (since it are instances of a reference type; if &lt;code&gt;OrderLine&lt;/code&gt; was a struct, it would not be possible to modify them.&lt;br /&gt;&lt;br /&gt;In C# 1.x, it looks like this:&lt;pre class="code-content"&gt;class Order&lt;br /&gt;{&lt;br /&gt;  private IList orderLines = new ArrayList();&lt;br /&gt;&lt;br /&gt;  public void AddOrderLine( OrderLine ol )&lt;br /&gt;  {&lt;br /&gt;    ol.Order = this;&lt;br /&gt;    orderLines.Add (ol);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public IList OrderLines&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;       return ArrayList.ReadOnly (orderLines);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Here, I return a readonly copy of the ArrayList.  It is still possible to iterate and change the items that are in the collection, but it is impossible to Add an &lt;code&gt;OrderLine&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt; in an uncontrolled fashion like this:&lt;pre class="code-content"&gt;Order o = new Order();&lt;br /&gt;OrderLine ol = new OrderLine();&lt;br /&gt;o.OrderLines.Add (ol);&lt;/pre&gt;&lt;/p&gt;In this case, a &lt;code&gt;System.NotSupportedException&lt;/code&gt; will be thrown.&lt;br /&gt;It would of course be nicer to not have an Add and Remove method on the read-only ArrayList property, like we've achieved in the C# 2.0 version (the &lt;code&gt;ReadOnlyCollection&lt;/code&gt; class does not have Add and Remove methods).  &lt;br /&gt;In .NET 1.x we can achieve that by letting the property return an &lt;code&gt;ICollection&lt;/code&gt; instead of an &lt;code&gt;IList&lt;/code&gt;, however, then we're not able to use an indexer to retrieve an &lt;code&gt;OrderLine&lt;/code&gt; like this:&lt;pre class="code-content"&gt;OrderLine ol = theOrder.OrderLines[i] as OrderLine;&lt;/pre&gt;Therefore, I prefer to return an &lt;code&gt;IList&lt;/code&gt; instead of an &lt;code&gt;ICollection&lt;/code&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115727773880561314?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115727773880561314/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115727773880561314' title='17 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115727773880561314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115727773880561314'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/09/collections-in-business-entities.html' title='Collections in Business Entities'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115667389279915468</id><published>2006-08-27T12:14:00.000+02:00</published><updated>2006-08-27T12:20:09.746+02:00</updated><title type='text'>1 month ago....</title><content type='html'>... I was still in Spain.  &lt;br /&gt;Surely, the weather was much better there then it is now here in Belgium and every evening, we could enjoy a beautiful sunset:&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/blog/FGH_0144.jpg" /&gt; &lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115667389279915468?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115667389279915468/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115667389279915468' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115667389279915468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115667389279915468'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/08/1-month-ago.html' title='1 month ago....'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115635723186463884</id><published>2006-08-23T20:14:00.000+02:00</published><updated>2007-01-20T01:05:25.576+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET 3.0'/><title type='text'>WinFX == .NET 3.0 ?</title><content type='html'>Scott Bellware makes an excellent point &lt;a href="http://codebetter.com/blogs/scott.bellware/archive/2006/08/23/148506.aspx"&gt;here&lt;/a&gt;, when he says that Microsoft should not rename WinFX to .NET 3.0.&lt;br /&gt;I can only agree with him that it will be confusing to rename WinFx to .NET 3.0, since WinFx is another product.&lt;br /&gt;WinFX is just an API, while .NET is a platform, so, why giving an API the name of a platform ?&lt;br /&gt;&lt;br /&gt;Anyway, I'm not going to repeat all of it here, so, if you also think that Microsoft will be making a huge mistake here, you should sign the &lt;a href="http://www.petitiononline.com/winfx/petition.html"&gt;petition&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115635723186463884?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115635723186463884/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115635723186463884' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115635723186463884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115635723186463884'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/08/winfx-net-30.html' title='WinFX == .NET 3.0 ?'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115601886739508978</id><published>2006-08-19T22:20:00.000+02:00</published><updated>2007-01-20T01:07:31.815+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Test Driven Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><title type='text'>DDD: A Quickstart - Part III</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;This is the 3rd post of a serie of posts on &lt;a href="http://domaindrivendesign.org/"&gt;Domain Driven Design&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Test_driven_development"&gt;Test Driven Development&lt;/a&gt;.&lt;br /&gt;In this article, I'll continue from where the &lt;a href="http://fgheysels.blogspot.com/2006/07/ddd-quickstart-implementation-using.html"&gt;previous article&lt;/a&gt; ended.  This means that I'll continue with the implementation of the Domain Model that was defined in &lt;a href="http://fgheysels.blogspot.com/2006/05/domain-driven-design-quickstart-part-1.html"&gt;the first article&lt;/a&gt; of this serie.&lt;/p&gt;&lt;p&gt;In the previous article, we tackled the functionality of Customers placing Orders, and Gold Customers receiving a discount.&lt;br /&gt;In this article, I'll focus on the behaviour for Bad Paying Customers and the creation of Invoices.&lt;/p&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=domain%20driven%20software%20design&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;h2&gt;Bad Paying Customers&lt;/h2&gt;&lt;p&gt;Now that we can determine whether a Customer is a 'Gold' Customer or not, we should build the functionality to determine whether a Customer is a bad payer or not. &lt;br /&gt;Just like I've done for the 'Gold Customer' case, I'd like to kick off with writing a small unit-test. &lt;br /&gt;&lt;br /&gt;A Customer is tagged as a bad paying Customer when 1/3 of his invoices are overdue/payed too late. The consequence of being a bad paying Customer is, that such a Customer cannot place Orders whose OrderTotal exceeds 250 euro. The test looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;public void TestBadPayingCustomerPlacesOrder()&lt;br /&gt;{&lt;br /&gt;  Customer c = custRep.GetCustomer (3);&lt;br /&gt;&lt;br /&gt;  // We 'know' that Customer 3 is a bad paying Customer.&lt;br /&gt;  // To check this, we can check the &lt;br /&gt;  // Status of the Customer. &lt;br /&gt;  Assert.AreEqual (CustomerStatus.BadPayer, &lt;br /&gt;    c.Status, &lt;br /&gt;    "This should be a bad paying Customer.");&lt;br /&gt;&lt;br /&gt;  // Get the Articles that we'll need.&lt;br /&gt;  Article art1 = artRep.GetArticle (1);&lt;br /&gt;  Article art2 = artRep.GetArticle (2);&lt;br /&gt;&lt;br /&gt;  // Check the prices of the articles, so that we &lt;br /&gt;  // do not make any false assumptiosn here.&lt;br /&gt;  Assert.AreEqual (15M, art1.Price, &lt;br /&gt;    "The 1st Article should have a unit-price of 15eur.");&lt;br /&gt;  Assert.AreEqual (12.5M, art2.Price, &lt;br /&gt;    "The 2nd Article should have a unit-price of 12.5eur.");&lt;br /&gt;&lt;br /&gt;  // Create the Order...&lt;br /&gt;  Order o = c.CreateNewOrder();&lt;br /&gt;&lt;br /&gt;  // and add the order-lines.&lt;br /&gt;  OrderLine ol1 = o.CreateNewOrderLine();&lt;br /&gt;  ol1.SetArticle (art1);&lt;br /&gt;  ol1.NumberOfItems = 10;&lt;br /&gt;&lt;br /&gt;  OrderLineAddQueryResult result = o.CanOrderLineBeAdded (ol1);&lt;br /&gt;  &lt;br /&gt;  Assert.AreEqual (OrderLineAddQueryResult.Yes, &lt;br /&gt;    result, &lt;br /&gt;    "The 1st OrderLine should pose no problem.");&lt;br /&gt;&lt;br /&gt;  // At this time, the OrderTotal should be 150eur.&lt;br /&gt;  Assert.AreEqual (150M, &lt;br /&gt;    o.TotalAmountToPay, &lt;br /&gt;    "The OrderTotal should be 150eur.");&lt;br /&gt;&lt;br /&gt;  // Create a 2nd OrderLine which will cause &lt;br /&gt;  // the Order-limit to be exceeded.&lt;br /&gt;  OrderLine ol2 = o.CreateNewOrderLine();&lt;br /&gt;  ol2.SetArticle (art2);&lt;br /&gt;  ol2.NumberOfItems = 10;&lt;br /&gt;&lt;br /&gt;  result = o.CanOrderLineBeAdded (ol2);&lt;br /&gt;&lt;br /&gt;  Assert.AreEqual (&lt;br /&gt;    OrderLineAddQueryResult.NoBecauseOrderLimitExceeded, &lt;br /&gt;    result,&lt;br /&gt;    "Bad Paying Customers are not allowed to place orders &gt; 250eur.");&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Before we can make this test succeed, we must be able to compile it. This means that we'll have to extend the &lt;code&gt;OrderLineAddQueryResult&lt;/code&gt; enumeration with the &lt;code&gt;NoBecauseOrderLimitExceeded&lt;/code&gt; value. This is a simple task, and just a matter of adding this value to the enumeration type. This is in fact so simple, that I'm not going to elaborate on that here.&lt;br /&gt;Once this is done, the test compiles, but it fails. This is due to the fact that we haven't implemented any functionality yet to determine whether a Customer is a bad paying Customer or not, so lets do that right away.&lt;br /&gt;A Bad Paying Customer is a Customer that has at least one out of three overdue invoices. To be able to check this, we need to introduce a new entity: the Invoice.&lt;br /&gt;Invoices are made for Orders that are shipped. An invoice contains a date when the Invoice has been created, and a due-date. Offcourse, it also contains the list of Articles that have been ordered and where the Customer has to pay for, and, it contains the amount that the Customer has to pay. &lt;br /&gt;Since &lt;code&gt;Invoices&lt;/code&gt; are created from &lt;code&gt;Order&lt;/code&gt;s that have been shipped, we can start with this unit-test:&lt;/p&gt;&lt;pre class="code-content"&gt;[Test]&lt;br /&gt;public void CreateInvoice()&lt;br /&gt;{&lt;br /&gt;    Order o = orderRepository.GetOrder (2);&lt;br /&gt;&lt;br /&gt;    Invoice inv = o.CreateInvoice();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This is rather simple!  But simple is good.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;As we can learn from the test, we'll need an &lt;code&gt;Invoice&lt;/code&gt; entity, and we'll need to write a &lt;code&gt;CreateInvoice&lt;/code&gt; method in the &lt;code&gt;Order&lt;/code&gt; class.&lt;br /&gt;Let's start off with the new entity.  The Invoice class contains these members:&lt;/p&gt;&lt;pre class="code-content"&gt;public class Invoice&lt;br /&gt;{&lt;br /&gt;  private int id;&lt;br /&gt;  private DateTime invoiceDate;&lt;br /&gt;  private DateTime dueDate;&lt;br /&gt;  private Customer owningCustomer;&lt;br /&gt;  private InvoiceStatus status;&lt;br /&gt;  private bool isOverdue;&lt;br /&gt;  private DateTime datePayed;&lt;br /&gt;  private decimal subTotal;&lt;br /&gt;  private decimal discount;&lt;br /&gt;  private IList invoiceLines = new ArrayList();&lt;br /&gt;&lt;br /&gt;  public int Id&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return id;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public DateTime InvoiceDate&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return invoiceDate;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public DateTime DueDate&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return dueDate;&lt;br /&gt;    }&lt;br /&gt;    set&lt;br /&gt;    {&lt;br /&gt;      dueDate = value;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Customer OwningCustomer&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return owningCustomer;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public InvoiceStatus Status&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return status;&lt;br /&gt;    }        &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public bool IsOverdue&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return isOverdue;&lt;br /&gt;    }&lt;br /&gt;    internal set&lt;br /&gt;    {&lt;br /&gt;      isOverdue = value;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public DateTime DatePayed&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return datePayed;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public decimal SubTotal&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return subTotal;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public decimal Discount&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return discount;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;An &lt;code&gt;Invoice&lt;/code&gt; should not be created directly by the user of our Domain classes, so it's not necessary (and not favorable) to have a public constructor in this class. Therefore, I've decided to create an internal constructor. An &lt;code&gt;Invoice&lt;/code&gt; is also created for one &lt;code&gt;Order&lt;/code&gt;, so the constructor can take the &lt;code&gt;Order&lt;/code&gt; for which the invoice is created, as an argument. Then, the constructor and it's implementation looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;public class Invoice&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  internal Invoice( Order o, DateTime invoiceDate, DateTime dueDate )&lt;br /&gt;  {&lt;br /&gt;    id = -1;&lt;br /&gt;    this.invoiceDate = invoiceDate;&lt;br /&gt;    this.dueDate = dueDate;&lt;br /&gt;    owningCustomer = o.OwningCustomer;&lt;br /&gt;    status = InvoiceStatus.Open;&lt;br /&gt;    isOverdue = false;&lt;br /&gt;    subTotal = o.OrderTotal;&lt;br /&gt;    discount = o.Discount;&lt;br /&gt;&lt;br /&gt;    foreach( OrderLine ol in o.OrderLines )&lt;br /&gt;    {&lt;br /&gt;      AddInvoiceLine (ol.ArticleId, &lt;br /&gt;        ol.ArticleName, &lt;br /&gt;        ol.ArticlePrice, &lt;br /&gt;        ol.NumberOfItems);&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;AddInvoiceLine&lt;/code&gt; method will add an &lt;code&gt;InvoiceLine&lt;/code&gt; object to the &lt;code&gt;invoiceLines&lt;/code&gt; collection for each &lt;code&gt;OrderLine&lt;/code&gt; object that is contained by the &lt;code&gt;Order&lt;/code&gt;:&lt;/p&gt;&lt;pre class="code-content"&gt;private void AddInvoiceLine( int articleId, &lt;br /&gt;  string articleName, &lt;br /&gt;  decimal articlePrice, &lt;br /&gt;  int numberOfItems )&lt;br /&gt;{&lt;br /&gt;  invoiceLines.Add (&lt;br /&gt;    new InvoiceLine (this, articleId, &lt;br /&gt;      articleName, articlePrice, numberOfItems));&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;An &lt;code&gt;InvoiceLine&lt;/code&gt; object contains information about the article for which the &lt;code&gt;Customer&lt;/code&gt; has to pay, and, offcourse, it contains a pointer to the &lt;code&gt;Invoice&lt;/code&gt; to which it belongs.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;CreateInvoice&lt;/code&gt; method should also be implemented.  This is quite simple; in this method, we'll just use the internal constructor of the &lt;code&gt;Invoice&lt;/code&gt; class, and we can also check if the &lt;code&gt;Order&lt;/code&gt; has been shipped, since it is not allowed to create &lt;code&gt;Invoices&lt;/code&gt; for &lt;code&gt;Orders&lt;/code&gt; that haven't been shipped.&lt;/p&gt;&lt;pre class="code-content"&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;  public Invoice CreateInvoice()&lt;br /&gt;  {&lt;br /&gt;    if( this.Status != OrderStatus.Shipped )&lt;br /&gt;    {&lt;br /&gt;      throw new ApplicationException ("The order hasn't been shipped yet.");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return new Invoice (this, DateTime.Now, DateTime.Now.AddDays (30));&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;As you can see, the &lt;code&gt;dueDate&lt;/code&gt; for the &lt;code&gt;Invoice&lt;/code&gt; is calculated in this method. By default, the &lt;code&gt;Customer&lt;/code&gt; has 30 days time to pay his invoice. &lt;br /&gt;Since this logic is quite simple, it can safely be placed inside the &lt;code&gt;CreateInvoice&lt;/code&gt; method. If the calculation of the due date would becomes more complicated, we could opt to factor this logic into a specification object.&lt;/p&gt;&lt;br /&gt;Once the &lt;code&gt;InvoiceLine&lt;/code&gt; class is implemented and the &lt;code&gt;CreateInvoice&lt;/code&gt; method has been written, the tests compile.  However, the first test still fails, because we still have no functionality written to determine if a &lt;code&gt;Customer&lt;/code&gt; is a bad payer.&lt;br /&gt;To do this, we can extend the &lt;code&gt;Status&lt;/code&gt; property of the &lt;code&gt;Customer&lt;/code&gt; class.&lt;/p&gt;&lt;pre class="code-content"&gt;public CustomerStatus Status&lt;br /&gt;{&lt;br /&gt;  get&lt;br /&gt;  {&lt;br /&gt;    CustomerStatus result = CustomerStatus.Normal;&lt;br /&gt;&lt;br /&gt;    // First get the amount of orders this &lt;br /&gt;    // customer has made in the last 3 months.&lt;br /&gt;    decimal orderTotal = DomainSettings.Instance.&lt;br /&gt;                           RepositoryFactoryObj.&lt;br /&gt;                           CreateOrderRepository().&lt;br /&gt;                           GetOrderTotalForCustomerSinceDate (this,&lt;br /&gt;                            (DateTime.Now.AddMonths (-3));&lt;br /&gt;                                                          &lt;br /&gt;    if( orderTotal &gt; DomainSettings.GoldAmountTreshold )&lt;br /&gt;    {&lt;br /&gt;      result = CustomerStatus.Gold;&lt;br /&gt;    } &lt;br /&gt;&lt;br /&gt;    // Check if the Customer is a bad Payer, &lt;br /&gt;    // if the Customer is a Gold Customer AND a bad payer, &lt;br /&gt;    // the Status should be 'Bad Payer'.&lt;br /&gt;        &lt;br /&gt;    // Get the number of invoices for this customer, &lt;br /&gt;    // and get the number of overdue invoices.&lt;br /&gt;    IInvoiceRepository invRep =   &lt;br /&gt;      DomainSettings.Instance.&lt;br /&gt;                     RepositoryFactoryObj.&lt;br /&gt;                     CreateInvoiceRepository();&lt;br /&gt;&lt;br /&gt;    int numberOfOverdueInvoices = &lt;br /&gt;      invRep.GetNumberOfOverdueInvoicesForCustomer (this);&lt;br /&gt;                                          &lt;br /&gt;    if( numberOfOverdueInvoices &gt; 0 )&lt;br /&gt;    {&lt;br /&gt;      int totalInvoices = invRep.GetNumberOfInvoicesForCustomer (this);&lt;br /&gt;                                    &lt;br /&gt;      if( numberOfOverdueInvoices / totalInvoices &gt; 0.333 )&lt;br /&gt;      {&lt;br /&gt;        result = CustomerStatus.BadPayer;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return result;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In order to get this code to compile, the &lt;code&gt;IInvoiceRepository&lt;/code&gt; interface and an implementation of it have to be created:&lt;/p&gt;&lt;pre class="code-content"&gt;public interface IInvoiceRepository&lt;br /&gt;{&lt;br /&gt;  int GetNumberOfOverdueInvoicesForCustomer( Customer c );&lt;br /&gt;  int GetNumberOfInvoicesForCustomer( Customer c );&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I'll also create an &lt;code&gt;InvoiceMemoryStoreRepository&lt;/code&gt; for the test-cases in the project that contains the unit-tests, and I'll extend the &lt;code&gt;IRepositoryFactory&lt;/code&gt; interface (and the classes that implement this interface) with a method that creates the correct &lt;code&gt;IInvoiceRepository&lt;/code&gt;. &lt;br /&gt;Once that is done, the &lt;code&gt;CanOrderLineBeAdded&lt;/code&gt; member method of the &lt;code&gt;Order&lt;/code&gt; class needs to be extended, so that we check if a &lt;code&gt;Customer &lt;/code&gt;is allowed to add the &lt;code&gt;OrderLine&lt;/code&gt; to it's &lt;code&gt;Order&lt;/code&gt;. This means that we'll have to adapt the &lt;code&gt;Order&lt;/code&gt; class a bit. &lt;br /&gt;First of all, I'd like to change the &lt;code&gt;isGoldCustomer&lt;/code&gt; boolean member that acted as some kind of a helper variable, in order that we do not have to check the &lt;code&gt;Status&lt;/code&gt; property of the &lt;code&gt;OwningCustomer&lt;/code&gt; (because checking this status can be considered as expensive). &lt;br /&gt;I want to do that because it is not sufficient any more to know if the 'owning customer' of the order, is a Gold Customer or not; now, I want to know whether the customer is a Normal Customer, a Gold Customer or a Bad Payer. Therefore, I removed the isGoldCustomer member variable, and changed it to a customerStatusHelper member, that is of type &lt;code&gt;CustomerStatus&lt;/code&gt;. &lt;br /&gt;I've put the code that has to keep track of the &lt;code&gt;CustomerStatus&lt;/code&gt; into a separate method that looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;private void DetermineCustomerStatus()&lt;br /&gt;{&lt;br /&gt;  customerStatusHelper = owningCustomer.Status;&lt;br /&gt;  customerStatusDetermined = true;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Then, the CalculateDiscount() member method of the Order class has to be changed as well:&lt;/p&gt;&lt;pre class="code-content"&gt;private void CalculateDiscount()&lt;br /&gt;{&lt;br /&gt;  if( customerStatusDetermined == false )&lt;br /&gt;  {&lt;br /&gt;    DetermineCustomerStatus();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( customerStatusHelper == CustomerStatus.Gold )&lt;br /&gt;  {&lt;br /&gt;    _discount = OrderTotal * 5 / 100;&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    _discount = 0M;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Although this is a rather small refactoring, thanks to our unit-test, we can be assured that our code is still correct.&lt;br /&gt;&lt;br /&gt;Now, we can add a test to the &lt;code&gt;CanOrderLineBeAdded&lt;/code&gt; member method to check whether the 'OrderLimit' is exceeded, when a &lt;code&gt;Customer&lt;/code&gt; who's a Bad Payer, makes an &lt;code&gt;Order&lt;/code&gt;:&lt;/p&gt;&lt;pre class="code-content"&gt;public OrderLineAddQueryResult CanOrderLineBeAdded( OrderLine ol )&lt;br /&gt;{&lt;br /&gt;  OrderLineAddQueryResult result = OrderLineAddQueryResult.Yes;&lt;br /&gt;  &lt;br /&gt;  if( customerStatusDetermined == false )&lt;br /&gt;  {&lt;br /&gt;    DetermineCustomerStatus();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( customerStatusHelper == CustomerStatus.BadPayer )&lt;br /&gt;  {&lt;br /&gt;    if( this.TotalAmountToPay + ol.LineTotal &gt; &lt;br /&gt;        DomainSettings.BadPayerOrderLimit )&lt;br /&gt;    {&lt;br /&gt;      result = OrderLineAddQueryResult.NoBecauseOrderLimitExceeded;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // the other checks that were shown before, are now not shown any more.&lt;br /&gt;  ...&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The GetReasonWhyOrderLineCantBeAdded needs to be extended as well, but I'm not going to discuss that.&lt;/p&gt;&lt;h2&gt;Creating Invoices&lt;/h2&gt;&lt;p&gt;Until this time, we're still unable to create &lt;code&gt;Invoices&lt;/code&gt; for &lt;code&gt;Orders&lt;/code&gt; that are shipped. As discussed earlier, creating &lt;code&gt;Invoices&lt;/code&gt; from shipped &lt;code&gt;Orders&lt;/code&gt; would best be implemented as a &lt;a href="http://patternshare.org/default.aspx/Home.DDD.Services"&gt;Service&lt;/a&gt;. &lt;br /&gt;This 'Domain Service' can then be used by a Windows Service, so that this task is done every night. The &lt;code&gt;CreateInvoices&lt;/code&gt; service should use an &lt;code&gt;IOrderRepository&lt;/code&gt; to get all the &lt;code&gt;Orders&lt;/code&gt; that are shipped, and for which no &lt;code&gt;Invoice&lt;/code&gt; has been created yet.&lt;br /&gt;For those &lt;code&gt;Orders&lt;/code&gt;, the service should create &lt;code&gt;Invoices&lt;/code&gt;. Given the text, the service should look like this:&lt;/p&gt;&lt;pre class="code-content"&gt;public static class CreateInvoicesService&lt;br /&gt;{&lt;br /&gt;  public static void CreateInvoices()&lt;br /&gt;  {&lt;br /&gt;    IOrderRepository ordersRep = &lt;br /&gt;      DomainSettings.RepositoryFactoryObj.CreateOrderRepository();&lt;br /&gt;    IInvoiceRepository invoiceRep = &lt;br /&gt;      DomainSettings.RepositoryFactoryObj.CreateInvoiceRepository();&lt;br /&gt;&lt;br /&gt;    IList orders = ordersRep.FindShippedOrdersWithoutInvoice();&lt;br /&gt;&lt;br /&gt;    foreach( Order o in orders )&lt;br /&gt;    {&lt;br /&gt;      Invoice inv = o.CreateInvoice();&lt;br /&gt;      invoiceRep.Save (inv);            &lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This is pretty straightforward. However, there is something that we haven't considered yet: transaction handling/boundaries.&lt;br /&gt;The Repository itself should not be responsible for starting and committing or rollbacking transactions.&lt;br /&gt;The Repository is not aware of the context of the transaction, so therefore, it is not the task of the repository to start and commit a transaction. Only the client knows when to start and commit a transaction, so it's his responsability. In this case, it's the &lt;code&gt;CreateInvoicesService&lt;/code&gt; service that is responsible for starting and committing (or rollbacking) the transaction.&lt;br /&gt;But since we haven't focused on persistence yet, we'll just keep this in the back of our mind. &lt;br /&gt;Now that we're able to create &lt;code&gt;Invoices&lt;/code&gt;, we're still not able to determine whether an &lt;code&gt;Invoice&lt;/code&gt; is overdue or not. This batch-process should also be implemented as a service.&lt;br /&gt;An &lt;code&gt;IInvoiceRepository&lt;/code&gt; should be used that gives us all &lt;code&gt;Invoices&lt;/code&gt; that haven't been payed yet.&lt;br /&gt;Then, each &lt;code&gt;Invoice&lt;/code&gt; that has been returned can be checked by a Specification class if the &lt;code&gt;Invoice&lt;/code&gt; is overdue or not. If the &lt;code&gt;Invoice&lt;/code&gt; is overdue, the &lt;code&gt;IsOverdue&lt;/code&gt; flag can be set, and the &lt;code&gt;Invoice&lt;/code&gt; can be persisted. If the logic to determine whether the &lt;code&gt;Invoice&lt;/code&gt; is overdue or not would be complicated, and depend on several other factors (like, for instance the &lt;code&gt;CustomerStatus&lt;/code&gt; of the &lt;code&gt;Customer&lt;/code&gt;), then this would be a good solution.&lt;br /&gt;In this case however, things are quite simple: an &lt;code&gt;Invoice&lt;/code&gt; is overdue if it is past its duedate (however, we could allow a retention period of say 4 days). This means that marking all &lt;code&gt;Invoices&lt;/code&gt; that are past their due date as overdue, could be done be just one query. In other words, the MarkOverdueInvoices Service could be as simple as just calling a method on the &lt;code&gt;IInvoiceRepository&lt;/code&gt;:&lt;/p&gt;&lt;pre class="code-content"&gt;public static class MarkOverdueInvoicesService&lt;br /&gt;{&lt;br /&gt;  public static void MarkOverdueInvoices()&lt;br /&gt;  {&lt;br /&gt;    IInvoiceRepository invRep = &lt;br /&gt;      DomainSettings.RepositoryFactoryObj.CreateInvoiceRepository();&lt;br /&gt;&lt;br /&gt;    invRep.MarkOverdueInvoices();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;So, I think that's about it. I think we've discussed the most important part of the Domain. Now, we can concentrate on the persistance of the objects in a relation database. For more information about test driven development, you can read these &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/GuidelinesforTDD.asp"&gt;guidelines&lt;/a&gt;, written by Jeffrey Palermo.&lt;/p&gt;&lt;p&gt;That's it for now.  In the next article (if it ever comes, since I still have to start writing...) of this series, I'll try to tackle the persistence functionality using NHibernate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115601886739508978?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115601886739508978/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115601886739508978' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115601886739508978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115601886739508978'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/08/ddd-quickstart-part-iii_19.html' title='DDD: A Quickstart - Part III'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115498336530796906</id><published>2006-08-07T22:26:00.000+02:00</published><updated>2007-01-20T01:05:40.019+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>New photography toy!</title><content type='html'>&lt;p&gt;I was planning to blog about this, but I constantly postponed it until I almost forgot about it.  And how could I forget this...  &lt;/p&gt;&lt;p&gt;After years of using only a film-camera, I've finally decided to go digital.  Well, I'm not really telling the truth here, since I already own a digital compact camera since 2003.&lt;br /&gt;Anyway, I finally decided to buy a digital SLR camera.  Since I already have a Nikon F55 film SLR, the choice was easy: my digital SLR would also be a Nikon.&lt;br /&gt;After reading some reviews and testing different models, I've made a decision on the model as well: it would be a Nikon D200.&lt;/p&gt;&lt;p&gt;I've ordered the camera in May 2006, and I got it in my hands in the first week of July 2006.&lt;/p&gt;&lt;p&gt;I have the D200 now for about one month, and it is a great camera.  I've done about 1000 shots in past month, and although I've already achieved some satisfying results, I feel that I haven't unleashed its full potential yet.&lt;br /&gt;I hope that I'll be able to show the progress I make by regularly posting a photo here on my blog.&lt;/p&gt;&lt;p&gt;Here you see my F55 film camera next to the D200.  Quite a difference in size!&lt;br /&gt;&lt;small&gt;Image taken with a compact digital camera&lt;/small&gt;&lt;/p&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://users.pandora.be/fgzone/pics/woei.jpg"&gt;&lt;/img&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=8&amp;l=as1&amp;asins=B000CRZCPO&amp;fc1=000000&amp;IS2=1&amp;lt1=_blank&amp;lc1=0000FF&amp;bc1=000000&amp;bg1=FFFFFF&amp;f=ifr" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115498336530796906?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115498336530796906/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115498336530796906' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115498336530796906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115498336530796906'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/08/new-photography-toy.html' title='New photography toy!'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115488586533544940</id><published>2006-08-06T19:21:00.000+02:00</published><updated>2006-08-06T19:38:38.763+02:00</updated><title type='text'>This is the end....</title><content type='html'>&lt;p&gt;... of my holiday.&lt;br /&gt;The last day of my holiday is coming to an end.  This means that, as from tomorrow, I'll be spending most of my time behind my workstation at work, developing in .NET.&lt;/p&gt;&lt;p&gt;I've had a nice 3 weeks of vacation wich have passed very fast, which is a good sign: it means that I haven't bored myself. :)&lt;br /&gt;My girlfriend and I made a trip to Spain, continued our search for a suitable and affordable piece of terrain where we could build a house.  Hopefully, that quest will come to an end soon, because it becomes quite exhausting.&lt;/p&gt;&lt;p&gt;Next to that, I've read some books, and I've continued reading &lt;a href="http://www.amazon.com/gp/product/0321268202/ref=sr_11_1/002-4414597-9020853?ie=UTF8"&gt;Applying Domain Driven Design and Patterns&lt;/a&gt;, and so far, I find it a very good book.&lt;/p&gt;&lt;p&gt;I've also spent some time preparing for a session about Unit Testing and Test Driven Design wich I'm planning to do in the near future at work.&lt;br /&gt;I hope to finish those preparations very soon, so that I can concentrate on the next episode of my blog article regarding Domain Driven Design.&lt;/p&gt;&lt;p&gt;I think I can say that the past 3 weeks were well spent, and I'll be ready to get back to work with a fresh, relaxed mind.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115488586533544940?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115488586533544940/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115488586533544940' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115488586533544940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115488586533544940'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/08/this-is-end.html' title='This is the end....'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115436998569422060</id><published>2006-07-31T20:15:00.000+02:00</published><updated>2007-01-20T01:06:15.969+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Back in Belgium</title><content type='html'>&lt;p&gt;I'm back from vacation.&lt;br /&gt;&lt;br /&gt;I've spent the past few days in Southern Spain, in the province of Andalucia.&lt;br /&gt;We stayed in a hotel nearby the 'La Barossa' beach, from where we've explored the nearby region a bit.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/med_sid.jpg" /&gt;&lt;br /&gt;&lt;p&gt;This picture has been taken in Medina Sidonia.  A 'pueblo blanco' (white town), a few km's more inward the country.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115436998569422060?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115436998569422060/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115436998569422060' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115436998569422060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115436998569422060'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/07/back-in-belgium.html' title='Back in Belgium'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-115325607997736327</id><published>2006-07-18T22:53:00.000+02:00</published><updated>2007-01-20T01:07:00.270+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Test Driven Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>DDD: A Quickstart - Implementation using Test Driven Development</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;In the &lt;a href="http://fgheysels.blogspot.com/2006/05/domain-driven-design-quickstart-part-1.html"&gt;first part&lt;/a&gt; of this serie, I've focussed on the modelling of the domain-model.&lt;br /&gt;Now, since the entities, aggregates, etc... have been roughly defined in the first part, I think it's time to dive into the code.&lt;br /&gt;Instead of directly writing classes to model the domain, I want to develop the domain layer using the &lt;a href="http://en.wikipedia.org/wiki/Test_driven_development"&gt;Test Driven Development Mantra&lt;/a&gt;.&lt;br /&gt;This means that I should start with writing a test case before I actually write the 'functional' code.  &lt;br /&gt;I will develop the domain using C#, so I'll use the &lt;a href="http://www.nunit.org/"&gt;NUnit&lt;/a&gt; Framework to create the unit tests.&lt;br /&gt;&lt;h2&gt;A first use case&lt;/h2&gt;&lt;br /&gt;It would be helpfull to have some kind of a use case, so that I can start with writing a test for that use case.&lt;br /&gt;I can start off with the use case that can be found in part 1 of this article:&lt;br /&gt;&lt;quote&gt;A customer places an order&lt;/quote&gt;.&lt;br /&gt;By writing a unit test for this scenario first, I'm forced to think about the class interfaces.  Writing the test first, will help me in creating a clear, simple and easy to use class interface and class hierarchy since I will design the classes in the way that I want to use them.&lt;br /&gt;The Unit Test for the use case 'Customer places an Order' looks like this:&lt;pre class='code-content'&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class OrderTestCase&lt;br /&gt;{&lt;br /&gt;  [Test]&lt;br /&gt;  public void TestNormalCustomerPlacesOrder()&lt;br /&gt;  {&lt;br /&gt;    ICustomerRepository custRep = &lt;br /&gt;      new CustomerMemoryStoreRepository();&lt;br /&gt;    IArticleRepository artRep = &lt;br /&gt;      new ArticleMemoryStoreRepository();&lt;br /&gt;&lt;br /&gt;    // Get a customer from the repository.  We should&lt;br /&gt;    // make sure that this customer is a normal customer.&lt;br /&gt;    Customer c = custRep.GetCustomer (1);&lt;br /&gt;&lt;br /&gt;    // Create a new order for the Customer.&lt;br /&gt;    Order o = c.CreateNewOrder ();&lt;br /&gt;&lt;br /&gt;    // The Customer orders some articles...&lt;br /&gt;    Article art1 = artRep.GetArticle (1);&lt;br /&gt;    Article art2 = artRep.GetArticle (2);&lt;br /&gt;    OrderLine ol1 = o.CreateNewOrderLine();&lt;br /&gt; &lt;br /&gt;    ol1.NumberOfItems = 2;&lt;br /&gt;    ol1.SetArticle (art1);&lt;br /&gt;&lt;br /&gt;    o.AddOrderLine (ol1);&lt;br /&gt;    OrderLine ol2 = o.CreateNewOrderLine();&lt;br /&gt;        &lt;br /&gt;    ol2.NumberOfItems = 5;&lt;br /&gt;    ol2.SetArticle (art2);&lt;br /&gt;&lt;br /&gt;    o.AddOrderLine (ol2);&lt;br /&gt;&lt;br /&gt;    // Now, the order-total should be equal&lt;br /&gt;    // to ( 2 * price of art1 ) + ( 5 * price of art2 )&lt;br /&gt;    Assert.AreEqual ((2 * art1.Price ) + ( 5 * art2.Price ),&lt;br /&gt;                      o.OrderTotal );&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This first test is pretty simple.  It shows how a Customer can place an Order, and it checks if the &lt;code&gt;OrderTotal&lt;/code&gt; of the &lt;code&gt;Order&lt;/code&gt; is correct.&lt;br /&gt;While this test case is very simple, you can already discover the advantage of Test Driven Development: by writing a Test first, I've already defined the API, the interface of the domain classes.  This will make sure that the interface of those classes will be clear and intention revealing.&lt;br /&gt;By writing the test first, you're forced to design the classes in a way where it's easy to use them.  I think the above test is quite self-describing and doesn't need any further explanation.&lt;/p&gt;&lt;p&gt;Now, we should make the test pass.  However, I know that good TDD practice preaches that you should make sure that the test fails first &lt;i&gt;meaningfully&lt;/i&gt;.  This is actually a quite important step, because it enables you to make sure that you know that what you're testing is correct.   However, to keep this article a little bit more concise, I've allowed myself to skip this step.&lt;br /&gt;At this point the test fails offcourse since we haven't written any functional code yet, so lets start with that right away.&lt;/p&gt;&lt;p&gt;The first thing that you encounter, is the &lt;code&gt;ICustomerRepository&lt;/code&gt; and the &lt;code&gt;IArticleRepository&lt;/code&gt; interface, along with the &lt;code&gt;CustomerMemoryStoreRepository&lt;/code&gt; and &lt;code&gt;ArticleMemoryRepository&lt;/code&gt; classes which implement these interfaces.&lt;br /&gt;For this test, it is sufficient that the &lt;code&gt;ICustomerRepository&lt;/code&gt; and the &lt;code&gt;IArticleRepository&lt;/code&gt; interfaces look like this:&lt;pre class="code-content"&gt;&lt;br /&gt;public interface ICustomerRepository&lt;br /&gt;{&lt;br /&gt;    Customer GetCustomer( int customerId );&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface IArticleRepository&lt;br /&gt;{&lt;br /&gt;    Article GetArticle( int articleId );&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;CustomerMemoryStoreRepository&lt;/code&gt; and the &lt;code&gt;ArticleMemoryStoreRepository&lt;/code&gt; class are not a part of the Domain Layer.  These classes are 'mock objects' that live in the assembly that contains the test cases.  They do not really access a database, they just hold some objects in memory.&lt;/p&gt;&lt;p&gt;Then, let's get along with the &lt;code&gt;Customer&lt;/code&gt; class.  To be able to make the above test pass, it would be sufficient that the Customer class only has a &lt;code&gt;CreateNewOrder()&lt;/code&gt; method at this point.  However, we also want to identify a customer (it is an entity), so let's give it a name and an id as well.&lt;br /&gt;This should be the implementation of the Customer class:&lt;pre class="code-content"&gt;&lt;br /&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;    private int _id;&lt;br /&gt;    private string _name;&lt;br /&gt;&lt;br /&gt;    public Customer() : this(-1, string.Empty)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Customer( int id, string name )&lt;br /&gt;    {&lt;br /&gt;        _id = id;&lt;br /&gt;        _name = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _id;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public string Name&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _name;&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            _name = value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Order CreateNewOrder()&lt;br /&gt;    {&lt;br /&gt;        return new Order(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;CreateNewOrder&lt;/code&gt; method creates a new Order, and passes the current Customer object as an argument.  This is necessary so that we know to which Customer an Order belongs.&lt;br /&gt;Since we have a reference to the Order class in the Customer class, lets continue with writing the Order class.&lt;/p&gt;&lt;p&gt;Since an Order is also an entity, we will want to uniquely identify an Order as well, so we're going to add an Id to this class as well.  Next to that, we also want to know when the Order was made, so let's give the Order class an OrderDate member as well.&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Order&lt;br /&gt;{&lt;br /&gt;    private int _id;&lt;br /&gt;    private DateTime _orderDate;&lt;br /&gt;    private Customer _customer;&lt;br /&gt;    private IList _orderLines = new ArrayList();&lt;br /&gt;&lt;br /&gt;    public Order( Customer c ) : this(-1, c, DateTime.Now)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Order( int id, Customer c, DateTime orderDate )&lt;br /&gt;    {&lt;br /&gt;        _id = id;&lt;br /&gt;        _customer = c;&lt;br /&gt;        _orderDate = orderDate;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _id;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public DateTime OrderDate&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _orderDate;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Customer OwningCustomer&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _customer;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public IList OrderLines&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _orderLines;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public decimal OrderTotal&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            decimal total = 0.0M;&lt;br /&gt;  &lt;br /&gt;            foreach( OrderLine ol in _orderLines )&lt;br /&gt;            {&lt;br /&gt;                total += ol.NumberOfItems * ol.ArticlePrice;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return total;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public OrderLine CreateNewOrderLine()&lt;br /&gt;    {&lt;br /&gt;        return new OrderLine (this);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void AddOrderLine( OrderLine ol )&lt;br /&gt;    {&lt;br /&gt;        // Perform some checks before adding.&lt;br /&gt;        if( ol.IsArticleSet == false )&lt;br /&gt;        {&lt;br /&gt;            throw new ApplicationException ("You must specify an Article.");&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        if( ol.NumberOfItems == 0 )&lt;br /&gt;        {&lt;br /&gt;            throw new ApplicationException ("You must order at least one item.");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        _orderLines.Add (ol);&lt;br /&gt;    }    &lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Note that I've used an IList for the _orderLines member, and not the generic IList&amp;lt;T&amp;gt; type which would provide strong typing.  The reason why I did this, is that I'm planning to use NHibernate as an O/R mapper between the domain classes and the relational database, and the version of NHibernate that I have now does not support generics yet.&lt;br /&gt;I think the &lt;code&gt;Order&lt;/code&gt; class is pretty straightforward and doesn't need any further explanation.  I think the code is pretty self-explaining.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The OrderLine and Article class haven't been created yet, so let's do that right now.&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class OrderLine&lt;br /&gt;{&lt;br /&gt;    private int _id;&lt;br /&gt;    private int _articleId;&lt;br /&gt;    private string _articleName;&lt;br /&gt;    private decimal _articlePrice;&lt;br /&gt;    private int _numberOfItems;&lt;br /&gt;    private Order _owningOrder;&lt;br /&gt;    private bool _isArticleSet;&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    internal bool IsArticleSet&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _isArticleSet;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int ArticleId&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _articleId;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public string ArticleName&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _articleName;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public decimal ArticlePrice&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;           return _articlePrice;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int NumberOfItems&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _numberOfItems;&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            _numberOfItems = value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Order OwningOrder&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _owningOrder;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public OrderLine( Order owningOrder )&lt;br /&gt;    {&lt;br /&gt;        _owningOrder = owningOrder;&lt;br /&gt;        id = -1;&lt;br /&gt;        _articlePrice = 0.0M;&lt;br /&gt;        _articleName = string.Empty;&lt;br /&gt;        _numberOfItems = 0;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    public void SetArticle( Article art )&lt;br /&gt;    {&lt;br /&gt;        if( art != null )&lt;br /&gt;        {&lt;br /&gt;            _articleId = art.Id;&lt;br /&gt;            _articleName = art.Name;&lt;br /&gt;            _articlePrice = art.Price;&lt;br /&gt;            _isArticleSet = true;&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            _articleId = -1;&lt;br /&gt;            _articleName = string.Empty;&lt;br /&gt;            _articlePrice = 0.0M;&lt;br /&gt;            _isArticleSet = false;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I've decided to not put a reference to an &lt;code&gt;Article&lt;/code&gt; object in the &lt;code&gt;OrderLine&lt;/code&gt; class, because, when an existing Order is retrieved, the price of the Article can already have changed opposed to the price of the Article at the time the Order was made.  So, we're not interested in the current price of the Article, but in the price of the Article at the time when the Customer has ordered that Article.&lt;br /&gt;Therefore, I've decided to just put the Article information that is of interest into the &lt;code&gt;OrderLine&lt;/code&gt; class, and make that information available through read-only properties.  I've created a &lt;code&gt;SetArticle&lt;/code&gt; method so that all the required Article information can be set at once, and in this way, we're sure that the Article information in the OrderLine class is correct.&lt;br /&gt;The &lt;code&gt;IsArticleSet&lt;/code&gt; flag is marked internal since it is of no use outside our domain-assembly.  At this time, only the &lt;code&gt;Order&lt;/code&gt; class uses it to check if the OrderLine that's being added has an Article set.&lt;/p&gt;&lt;p&gt;The Article class is very simple:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Article&lt;br /&gt;{&lt;br /&gt;   private int _id;&lt;br /&gt;   private string _name;&lt;br /&gt;   private decimal _price;&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _id;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public string Name&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _name;&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            _name = value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public decimal Price&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return _price;&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            _price = value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Article() : this(-1, string.Empty, 0.0M)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Article( int id, string name, decimal price )&lt;br /&gt;    {&lt;br /&gt;        _id = id;&lt;br /&gt;        _name = name;&lt;br /&gt;        _price = price;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This code now makes sure that the test we've written passes.   However, as I've said earlier, if I was to follow the TDD rules strictly, I should write code first that makes the test fail meaningfully.  I've chosen to skip this step though, so that I can make this post a little bit shorter.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;At this stage, we have the functionality that we need for a Customer to place Orders.  However, we're not there yet.  The code we have now, does not take 'Gold Customers' and 'Bad Paying Customers' into account.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Test Case for Gold Customers&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;To implement the functionality which gives Gold Customers a discount, I'll start of with writing a Unit Test again.  This test is pretty similar as the previous one, but you should note that the Repositories are now promoted to be member variables of the class that contains the test-methods.&lt;/p&gt;&lt;p&gt;This is how the test looks like:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TestGoldCustomerPlacesOrder()&lt;br /&gt;{&lt;br /&gt;  // 2 is a gold-customer..&lt;br /&gt;  Customer c = custRep.GetCustomer (2); &lt;br /&gt;&lt;br /&gt;  // To be sure of it, test it...&lt;br /&gt;  Assert.IsTrue (c.Status == CustomerStatus.Gold, &lt;br /&gt;                 "We should have a gold customer.");&lt;br /&gt;&lt;br /&gt;  Order o = c.CreateNewOrder ();&lt;br /&gt;&lt;br /&gt;  Article art1 = artRep.GetArticle (1);&lt;br /&gt;  Article art2 = artRep.GetArticle (2);&lt;br /&gt;&lt;br /&gt;  OrderLine ol1 = o.CreateNewOrderLine ();&lt;br /&gt;&lt;br /&gt;  ol1.SetArticle (art1);&lt;br /&gt;  ol1.NumberOfItems = 10;&lt;br /&gt;&lt;br /&gt;  o.AddOrderLine (ol1);&lt;br /&gt;&lt;br /&gt;  OrderLine ol2 = o.CreateNewOrderLine ();&lt;br /&gt;&lt;br /&gt;  ol2.SetArticle (art2);&lt;br /&gt;  ol2.NumberOfItems = 7;&lt;br /&gt;&lt;br /&gt;  o.AddOrderLine (ol2);&lt;br /&gt;&lt;br /&gt;  // The expected OrderTotal is the 'regular'&lt;br /&gt;  // OrderTotal minus a 5% discount.&lt;br /&gt;  decimal expected = ( 10 * art1.Price ) + ( 7 * art2.Price );&lt;br /&gt;  expected -= ( expected * 5 ) / 100;&lt;br /&gt;&lt;br /&gt;  Assert.AreEqual (expected, o.TotalAmountToPay);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In the above test, I retrieve the Customer with Id 2 out of the Repository.  I assume that this is a Gold Customer, and, to be sure, the Status property of the Customer is tested.&lt;br /&gt;Note that I haven't defined this property in the first version of the Customer class since there was no need for that property in the first unit test.  This means that I'll have to extend the Customer class with a Status property.&lt;br /&gt;This Status property will return an enumeration-type which indicates whether this Customer is a 'Normal', 'Gold' or 'Bad Paying' Customer.  This also implies that I'll have to create that enumeration type.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's start of with creating the enumeration type, and adding the Status property to the Customer class.  The enumeration is very simple:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public enum CustomerStatus&lt;br /&gt;{&lt;br /&gt;  Normal,&lt;br /&gt;  Gold,&lt;br /&gt;  BadPayer&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Determining the correct Status of a Customer requires a bit more work.  First of all, we need to determine whether the Customer is a 'Gold Customer'.  We'll have to do this by getting the total amount of money that a Customer has spent on Orders in the last 3 months.  Since the Customer class does not contain a collection of the Orders that he made, we'll have to ask a Repository what that amount is for a specific Customer.&lt;br /&gt;If this amount is over 2500 euro, the Customer is a Gold Customer.&lt;br /&gt;To determine whether the Customer is a bad payer, we should do something similar; we'll need to know how many of his invoices have been or are overdue.  To be able to know this, we'll also have to call in a repository.&lt;br /&gt;In a first case, I'll concentrate on determining whether a Customer is a Gold Customer or not.&lt;/p&gt;&lt;p&gt;To do this, I'll have to extend the Customer class with the &lt;code&gt;Status&lt;/code&gt; property:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  public CustomerStatus Status&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      CustomerStatus result = CustomerStatus.Normal;&lt;br /&gt;&lt;br /&gt;      // First get the amount of orders this &lt;br /&gt;      // customer has made in the last 3 months.&lt;br /&gt;      decimal orderTotal = DomainSettings.Instance.&lt;br /&gt;                             RepositoryFactoryObj.&lt;br /&gt;                             CreateOrderRepository().&lt;br /&gt;                             GetOrderTotalForCustomerSinceDate (this,&lt;br /&gt;                              (DateTime.Now.AddMonths (-3));&lt;br /&gt;                                                          &lt;br /&gt;      if( orderTotal &gt; DomainSettings.GoldAmountTreshold )&lt;br /&gt;      {&lt;br /&gt;        result = CustomerStatus.Gold;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      return result;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This code is pretty straightforward I guess.  First of all, we assume that the Customer is a Normal Customer.  Then, I 'ask' the &lt;code&gt;OrderRepository&lt;/code&gt; to give me the total amount for which the given Customer has placed Orders in the last 3 months.   If this value is higher then a specific value, we can say that this Customer is a Gold Customer.&lt;/p&gt;&lt;p&gt;To be able to compile and run this code, we'll need some additional classes.&lt;br /&gt;The first new class that springs into view, is the &lt;code&gt;DomainSettings&lt;/code&gt; class, which appears to be a &lt;a href="http://www.castle-cadenza.demon.co.uk/single.htm"&gt;singleton&lt;/a&gt;.&lt;br /&gt;I've decided to create this class to get easy access to things like repositories, and usefull constants, like the &lt;code&gt;GoldAmountTreshold&lt;/code&gt; property.  The &lt;code&gt;DomainSettings&lt;/code&gt; class looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class DomainSettings&lt;br /&gt;{&lt;br /&gt;  private static DomainSettings _instance;&lt;br /&gt;&lt;br /&gt;  public static DomainSettings Instance&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      if( _instance == null )&lt;br /&gt;      {&lt;br /&gt;        _instance = new DomainSettings();&lt;br /&gt;      }&lt;br /&gt;      return _instance;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public const decimal GoldAmountTreshold = 2500;&lt;br /&gt;&lt;br /&gt;  private IRepositoryFactory _repositoryFactoryObj;&lt;br /&gt;&lt;br /&gt;  public IRepositoryFactory RepositoryFactoryObj&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      if( _repositoryFactoryObj == null )&lt;br /&gt;      {&lt;br /&gt;        object repType = Configuration.&lt;br /&gt;                           ConfigurationManager.&lt;br /&gt;                           AppSettings["repositorytype"];&lt;br /&gt;&lt;br /&gt;        if( repType != null )&lt;br /&gt;        {&lt;br /&gt;          switch( repType.ToString().Trim().ToLower() )&lt;br /&gt;          {&lt;br /&gt;            case "testrepositories" :&lt;br /&gt;              // Since I've put the 'test/mock' &lt;br /&gt;              // repositories in another assembly, &lt;br /&gt;              // along with my unit-tests, &lt;br /&gt;              // use reflection to load &lt;br /&gt;              // the correct assembly and type.&lt;br /&gt;              Assembly asm = Assembly.LoadFrom ("BlogShopTests.dll");&lt;br /&gt;              object o = asm.CreateInstance&lt;br /&gt;                          ("BlogShopTests.RepositoryMocks.TestRepositoryFactory", &lt;br /&gt;                           true);&lt;br /&gt;              _repositoryFactoryObj = o as IRepositoryFactoryObj;           &lt;br /&gt;              break;&lt;br /&gt;            default :&lt;br /&gt;              throw new ConfigurationErrorsException (&lt;br /&gt;                "Unknown repository-type: " + repType.ToString());&lt;br /&gt;          }   &lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;          throw new ConfigurationErrorsException (&lt;br /&gt;            "Repository type must be defined in the app-settings.");&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      return _repositoryFactoryObj;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;As you can see, I've implemented the &lt;code&gt;DomainSettings&lt;/code&gt; class as a singleton, so that there can only be one instance of that class.  The most interesting thing in this class is without any doubt the &lt;code&gt;RepositoryFactoryObj&lt;/code&gt; property.&lt;br /&gt;Since the unit-tests I've written so far are using 'mock' repositories instead of the real ones that will be used eventually, I wanted to be able to hide which implementation of the Repository that should be used.  This is extremely usefull in the &lt;code&gt;Status&lt;/code&gt; property of the &lt;code&gt;Customer&lt;/code&gt; class.  When running the tests, the 'mock' repositories should be used, and, when running the code 'for real', the 'real' Repository should be used.&lt;br /&gt;To be able to do this, I've provided the &lt;code&gt;DomainSettings&lt;/code&gt; class with a property that returns a Factory that will create the correct repository objects.&lt;br /&gt;The concrete type of the factory that must be used, depends on a value in the configuration file.&lt;br /&gt;Since the &lt;code&gt;TestRepositoryFactory&lt;/code&gt; and the TestRepositories are in the assembly that contains all the unit-tests, and since I do not want to create a dependency between the assembly that contains the Domain classes, and the assembly that contains the test classes, I use reflection to create the &lt;code&gt;TestRepositoryFactory&lt;/code&gt;.  The &lt;a href="http://en.wikipedia.org/wiki/Abstract_factory_pattern"&gt;Abstract Factory&lt;/a&gt; approach for the creation of the Repositories, allows me to hide which specific factory is to be used.  In other words: the user -in this case the Customer class- is left unaware of which specific Factory that's being used and therefore, which specific repository that will be used.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To be able to create and use the abstract factory, an interface is needed which describes the methods that a 'RepositoryFactory' object must have.  In our client code, we can then talk to that interface without knowing which specific implementation is being used.&lt;br /&gt;At this moment, it is sufficient if the interface looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public interface IRepositoryFactory&lt;br /&gt;{&lt;br /&gt;    ICustomerRepository CreateCustomerRepository();&lt;br /&gt;    IOrderRepository CreateOrderRepository();&lt;br /&gt;    IArticleRepository CreateArticleRepository();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Once this is done, the implementation of the &lt;code&gt;TestRepositoryFactory&lt;/code&gt; is fairly easy; it just instantiates and returns an instance of the correct Repository:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public TestRepositoryFactory : IRepositoryFactory&lt;br /&gt;{&lt;br /&gt;    public ICustomerRepository CreateCustomerRepository()&lt;br /&gt;    {&lt;br /&gt;        return new CustomerMemoryStoreRepository();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public IOrderRepository CreateOrderRepository()&lt;br /&gt;    {&lt;br /&gt;        return new OrderMemoryStoreRepository();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public IArticleRepository CreateArticleRepository()&lt;br /&gt;    {&lt;br /&gt;        return new ArticleMemoryStoreRepository();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In the &lt;code&gt;DomainSettings&lt;/code&gt; class, it is now just a matter of creating the correct &lt;code&gt;IRepositoryFactory&lt;/code&gt; object depending on the value that's found in the configuration file.&lt;br /&gt;Once the &lt;code&gt;GetOrderTotalForCustomerSinceDate&lt;/code&gt; is implemented, we're able to determine if the Customer is a Gold Customer or not.  The implementation of the &lt;code&gt;GetOrderTotalForCustomerSinceDate&lt;/code&gt; method of the &lt;code&gt;OrderMemoryStoreRepository&lt;/code&gt; could be as simple as just returning true if the given Customer has a specific Id, and otherwise false.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;We still need to make our test pass, because at this point, we haven't written any functionality yet to calculate the OrderTotal for Gold Customers.  As we learn from the user story, Gold Customers receive a discount of 5% on their OrderTotal.&lt;br /&gt;It is usefull to extend the &lt;code&gt;Order&lt;/code&gt; class with a member variable that holds the discount.  For Normal Customers, this discount variable will just contain 0.&lt;br /&gt;I think that the best place calculate the discount -if there's one-, is in the &lt;code&gt;AddOrderLine&lt;/code&gt; method of the &lt;code&gt;Order&lt;/code&gt; class.  Each time an &lt;code&gt;OrderLine&lt;/code&gt; is added to an &lt;code&gt;Order&lt;/code&gt;, the amount of the discount for a Gold Customer changes.&lt;br /&gt;There's also something else were I haven't paid attention to yet: once an &lt;code&gt;Order&lt;/code&gt; is confirmed by the &lt;code&gt;Customer&lt;/code&gt;, it should not be possible to add or remove &lt;code&gt;OrderLine&lt;/code&gt;s from it. &lt;br /&gt;It is however possible that a user cancels the complete Order.  This pops up another issue: an Order should have an &lt;code&gt;OrderStatus&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Let's start with one thing at a time, and begin with the calculation of the discount.&lt;br /&gt;As I've said earlier, the best place to calculate this discount is in the &lt;code&gt;AddOrderLine&lt;/code&gt; member method of the &lt;code&gt;Order&lt;/code&gt; class.  Thus, we can extend this method so that it looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public void AddOrderLine( OrderLine ol )&lt;br /&gt;{&lt;br /&gt;  // Perform some checks here to see if all necessary stuff is provided.&lt;br /&gt;  if( ol.NumberOfItems == 0 )&lt;br /&gt;  {&lt;br /&gt;    throw new ApplicationException ("You must at least order 1 item.");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( ol.IsArticleSet == false )&lt;br /&gt;  {&lt;br /&gt;    throw new ApplicationException (&lt;br /&gt;      "You must specify the article that must be ordered.");&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.OrderLines.Add (ol);&lt;br /&gt;&lt;br /&gt;  // Calculate the discount.&lt;br /&gt;  CalculateDiscount ();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In the CalculateDiscount method, we'll just have to check the Status property of the Customer to which the Order belongs to. However, since determining the Status of a Customer is a rather expensive operation (remember that we have to query the database to determine the status) and the fact that the AddOrderLine method is likely to be called more then one time (an Order can contain more then one OrderLine), it would be better to call this Status property only one time. We can do this like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;private bool customerStatusDetermined = false;&lt;br /&gt;private bool isGoldCustomer = false;&lt;br /&gt;&lt;br /&gt;private void CalculateDiscount()&lt;br /&gt;{&lt;br /&gt;  if( customerStatusDetermined == false )&lt;br /&gt;  {&lt;br /&gt;    if( _owningCustomer != null )&lt;br /&gt;    {&lt;br /&gt;      isGoldCustomer = _owningCustomer.Status == CustomerStatus.Gold;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      isGoldCustomer = false;&lt;br /&gt;    }&lt;br /&gt;    customerStatusDetermined = true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( isGoldCustomer )&lt;br /&gt;  {&lt;br /&gt;    _discount = this.OrderTotal * &lt;br /&gt;      DomainSettings.Instance.GoldDiscountPercentage;&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    _discount = 0M;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I think that this code is pretty easy as well: the &lt;code&gt;CustomerStatus&lt;/code&gt; is determined if this hasn't been done yet, and if the &lt;code&gt;Customer&lt;/code&gt; is a Gold Customer, we calculate the discount on the &lt;code&gt;Order&lt;/code&gt;.&lt;br /&gt;Note that we'll have to add an extra property to the &lt;code&gt;DomainSettings&lt;/code&gt; class as well, that contains the percentage of the discount for Gold Customers:&lt;br /&gt;&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class DomainSettings&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;  private decimal _discountPercentageForGoldCustomers = 0.05M;&lt;br /&gt;&lt;br /&gt;  public decimal GoldDiscountPercentage&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return _discountPercentageForGoldCustomers;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;You should also note that the Test that has been written to test the 'Gold Customer' case, checks the &lt;code&gt;TotalAmountToPay&lt;/code&gt; property of the &lt;code&gt;Order&lt;/code&gt; class.   This property is quite simple, and just returns the &lt;code&gt;OrderTotal&lt;/code&gt; minus the &lt;code&gt;Discount&lt;/code&gt;&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;  public decimal TotalAmountToPay&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return OrderTotal - Discount;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  ...&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This functionality makes our 2nd test pass.  Now, I've already talked about  the fact that an &lt;code&gt;Order&lt;/code&gt; should have a Status.   This means that, when a &lt;code&gt;Customer&lt;/code&gt; confirms his &lt;code&gt;Order&lt;/code&gt;, the &lt;code&gt;Order&lt;/code&gt; should have the status 'Confirmed'.  Orders that have been confirmed cannot be changed anymore.  This means that it should be impossible to add or remove &lt;code&gt;OrderLines&lt;/code&gt; from it.  However, a &lt;code&gt;Customer&lt;/code&gt; should be able to cancel a confirmed &lt;code&gt;Order&lt;/code&gt;.  However, once the &lt;code&gt;Order&lt;/code&gt; is shipped, it cannot be cancelled anymore.&lt;br /&gt;Knowing all this, we can say that an &lt;code&gt;Order&lt;/code&gt; can have 3 states:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Confirmed&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cancelled&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Shipped&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;However, this is not sufficient.  When an &lt;code&gt;Order&lt;/code&gt; is just created, it cannot have the Confirmed, Cancelled or Shipped status.  As long as the &lt;code&gt;Order&lt;/code&gt; has not been confirmed yet, it has the status 'Pending'.  This means that we have in fact 4 OrderStates:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Confirmed&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cancelled&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Shipped&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pending&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;We can now start to create some unit-tests in where we will test this behaviour.  First of all, we'll create a unit-test in where we create an &lt;code&gt;Order&lt;/code&gt;, add some &lt;code&gt;OrderLines&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt; and confirm the &lt;code&gt;Order&lt;/code&gt;.  Afterwards, we'll check if we can still add or remove &lt;code&gt;OrderLines&lt;/code&gt; from that &lt;code&gt;Order&lt;/code&gt;.&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TestConfirmOrder()&lt;br /&gt;{&lt;br /&gt;  Customer c = custRep.GetCustomer (1);&lt;br /&gt;&lt;br /&gt;  Article art1 = artRep.GetArticle (1);&lt;br /&gt;  Article art2 = artRep.GetArticle (2);&lt;br /&gt;&lt;br /&gt;  Order o = c.CreateNewOrder();&lt;br /&gt;&lt;br /&gt;  OrderLine ol1 = o.CreateNewOrderLine();&lt;br /&gt;  ol1.SetArticle (art1);&lt;br /&gt;  ol1.NumberOfItems = 5;&lt;br /&gt;  o.AddOrderLine (ol1);&lt;br /&gt;&lt;br /&gt;  o.ConfirmOrder();&lt;br /&gt;&lt;br /&gt;  Assert.AreEqual (OrderStatus.Confirmed, &lt;br /&gt;    o.Status, &lt;br /&gt;    "The OrderStatus should be confirmed.");&lt;br /&gt;&lt;br /&gt;  // Now, the Order is confirmed, so it should not be &lt;br /&gt;  // possible to still add or remove&lt;br /&gt;  // OrderLines&lt;br /&gt;  OrderLine ol2 = o.CreateNewOrderLine ();&lt;br /&gt;  ol2.SetArticle (art2);&lt;br /&gt;  ol2.NumberOfItems = 10;&lt;br /&gt; &lt;br /&gt;  Assert.AreEqual (o.CanOrderLineBeAdded (ol2), &lt;br /&gt;    OrderLineAddQueryResult.NoBecauseOrderIsConfirmed,&lt;br /&gt;    "It should not be possible to add an orderline now."); &lt;br /&gt;   &lt;br /&gt;  // Now, just check what if the Status is correct if we cancel the order.&lt;br /&gt;  o.CancelOrder ();&lt;br /&gt;&lt;br /&gt;  Assert.AreEqual (OrderStatus.Cancelled, &lt;br /&gt;    o.Status, &lt;br /&gt;    "The OrderStatus should be cancelled.");&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;To be able to compile this test, we'll have to add some methods to our classes.&lt;br /&gt;The first one that we encounter, is the &lt;code&gt;ConfirmOrder&lt;/code&gt; method, which sets the status of our &lt;code&gt;Order&lt;/code&gt; to &lt;code&gt;Confirmed&lt;/code&gt;.  This means that we'll have to add a property &lt;code&gt;Status&lt;/code&gt; to the &lt;code&gt;Order&lt;/code&gt; class and create an enumerated type &lt;code&gt;OrderStatus&lt;/code&gt;.&lt;br /&gt;A little bit further, you can also see the &lt;code&gt;CancelOrder&lt;/code&gt; method that sets the &lt;code&gt;Status&lt;/code&gt; of the &lt;code&gt;Order&lt;/code&gt; to &lt;code&gt;Cancelled&lt;/code&gt;.&lt;br /&gt;Adding these methods is a piece of cake:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Order&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;  private OrderStatus _status = OrderStatus.Pending;&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;    &lt;br /&gt;  public OrderStatus Status&lt;br /&gt;  {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      return _status;   &lt;br /&gt;    }   &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  public void ConfirmOrder()&lt;br /&gt;  {&lt;br /&gt;    if( _status != OrderStatus.Pending )&lt;br /&gt;    {&lt;br /&gt;      throw new ApplicationException (&lt;br /&gt;        "An order can only be confirmed when " +&lt;br /&gt;        "it's current status is Pending."); &lt;br /&gt;    }  &lt;br /&gt;    _status = OrderStatus.Confirm;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void CancelOrder()&lt;br /&gt;  {&lt;br /&gt;    if( _status == OrderStatus.Shipped )&lt;br /&gt;    {&lt;br /&gt;      throw new ApplicationException (&lt;br /&gt;        "An order that is shipped, " +&lt;br /&gt;        "cannot be cancelled.");  &lt;br /&gt;    }  &lt;br /&gt;    _status = OrderStatus.Cancelled;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;As you can see, these methods contain some simple business-logic: a check is made if the Order can be confirmed, or can be cancelled. &lt;br /&gt;Now, I still need to implement the &lt;code&gt;OrderStatus&lt;/code&gt; enumeration:&lt;pre class="code-content"&gt;&lt;br /&gt;public enum OrderStatus&lt;br /&gt;{&lt;br /&gt;    Pending,&lt;br /&gt;    Confirmed,&lt;br /&gt;    Cancelled,&lt;br /&gt;    Shipped&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The above code is pretty obvious, but, it is not sufficient yet in order to make the test pass, or even compile.  As you can see in the test, another method is needed: &lt;code&gt;CanOrderLineBeAdded&lt;/code&gt;.   As you can see in the test, this method does not return a simple boolean value, even though the name of the method let's you think that it would.  The reason why I choose to not return a boolean here, is very simple: instead of just knowing whether it is possible or not to add an &lt;code&gt;OrderLine&lt;/code&gt; to an &lt;code&gt;Order&lt;/code&gt;, I also want to know &lt;i&gt;why&lt;/i&gt; it would not be possible.  Therefore, I've choosen to return an enumeration, which has these possible values:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public enum OrderLineAddQueryResult&lt;br /&gt;{&lt;br /&gt;    Yes,&lt;br /&gt;    NoBecauseOrderIsConfirmed,&lt;br /&gt;    NoBecauseOrderIsCancelled,&lt;br /&gt;    NoBecauseOrderIsShipped&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Then, the &lt;code&gt;CanOrderLineBeAdded&lt;/code&gt; member method of the &lt;code&gt;Order&lt;/code&gt; class looks like this:&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public OrderLineQueryResult CanOrderLineBeAdded( OrderLine ol )&lt;br /&gt;{&lt;br /&gt;  OrderLineAddQueryResult result = OrderLineAddQueryResult.Yes;&lt;br /&gt;&lt;br /&gt;  switch( this.Status )&lt;br /&gt;  {&lt;br /&gt;    case OrderStatus.Confirmed :&lt;br /&gt;      result = OrderLineAddQueryResult.&lt;br /&gt;        NoBecauseOrderIsConfirmed;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;    case OrderStatus.Cancelled :&lt;br /&gt;      result = OrderLineAddQueryResult.&lt;br /&gt;        NoBecauseOrderIsCancelled;&lt;br /&gt;      break;&lt;br /&gt;&lt;br /&gt;      case OrderStatus.Shipped :&lt;br /&gt;        result = OrderLineAddQueryResult.&lt;br /&gt;          NoBecauseOrderIsShipped;&lt;br /&gt;        break;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return result;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The purpose of this method is that the user of our class can check if an &lt;code&gt;OrderLine&lt;/code&gt; can be added, without exceptions being thrown. This method is usefull to know for instance which UI controls should be enabled/disabled, or what message should be given to the user of the software.&lt;br /&gt;But, the existance of this method does not ensures us that this method shall be used as well, therefore, we should enforce this rule in the AddOrderLine method of the Order class as well.&lt;/p&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public void AddOrderLine( OrderLine ol )&lt;br /&gt;{&lt;br /&gt;  OrderLineQueryAddResult result = this.CanOrderLineBeAdded (ol);&lt;br /&gt;  if( result != OrderLineQueryAddResult.Yes )&lt;br /&gt;  {&lt;br /&gt;    throw new ApplicationException(&lt;br /&gt;      GetReasonWhyOrderLineCantBeAdded (result));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( ol.NumberOfItems == 0 )&lt;br /&gt;  {&lt;br /&gt;    throw new ApplicationException ("You must at least order 1 item.");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if( ol.IsArticleSet == false )&lt;br /&gt;  {&lt;br /&gt;    throw new ApplicationException (&lt;br /&gt;      "You must specify the article that must be ordered.");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  this.OrderLines.Add (ol);&lt;br /&gt;&lt;br /&gt;  CalculateDiscount();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I will not elaborate here on the &lt;code&gt;GetReasonWhyOrderLineCantBeAdded&lt;/code&gt; member method very much.  It is sufficient if you know that this is just a private method which returns an error-message, depending on the value of the &lt;code&gt;OrderLineQueryAddResult&lt;/code&gt; parameter.&lt;br /&gt;The business rules that are in the AddOrderLine method can offcourse be avoided by simply not using the AddOrderLine method at all, and instead, using the Add method of the OrderLines property which is publicly exposed. Therefore, it should be better if we do not expose the Order collection publicly, but, for now, I'll leave it like it is.&lt;/p&gt;&lt;p&gt;As this is one very long post already,  I'll end it right here.  I'll keep the rest of the implementation for another article.&lt;br /&gt;Please, feel free to post your comments, critics, questions, remarks.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-115325607997736327?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/115325607997736327/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=115325607997736327' title='8 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115325607997736327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/115325607997736327'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/07/ddd-quickstart-implementation-using.html' title='DDD: A Quickstart - Implementation using Test Driven Development'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114848368156025284</id><published>2006-05-24T17:03:00.000+02:00</published><updated>2007-01-20T11:45:49.072+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Test Driven Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><title type='text'>Domain Driven Design: A Quickstart (Part 1)</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;Some time ago, I bought the book &lt;a href="http://www.amazon.com/gp/product/0321125215/sr=8-1/qid=1145095062/ref=pd_bbs_1/102-1392002-7603313?%5Fencoding=UTF8"&gt;Domain Driven Design, tackling complexity in the heart of software&lt;/a&gt;.&lt;br /&gt;Since reading it, I became very interested in the &lt;a href="http://behaviour-driven.org/DomainDrivenDesign/"&gt;Domain Driven Design paradigm&lt;/a&gt;.   For enterprise applications, it would be ideal if you could express the core of the application (the domain layer; the part of the program that contains the business logic) in a good model.&lt;br /&gt;The Object Oriented Programming paradigm provides a good way to express the model in a computer program.&lt;br /&gt;&lt;br /&gt;So, although the behaviour can be expressed in an OO fashion, the data needs to be persisted as well.  In most cases, a relational database is used to persist the data.   Combining OO and RDBMS'es gives us the problem of the &lt;a href="http://www.agiledata.org/essays/impedanceMismatch.html"&gt;Object / Relational mismatch&lt;/a&gt;.  You can offcourse solve this &lt;a href="http://en.wikipedia.org/wiki/Object-Relational_Impedance_Mismatch"&gt;object-relational impedance mismatch&lt;/a&gt; yourself by writing a &lt;acronym title="Data Access Layer"&gt;DAL&lt;/acronym&gt; that nicely maps the classes of your domain model to the tables of your relational database.  In most cases, this means that you'll have to write a lot of code.  Instead of implementing this functionality yourself, you could also opt for using one of the many existing O/R mapping tools, like &lt;a href="http://www.hibernate.org/"&gt;NHibernate&lt;/a&gt; or &lt;a href="http://llblgen.com/defaultgeneric.aspx"&gt;LLBLGen&lt;/a&gt;.&lt;br /&gt;As Frans Bouma once explained in &lt;a href="http://weblogs.asp.net/fbouma/archive/2004/10/09/240225.aspx"&gt;one of his blogposts&lt;/a&gt;, there are different types of O/R mappers.   NHibernate fits in another category then LLBLGen; In Frans' categorization, NHibernate fits in the 'Domain Approach', while LLBLGen fits in the Entity approach.&lt;br /&gt;Since I'm interested in the Domain Driven approach, I've taken a look at NHibernate, and, while &lt;a href="http://fgheysels.blogspot.com/2006/03/nhibernate-wheres-productivity.html"&gt;it's not 100% perfect&lt;/a&gt;, it still has a lot of advantages.  It releases you from some boring tasks (like mapping - hey, that's why it's called an O/R mapper), and takes care of some more complex tasks (caching, state-tracking, ...).&lt;br /&gt;&lt;br /&gt;The idea of this blogpost is to provide a little quickstart in Domain Driven Design and NHibernate, by creating a piece of software for a particular use case.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The Case&lt;/h2&gt;&lt;br /&gt;My idea was to create a simple application for a shop/manufacturer.   A customer can order multiple goods at a time, and, when a customer has ordered for over 2500euro in the past 3 months, this customer is a gold customer.&lt;br /&gt;When the order is shipped, an invoice has to be created for that Order.  Gold Customers receive a discount of 5% on their invoice.  On the other hand, customers that are known as 'bad payers', cannot place orders that have an order total that exceeds 250 euro.  A customer is tagged as a 'bad paying customer', when 1/3rd of his invoices have been overdue.&lt;br /&gt;Let’s say that a customer can make an order by phone, and via the website of the shop.&lt;br /&gt;Pretty simple, no ? :)   This is off-course not a real-world example, but it should be sufficient for the purpose of this article.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Modelling the domain&lt;/h2&gt;&lt;br /&gt;Following the Domain Driven Design principle, a model consists of &lt;a href="http://patternshare.org/default.aspx/Home.DDD.Entities"&gt;entities&lt;/a&gt;, &lt;a href="http://patternshare.org/default.aspx/Home.DDD.ValueObjects"&gt;value objects&lt;/a&gt; and &lt;a href="http://patternshare.org/default.aspx/Home.DDD.Services"&gt;services&lt;/a&gt;. We can already extract some entities out of the given text:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Customer&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Article&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Order&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Invoice&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Another entity that is not so obvious, is the &lt;strong&gt;OrderLine&lt;/strong&gt; entity.  This one is needed because a Customer can order more then one article at a time, so we need to know which Articles have been ordered, and how many of them are ordered.&lt;br /&gt;For the Invoice entity, it's the same story: there must be an InvoiceLine entity that represents each 'line' on the invoice.&lt;br /&gt;This means that, at this time, our model consists of 6 entities.   There are no Value objects and Services defined yet.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;The entities that we've defined can be drawn in a first schema:&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/schema1a.jpg"&gt;&lt;br /&gt;As you can see, a customer can have 0, 1 or more Orders, an Order contains one or more OrderLines, and every OrderLine must contain exactly one Article.&lt;br /&gt;For each Order, there can be one Invoice.&lt;br /&gt;If this were a database schema, this would be perfect.   However, this is an (concise) UML diagram, and the classes in this diagram should not describe how our data must be persisted, but how our application should behave.&lt;br /&gt;&lt;br /&gt;Now, there are some things in this ‘design’ that can be improved.   If you look at the Customer and Order classes in the schema, you see that a Customer &lt;i&gt;has&lt;/i&gt; a collection of Orders.   This is in fact correct, but, I wonder if this is necessary to express in our domain-model.&lt;br /&gt;In this case, we’re more interested in knowing to which Customer a specific Order belongs, rather then knowing or getting all the Orders of a specific Customer.   To get a list of all the Orders of a specific Customer, we can always add a method in a Repository that gives us the list of Orders for a Customer, instead of giving the Customer class a collection of Orders.  (I will come back on the Repository part later).  This will simplify things a bit.   This also means that, if we have customers that have made a lot of Orders, the Customer Object for that Customer doesn’t have to hold a large collection of Order objects.&lt;br /&gt;For the relationship between the Order and OrderLine class, things are a bit different.  I do not think we can give a direction to this relationship, since, we do want to know the OrderLines of an Order, since they are coupled to each other: an Order exists only because of its OrderLines.   And for each OrderLine, we do want to know to which Order it belongs.  So, this association has to be kept bidirectional.&lt;br /&gt;Then again, the relationship between Order and Invoice, doesn't have to be bidirectional.  I do not even know if we should have a 'coded' relationship between these 2 entities, because I don't think that it will often occur that we need to see the invoice that is linked to an order, or, the related order of an invoice.  If we do need that, we can always get them by calling a method on the repository.  However, I will keep the link between Order and Invoice on the schema, since, they're in a way linked to each other.&lt;br /&gt;&lt;br /&gt;This gives us the following schema:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/schema2a.jpg"&gt;&lt;br /&gt;&lt;br /&gt;In this schema, you can see the directions of the associations.&lt;br /&gt;&lt;br /&gt;The next step, is to define the &lt;a href="http://patternshare.org/default.aspx/Home.DDD.Aggregates"&gt;aggregates&lt;/a&gt; in the model.  An aggregate ‘clusters’ the entities and value objects that belong together.&lt;br /&gt;In this case, we can define 4 aggregates: Customer, Order, Invoice and Product.&lt;br /&gt;The Customer and Product aggregate only contain 1 entity, while the Order aggregate and the Invoice aggregate contains 2 entities; the Order and the OrderLine entity make up the Order aggregate, and the Order entity is the ‘aggregate root’.  The aggregate root is the only object in the aggregate, where other objects that are outside of that aggregate, may have references to.&lt;br /&gt;The Invoice aggregate is very similar: it's made up by the Invoice and the InvoiceLine entity, and the Invoice entity is the aggregate root.&lt;br /&gt;&lt;br /&gt;Once we know the aggregates, we can define the repositories for our domain model.  A repository is an abstraction which gives us references to our aggregates, and allows us to persist those aggregates.   The underlying infrastructure can be a relational database, a file, …  but our model doesn’t need to know that.   We just have to be able to get aggregates, and save them back, so the repository provides us this abstraction.&lt;br /&gt;We should not create a repository for every class in our model, we should create a repository per aggregate.  In our example, it makes no sense to be able to retrieve OrderLine objects, without retrieving the corresponding Order object.&lt;br /&gt;Knowing all this, we can extend our schema:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/schema3.JPG"/&gt;&lt;br /&gt;&lt;br /&gt;Here, you can see the 4 repositories (I've added some example operations to it), and the 4 aggregates.   I've also drawn the aggregate boundaries of the Order and the Invoice aggregate.  Since the other 2 aggregates (Customer and Product) only consist out of 1 entity, it is not necessary to draw their boundaries as well.&lt;br /&gt;&lt;br /&gt;There is one thing that we'll need to keep in the back of our mind: we have to be able to create Invoices for Orders that are shipped and that have no Invoice yet.  It would be a good idea to create a batch-process that runs every night, and that creates Invoices for Orders that are shippend and have no invoice yet.  In other words: this would be ideally implemented as a &lt;a href="http://patternshare.org/default.aspx/Home.DDD.Services"&gt;service&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now that we have identified the entities, aggregates and repositories that make up our domain model, we could start to put the model into code,&lt;br /&gt;but, I'll keep that for another post that I hope to finish soon. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114848368156025284?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114848368156025284/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114848368156025284' title='8 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114848368156025284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114848368156025284'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/05/domain-driven-design-quickstart-part-1.html' title='Domain Driven Design: A Quickstart (Part 1)'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114648584129400651</id><published>2006-05-01T14:11:00.000+02:00</published><updated>2007-01-20T11:50:04.978+01:00</updated><title type='text'>Is Software Development too hard, or too easy ?</title><content type='html'>&lt;p&gt;I've come across some rather interesting blog-articles, like &lt;a href="http://codebetter.com/blogs/scott.bellware/archive/2006/04/25/143303.aspx" target="_blank"&gt;this one&lt;/a&gt; from Scott Bellware and &lt;a href="http://codebetter.com/blogs/jeffrey.palermo/archive/2006/04/27/143522.aspx" target="_blank"&gt;this one&lt;/a&gt; from Jeffrey Palermo.&lt;/p&gt;&lt;p&gt;Both articles are a reaction to &lt;a href="http://www.ftponline.com/vsm/2006_04/magazine/departments/guestop/" target="_blank"&gt;this article&lt;/a&gt; from Rockford Lhotka.  Rockford Lhotka says in his article that software development is too hard, that we -developers- have to spend too much time doing 'plumbing work', instead of concentrating on delivering business value.&lt;br /&gt;In a way, he has a point: the main point in writing a business application, is to create an application that solves the business problem, and, since time is money and business is constantly changing, it should be done as quick as possible.  Isn't this what we're all striving for ? &lt;/p&gt;&lt;p&gt;However, this should not be done at all costs.   I mean, there are RAD tools available that will allow you to 'develop' an application quickly, but, if those tools are used in an inappropriate way, you'll end up with an 'application' that is a hell to maintain and to extend.&lt;br /&gt;I think we've all seen those kind of applications: in a first phase, those app's do what they have to do, but, as requirements change, the code gets messier and gets hard to understand and it gets even harder to implement new functionality or change existing functionality.&lt;/p&gt;&lt;p&gt;This is where the opinion of Jeffrey Palermo comes in: the RAD tools allow you to build an application without requiring to know what's going on under the hood, and they make it possible that somebody who is not trained in software engineering can create an application.   However, the quality of that application will most likely be poor.&lt;br /&gt;To put it in his words:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;It’s too easy for an unskilled person to throw a screen together and deploy it.  It’s too easy for Joe blow to create a database application that pulls over entire tables to the client for modifying one record (but it works – initially).  It’s too easy for a newbie to get excited about a new technology and completely screw up an application with web service calls to itself and overdo sending XML to Sql Server 2000.  It’s too easy to a database guy to throw tons of business logic in stored procedures, call them from ASP and call it an application (until a skilled programmer looks at it later and has a heart attack).&lt;/i&gt;&lt;/blockquote&gt;&lt;p&gt;The problem with RAD tools, is that everybody can now create an application that does what it should do.  It allows people that are not trained in software engineering to create an application that does what it has to do, and it's possible that the user of the application doesn't notice that the application is actually a piece of crap.  And I believe this happens all too often.&lt;/p&gt;&lt;p&gt;My opinion is that RAD tools can reduce the workload, but they should be used with care.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;When a RAD tool is used inappropriatly to create a business application, the chances are big that the developer uses the 'Smart UI' antipattern.  This means that all the business logic of the application is put directly into the user interface.   This is off course problematic when the requirements change.  Since the business functionality is scattered throughout the user interface, the programmer who has to maintain this application will have to delve into the UI to find all the code and the related code that has to be changed.  When the application is large, it's easy to forget or overlook something that has to be changed, and it will result in a buggy application.&lt;br /&gt;Or, imagine that you've build a Windows application, and once it's delivered, your customer or boss wants to have a web interface for this application as well.  When all business logic is implemented in the user interface, this means that you will not be able to just reuse that code in the web application.  The result will most likely be duplicated business logic.&lt;/p&gt;&lt;p&gt;That's why I believe that a RAD tool should be used with care.  In my opinion, you should use the RAD features of your development tool to build the User Interface, and that's about it.&lt;br /&gt;Developing software is more then just dragging some components on a form, glueing them together by setting some properties and using wizards to get the data from the database and bind it to some kind of control.&lt;br /&gt;Scott Bellware is right when he says that &lt;a href="http://geekswithblogs.net/sbellware/archive/2005/08/07/49519.aspx" target="_blank"&gt;the RAD functionality of Microsoft's development tools encourages one to create badly designed software&lt;/a&gt;.  Microsoft shows in demo's how one could create an application very fast, with a very small number of lines of code with their RAD tools.  The sad thing is that there are developers attending these demo's who think afterwards that this must be the way to develop applications.  And this is not only true for developers attending these sessions.  Managers seeing these demo's can think that software development isn't that hard at all, and they also do not understand how it comes that it takes so much time developing an application.&lt;br /&gt;This is not a good way of building software.  Those RAD tools and wizards are very good for giving demo-sessions (and selling the tools), but they're defenitly not showing a correct way on how to build software.&lt;br /&gt;What about the maintainability and flexibility of software created in this way ? What about the ability to create unit-tests to test the functionality of the implemented business rules ?  It is all impossible with applications that are developed in this way.&lt;/p&gt;&lt;p&gt;For the core of the application -the business functionality- the development team should create a domain model that expresses the business problem that the application must tackle.  This means off course&lt;br /&gt;that the initial development cost of the application will be higher, but, this development cost should be seen as an investment.  The model will be easier to maintain, extend and to reuse, and, by using &lt;a href="http://www.martinfowler.com/articles/newMethodology.html"&gt;Agile development&lt;br /&gt;techniques&lt;/a&gt;, the customer can be involved in the development process.  By using small development iterations and having customer input after each iteration, the customer knows that the development of the application is going forward, and he can ring the alarm when he sees that the functionality of the application or the business logic is wrong.&lt;/p&gt;&lt;p&gt;To conclude: RAD tools provide a way to make software development easier, and because of that, one could be tempted to create an application in a quick and dirty way.  However, building high-quality software still requires skilled and educated/trained developers.  They're not only required to be able to create a good domain model.  They also have to be skilled in a way that they know for what they should and shouldn't use RAD tools.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114648584129400651?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114648584129400651/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114648584129400651' title='4 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114648584129400651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114648584129400651'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/05/is-software-development-too-hard-or.html' title='Is Software Development too hard, or too easy ?'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114388721757914994</id><published>2006-04-01T12:20:00.000+02:00</published><updated>2007-01-20T11:46:32.872+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>.NET 2.0: Could not find schema information for ... part 2</title><content type='html'>Today, I've been playing a bit with the new Configuration classes in .NET 2.0.&lt;br /&gt;Again, I came across those annoying 'Could not find schema-information for ... ' messages.&lt;br /&gt;I do not like warnings and information messages in my projects, so I had the urge to solve these issues.&lt;br /&gt;&lt;adsense&gt;&lt;br /&gt;I was playing a bit with the 'Settings' class in a Windows Forms application.  I added a user-scope setting, and I received this warning from Visual Studio.NET 2005:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;The requirePermission attribute is not declared&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;After some crawling on the web, I've found an article written by Peter Richie.  It appears to be a negligency from Microsoft: it seems that the DotNetConfig.xsd schema-file is not complete.  Peter Richie has adapted this XSD file, so you can download this file and replace the original file with his one.  By doing so, I got rid of this error.&lt;br /&gt;Click &lt;a href="http://www.peterritchie.com/Hamlet/Articles/75.aspx"&gt;here&lt;/a&gt; for Peter's article.&lt;br /&gt;I can't understand how Microsoft could be so sloppy to not deliver a correct xsd.&lt;br /&gt;&lt;br /&gt;Then, I was still annoyed by some 'Information messages' Visual Studio gave me.  These were due to a custom configuration section for NHibernate I have in my app.config file.&lt;br /&gt;This is a snippet from my App.Config file:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;&amp;lt;configSections&amp;gt;&lt;br /&gt;   &amp;lt;section name="nhibernate"&lt;br /&gt;      type="System.Configuration.NameValueSectionHandler"/&amp;gt;&lt;br /&gt;&amp;lt;/configSections&amp;gt;&lt;br /&gt;&amp;lt;nhibernate&amp;gt;&lt;br /&gt;    &amp;lt;add key="hibernate.connection.provider"&lt;br /&gt;         value="NHibernate.Connection.DriverConnectionProvider"/&amp;gt;&lt;br /&gt;    ...&lt;br /&gt;&amp;lt;/nhibernate&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=13&amp;l=st1&amp;mode=books&amp;search=visual%20studio%20tips&amp;fc1=&amp;lt1=_blank&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="468" height="60" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;br /&gt;The 'information message' here told my that a schema definition for the element nhibernate couldn't be found.  Off-course, this is not an error, but...  I find those things annoying.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;So, to get rid of those messages, I've created a simple XSD that describes this 'nhibernate' section.  (You can download this XSD &lt;a href="http://users.pandora.be/fgzone/blog/nhibernate_configuration.xsd"&gt;here&lt;/a&gt;).  I've copied this xsd to the Program Files\Microsoft Visual Studio 8\Xml\Schemas\ directory.  The next step I had to do, was to include this schema in the DotNetConfig.xsd file.&lt;br /&gt;So, in the same directory, edit the DotNetConfig.xsd file and add this line:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt; &amp;lt;xs:include schemaLocation="nhibernate_configuration.xsd"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you have a rather complex custom Configuration Section in your App.Config file, you can create your own XSD for it.  &lt;br /&gt;You're not only going to get rid of those 'could not find schema information...' messages, but you'll also have Intellisense for your custom configuration section in VS.NET 2005 as well!&lt;br /&gt;&lt;br /&gt;Click &lt;a href="http://fgheysels.blogspot.com/2006/02/problem-with-custom-configsections-in.html"&gt;here&lt;/a&gt; for my first post about this problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114388721757914994?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114388721757914994/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114388721757914994' title='10 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114388721757914994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114388721757914994'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/04/net-20-could-not-find-schema.html' title='.NET 2.0: Could not find schema information for ... part 2'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114374186825275767</id><published>2006-03-30T19:57:00.000+02:00</published><updated>2007-01-20T01:08:29.269+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Low key portraits with a Hasselblad</title><content type='html'>The past few weeks, I (and my fellow photography students) have been playing with &lt;a href="http://www.leoscamera.com/Photo/MediumFormat/hasselblad/501CM.html"&gt;Hasselblad&lt;/a&gt; camera's in the studio.&lt;br /&gt;&lt;br /&gt;This is a low key portrait that one of my collegue students took from me.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:center"&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/hb2.jpg" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, I was not able to scan the entire picture (it is to big for my scanner), so that I cannot present it here in its original and typical square Hasselblad format.&lt;br /&gt;&lt;br /&gt;I also took some very nice shots from other persons, but I'm not going to post them here (yet), since I do not have the permission of those persons to do so.&lt;br /&gt;&lt;br /&gt;I really like those Hasselblad camera's.  They're great to work with and provide great quality.  I even think of buying one (a 2nd hand, because these things are expensive...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114374186825275767?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114374186825275767/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114374186825275767' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114374186825275767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114374186825275767'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/03/low-key-portraits-with-hasselblad.html' title='Low key portraits with a Hasselblad'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114194037781888189</id><published>2006-03-09T22:03:00.000+01:00</published><updated>2007-01-20T11:47:16.484+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>NHibernate: where's the productivity ?</title><content type='html'>I've been playing around with NHibernate the past few days, and I've encountered a rather strange bug...&lt;br /&gt;&lt;br /&gt;I wanted to perform a rather simple HQL query.  I wanted to know the total value of the Orders that were placed by a Customer.&lt;br /&gt;So, I thought that this simple HQL query would do the job:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;IQuery q = theSession.CreateQuery("select sum(ol.Price * ol.NumberOfItems) &lt;br /&gt;    from OrderLine ol");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, this didn't work...&lt;br /&gt;This query did work however:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;IQuery q = theSession.CreateQuery("select sum(ol.Price) from OrderLine ol")&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It appeared that the query (the one with the multiplication in the sum function) that was sent to the database looked like this:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;select sum(orderline0_.NumberOfItems*orderline0_.ItemPrice) as x0_0_ &lt;br /&gt;from tblOrderLine orderline0_&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;while NHibernate was then trying to access a result-column that was called x1_0_&lt;br /&gt;This will offcourse not work, since the field has been given the alias x0_0_&lt;br /&gt;&lt;br /&gt;After some searching, I've found out that this was a bug that also existed in Hibernate, but has been fixed in Hibernate 3.0.&lt;br /&gt;&lt;br /&gt;So, I had to look for an alternative.&lt;br /&gt;The idea came up that I could retrieve all the orderlines that belong to a specific Customer, and then, do the calculation myself.&lt;br /&gt;So, I came up with this HQL query:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;IQuery q = theSession.CreateQuery ("from OrderLine ol " +&lt;br /&gt;     " inner join Order inner join Customer " +&lt;br /&gt;     " where Customer.Id = :custId");&lt;/pre&gt;&lt;br /&gt;This didn't work as well...  An exception was thrown saying that the BY keyword was expected after a GROUP or ORDER.   However, I didn't use any group by or order by statements ?  Then, I've noticed that I've a class that's called Order, so maybe I had to escape the classname.&lt;br /&gt;However, I couldn't find how to do that (if it is possible), so I've rewritten the query to something like this:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;IQuery q = theSession.CreateQuery ("select ol " +&lt;br /&gt;    " from OrderLine ol, Order o, Customer c" +&lt;br /&gt;    " where ol.Owner = o and o.Owner = c " +&lt;br /&gt;    " and c.Id = :custId");&lt;/pre&gt;&lt;br /&gt;This didn't work as well...  &lt;br /&gt;The exception now said:&lt;br /&gt;&lt;code&gt;Could not resolve property:Id of Customer&lt;/code&gt;&lt;br /&gt;I found this very strange, since this class has an Id property.  &lt;br /&gt;After some research, I found out that the Id did not appear in my hibernate mapping file indeed.  &lt;br /&gt;This is, because the Id is used as the 'id' in my mapping file, and, since the Id property is read-only in my class, I use a field access method to set this field.  This is specified in my hbm mapping file like this:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;&amp;lt;class name="NamespaceName.Customer, NamespaceName" table="tblCustomer"&amp;gt;&lt;br /&gt;    &amp;lt;id name="id" access="field" column="Id"... &amp;gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, what should I do next ? I didn't want to make the Id property of my Customer clas s writable.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;I tried to add the Id property to my Customer mapping file, without touching the 'id' element.&lt;br /&gt;So, I've added the Id property to my mapping file, but, since I've introduced the same database column twice, I had to use some attributes:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;&amp;lt;class name="NamespaceName.Customer, NamespaceName" table="tblCustomer"&amp;gt;&lt;br /&gt;    &amp;lt;id name="id" access="field" column="Id"... &amp;gt;&lt;br /&gt;    ...&lt;br /&gt;    &amp;lt;property name="Id" update="false" insert="false"&amp;gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I tried to rerun my query.  However, retrieving a Customer was already a problem.  I received an exception that said that the property value of the Id property could not be set via reflection. Damn.&lt;br /&gt;I really don't want that somebody can set the Id of a Customer via the property...&lt;br /&gt;&lt;br /&gt;Luckely, .NET 2.0 came to the rescue.  In .NET 2.0, you can define different access-modifiers on your property getter and setter.  So, my Customer class looked like this :&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;    private int id;&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;       get { return id; }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I've changed this to&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;    private int id;&lt;br /&gt;&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;       get { return id; }&lt;br /&gt;       private set { id = value; }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And this finally did the trick...&lt;br /&gt;&lt;br /&gt;I've spent something like 2 hours in getting something simple like this to work in NHibernate.  With a self-written Data Access Layer, this would have taken me something about 10 minutes I guess, to implement this functionality.&lt;br /&gt;Conclusion: there's a lot of work to be done on NHibernate before I will really call it 'productive'.  However, it is a promising project though.&lt;br /&gt;&lt;br /&gt;For more information about my quest, I refer to a &lt;a href="http://forum.hibernate.org/viewtopic.php?t=956459"&gt;topic I've opened regarding this issue on the NHibernate support forum.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114194037781888189?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114194037781888189/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114194037781888189' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114194037781888189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114194037781888189'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/03/nhibernate-wheres-productivity.html' title='NHibernate: where&apos;s the productivity ?'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114167758769290138</id><published>2006-03-06T21:30:00.000+01:00</published><updated>2006-03-06T21:39:47.713+01:00</updated><title type='text'>Troubles with Visual Sourcesafe, looking for alternatives</title><content type='html'>Today, I've encountered some rather unpleasant moments with Visual Sourcesafe.  Around 4 o'clock, it appeared that my VSS repository was corrupt. :(&lt;br /&gt;Trying to restore the latest backup didn't succeed as well, since there was a problem with that tape. :(&lt;br /&gt;&lt;br /&gt;This means I had to use the backup of last thursday...  Luckely, after some extra work, I've been able to restore most of my work.&lt;br /&gt;&lt;br /&gt;However, I do not want to experience these problems again, so, just like &lt;a href="http://codebetter.com/blogs/sam.gentile/archive/2006/03/02/139641.aspx"&gt;Sam Gentile&lt;/a&gt;, I'm going to ditch VSS, and look for an alternative.&lt;br /&gt;&lt;br /&gt;At this moment, I've 2 options in my mind:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;SubVersion&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sourcegear's Vault&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Some time ago, I've downloaded a trial version of Vault, and it looked ok to me.  However, I've heard that it doesn't integrate very well in VS.NET (haven't tried that yet).  &lt;br /&gt;On the other hand, we have Subversion, however, I haven't worked with it before.  &lt;br /&gt;&lt;br /&gt;Anybody who has experience with one of these 2 systems and wants to share his/her opinion ?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114167758769290138?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114167758769290138/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114167758769290138' title='7 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114167758769290138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114167758769290138'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/03/troubles-with-visual-sourcesafe.html' title='Troubles with Visual Sourcesafe, looking for alternatives'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114157466901614058</id><published>2006-03-05T16:34:00.000+01:00</published><updated>2006-03-05T17:10:32.266+01:00</updated><title type='text'>Formula One: The new season is coming...</title><content type='html'>At last, after a long winter of F1-less Sundays, the new Formula 1 season will start next sunday.  &lt;br /&gt;I find it always interesting to see during the first race of the year which teams have done their home-work, and will be the teams to beat during the season.&lt;br /&gt;I bet that, for the 2006 season, Renault and Honda will be the favorites for the World Championship with Ferrari and McLaren on their heels.&lt;br /&gt;&lt;br /&gt;Who will be world champion ?  Will Alonso extend his title, or is Schumacher able to fight back ?  Can Raikonnen, who has been close to catch the title last year and in 2003, finally clinch the championship ? &lt;br /&gt;However, I think that Jenson Button also has a chance to win some races this year...&lt;br /&gt;&lt;br /&gt;All questions that will be answered during the next months, and the first race in Bahrein should already give a strong indication of who holds the best cards.&lt;br /&gt;&lt;br /&gt;Anyway, I'll be supportering for Kimi Raikonnen and Jacques Villeneuve.  I just like the driving style of these 2 guys.&lt;br /&gt;I hope that it will be an interesting season, with a lot of exciting duels on the track.   &lt;br /&gt;&lt;br /&gt;&lt;div style="float:left"&gt;&lt;img src="http://users.pandora.be/fgzone/vid/gv.jpg" /&gt;&lt;/div&gt;&lt;br /&gt;To get into the F1-mood, &lt;a href="http://users.pandora.be/fgzone/vid/villeneuve_arnoux.avi"&gt;here&lt;/a&gt; (18mb) is a video-clip of one of the most exciting duels in F1 history.&lt;br /&gt;This video shows the last 3 laps of the 1979 French Grand Prix which was held in Dijon.  René Arnoux and Gilles Villeneuve are contending for 2nd place.  René Arnoux is driving a Renault Turbo (with a defect Turbo), while Villeneuve is driving an atmospherical Ferrari with worn out tires. (You'll see the reason for those worn out tires...).  The commentary is from the legendary Murray Walker.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114157466901614058?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114157466901614058/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114157466901614058' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114157466901614058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114157466901614058'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/03/formula-one-new-season-is-coming.html' title='Formula One: The new season is coming...'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114124074787730496</id><published>2006-03-01T20:15:00.000+01:00</published><updated>2006-03-01T20:19:07.903+01:00</updated><title type='text'>It's been a while...</title><content type='html'>It's been a while since I've made a post on my blog.  I'm currently busy reading the book &lt;a href="http://www.amazon.com/gp/product/0135974445/sr=8-1/qid=1141240471/ref=pd_bbs_1/103-7316959-3109466?%5Fencoding=UTF8"&gt;Agile Software Development&lt;/a&gt; and I'm also playing with NHibernate a little bit.&lt;br /&gt;I am thinking of creating a little tutorial / Quickstart about NHibernate and post it here, but I'm not sure yet.  It is a lot of work, and takes some time... :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114124074787730496?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114124074787730496/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114124074787730496' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114124074787730496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114124074787730496'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/03/its-been-while.html' title='It&apos;s been a while...'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-114025883026364693</id><published>2006-02-18T10:56:00.000+01:00</published><updated>2007-01-20T11:47:53.293+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>A 'problem' with custom configSections in .NET 2.0</title><content type='html'>Yesterday evening, I was trying to port a simple .NET 1.1 test project of mine to .NET 2.0.&lt;br /&gt;This project contained an App.Config file that contains a custom configuration section like this:&lt;br /&gt;&lt;pre class="code-content"&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;    &amp;lt;configSections&amp;gt;&lt;br /&gt;        &amp;lt;section name="test" &lt;br /&gt;             type="System.Configuration.NameValueSectionHandler"/&amp;gt;&lt;br /&gt;    &amp;lt;/configSections&amp;gt;&lt;br /&gt;    &amp;lt;test&amp;gt;&lt;br /&gt;       &amp;lt;add key="xx" value="yy"/&amp;gt;&lt;br /&gt;    &amp;lt;/test&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When I compiled my project, I got the following error message from VS.NET:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Could not find schema information for the element test&lt;br /&gt;Could not find schema information for the element add&lt;br /&gt;Could not find schema information for the element key&lt;br /&gt;Could not find schema information for the element value&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;div style="float:left"&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=8&amp;l=st1&amp;mode=books&amp;search=working%20with%20visual%20studio%20&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="120" height="240" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/div&gt;I was really surprised...  This just worked in .NET 1.1, why shouldn't it work in .NET 2.0 ?&lt;br /&gt;I spent a lot of time searching on Google, newsgroups, etc... for more information about this error and how to solve it, but I haven't found any clue.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;This morning, I restarted my quest, and I was able to solve the problem.&lt;br /&gt;Apparently, I had a syntax error somewhere in my code.  In the configuration file, there was &lt;strong&gt;nothing&lt;/strong&gt; wrong.  Once this syntax error was solved, VS.NET didn't complain anymore.&lt;br /&gt;I tried the same thing in my .NET 1.1 project: I added a syntax error in my code, and compiled.  The compiler complained about the syntax error, but not about the configuration file (which is good, because the config file is correct).&lt;br /&gt;&lt;br /&gt;This is very frustrating.  Why is VS.NET 2005 complaining about something in the configuration file, while the configuration file is just correct ?&lt;br /&gt;On top of it, this 'configuration file error' appears as the first error in my error-list.&lt;br /&gt;&lt;br /&gt;I really lost a lot of time due to this strange behaviour of VS.NET; why is VS.NET 2005 behaving like that, which is incorrect IMHO, while VS.NET 2003 is acting like it should.&lt;br /&gt;This error is very confusing and it can really take your productivity down.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt; I've been playing with configuration-settings in .NET 2.0 again, and again, I came across this annoying error.  I've made 2 comments on this article that should provide a way to fix this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-114025883026364693?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/114025883026364693/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=114025883026364693' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114025883026364693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/114025883026364693'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/02/problem-with-custom-configsections-in.html' title='A &apos;problem&apos; with custom configSections in .NET 2.0'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113994310956636502</id><published>2006-02-14T19:51:00.000+01:00</published><updated>2007-01-20T01:11:18.209+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Domain Driven Design'/><title type='text'>Domain Driven Design / Entity (Yourdon-Chen) approach</title><content type='html'>The last few months, I've been reading a lot in the book &lt;a href="http://www.amazon.com/gp/product/0321125215/sr=8-1/qid=1139654841/ref=pd_bbs_1/102-5902810-0992137?%5Fencoding=UTF8"&gt;Domain Driven Design&lt;/a&gt; by Eric Evans.&lt;br /&gt;The book preaches really useable things such as 'the separation of concerns', and 'expressive domain models'.  These are practices where I try to strive to, and the more I read in the book, the more this 'Domain Driven paradigm' appears to me as 'the Holy Grail' in software developmment.&lt;br /&gt;&lt;br /&gt;However, when you want to turn theory into practice, you sometimes run into problems...&lt;br /&gt;&lt;br /&gt;For instance, suppose you have a simple domain model for some shop.   In this domain model, you'd have a Customer entity, and this Customer has Orders.  An Order consists of one ore more OrderLines.&lt;br /&gt;Suppose we should treat a Customer who ordered for more then 1000€ in the last 6 months as a 'Gold Customer'.&lt;br /&gt;&lt;br /&gt;In a Domain Driven Design, it would feel natural to add a member to the Customer which tests this condition, and it could look like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;    public bool IsGoldCustomer()&lt;br /&gt;    {&lt;br /&gt;        decimal total = 0;&lt;br /&gt;&lt;br /&gt;        foreach( Order o in _orders )&lt;br /&gt;        {&lt;br /&gt;            if( o.OrderDate &gt;= sixMonthsAgo )&lt;br /&gt;            {&lt;br /&gt;                total += o.TotalOrderValue;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return ( total &gt; 1000 );&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can imagine, this will not be very performant.  First of all, this code requires us to traverse over the entire OrderCollection of that customer.   &lt;br /&gt;It could also be that the &lt;code&gt;TotalOrderValue&lt;/code&gt; property is also a calculated value.  &lt;br /&gt;This means that, for every Order who's orderdate falls within the last 6 months, we'll have to retrieve the OrderLines (suppose that the OrderLines are lazy-loaded) in order to be able to determine the &lt;code&gt;TotalOrderValue&lt;/code&gt; of that Order.&lt;br /&gt;As you can see, this means that we'll have to do a lot of query-ing and we also have to retrieve a lot of records out of the database and load them into memory...  For just such a simple check...&lt;br /&gt;This is not really a best practice.&lt;br /&gt;&lt;br /&gt;Luckely, our database is able to determine the amount of placed orders in the last 6 months for a specific customer in a much faster way, and without the need of loading a lot of objects into memory.&lt;br /&gt;By using a &lt;code&gt;Specification&lt;/code&gt; pattern, we can make use of the power that our database offers, and still have the logic encapsulated into the domain:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;public class CustomerGoldStatusSpecification : CustomerSpecification&lt;br /&gt;{&lt;br /&gt;  public bool IsSatisfied( Customer c )&lt;br /&gt;  {&lt;br /&gt;    decimal total = &lt;br /&gt;       customerRepository.&lt;br /&gt;           GetTotalOrderValueForCustomerInPeriod(c.Id, sixMonthsAgo, today);&lt;br /&gt;&lt;br /&gt;    return (total &gt; 1000);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this way, the logic is still encapsulated in the domain model, since the CustomerGoldSpecification class is a part of the domain, and, we also make use of the power of our database; the specification uses a method on the Customer Repository which queries the database for the value that we want.&lt;br /&gt;&lt;br /&gt;Nice. :)&lt;br /&gt;&lt;br /&gt;However....&lt;br /&gt;&lt;br /&gt;If we take this logic out of our 'entity' classes, then what difference does this 'Domain Driven' approach make with the Yourdon/Chen entity approach ?&lt;br /&gt;If we take the 'complex' logic and behaviour out of our domain classes, what else is left but some dumb entities that are nothing more then a data-holder, while all our logic is in specifications / managers ?&lt;br /&gt;&lt;br /&gt;One could ofcourse say that the &lt;code&gt;Specification&lt;/code&gt; class can be made internal, and the &lt;code&gt;Customer&lt;/code&gt; should use this specification class like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public class Customer&lt;br /&gt;{&lt;br /&gt;    public bool IsGoldCustomer()&lt;br /&gt;    {&lt;br /&gt;       CustomerGoldStatusSpecification spec = &lt;br /&gt;           new  CustomerGoldStatusSpecification();&lt;br /&gt;&lt;br /&gt;       return spec.IsSatisfied (this);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;(You can offcourse also directly access the repository from this Customer class)&lt;br /&gt;&lt;br /&gt;Am I exagerating about this ? Is the situation I've described here, and the solution a good solution ? Is there still a difference between this approach and the Entity/Yourdon approach ?&lt;br /&gt;I'd like to hear your comments and opinions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113994310956636502?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113994310956636502/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113994310956636502' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113994310956636502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113994310956636502'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/02/domain-driven-design-entity-yourdon.html' title='Domain Driven Design / Entity (Yourdon-Chen) approach'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113959736785019191</id><published>2006-02-10T19:44:00.000+01:00</published><updated>2007-01-20T01:09:25.541+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Better performance when loading a typed dataset.</title><content type='html'>At work, we had a little performance problem when we wanted to fill a rather large typed dataset that had a number of constraints defined.&lt;br /&gt;&lt;br /&gt;Setting the &lt;code&gt;EnforceConstraints&lt;/code&gt; property of the dataset to false before filling up the dataset, and setting it to true afterwards, resulted in an immense performance boost.&lt;br /&gt;Without setting this property to false, it took minutes to load the dataset.  When we made use of this property, it only took a second or two.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113959736785019191?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113959736785019191/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113959736785019191' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113959736785019191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113959736785019191'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/02/better-performance-when-loading-typed.html' title='Better performance when loading a typed dataset.'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113891377702663764</id><published>2006-02-02T21:52:00.000+01:00</published><updated>2007-01-20T11:49:29.407+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Business Objects Framework - Part 3: Lazy Loading</title><content type='html'>This is the 3rd part of my 'Business Objects' serie.  In this article, I'll discuss the LazyLoadCollection.  (Part 1 and 2 can be found &lt;a href="http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-1_08.html"&gt;here&lt;/a&gt; and &lt;a href="http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-2_21.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;Suppose you have a class &lt;code&gt;Customer&lt;/code&gt;.  This &lt;code&gt;Customer&lt;/code&gt; class has collection of 'Orders'.  Each &lt;code&gt;Order&lt;/code&gt; contains a collection of &lt;code&gt;OrderLines&lt;/code&gt;.&lt;br /&gt;Now, if you retrieve one or more Customers out of the database, it is maybe not such a good idea that you retrieve all the Orders and OrderLines with it.  It is probable that  the user is not interested in all the orders or all the orderlines of each customer that you have retrieved.&lt;br /&gt;If you do this, it means that a lot of objects will be loaded into memory; objects you might not need.&lt;br /&gt;Therefore, it is sometimes a good idea to load this information only when you need it.  In the scenario I've described here, I think it is a good idea that, when you load a Customer out of the database you should also get his Orders.   &lt;br /&gt;In most cases, you want to have an overview of the Orders of a Customer.   This means that you will need the Orders of the Customer in most of the cases.&lt;br /&gt;However, you're only interested in the &lt;code&gt;OrderLines&lt;/code&gt; of an &lt;code&gt;Order&lt;/code&gt;, once you decide to view the details of an &lt;code&gt;Order&lt;/code&gt;.  Here, you should only retrieve the OrderLines out of the database, when they're needed.   This is where the Lazy Loading comes in.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The LazyBusinessObjectCollection class&lt;/h2&gt;&lt;br /&gt;The Lazy Load functionality must be transparant to the user of our &lt;code&gt;Order&lt;/code&gt; class.   This means that, the user must be able to Add, Remove and see the OrderLines of an Order just like he/she can do with a normal collection class.&lt;br /&gt;Even if the OrderLines of an Order have not been loaded yet, it must be possible that  OrderLines are being added.&lt;br /&gt;&lt;br /&gt;So, how can we do that ?  The &lt;a href="http://home.earthlink.net/~huston2/dp/proxy.html" target="_blank"&gt;Proxy Design Pattern&lt;/a&gt; offers us a way to do that.&lt;br /&gt;Instead of giving the &lt;code&gt;Order&lt;/code&gt; class a 'real collection' as a member to hold it's orderlines, we will give it a proxy object that acts as a collection.&lt;br /&gt;&lt;!--adsense--&gt;&lt;br /&gt;This means that we must create a class that has all functionality of a collection.  The easy way to do so, is to create a class that is a wrapper around a Collection object, and acts as a Collection as well.&lt;br /&gt;We can create a class that is a wrapper around the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class that we've created earlier.  This wrapper class must also implement the &lt;code&gt;ICollection&lt;/code&gt; interface so that it can act as a collection.  We also want Undo-support, so this class must implement our &lt;code&gt;IUndoable&lt;/code&gt; interface as well.&lt;br /&gt;&lt;br /&gt;The skeleton of our &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; will look like this:&lt;br /&gt;&lt;pre class="code-content"&gt;[Serializable]&lt;br /&gt;public class LazyBusinessObjectCollection&amp;lt;T&amp;gt; : IUndoable, ICollection&amp;lt;T&amp;gt; &lt;br /&gt;    where T : BusinessObject&lt;br /&gt;{&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Earlier, I said that the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; class would be a wrapper around the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class.  This means that the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; class should have a member of type &lt;code&gt;BusinessObjectCollection&lt;/code&gt;.&lt;br /&gt;&lt;pre class="code-content"&gt;[Serializable]&lt;br /&gt;public class LazyBusinessObjectCollection&amp;lt;T&amp;gt; : IUndoable, ICollection&amp;lt;T&amp;gt; &lt;br /&gt;    where T : BusinessObject&lt;br /&gt;{&lt;br /&gt;    private BusinessObjectCollection _collection = null;&lt;br /&gt;&lt;br /&gt;    private BusinessObjectCollection CollectionObj&lt;br /&gt;    {&lt;br /&gt;        if( _collection == null )&lt;br /&gt;        {&lt;br /&gt;            // initialize and load the items.&lt;br /&gt;        }&lt;br /&gt;        return _collection;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I've also created a property that accesses the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; member.  This property should be used everytime we access our &lt;code&gt;BusinessObjectCollection&lt;/code&gt; from within the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; class, since this property will take care that the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; is loaded if this is necessary.&lt;br /&gt;&lt;br /&gt;To be able to populate the collection, we need something that can loads the data for us.  We want to decouple the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; from the code that retrieves the items for us, since, this 'data retrieval code' will be most likely in some kind of Data Access Layer.&lt;br /&gt;&lt;br /&gt;So, I decided to create an interface &lt;code&gt;ILazyLoader&lt;/code&gt;&lt;br /&gt;This interface looks like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public interface ILazyLoader&amp;lt;T&amp;gt; where T : BusinessObject&lt;br /&gt;{&lt;br /&gt;    List&amp;lt;T&amp;gt; GetObjects();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This interface defines only one method that gives us a list of BusinessObjects.  This means that the class that implements this interface is responsible for retrieving and returning the BusinessObjects.&lt;br /&gt;&lt;br /&gt;So, we can give an &lt;code&gt;ILazyLoader&lt;/code&gt; to our &lt;code&gt;LazyBusinessCollection&lt;/code&gt; class.  This &lt;code&gt;ILazyLoader&lt;/code&gt; can be provided by the constructor of the &lt;code&gt;LazyBusinessObjectsCollection&lt;/code&gt; class, and the property that we've created earlier, can access the &lt;code&gt;GetObjects&lt;/code&gt; method which will give the Objects that the collection must contain:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;[Serializable]&lt;br /&gt;public class LazyBusinessObjectCollection&amp;lt;T&amp;gt; : IUndoable, ICollection&amp;lt;T&amp;gt; &lt;br /&gt;    where T : BusinessObject&lt;br /&gt;{&lt;br /&gt;    private BusinessObjectCollection _collection = null;&lt;br /&gt;    private ILazyLoader&amp;lt;T&amp;gt;           _loader;&lt;br /&gt;&lt;br /&gt;    private BusinessObjectCollection CollectionObj&lt;br /&gt;    {&lt;br /&gt;        if( _collection == null )&lt;br /&gt;        {&lt;br /&gt;            _collection = new BusinessObjectCollection&amp;lt;T&amp;gt; ();&lt;br /&gt;&lt;br /&gt;            foreach( T item in _loader.GetObjects () )&lt;br /&gt;            {&lt;br /&gt;                _collection.Add (item);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return _collection;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public BusinessObjectCollection( ILazyLoader&amp;lt;T&amp;gt; loader )&lt;br /&gt;    {&lt;br /&gt;        if( loader == null )&lt;br /&gt;        {&lt;br /&gt;           throw new ArgumentException ("You must provide an ILazyLoader", &lt;br /&gt;                                        "loader");&lt;br /&gt;        }&lt;br /&gt;        _loader = loader;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pretty simple huh ? &lt;br /&gt;&lt;br /&gt;Now, an ILazyLoader can look like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public OrderLinesLoader : ILazyLoader&amp;lt;OrderLine&amp;gt;&lt;br /&gt;{&lt;br /&gt;    private int _orderId;&lt;br /&gt;&lt;br /&gt;    public OrderLinesLoader( int orderId )&lt;br /&gt;    {&lt;br /&gt;       _orderId = orderId;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public List&amp;lt;OrderLine&amp;gt; GetObjects()&lt;br /&gt;    {&lt;br /&gt;        // Just for simplicity of the example, normally, you should &lt;br /&gt;        // use parametrized queries, etc.... Consider this as pseudo-code&lt;br /&gt;        string sql = "SELECT * FROM orderlines " +&lt;br /&gt;                     "WHERE OrderId = " + _orderId.ToString();&lt;br /&gt;    &lt;br /&gt;        DataReader dr = dbHelper.Execute (sql);&lt;br /&gt;        List&amp;lt;OrderLine&amp;gt; lines = new List&amp;lt;OrderLine&amp;gt;();&lt;br /&gt;        while( dr.Read() )&lt;br /&gt;        {&lt;br /&gt;           OrderLine ol = CreateOrderLineFromReader (dr);&lt;br /&gt;           lines.Add (ol);&lt;br /&gt;        }&lt;br /&gt;     &lt;br /&gt;        return lines;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, the repository or factory who gives us an &lt;code&gt;Order&lt;/code&gt; object, is responsible for initializing the &lt;code&gt;OrderLines&lt;/code&gt; member -which is an &lt;code&gt;LazyBusinessObjectsCollection&lt;/code&gt; of the &lt;code&gt;Order&lt;/code&gt; class.&lt;br /&gt;&lt;br /&gt;We still have to implement the &lt;code&gt;ICollection&lt;/code&gt; and &lt;code&gt;IUndoable&lt;/code&gt; interface.  I will not put the implementation of the &lt;code&gt;ICollection&lt;/code&gt; interface here to save some space; besides, it is pretty simple code: you can just delegate these calls to the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; property (note: make sure to use the property) of the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; class.&lt;br /&gt;The same applies to the implementation of &lt;code&gt;IUndoable&lt;/code&gt;: just delegate the calls to the &lt;code&gt;BusinessObjectCollection&lt;/code&gt;.  However, here we can use the field instead of the property, since we only have to delegate these calls if the collection has been loaded:&lt;br /&gt;&lt;pre class="code-content"&gt;public void CreateSnapshot()&lt;br /&gt;{&lt;br /&gt;    if( _collection != null )&lt;br /&gt;    {&lt;br /&gt;        _collection.CreateSnapshot ();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113891377702663764?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113891377702663764/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113891377702663764' title='5 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113891377702663764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113891377702663764'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/02/business-objects-framework-part-3-lazy.html' title='Business Objects Framework - Part 3: Lazy Loading'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113838660526030200</id><published>2006-01-27T19:09:00.000+01:00</published><updated>2007-01-02T19:14:53.646+01:00</updated><title type='text'>Buying Books</title><content type='html'>I've bought 2 books recently.&lt;br /&gt;&lt;br /&gt;The first one is &lt;a href="http://www.amazon.com/gp/product/0321246756?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0321246756"&gt;Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (Microsoft .NET Development Series)&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=0321246756" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div style="float:left"&gt;&lt;img src="http://images.amazon.com/images/P/0321246756.01._AA240_SCLZZZZZZZ_.jpg" alt="framework design guidelines cover" /&gt;&lt;/div&gt;  This is a usefull book with some interesting tips for anyone who's interested in or is busy with designing and building frameworks.  If you use &lt;a href="http://www.gotdotnet.com/team/fxcop/"&gt;FxCop&lt;/a&gt;, chances are that you're already familiar with most of the tips and guidelines that are in the book.  Nonetheless, this book is usefull as reference-book but you can also read it from cover to cover.  Most of the guidelines in the book are commented by people like Jeffrey Richter, Rico Mariani, etc... and these comments are very valuable as well.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear:both"&gt;The second book I've bought is &lt;a href="http://www.amazon.com/gp/product/0135974445?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0135974445"&gt;Agile Software Development, Principles, Patterns, and Practices&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=0135974445" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt; by Robert Martin.&lt;/div&gt;&lt;br /&gt;&lt;div style="float: left"&gt;&lt;img src="http://images.amazon.com/images/P/0135974445.01._AA240_SCLZZZZZZZ_.jpg" alt="agile software development cover" /&gt;&lt;/div&gt;I haven't found time to start reading this book, but due to the reviews on Amazon and other fora, I'm expecting a lot of it!  Maybe I'll post a little review on my blog about this book, after I've read it.&lt;br /&gt;&lt;div style="clear:both"&gt;&lt;br /&gt;Again, two books that I can remove from my &lt;a href="http://www.amazon.com/gp/registry/registry.html/103-0819169-1846241?%5Fencoding=UTF8&amp;type=wishlist&amp;id=115CK4SVCLBP2"&gt;book whishlist&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Correction:&lt;/strong&gt; I didn't really buy the 1st book; it was rather a  present. :)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113838660526030200?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113838660526030200/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113838660526030200' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113838660526030200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113838660526030200'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/buying-books.html' title='Buying Books'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113783372021776358</id><published>2006-01-21T09:54:00.000+01:00</published><updated>2007-01-20T11:50:28.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Business Objects Framework – Part 2</title><content type='html'>This is the 2nd article of a serie.  The 1ste one can be found &lt;a href="http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-1_08.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In this article, I'll explain the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class.  Before starting off with this class, I'd like to go back to the &lt;code&gt;BusinessObject&lt;/code&gt; class first, because I want to do a little modification on that class.&lt;br /&gt;&lt;h2&gt;Implementation of IEditableObject in BusinessObject&lt;/h2&gt;&lt;br /&gt;The IEditableObject interface is defined in the .NET framework to control the undo-functionality used by the databinding infrastructure.&lt;br /&gt;There are 2 situations in where this interface is used:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;If the object is a child of a collection, and this collection is collection is databound to a grid, the IEditableObject interface is used so that the user of the application gets the expected functionality.  For instance, when he creates a new row and then presses Escape on that row, the new row should not be added to the collection.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When an object is databound to the forms on a control, the &lt;code&gt;IEditableObject.BeginEdit&lt;/code&gt; method is called when the object has been changed by the databinding.  However, the &lt;code&gt;EndEdit&lt;/code&gt; and &lt;code&gt;CancelEdit&lt;/code&gt; methods are not called automatically by the infrastructure, and thus, it is the responsability of the application-developer to call these methods.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Since the &lt;code&gt;BusinessObject&lt;/code&gt; class already contains the methods defined by the &lt;code&gt;IUndoable&lt;/code&gt; interface that are able to undo / commit any changes made to the &lt;code&gt;BusinessObject&lt;/code&gt;, I think it is better that the application developer has no 'direct access' to the methods of the &lt;code&gt;IEditableObject&lt;/code&gt; interface.  The application developer should use our &lt;code&gt;IUndoable&lt;/code&gt; methods instead, and the &lt;code&gt;IEditableObject&lt;/code&gt; interface should only be used if the &lt;code&gt;BusinessObject&lt;/code&gt; is contained in a collection that is databound.&lt;br /&gt;&lt;br /&gt;To achieve that the application-developer cannot call the methods of the &lt;code&gt;IEditableObject&lt;/code&gt; directly, the &lt;code&gt;IEditableObject&lt;/code&gt; interface should be implemented explicitly.&lt;br /&gt;This is done like this:&lt;br /&gt;&lt;!-- adsense--&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;void IEditableObject.BeginEdit()&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IEditableObject.CancelEdit()&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IEditableObject.EndEdit()&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The effect of implementing the &lt;code&gt;IEditableObject&lt;/code&gt; interface explicitly is, that the &lt;code&gt;BeginEdit&lt;/code&gt;, &lt;code&gt;CancelEdit&lt;/code&gt; and &lt;code&gt;EndEdit&lt;/code&gt; methods will not appear in the 'Intellisense' list and, that these methods cannot be called directly on instances of &lt;code&gt;BusinessObject&lt;/code&gt;.&lt;br /&gt;These methods are only callable if the &lt;code&gt;BusinessObject&lt;/code&gt; instance is cast to the &lt;code&gt;IEditableObject&lt;/code&gt; type.&lt;br /&gt;&lt;br /&gt;Now, the application developer that uses the &lt;code&gt;BusinessObject&lt;/code&gt; class, is in a way forced to use the methods of our &lt;code&gt;IUndoable&lt;/code&gt; interface if he wants undo-support.  &lt;br /&gt;&lt;br /&gt;Now we also have to manipulate our &lt;code&gt;_bindingEdit&lt;/code&gt; variable in the &lt;code&gt;IUndoable&lt;/code&gt; methods instead of in the &lt;code&gt;IEditableObject&lt;/code&gt; members:&lt;br /&gt;&lt;pre class="code-content"&gt;public void CreateSnapshot()&lt;br /&gt;{&lt;br /&gt;    _bindingEdit = true;&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void CommitSnapshot()&lt;br /&gt;{&lt;br /&gt;    _bindingEdit = false;&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void RevertToPreviousState()&lt;br /&gt;{&lt;br /&gt;    _bindingEdit = false;&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IEditableObject.BeginEdit()&lt;br /&gt;{&lt;br /&gt;    if( _bindingEdit == false )&lt;br /&gt;    {&lt;br /&gt;        this.CreateSnapshot ();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IEditableObject.CancelEdit()&lt;br /&gt;{&lt;br /&gt;    if( _bindingEdit )&lt;br /&gt;    {&lt;br /&gt;        this.RevertToPreviousState ();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void IEditableObject.EndEdit()&lt;br /&gt;{&lt;br /&gt;    if( _bindingEdit )&lt;br /&gt;    {&lt;br /&gt;        this.CommitSnapshot ();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, we can have a look at the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Collections of BusinessObjects&lt;/h2&gt;&lt;br /&gt;&lt;div style="float:left;margin-right:5px"&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=10&amp;l=st1&amp;mode=books&amp;search=advanced%20C%23%20programming&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="120" height="450" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;We will need a class that is able to store multiple &lt;code&gt;BusinessObject&lt;/code&gt; objects.  So, in fact, we'll need to be able to store a &lt;emp&gt;collection of businessobjects&lt;/emp&gt;.&lt;br /&gt;In .NET 2.0, we can use Generics, so that we'll have to create only one &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class, and in our application code, we can define what kind of &lt;code&gt;BusinessObject&lt;/code&gt; instances this class should contain.&lt;br /&gt;&lt;br /&gt;Since CSLA.NET was developped using .NET 1.x, the Collection classes inherited from &lt;code&gt;CollectionBase&lt;/code&gt;.  However, with .NET 2.0, I can use the &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; class as a base-class for my the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class.  &lt;br /&gt;This new &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; class allows me to write less code (I do not have to write my own strong typed Add, Remove, etc... classes), and I do not have to choose in my methods whether I'll use the &lt;code&gt;List&lt;/code&gt; or the &lt;code&gt;InnerList&lt;/code&gt; property.   There's a subtle difference in these properties: &lt;code&gt;List&lt;/code&gt; raises events when an item is added / removed, while &lt;code&gt;InnerList&lt;/code&gt; does not raise those events.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear:both"&gt;The rough skeleton of the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class looks like this:&lt;/div&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;[Serializable]&lt;br /&gt;public class BusinessObjectCollection&amp;lt;T&amp;gt; : Collection&amp;lt;T&amp;gt;, &lt;br /&gt;                                                 IUndoable, &lt;br /&gt;                                                 IBindingList &lt;br /&gt;                                                 where T : BusinessObject&lt;br /&gt;{&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;As you can see, the class is Serializable and inherits from &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; which I just discussed.&lt;br /&gt;Generics are used here so that the application-developer can indicate what kind of objects he wants to put in his collection.   However, there is a restriction: the objects that the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; can contain, must be inherited from &lt;code&gt;BusinessObject&lt;/code&gt;.&lt;br /&gt;So, the application developer can use our &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class like this:&lt;br /&gt;&lt;pre class="code-content"&gt;BusinessObjectCollection&amp;lt;Customer&amp;gt; customers = &lt;br /&gt;    new BusinessObjectCollection&amp;lt;Customer&amp;gt;();&lt;/pre&gt;&lt;br /&gt;This will only compile if the &lt;code&gt;Customer&lt;/code&gt; class inherits from &lt;code&gt;BusinessObject&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Now, as I said earlier, since this class inherits from &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt;, I do not have to write Add, Remove, etc... methods.  If I should have used the &lt;code&gt;CollectionBase&lt;/code&gt; class, and if I wanted strong typed Add and Remove methods, I should have written those methods like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public T this[ int index ]&lt;br /&gt;{&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        return (T)List[index];&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public int Add( T item )&lt;br /&gt;{&lt;br /&gt;    return List.Add (item);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Not only should I have to do this for the &lt;code&gt;Add&lt;/code&gt; method and for the indexer, but I should 've done it for the &lt;code&gt;Insert&lt;/code&gt;, &lt;code&gt;Remove&lt;/code&gt;, ... methods as well.   &lt;br /&gt;Now I do not have to do this, because the &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; class already provides this for me.   This means that inheriting from &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; saves me a lot of tedious work.&lt;br /&gt;&lt;br /&gt;What I do have to do, is, providing extra functionality when a &lt;code&gt;BusinessObject&lt;/code&gt; object is added to the collection.  If I should have inherited from &lt;code&gt;CollectionBase&lt;/code&gt;, I should have been carefull whether I'll use the &lt;code&gt;List&lt;/code&gt; or the &lt;code&gt;InnerList&lt;/code&gt; property in my own Add and Remove methods, since the &lt;code&gt;List&lt;/code&gt; property raises events that I can respond to if I want extra functionality.   With the &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; class, I do not have to pay attention to it because this class does not have an &lt;code&gt;InnerList&lt;/code&gt; nor &lt;code&gt;List&lt;/code&gt; property.  It only has an &lt;code&gt;Items&lt;/code&gt; property.&lt;br /&gt;&lt;br /&gt;I only have to override the &lt;code&gt;InsertItem&lt;/code&gt; and the &lt;code&gt;RemoveItem&lt;/code&gt; methods.&lt;br /&gt;&lt;br /&gt;Every time a &lt;code&gt;BusinessObject&lt;/code&gt; is added to the collection, I'll need to keep track of the editlevel at which the object has been added.  This is necessary for the 'Undo' functionality of the collection.&lt;br /&gt;Overriding the &lt;code&gt;InsertItem&lt;/code&gt; method, allows me to do that:&lt;br /&gt;&lt;pre class='code-content'&gt;protected override InsertItem( int index, T item )&lt;br /&gt;{&lt;br /&gt;    item.EditLevelAdded = _editLevel;&lt;br /&gt;    base.InsertItem (index, item);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This means that, the &lt;code&gt;BusinessObject&lt;/code&gt; class needs an internal member called &lt;code&gt;EditLevelAdded&lt;/code&gt;, that keeps track of the 'editlevel' of the collection at which the item has been added to the collection.&lt;br /&gt;This code also means that our &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class needs a private member &lt;code&gt;_editLevel&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;When a BusinessObject is removed from the collection, we will need to keep track of the BusinessObject as well, since, when the changes that have been made to our collection are undone, it is quite possible that the previously deleted business-object is to be undeleted.&lt;br /&gt;This means that the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; will need a list that contains the &lt;code&gt;BusinessObjects&lt;/code&gt; that were deleted from the collection.  To achieve this, the &lt;code&gt;BusinessObjectCollection&lt;/code&gt; class needs a member that is able to save this collection of deleted &lt;code&gt;BusinessObject&lt;/code&gt; instances. &lt;br /&gt;Again, the &lt;code&gt;Collection&amp;lt;T&amp;gt;&lt;/code&gt; class is perfectly suited for this. (In .NET 1.x you'll have to create a nested type that inherits from &lt;code&gt;CollectionBase&lt;/code&gt; that represents this collection).&lt;br /&gt;&lt;br /&gt;Now, every time that an item is being removed from our collection, we'll have to add it to the collection that keeps track of the deleted BusinessObjects:&lt;br /&gt;&lt;pre class="code-content"&gt;private Collection&amp;lt;T&amp;gt; _deletedItems = new Collection&amp;lt;T&amp;gt; ();&lt;br /&gt;&lt;br /&gt;protected override RemoveItem( int index )&lt;br /&gt;{&lt;br /&gt;    // Since we do not have direct access to the item that's &lt;br /&gt;    // being removed here, we'll have to get it first.&lt;br /&gt;    T businessItem = Items[index];&lt;br /&gt;    if( businessItem != null )&lt;br /&gt;    {&lt;br /&gt;        businessItem.MarkDeleted();&lt;br /&gt;        _deletedItems.Add (businessItem);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    base.RemoveItem(index);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Since one of my design goals was to get the data access code out of the BusinessObjects, this means that we'll have to have a way to be able to tell our Data Access components which items were removed, and should be deleted in the database.&lt;br /&gt;This means that we'll have to add these 2 extra methods:&lt;br /&gt;&lt;pre class="code-content"&gt;public T[] GetDeletedBusinessObjects()&lt;br /&gt;{&lt;br /&gt;    List&amp;lt;T&amp;gt; items = new List&amp;lt;T&amp;gt; ();&lt;br /&gt;    foreach( T item in _deletedItems )&lt;br /&gt;    {&lt;br /&gt;        items.Add (item);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return items.ToArray();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void ClearDeleted()&lt;br /&gt;{&lt;br /&gt;    _deletedItems.Clear();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now, Data Access Components can use these 2 methods of the collection to see which BusinessObjects should be deleted, and once this is done, they should also be removed from the 'deletedItems list'.&lt;br /&gt;&lt;h2&gt;Implementing IUndoable&lt;/h2&gt;&lt;br /&gt;We still need to implement the &lt;code&gt;IUndoable&lt;/code&gt; interface.&lt;br /&gt;Implementing the &lt;code&gt;CreateSnapshot&lt;/code&gt; method is quite easy.  We only have to make sure that we call the &lt;code&gt;CreateSnapshot&lt;/code&gt; method of every &lt;code&gt;BusinessObject&lt;/code&gt; that is in our collection, but, we must also not forget to create a snapshot on the BusinessObjects that are in our &lt;code&gt;_deletedItems&lt;/code&gt; collection.&lt;br /&gt;&lt;pre class="code-content"&gt;private int _editLevel = 0;&lt;br /&gt;&lt;br /&gt;public void CreateSnapshot()&lt;br /&gt;{&lt;br /&gt;    _editLevel++;&lt;br /&gt;&lt;br /&gt;    foreach( T item in Items )&lt;br /&gt;    {&lt;br /&gt;        T.CreateSnapshot();&lt;br /&gt;    }&lt;br /&gt;    foreach( T item in _deletedItems )&lt;br /&gt;    {&lt;br /&gt;       T.CreateSnapshot();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The implementation of &lt;code&gt;CommitSnapshot&lt;/code&gt; is fairly simple as well.  We just need to call the &lt;code&gt;CommitSnapshot&lt;/code&gt; method on every &lt;code&gt;BusinessObject&lt;/code&gt; that is in our collection and in the &lt;code&gt;_deletedItems&lt;/code&gt; collection.&lt;br /&gt;&lt;br /&gt;Implementing &lt;code&gt;RevertToPreviousState&lt;/code&gt; is a bit more complex, since we might have a situation in where a deleted BusinessObject should be undeleted; for an exhaustive explanation of the functionality, I'll refer to the &lt;a href="http://www.amazon.com/gp/product/1590593448/qid=1136207968/sr=8-1/ref=pd_bbs_1/103-3086553-6946215?n=507846&amp;s=books&amp;v=glance"&gt;Expert C# Business Objects&lt;/a&gt; book.&lt;br /&gt;This is the code:&lt;br /&gt;&lt;pre class="code-content"&gt;&lt;br /&gt;_editLevel--;&lt;br /&gt;&lt;br /&gt;if( _editLevel &lt; 0 )&lt;br /&gt;{&lt;br /&gt;    _editLevel = 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;for( int i = Items.Count - 1; i &gt;= 0; i-- )&lt;br /&gt;{&lt;br /&gt;    T item = Items[i];&lt;br /&gt;&lt;br /&gt;    item.RevertToPreviousState ();&lt;br /&gt;&lt;br /&gt;    if( item.EditLevelAdded &gt; _editLevel )&lt;br /&gt;    {&lt;br /&gt;        base.Items.Remove (item);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;for( int i = _deletedItems.Count - 1; i &gt;= 0; i-- )&lt;br /&gt;{&lt;br /&gt;    T item = _deletedItems[i];&lt;br /&gt;&lt;br /&gt;    item.RevertToPreviousState ();&lt;br /&gt;&lt;br /&gt;    if( item.EditLevelAdded &gt; _editLevel )&lt;br /&gt;    {&lt;br /&gt;        _deletedItems.Remove (item);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if( item.IsDeleted == false )&lt;br /&gt;    {&lt;br /&gt;        int saveLevel = item.EditLevelAdded;&lt;br /&gt;&lt;br /&gt;        // Add the business object back to the list.&lt;br /&gt;        Items.Add (item);&lt;br /&gt;&lt;br /&gt;        if( item.EditLevelAdded != saveLevel )&lt;br /&gt;        {&lt;br /&gt;            item.EditLevelAdded = saveLevel;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        _deletedItems.Remove (item);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the next article, I'll discuss the &lt;code&gt;LazyBusinessObjectCollection&lt;/code&gt; class.&lt;br /&gt;&lt;center&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=8&amp;l=st1&amp;mode=books&amp;search=expert%20c%23%20business%20objects&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="120" height="240" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113783372021776358?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113783372021776358/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113783372021776358' title='6 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113783372021776358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113783372021776358'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-2_21.html' title='Business Objects Framework – Part 2'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113768336037511335</id><published>2006-01-19T16:03:00.000+01:00</published><updated>2006-01-19T16:09:20.386+01:00</updated><title type='text'>Car troubles.</title><content type='html'>Yesterday evening, my car produced a very strange noise when turning.&lt;br /&gt;It seemed that one of my suspensions snapped.  &lt;br /&gt;&lt;br /&gt;Now, my car (Ford Focus) is being repared and I've received a spare-car....  A Peugeot 106.   I do like karting, and I do it alot, but I do not like to drive a kart during rush-hour.   &lt;br /&gt;This Peugeot 106 is nothing more then a kart with a back-seat, and wrapped up with some silver foil.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113768336037511335?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113768336037511335/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113768336037511335' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113768336037511335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113768336037511335'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/car-troubles.html' title='Car troubles.'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113734476529350572</id><published>2006-01-15T18:04:00.000+01:00</published><updated>2006-01-15T18:06:05.293+01:00</updated><title type='text'>Registration &amp; Comments</title><content type='html'>When I started this blog, you had to register before you could post a comment.  This has been changed so that you can post a comment without being registered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113734476529350572?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113734476529350572/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113734476529350572' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113734476529350572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113734476529350572'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/registration-comments.html' title='Registration &amp; Comments'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113717181042823179</id><published>2006-01-13T17:55:00.000+01:00</published><updated>2007-01-20T01:09:37.317+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Case sensitive queries in SQL Server</title><content type='html'>In SQL Server, whether your database is case sensitive or not, depends on the collation that your database uses.&lt;br /&gt;&lt;br /&gt;If you have a database that has been setup with a case insensitive collation sequence, but you want to retrieve or sort some data in a case sensitive way, you can do it like this:&lt;br /&gt;&lt;pre class="code-content"&gt;SELECT * &lt;br /&gt;FROM MyTable &lt;br /&gt;WHERE aField = 'a' COLLATE Latin1_General_CS_AS&lt;/pre&gt;&lt;br /&gt;or, for sorting:&lt;br /&gt;&lt;pre class="code-content"&gt;SELECT *&lt;br /&gt;FROM MyTable&lt;br /&gt;ORDER BY aField COLLATE Latin1_General_CS_AS&lt;/pre&gt;&lt;br /&gt;As you can see, you just have to define a case sensitive (CS stands for Case Sensitive) collation in your query.&lt;br /&gt;&lt;br /&gt;Of course, if you have a table that contains data that should be treated in a case-sensitive way, you can create that table/columns with a case sensitive collation sequence.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113717181042823179?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113717181042823179/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113717181042823179' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113717181042823179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113717181042823179'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/case-sensitive-queries-in-sql-server.html' title='Case sensitive queries in SQL Server'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113709417141092739</id><published>2006-01-12T20:24:00.000+01:00</published><updated>2007-01-20T01:10:00.141+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Some of my pictures...</title><content type='html'>I've been practicing photography for more than one year now, and I'd like to share some of my work with you.&lt;br /&gt;&lt;br /&gt;These are 4 pictures I've taken somewhere between september 2004 and may 2005.  All of them were shot with a Nikon F55 equipped with a 28-80mm zoomlens.  I've used a T-Max 400 ISO film, except for the photo with the karts, which was shot on a T-Max 3200 ISO.&lt;br /&gt;&lt;br /&gt;I'd like to hear your comments!&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/mrt.jpg" alt="pointing child" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/train.jpg" alt="train" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/kart.jpg" alt="karts prepare to start"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fgzone/pics/blog/fanfare.jpg" alt="fanfare" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113709417141092739?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113709417141092739/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113709417141092739' title='7 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113709417141092739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113709417141092739'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/some-of-my-pictures.html' title='Some of my pictures...'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113673865064207914</id><published>2006-01-08T17:43:00.000+01:00</published><updated>2007-01-20T11:49:03.764+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Business Objects Framework – Part 1</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;&lt;div style="float:left;margin-right:5px"&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?t=fredgheywebl-20&amp;o=1&amp;p=8&amp;l=st1&amp;mode=books&amp;search=expert%20c%23%20business%20objects&amp;fc1=&amp;lt1=&amp;lc1=&amp;bg1=&amp;f=ifr" marginwidth="0" marginheight="0" width="120" height="240" border="0" frameborder="0" style="border:none;" scrolling="no"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div&gt;More than one year ago, I started reading &lt;a href="http://www.amazon.com/gp/product/1590593448?ie=UTF8&amp;tag=fredgheywebl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1590593448"&gt;Expert C# Business Objects&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fredgheywebl-20&amp;l=as2&amp;o=1&amp;a=1590593448" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt; by Rockford Lhotka.&lt;br /&gt;In his book, ‘Rocky’ Lhotka explains us how he has developed his &lt;a href="http://www.lhotka.net/ArticleIndex.aspx?area=CSLA%20.NET" target="_blank"&gt; CSLA.NET&lt;/a&gt; framework.&lt;br /&gt;While his framework has some really nice features, that tackle some problems, it also has some disadvantages.&lt;/div&gt;&lt;br /&gt;&lt;div style="clear:both"&gt;&lt;br /&gt;There are 2 issues in the CSLA.NET framework that are –in my opinion- a big problem:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;There is data-access code inside the business object.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The business object takes care of transaction-handling.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;The first issue can be resolved by factoring out the data-access code out of the business object, and putting it in a separate object.&lt;br /&gt;The second issue is a bigger problem.  In my opinion, the business object should not be responsible for transaction handling, since the business object does not know the context in where it is used.   It is the client itself who should be responsible for the transaction handling, since this is the only place where the context in where the Business Object is used, is known.&lt;br /&gt;(Although the framework has some disadvantages, I think the book is recommended reading material for anyone who is interested in .NET &amp; enterprise applications).&lt;br /&gt;&lt;br /&gt;Because of these drawbacks of the CSLA.NET framework, I’ve decided to create my own 'framework', in where I'll implement some good things of CSLA.NET (like n-level undo support, data-binding), but, try to find another solution for the things I do not like.&lt;br /&gt;I also wanted to be able to support lazy-loading of collections.&lt;br /&gt;&lt;br /&gt;I will try to explain the things I've done in order to create this set of classes in  a serie of articles on this weblog.   &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;N-Level Undo support&lt;/h2&gt;&lt;br /&gt;So, I already told that the CSLA.NET framework has some features that I really like, like n-level undo support.&lt;br /&gt;This is a feature that I also want in my 'framework'.&lt;br /&gt;Rockford Lhotka has decided to create an &lt;code&gt;UndoableBase&lt;/code&gt; base-class that already contains all the functionality for n-level undo support. However, I've thought that it would be appropriate to create an interface that would define the contract to which all types that support n-level undo support must comply to.&lt;br /&gt;&lt;br /&gt;This interface just defines 3 methods and looks like this:&lt;br /&gt;&lt;pre class="code-content"&gt;public interface IUndoable&lt;br /&gt;{&lt;br /&gt;    void CreateSnapshot();&lt;br /&gt;    void CommitSnapshot();&lt;br /&gt;    void RevertToPreviousState();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;If you're familiar with the CSLA.NET framework, you probable know (or you can see), that, in the &lt;code&gt;UndoableBase&lt;/code&gt; class, a check is performed if a member of the class inherits from the &lt;code&gt;BusinessBase&lt;/code&gt; or &lt;code&gt;BusinessCollectionBase&lt;/code&gt; class when a snapshot is taken and when the state of an object is reverted to its previous state.  &lt;br /&gt;This is a little bit 'dirty' in my opinion, since this code creates some kind of a 'circular reference'. (&lt;code&gt;BusinessBase&lt;/code&gt; inherits from &lt;code&gt;UndoableBase&lt;/code&gt;, and &lt;code&gt;UndoableBase&lt;/code&gt; 'knows' about the existence of &lt;code&gt;BusinessBase&lt;/code&gt;).&lt;br /&gt;The introduction of this interface will resolve this problem:&lt;br /&gt;Instead of the two checks (one for &lt;code&gt;BusinessBase&lt;/code&gt; and one for &lt;code&gt;BusinessCollectionBase&lt;/code&gt;), only one check will have to be performed.  The circular reference will also be gone, because the class(es) that will implement &lt;code&gt;IUndoable&lt;/code&gt; interface, should then only check if their members implement &lt;code&gt;IUndoable&lt;/code&gt; interface as well.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;BusinessObject base class&lt;/h2&gt;&lt;br /&gt;Then, I’ve created an abstract base class &lt;code&gt;BusinessObject&lt;/code&gt; from which all the concrete business-objects of the problem-domain should inherit from.&lt;br /&gt;&lt;code&gt;BusinessObject&lt;/code&gt; implements the &lt;code&gt;IUndoable&lt;/code&gt; and the &lt;code&gt;&lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemComponentModelIEditableObjectClassTopic.asp" target="_blank"&gt;IEditableObject&lt;/a&gt;&lt;/code&gt; interfaces.&lt;br /&gt;The latter interface is necessary if you want to support data-binding.&lt;br /&gt;&lt;br /&gt;While mr Lhotka created 3 'base-classes' (&lt;code&gt;BusinessBase&lt;/code&gt; which inherits from &lt;code&gt;UndoableBase&lt;/code&gt; which in turn inherits from &lt;code&gt;BindableBase&lt;/code&gt;), I've decided to create only one base-class from which all my business objects will inherit from.  I've done this because, at this moment, I do not see the advantage of having the other 2 base-classes.&lt;br /&gt;&lt;!-- adsense --&gt;&lt;br /&gt;Since the &lt;code&gt;BusinessObject&lt;/code&gt; base-class implements the &lt;code&gt;IUndoable&lt;/code&gt; and &lt;code&gt;IEditableObject&lt;/code&gt; interfaces, this class is off-course responsible for the n-level undo support and for the databinding.&lt;br /&gt;Apart from that, &lt;code&gt;BusinessObject&lt;/code&gt; also contains some 'state tracking' functionality (which is necessary if you do not use an ORM-tool like NHibernate which does the state tracking for you).&lt;br /&gt;&lt;br /&gt;The rough skeleton of the &lt;code&gt;BusinessObject&lt;/code&gt; class looks like this:&lt;br /&gt;&lt;pre class="code-content"&gt;[Serializable]&lt;br /&gt;public abstract class BusinessObject : IUndoable, IEditableObject&lt;br /&gt;{&lt;br /&gt;    #region State Tracking functionality&lt;br /&gt;    #endregion&lt;br /&gt;&lt;br /&gt;    #region IUndoable implementation&lt;br /&gt;    #endregion&lt;br /&gt;&lt;br /&gt;    #region IEditableObject implementation&lt;br /&gt;    #endregion&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The class is abstract because it is offcourse intented that you do not use this class directly.  You should inherit your 'specific' businessobjects from this class.&lt;br /&gt;The class is also marked as 'Serializable'.  This is necessary if you want to use your business objects for instance in a remoting scenario.&lt;br /&gt;Another reason for the Serializable attribute will be clear soon, since Serialization is used to achieve the n-undo functionality.&lt;br /&gt;&lt;br /&gt;I still have to discuss the implementation of this class, so let's do it right away.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;State tracking&lt;/h3&gt;&lt;br /&gt;The state tracking code is quite simple.  We only need some flags and methods that take care of this.&lt;br /&gt;This code is quite simple and nothing (or very little) has been changed compared with the code that is contained in the &lt;code&gt;BusinessBase&lt;/code&gt; class of the CSLA.NET framework, so instead of commenting this code in this post, I'll refer to the 'Expert C# Business Objects' book for further information.&lt;br /&gt;&lt;pre class="code-content"&gt;#region State Tracking functionality&lt;br /&gt;&lt;br /&gt;private bool    _isNew     = true;&lt;br /&gt;private bool    _isDirty   = false;&lt;br /&gt;private bool    _isDeleted =false;&lt;br /&gt;&lt;br /&gt;protected void  MarkDirty()&lt;br /&gt;{&lt;br /&gt;    _isDirty = true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;protected void MarkClean()&lt;br /&gt;{&lt;br /&gt;    _isDirty = false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;protected void MarkNew()&lt;br /&gt;{&lt;br /&gt;    _isNew = true;&lt;br /&gt;    _isDeleted = false;&lt;br /&gt;    MarkDirty();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;internal void MarkDeleted()&lt;br /&gt;{&lt;br /&gt;    _isDeleted = true;&lt;br /&gt;    MarkDirty();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;protected void MarkOld()&lt;br /&gt;{&lt;br /&gt;    _isNew = false;&lt;br /&gt;    MarkClean();&lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;public bool IsNew&lt;br /&gt;{&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        return _isNew;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public bool IsDeleted&lt;br /&gt;{&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        return _isDeleted;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public virtual bool IsDirty&lt;br /&gt;{&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        return _isDirty;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endregion&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Implementing N-level undo support&lt;/h3&gt;&lt;br /&gt;Implementing the n-level undo support requires some more work and is a little bit more complicated then the state-tracking.&lt;br /&gt;Just like in the CSLA.NET framework, this functionality relies heavely on .NET reflection.  (In fact, I've played copy-cat here.  Apart from the usage of the &lt;code&gt;IUndoable&lt;/code&gt; interface, this code is almost identical as the code that Rockford Lhotka has written).&lt;br /&gt;&lt;br /&gt;Since the CSLA.NET framework is free to be downloaded (you can get it &lt;a href="http://www.lhotka.net/ArticleIndex.aspx?Area=Downloads" target="_blank"&gt;here&lt;/a&gt;), I'll only post some relevant code.  &lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;#region IUndoable implementation&lt;br /&gt;&lt;br /&gt;[NotUndoable]&lt;br /&gt;/// The stack that will keep track of all our states.&lt;br /&gt;private Stack _stateStack = new Stack();&lt;br /&gt;&lt;br /&gt;public void CreateSnapshot()&lt;br /&gt;{&lt;br /&gt;    Type         currentType;&lt;br /&gt;    Hashtable    state = new Hashtable();&lt;br /&gt;    FieldInfo[]  fields;&lt;br /&gt;    string       fieldName;&lt;br /&gt;&lt;br /&gt;    currentType = this.GetType();&lt;br /&gt;&lt;br /&gt;    do&lt;br /&gt;    {&lt;br /&gt;        // Get the members of this type.&lt;br /&gt;        fields = currentType.GetFields (BindingFlags.Public |&lt;br /&gt;                                        BindingFlags.NonPublic |&lt;br /&gt;                                        BindingFlags.Instance);&lt;br /&gt;&lt;br /&gt;        foreach( FieldInfo field in fields )&lt;br /&gt;        {&lt;br /&gt;            // If this field is declared in the current type&lt;br /&gt;            // and the field is undoable, keep track of it's state&lt;br /&gt;            if( field.DeclaringType == currentType &amp;&amp;&lt;br /&gt;                this.IsUndoableField (field) )&lt;br /&gt;            {&lt;br /&gt;               object fieldValue = field.GetValue(this);&lt;br /&gt;&lt;br /&gt;               // If the member implements IUndoable, cascade&lt;br /&gt;               // the call.&lt;br /&gt;               IUndoable uf = fieldValue as IUndoable;&lt;br /&gt; &lt;br /&gt;               if( uf != null )&lt;br /&gt;               {&lt;br /&gt;                   uf.CreateSnapshot();&lt;br /&gt;               }&lt;br /&gt;               else&lt;br /&gt;               {&lt;br /&gt;                   fieldName = currentType.Name + "." + field.Name;&lt;br /&gt;                   state.Add (fieldName, fieldValue);&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        currentType = currentType.BaseType;&lt;br /&gt;&lt;br /&gt;    }while( currentType != typeof(BusinessObject) );&lt;br /&gt;&lt;br /&gt;    // Now, use serialization to save the state in the&lt;br /&gt;    // statestack&lt;br /&gt;    MemoryStream buffer = new MemoryStream ();&lt;br /&gt;    BinaryFormatter fmt = new BinaryFormatter ();&lt;br /&gt;    fmt.Serialize (buffer, state);&lt;br /&gt;    _stateStack.Push (buffer.ToArray ());&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;#endregion&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you have a look at the code above, and compare it with the CSLA.NET code, you'll notice the slight difference.  &lt;br /&gt;The implementation of &lt;code&gt;RevertToPreviousState&lt;/code&gt; and &lt;code&gt;CommitSnapshot&lt;/code&gt; is also almost identical to the &lt;code&gt;UndoChanges&lt;/code&gt; and &lt;code&gt;AcceptChanges&lt;/code&gt; methods of the &lt;code&gt;UndoableBase&lt;/code&gt; class of the CSLA.NET framework.  Just as in the &lt;code&gt;CreateSnapshot&lt;/code&gt; method, the only change I've made, is checking for the IUndoable interface.&lt;br /&gt;The &lt;code&gt;RevertToPreviousState&lt;/code&gt; and &lt;code&gt;CommitSnapshot&lt;/code&gt; pop the last saved state from the stack.  &lt;code&gt;CommitSnapshot&lt;/code&gt; just throws it away, and calls &lt;code&gt;CommitSnapshot&lt;/code&gt; for every member of the class that is an &lt;code&gt;IUndoable&lt;/code&gt; type, while &lt;code&gt;ReverToPreviousState&lt;/code&gt; uses the last saved state to reset the value of all class-members to their previous state.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;[NotUndoable]&lt;/code&gt; attribute is a custom attribute that defines that a field that is decorated with this attribute should not be 'undoable'.&lt;br /&gt;The &lt;code&gt;IsUndoableField&lt;/code&gt; method is a private member method of &lt;code&gt;BusinessObject&lt;/code&gt;.  This method just checks whether or not the given field is decorated with the &lt;code&gt;NotUndoable&lt;/code&gt; Attribute.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;IEditableObject implementation&lt;/h3&gt;&lt;br /&gt;The IEditableObject implementation makes use of the IUndoable functionality.&lt;br /&gt;For databinding purposes, it must be possible that the state of an object is saved when a user changes something to the object.   This is necessary because the object must be reset in it's original state when the user decides to cancel the changes he has made.&lt;br /&gt;&lt;br /&gt;We also need a flag that indicates wether or not we have to save the current state.&lt;br /&gt;This is necessary because, in a databinding scenario, the &lt;code&gt;BeginEdit&lt;/code&gt; method is called on every databound control in where you change the (databound) content.&lt;br /&gt;Since it is not necessary to keep track of the changes on such a fine-grained level (we will want to keep track of the changes on a 'form level'), we control the 'state support' by this flag.&lt;br /&gt;&lt;pre class="code-content"&gt;#region IEditableObject Implementation&lt;br /&gt;&lt;br /&gt;[NotUndoable]&lt;br /&gt;private bool _bindingEdit = false;&lt;br /&gt;&lt;br /&gt;public void BeginEdit()&lt;br /&gt;{&lt;br /&gt;    if( _bindingEdit == false )&lt;br /&gt;    {&lt;br /&gt;        this.CreateSnapshot ();&lt;br /&gt;        _bindingEdit = true;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void CancelEdit()&lt;br /&gt;{&lt;br /&gt;    if( _bindingEdit )&lt;br /&gt;    {&lt;br /&gt;        this.RevertToPreviousState ();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void EndEdit()&lt;br /&gt;{&lt;br /&gt;   if( _bindingEdit )&lt;br /&gt;   {&lt;br /&gt;       this.CommitSnapshot ();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endregion&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, that's enough typing for today.  If you have comments, feel free to post them here or to send me an &lt;a href="javascript:spamFreeMail('frederik.gheysels')"&gt;email&lt;/a&gt;.&lt;br /&gt;In the next article of this series, I'll discuss the base classes that are used for collections of BusinessObjects.&lt;br /&gt;&lt;br /&gt;Since I have modified the CSLA.NET framework for learning purposes, the same &lt;a href="http://www.lhotka.net/Articles.aspx?id=201678c1-4111-4e1e-accb-06a19a088166" target="_blank"&gt;licence applies to it as the licence that applies to the CSLA.NET framework&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113673865064207914?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113673865064207914/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113673865064207914' title='6 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113673865064207914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113673865064207914'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/business-objects-framework-part-1_08.html' title='Business Objects Framework – Part 1'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113649511343609690</id><published>2006-01-05T22:01:00.000+01:00</published><updated>2006-01-05T22:07:15.920+01:00</updated><title type='text'>Airplanes....</title><content type='html'>&lt;div style="float:left;margin-right:5px"&gt;&lt;br /&gt;&lt;img src="http://users.pandora.be/fg/air.jpg" alt="airshow" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;If you like airplanes and airshows as much as I do, then, you should definitly check out &lt;a href="http://users.pandora.be/fg/airshow.mpg" target="_blank"&gt;this movie&lt;/a&gt; (39mb).  It's an advertorial for some airshow, really nice movie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113649511343609690?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113649511343609690/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113649511343609690' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113649511343609690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113649511343609690'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/airplanes.html' title='Airplanes....'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113621905754066784</id><published>2006-01-02T17:23:00.000+01:00</published><updated>2007-01-20T01:11:00.391+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>C# 3.0 Orcas : playing with LINQ</title><content type='html'>I’ve been playing around with the technology preview of C# 3.0 today.  The most important new feature of C# 3.0 is –without any doubt - &lt;acronym title="Language Integrated Query"&gt;LINQ&lt;/acronym&gt;.&lt;br /&gt;&lt;br /&gt;LINQ enables you to search through collections in an SQL – like way.&lt;br /&gt;&lt;br /&gt;For instance, instead of looping through a collection to find the Persons that live in the city that has the postal-code ‘9000’ like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;List&amp;lt;person&amp;gt; persons = new List&amp;lt;person&amp;gt;();&lt;br /&gt;&lt;br /&gt;persons.Add (new Person("Person1", "9000"));&lt;br /&gt;persons.Add (new Person("Person2", "9870"));&lt;br /&gt;persons.Add (new Person("Person3", "9000"));&lt;br /&gt;&lt;br /&gt;foreach( Person p in persons )&lt;br /&gt;{&lt;br /&gt;   if( p.PostalCode == "9000" )&lt;br /&gt;   {&lt;br /&gt;      Console.WriteLine (p.Name);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You will be able to get the same results, by writing the code like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;List&amp;lt;person&amp;gt; persons = new List&amp;lt;person&amp;gt;();&lt;br /&gt;&lt;br /&gt;persons.Add (new Person("Person1", "9000"));&lt;br /&gt;persons.Add (new Person("Person2", "9870"));&lt;br /&gt;persons.Add (new Person("Person3", "9000"));&lt;br /&gt;&lt;br /&gt;var result = from p in persons&lt;br /&gt;                 where p.PostalCode == "9000"&lt;br /&gt;                 select p.Name;&lt;br /&gt;&lt;br /&gt;foreach( string name in result )&lt;br /&gt;{&lt;br /&gt;  Console.WriteLine (name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;adsense&gt;&lt;br /&gt;You can imagine that, for complex queries, using LINQ can be an advantage since it keeps your code more readable / understandable.&lt;br /&gt;&lt;br /&gt;In order to be able to deliver this functionality, Microsoft had to add some other features to the language:&lt;br /&gt;&lt;h2&gt;Implicit types&lt;/h2&gt;&lt;br /&gt;C# 3.0 introduces the &lt;code&gt;var&lt;/code&gt; keyword.  If you take a look at the LINQ code example, you’ll see that I’ve used this keyword.&lt;br /&gt;&lt;br /&gt;With this keyword, you don’t have to declare the type of the variable.  So, when I first saw this, I thought ‘Oh no, is this the same like the ‘variant’ type in VB, and is C# going ‘the VB way’ ?&lt;br /&gt;Luckely, Microsoft has defined some ‘rules’:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;When declaring a ‘var’, you’ll have to initialize the variable&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You cannot initialize the variable to null.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;So, these restrictions save us from the ‘variant’, because, because of these rules, the type of the variable is specified by the initializer.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;var&lt;/code&gt; keyword can be handy in some situations.   For instance, suppose you want a &lt;code&gt;Dictionary&lt;/code&gt; object like this:&lt;br /&gt;&lt;pre class="code-content"&gt;Dictionary&amp;lt;CustomerKey, Customer&amp;gt; custs = new Dictionary&amp;lt;CustomerKey, Customer&amp;gt; ();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You’ll be able to write it in a shorter way like this:&lt;br /&gt;&lt;pre class="code-content"&gt;var custs = new Dictionary&amp;lt;CustomerKey, Customer&amp;gt; ();&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Extension Methods&lt;/h2&gt;&lt;br /&gt;Extension methods allow you to ‘extend’ the functionality of a class without the need to change (and recompile) that class, and, without the need to create a subclass of that class (which is not possible if the class that you want to inherit from, is &lt;code&gt;sealed&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Suppose you want to extent the Person class I’ve used in the previous example using an extension method, you’ll have to do it like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-content"&gt;public static class PersonExtension&lt;br /&gt;{&lt;br /&gt;  public static string GetPostalCodeAndName( this Person p )&lt;br /&gt;  {&lt;br /&gt;       return  p.PostalCode + " " + p.Name;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An extension method is a static method with at least one argument: The first argument of the extension method must be a variable of the type that you want to extend, and, it must be preceded by the this keyword.&lt;br /&gt;&lt;br /&gt;The extension method can then be used like this:&lt;br /&gt;&lt;pre class="code-content"&gt;Person p = new Person("Person1", "9000");&lt;br /&gt;&lt;br /&gt;Console.WriteLine (p.GetPostalCodeAndName());&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Luckely, you’re not able to access the non-public members of the class in the extension method.&lt;br /&gt;&lt;h2&gt;Anonymous types and Initializers&lt;/h2&gt;&lt;br /&gt;These 2 features are also added to the c# specification in order to make LINQ possible.   However, apart from that, I do not see why I’m going to use these 2 features, so I’m not going to discuss them here.&lt;br /&gt;You’ll find more information about these features (and about the other new C# 3.0 features) on the &lt;a href="http://msdn.microsoft.com/vcsharp/future/" target="_blank"&gt;MSDN&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;DLINQ&lt;/h2&gt;&lt;br /&gt;DLINQ is a MS research project that makes use of the LINQ technology.  It allows the developer to query a database using similar statements like the ones you use with LINQ instead of using embedded SQL or calling stored procedures.&lt;br /&gt;&lt;br /&gt;I haven’t played with DLINQ (yet), but anyway, I’m a little bit sceptical on it.&lt;br /&gt;From what I’ve seen, you have to specify mapping information &lt;em&gt;in&lt;/em&gt; your ‘domain object’, so that messes things a little bit up. &lt;br /&gt;I also wonder how this will behave with a complex domain model; in applications with a complex domain model, the domain model is not a one-to-one mapping of the database.&lt;br /&gt;Anyway, it’s an experimental project, and we’ll see what the future brings.&lt;br /&gt;&lt;br /&gt;If you want to experiment with LINQ, you can get the technology preview &lt;a href="http://download.microsoft.com/download/4/7/0/4703eba2-78c4-4b09-8912-69f6c38d3a56/linq%20preview.msi"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update:&lt;/strong&gt;The C# code that used generics was not shown properly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113621905754066784?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113621905754066784/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113621905754066784' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113621905754066784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113621905754066784'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/c-30-orcas-playing-with-linq.html' title='C# 3.0 Orcas : playing with LINQ'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113619981674044792</id><published>2006-01-02T12:02:00.000+01:00</published><updated>2006-01-02T12:03:36.740+01:00</updated><title type='text'>Happy New Year!</title><content type='html'>I'd like to wish you all the best for 2006!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113619981674044792?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113619981674044792/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113619981674044792' title='0 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113619981674044792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113619981674044792'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2006/01/happy-new-year.html' title='Happy New Year!'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113588581395963870</id><published>2005-12-29T20:27:00.000+01:00</published><updated>2007-01-20T01:10:14.491+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio.NET'/><title type='text'>Improving productivity in Visual Studio.NET</title><content type='html'>John Skeet has written a &lt;a href="http://msmvps.com/blogs/jon.skeet/archive/2005/12/22/79631.aspx" target="_blank"&gt;nice article&lt;/a&gt; in where he compares Visual Studio.NET and Eclipse.   As we all know, VS.NET 2005 is the first IDE brought to us by Microsoft which gives us built-in refactoring support (finally).  While this is a nice improvement, you'll learn from the article that other IDE's still have more to offer then Visual Studio.NET.&lt;br /&gt;&lt;br /&gt;John mentionned in his article a tool, called &lt;a href="http://www.usysware.com/dpack/" target="_blank"&gt;DPack&lt;/a&gt;.  This is a free VS.NET add-in, which allows you to navigate through your source more quickly.&lt;br /&gt;&lt;br /&gt;The add-in offers a 'Solution explorer', which allows you to navigate to a specific class, without scrolling through the solution explorer or the class view.&lt;br /&gt;It just takes one keystroke to bring it up, and by starting typing the name of the type to which you want to go, you can quickly go to the file that contains that type.&lt;br /&gt;There's also a 'Code Browser' which allows you to browse quicker through the current code-file.&lt;br /&gt;&lt;br /&gt;DPack also offers some shortcuts which allow you to navigate through your class more efficiently; Alt-Up arrow for instance takes you to the previous method signature, and Alt-Down Arrow brings you to the next method signature.&lt;br /&gt;&lt;br /&gt;I've tried the VS.NET 2003 version on a fairly large solution at work, and it performs quite well.&lt;br /&gt;&lt;br /&gt;Oh, it is also able to tell you how many code-lines and comment-lines your project contains.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113588581395963870?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113588581395963870/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113588581395963870' title='2 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113588581395963870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113588581395963870'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2005/12/improving-productivity-in-visual.html' title='Improving productivity in Visual Studio.NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113585349793714626</id><published>2005-12-29T11:27:00.000+01:00</published><updated>2007-01-20T01:10:28.463+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>Self-portrait</title><content type='html'>Well, I've told that this blog would also be about photography, but, until this moment, there's not even one picture present.&lt;br /&gt;&lt;br /&gt;So, let's start off with a self-portrait I've taken 2 months ago.&lt;br /&gt;&lt;div style="padding-left:20px"&gt;&lt;br /&gt;   &lt;img src="http://users.pandora.be/fgzone/pics/blog/zelfportr.jpg" alt="selfportrait"&gt;&lt;/img&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This picture has been taken with an analog Nikon SLR camera with a 50mm lens.  I've used a Kodak Tri-X 400 ISO film.   I have printed the photo myself.&lt;br /&gt;&lt;br /&gt;Most of my pictures are taken with an analog SLR camera, since I do not have (yet) a DSLR.&lt;br /&gt;So, if I want to put them on the Internet, I have to scan them.   Since I do not own a super-quality scanner, the quality of my digital -scanned- images, is not as good as the quality of my original prints.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113585349793714626?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113585349793714626/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113585349793714626' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113585349793714626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113585349793714626'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2005/12/self-portrait.html' title='Self-portrait'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113576725759182004</id><published>2005-12-28T11:36:00.000+01:00</published><updated>2007-01-20T01:12:51.330+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Avoiding SQL injection and date problems with queries in .NET</title><content type='html'>I've already written a little piece about this subject a while ago on a dutch board.&lt;br /&gt;Since I've seen that a lot of people are still writing embedded queries in their applications using string-concatenation, I've decided to write a little article about it on my blog.&lt;br /&gt;&lt;br /&gt;First things first:&lt;br /&gt;Writing your embedded queries using string-concatenation like this:&lt;br /&gt;&lt;pre class="code-content"&gt;string sqlQuery = "SELECT column FROM table WHERE column = \'" + &lt;br /&gt;                  aVariable +"\'";&lt;br /&gt;&lt;/pre&gt;is &lt;strong&gt;bad practice&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;It is not easy to read, it's hard to maintain, it does not offer optimal performance but, most of all, it creates a security-leak; your query is now vulnerable to SQL-injection.&lt;br /&gt;&lt;br /&gt;The variable 'aVariable' can contain anything, and, if you do not check the contents of the variable, some unpleasant things can happen.&lt;br /&gt;Suppose that aVariable contains this:&lt;br /&gt;&lt;pre class="code-content"&gt;a';drop table tablename;--&lt;/pre&gt;&lt;br /&gt;Now, if you haven't checked the contents of the variable, and took some appropriate action, the query that will be executed will look like this:&lt;br /&gt;&lt;pre class="code-content"&gt;SELECT column FROM table WHERE column = 'a';drop table tablename;--'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, 2 queries will be executed:&lt;br /&gt;first, a select will take place, and then, a drop table statement is executed.   As you can imagine, you -or your employer- will not be happy with this.&lt;br /&gt;&lt;adsense&gt;&lt;br /&gt;Another problem with string-concatenation comes to the surface when you want to filter on a date.&lt;br /&gt;There are numerous ways of representing a date: dd/mm/yyyy, yyyy-mm-dd, mm/dd/yyyy, etc...&lt;br /&gt;So, if you use string concatenation to create a query that filters on a date, you'll have to make sure that you use the same date-notation in your query as the date-notation that your DBMS uses.&lt;br /&gt;&lt;br /&gt;So, to avoid these date and SQL injection problems, you can ofcourse write a bunch of code that tackles these issues but...&lt;br /&gt;Why don't we let the database take care of these problems ?&lt;br /&gt;&lt;br /&gt;This can easily be achieved by using &lt;em&gt;parametrized queries&lt;/em&gt; instead of writing the query using string contatenation.&lt;br /&gt;&lt;br /&gt;So, instead of writing your query like this:&lt;br /&gt;&lt;pre class="code-content"&gt;string sqlQuery = "INSERT INTO customer " +&lt;br /&gt;                  "( custName, city ) " +&lt;br /&gt;                  "VALUES " +&lt;br /&gt;                  "(\'" + name + "\', \'" + city + "\')";&lt;br /&gt;&lt;/pre&gt;you can write it like this in .NET (when you make use of the SqlClient)&lt;br /&gt;&lt;pre class="code-content"&gt;string sqlQuery = "INSERT INTO customer " +&lt;br /&gt;                  "( custName, birthdate, city ) " +&lt;br /&gt;                  "VALUES " +&lt;br /&gt;                  "(@p_name, @p_birthdate, @p_city")";&lt;br /&gt;&lt;/pre&gt;Or, if you use the OleDb namespace, you'll have to write it like this:&lt;br /&gt;&lt;pre class="code-content"&gt;string sqlQuery = "INSERT INTO customer " +&lt;br /&gt;                  "( custName, birthdate, city ) " +&lt;br /&gt;                  "VALUES " +&lt;br /&gt;                  "(?, ?, ?)";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, instead of 'inserting' the variables directly into the query, we use some kind of a 'placeholder' (a parameter).&lt;br /&gt;&lt;br /&gt;Now, we still can't execute the query, because we need to give the DbCommand that we will use some additional information about the parameters. (We need to specify the values for instance).&lt;br /&gt;&lt;br /&gt;This is the code that is needed to execute &lt;pre class="code-content"&gt;string sqlQuery = "INSERT INTO customer " +&lt;br /&gt;                  "( custName, birthdate, city ) " +&lt;br /&gt;                  "VALUES " +&lt;br /&gt;                  "(@p_name, @p_birthdate, @p_city")";&lt;br /&gt;&lt;br /&gt;SqlCommand cmd = new SqlCommand(theConnectionObj);&lt;br /&gt;cmd.Parameters.Add ("@p_name",     SqlDbType.Varchar);&lt;br /&gt;cmd.Parameters.Add ("@p_city",     SqlDbType.Varchar);&lt;br /&gt;cmd.Parameters.Add ("@p_birthdate, SqlDbType.DateTime);&lt;br /&gt;&lt;br /&gt;cmd.Parameters["@p_name"].Value = custName;&lt;br /&gt;cmd.Parameters["@p_birthdate"].Value = dateOfBirth;&lt;br /&gt;cmd.Parameters["@p_city"].Value = cityName;&lt;br /&gt;&lt;br /&gt;cmd.ExecuteNonQuery();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, the Parameters property of the DbCommand is used to give some information about the parameters that our query contains.  For each parameter, we specify the datatype and we give it a value.&lt;br /&gt;Now, we do not have to worry about escaping quotes that may appear in the customers' name, we do not have to worry about the date-notation of the date of birth, we do not have to worry about possible sql statements that are injected into the query, etc... &lt;br /&gt;&lt;br /&gt;(Please, check the MSDN for more information about the Parameters collection, and the overloaded 'Add' methods.  It is possible to specify output-parameters, etc..).&lt;br /&gt;&lt;br /&gt;If you use the OleDb classes instead of the SqlClient classes, you might wonder how you will be able to make the distinction between the different parameters in one query, since you're not able to use named parameters. (You'll use a question mark as a place-holder).&lt;br /&gt;Well, with the OleDb classes, you'll have to add the parameters in the correct order.&lt;br /&gt;So, the first parameter in your query, must also be the first one that you'll add to your 'Parameter' collection.&lt;br /&gt;&lt;br /&gt;Parametrized queries are not only available in .NET, they're available in any serious language.   How to use them, depends from language to language, so, refer to the documentation / manual of the language you use to see how to use them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113576725759182004?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113576725759182004/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113576725759182004' title='1 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113576725759182004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113576725759182004'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2005/12/avoiding-sql-injection-and-date.html' title='Avoiding SQL injection and date problems with queries in .NET'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20196010.post-113561082727233655</id><published>2005-12-26T16:02:00.000+01:00</published><updated>2005-12-26T23:15:48.090+01:00</updated><title type='text'>First post...</title><content type='html'>Well, I finally took the decision to jump in the 'blogosphere', so here it is, my weblog.&lt;br /&gt;&lt;br /&gt;While the decision to blog has been taken a while ago, my initial idea was to create my own website using ASP.NET.   However, the lack of spare time to develop this site, and the costs to host an ASP.NET driven website, forced me to start a blog on blogger.com&lt;br /&gt;&lt;br /&gt;Anyway, maybe my own website will come over time, but, in the meantime, check this weblog regularly for my views on software development, photography, etc...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20196010-113561082727233655?l=fgheysels.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fgheysels.blogspot.com/feeds/113561082727233655/comments/default' title='Reacties plaatsen'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20196010&amp;postID=113561082727233655' title='3 reacties'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113561082727233655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20196010/posts/default/113561082727233655'/><link rel='alternate' type='text/html' href='http://fgheysels.blogspot.com/2005/12/first-post.html' title='First post...'/><author><name>Frederik Gheysels</name><uri>http://www.blogger.com/profile/15416462808733991725</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
