<?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:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6817808781210919491</id><updated>2010-01-29T07:16:18.729+01:00</updated><title type='text'>Mirosław Jedynak - professional .NET blog</title><subtitle type='html'>News, interesting features, resolved problems coupled with .Net Framework</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>7</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-7817107051456960377</id><published>2009-02-03T17:52:00.015+01:00</published><updated>2009-02-04T13:20:01.251+01:00</updated><title type='text'>Static typed property names</title><content type='html'>String names of properties is common problem in dynamically developed software.&lt;br /&gt; &lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Common usages of property names literals&lt;/span&gt;&lt;br /&gt;Common usages of such literal are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Property names in Nhibernate queries&lt;/b&gt;&lt;br /&gt;Suppose you want to query all user for specific value of property “LastProjectCode” with detached criteria class:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&lt;br /&gt;&lt;span style="color: #2b91af;"&gt;DetachedCriteria&lt;/span&gt; query = &lt;span style="color: #2b91af;"&gt;DetachedCriteria&lt;/span&gt;.For(&lt;span style="color: blue;"&gt;typeof&lt;/span&gt; (&lt;span style="color: #2b91af;"&gt;User&lt;/span&gt;)).Add(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;.Eq(&lt;span style="color: #a31515;"&gt;&amp;quot;&lt;b&gt;LastProjectCode&lt;/b&gt;&amp;quot;&lt;/span&gt;, 12345));&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Implementing INotifyPropertyChanged interface&lt;/b&gt;&lt;br /&gt;Standard implementation unfortunately have to pass property name as literal when event is raised. It looks like below:&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; LastProjectCode&lt;/p&gt;&lt;p style="margin: 0px;"&gt;{&lt;/p&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;get&lt;/span&gt; { &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;this&lt;/span&gt;.lastProjectCode; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;set&lt;/span&gt;{&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (&lt;span style="color: blue;"&gt;this&lt;/span&gt;.lastProjectCode != &lt;span style="color: blue;"&gt;value&lt;/span&gt;)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;this&lt;/span&gt;.lastProjectCode = &lt;span style="color: blue;"&gt;value&lt;/span&gt;;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; OnPropertyChanged(&lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;PropertyChangedEventArgs&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;&amp;quot;&lt;b&gt;LastProjectCode&lt;/b&gt;&amp;quot;&lt;/span&gt;));&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Providing error information with IDataErrorInfo&lt;/b&gt;&lt;br /&gt;Component using entity (like user) queries it for error by calling property &lt;i&gt;this[string columnName]&lt;/i&gt;. Implementation is usually based on many if condition checking for column name like below:&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; &lt;span style="color: blue;"&gt;this&lt;/span&gt;[&lt;span style="color: blue;"&gt;string&lt;/span&gt; columnName]&lt;/p&gt;&lt;p style="margin: 0px;"&gt;{&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;get&lt;/span&gt;{&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;string&lt;/span&gt; result = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (columnName == &lt;span style="color: #a31515;"&gt;&amp;quot;&lt;b&gt;LastProjectCode&lt;/b&gt;&amp;quot;&lt;/span&gt; &amp;amp;&amp;amp; LastProjectCode &amp;lt; 0)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; result = &lt;span style="color: #a31515;"&gt;&amp;quot;Last project code cannot be negative.&amp;quot;&lt;/span&gt;;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; result;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Disadvantages&lt;/span&gt;&lt;br /&gt;All mentioned situation have at least 2 major disadvantages&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Possibility to mistype name&lt;/li&gt;&lt;li&gt;Hard refactoring (since refactoring cannot determine if name of property in literal is really name of property)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;To avoid it I have wrote TypeHelper class where you can find out property name in static way like this: &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Solution&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;string&lt;/span&gt; propertyName = &lt;span style="color: #2b91af;"&gt;TypeHelper&lt;/span&gt;.GetPropertyName&amp;lt;&lt;span style="color: #2b91af;"&gt;User&lt;/span&gt;&amp;gt;(u =&amp;gt; u.LastProjectCode);&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Now both disadvantages disappeared&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You cannot misspell word since compiler checks it&lt;/li&gt;&lt;li&gt;Refactoring is possible because refactoring engine knows all type names at compile type and none of them is "hidden" in literals&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Usage&lt;/span&gt;&lt;br /&gt;Here is how you can extract property names and PropertyInfo object in staticly typed way:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;string&lt;/span&gt; propertyName = &lt;span style="color: #2b91af;"&gt;TypeHelper&lt;/span&gt;.GetPropertyName&amp;lt;&lt;span style="color: #2b91af;"&gt;User&lt;/span&gt;&amp;gt;(u =&amp;gt; u.LastProjectCode);&lt;/p&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; property1 = &lt;span style="color: #2b91af;"&gt;TypeHelper&lt;/span&gt;.GetProperty((&lt;span style="color: #2b91af;"&gt;SomeClass&lt;/span&gt; o) =&amp;gt; o.InstanceProperty.Length);&lt;/p&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; property2 = &lt;span style="color: #2b91af;"&gt;TypeHelper&lt;/span&gt;.GetProperty(() =&amp;gt; &lt;span style="color: #2b91af;"&gt;SomeClass&lt;/span&gt;.StaticProperty.Length);&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Implementation&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;br /&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;TypeHelper&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;{&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; GetPropertyInternal(&lt;span style="color: #2b91af;"&gt;LambdaExpression&lt;/span&gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af;"&gt;MemberExpression&lt;/span&gt; memberExpression;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;if&lt;/span&gt; (p.Body &lt;span style="color: blue;"&gt;is&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;UnaryExpression&lt;/span&gt;)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af;"&gt;UnaryExpression&lt;/span&gt; ue = (&lt;span style="color: #2b91af;"&gt;UnaryExpression&lt;/span&gt;)p.Body;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; memberExpression = (&lt;span style="color: #2b91af;"&gt;MemberExpression&lt;/span&gt;)ue.Operand;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;else&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; memberExpression = (&lt;span style="color: #2b91af;"&gt;MemberExpression&lt;/span&gt;)p.Body;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; (&lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt;)(memberExpression).Member;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; GetPropertyName&amp;lt;TObject&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;TObject, &lt;span style="color: blue;"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyNameInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; GetPropertyName&amp;lt;TObject, T&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;TObject, T&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyNameInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; GetPropertyName&amp;lt;T&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;T&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyNameInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;string&lt;/span&gt; GetPropertyNameInternal(&lt;span style="color: #2b91af;"&gt;LambdaExpression&lt;/span&gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyInternal(p).Name;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; GetProperty&amp;lt;TObject&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;TObject, &lt;span style="color: blue;"&gt;object&lt;/span&gt;&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; GetProperty&amp;lt;TObject, T&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;TObject, T&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;PropertyInfo&lt;/span&gt; GetProperty&amp;lt;T&amp;gt;(&lt;span style="color: #2b91af;"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af;"&gt;Func&lt;/span&gt;&amp;lt;T&amp;gt;&amp;gt; p)&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue;"&gt;return&lt;/span&gt; GetPropertyInternal(p);&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p style="margin: 0px;"&gt;}&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-7817107051456960377?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/7817107051456960377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=7817107051456960377' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/7817107051456960377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/7817107051456960377'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2009/02/static-typed-propety-names.html' title='Static typed property names'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-9067882572703957482</id><published>2008-06-06T13:25:00.013+02:00</published><updated>2008-12-10T22:25:23.899+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='aspnetmvc'/><category scheme='http://www.blogger.com/atom/ns#' term='aspnet'/><title type='text'>ASP.NET MVC in legacy solutions (classic Web Forms)</title><content type='html'>ASP.NET MVC is new approach, when building web sites using ASP.NET. It’s still quite new and at this moment version &lt;span style="font-weight: bold;"&gt;ASP.NET MVC Preview 3&lt;/span&gt; has been released. All examples in this article conform to this version.&lt;br /&gt;&lt;br /&gt;While MVC framework is still evolving more and more people start using it even in production environment. Creating new application from scratch with ASP.NET MVC is explained in many places and won’t be covered here. The biggest challenge is to introduce new approach into existing solution and then migrate site day by day, page by page to MVC. Before describing required steps which must be taken to use classic Web Forms and MVC in parallel way some assumptions/limitations must be introduced.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Assumptions/limitations:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pages using MVC controller cannot use classic Web Forms Master Page – new Master Page, which looks same as old one must be created and all changes must be synchronized in both files,&lt;/li&gt;&lt;li&gt;Classic controls which depends on post back data cannot be used – controls which does not used ViewState will work properly if tag &amp;lt;form runat="”server”"/&amp;gt; surrounds page content,&lt;/li&gt;&lt;li&gt;All links to new pages which has been rewritten to ASP.NET MVC must be updated – you may also consider using &lt;a href="http://blog.eworldui.net/post/2008/04/ASPNET-MVC---Legacy-Url-Routing.aspx"&gt;LegacyRouteHandler&lt;/a&gt;, which redirects user from old pages to new one.&lt;/li&gt;&lt;li&gt;It’s strongly recommended that, ale page content is rewritten to MVC – it means, you shouldn’t use classic controls together with MVC Components. Disadvantage is that it may lead to temporary duplicating functionality, but clever with refactoring code duplication is minimal.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;1.  Convert VS 2005 Web Project to VS 2008 Web Application&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ASP.NET MVC may be used only with Web application so all solutions using Web Projects must be converted (both VS 2005 and VS 2008 projects). In details it described &lt;a href="http://msdn.microsoft.com/en-us/library/aa983476.aspx"&gt;here&lt;/a&gt;, but in short:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a new Visual Studio 2008 Web application project in a new solution,&lt;/li&gt;&lt;li&gt;Add references to a Visual Studio 2008 Web application project,&lt;/li&gt;&lt;li&gt;Copy Web site project files to a Web application project,&lt;/li&gt;&lt;li&gt;Convert pages and classes to use partial classes in a Web application project,&lt;/li&gt;&lt;li&gt;Run the Web Application Project&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;2. Create routes definition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Routes definition defines which route handler and which controller will be invoked for specific URL. Goal of this section it to describe configuration which works for both Web Forms and MVC&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Reference MVC assemblies: &lt;span style="font-style: italic;"&gt;System.Web, System.Web.Abstractions, System.Web.Routing&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Define routes in Application.Start event – notice adding “.mvc” appendix to route definition which must be handled by MVC framework. Since none of current pages/items uses .mvc extension (usually aspx, ashx) old request will be treated as old Web Forms requests.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/SEkgS9DP8XI/AAAAAAAAAKw/9Moh_3IdiE4/s1600-h/0_application_start_asp_net_mvc.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/SEkgS9DP8XI/AAAAAAAAAKw/9Moh_3IdiE4/s400/0_application_start_asp_net_mvc.GIF" alt="" id="BLOGGER_PHOTO_ID_5208729954028614002" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;3. Update web.config&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You must add some new sections to web config to start using MVC framework. Steps below are necessary however some optional section has been omitted for clearance.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Add MVC assemblies to &lt;span style="font-style: italic;"&gt;&lt;system.web&gt;system.web\compilation&lt;compilation&gt;\assemblies&lt;/compilation&gt;&lt;/system.web&gt;&lt;/span&gt;&lt;span&gt;&lt;system.web&gt;&lt;compilation&gt;&lt;assemblies&gt; section&lt;br /&gt;&lt;/assemblies&gt;&lt;/compilation&gt;&lt;/system.web&gt;&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/SEkgdvb_qZI/AAAAAAAAAK4/fspr_Mw1ksY/s1600-h/1_compilation_assemblies_asp_net_mvc.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/SEkgdvb_qZI/AAAAAAAAAK4/fspr_Mw1ksY/s400/1_compilation_assemblies_asp_net_mvc.gif" alt="" id="BLOGGER_PHOTO_ID_5208730139352869266" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Add namespaces to&lt;span style="font-style: italic;"&gt; pages&lt;pages&gt;\namespaces&lt;namespaces&gt;&lt;/namespaces&gt;&lt;/pages&gt;&lt;/span&gt; section&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/SEkgzvISDlI/AAAAAAAAALA/IygIV7io7FQ/s1600-h/2_pages_namespaces_asp_net_mvc.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/SEkgzvISDlI/AAAAAAAAALA/IygIV7io7FQ/s400/2_pages_namespaces_asp_net_mvc.gif" alt="" id="BLOGGER_PHOTO_ID_5208730517227310674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Add Http Handlers and Http Modules&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/SEkg5Xc7XhI/AAAAAAAAALI/gVzdbVFMeEY/s1600-h/3_http_handlers_and+modules_asp_net_mvc.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/SEkg5Xc7XhI/AAAAAAAAALI/gVzdbVFMeEY/s400/3_http_handlers_and+modules_asp_net_mvc.JPG" alt="" id="BLOGGER_PHOTO_ID_5208730613950668306" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you plan use it with copy &lt;span style="font-style: italic;"&gt;&lt;system.webserver&gt;&lt;/system.webserver&gt;&lt;/span&gt; section &lt;span style="font-style: italic;"&gt;system.webserver&lt;/span&gt; from default web.config for Visual Studio 2008&lt;br /&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/6817808781210919491-9067882572703957482?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/9067882572703957482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=9067882572703957482' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/9067882572703957482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/9067882572703957482'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/06/aspnet-mvc-in-legacy-solutions-classic.html' title='ASP.NET MVC in legacy solutions (classic Web Forms)'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ptUML5AvT6I/SEkgS9DP8XI/AAAAAAAAAKw/9Moh_3IdiE4/s72-c/0_application_start_asp_net_mvc.GIF' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-7566585387217267468</id><published>2008-04-19T14:41:00.009+02:00</published><updated>2008-12-10T22:25:24.047+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint 2007'/><title type='text'>Sharepoint 2007: Creating page from layout with “No parameterless constructor defined for this object” exception</title><content type='html'>&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold; "&gt;Symptom&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;After creating page from layout “No parameterless constructor defined for this object” exception was thrown.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/SAnpW52XP5I/AAAAAAAAAJg/vKx8QoUVeiM/s1600-h/sharepoint_error.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/SAnpW52XP5I/AAAAAAAAAJg/vKx8QoUVeiM/s400/sharepoint_error.JPG" alt="" id="BLOGGER_PHOTO_ID_5190936625216438162" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Description&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;I have created site definition using &lt;a href="http://msdn2.microsoft.com/en-us/library/ms447717.aspx"&gt;webtemp.xml&lt;/a&gt;. Inside site definition I have had few features activated and one of them created layouts for publishing template. Inside some I have added section for automatically adding default webparts using &lt;a href="http://msdn2.microsoft.com/en-us/library/ms997833.aspx"&gt;AllUsersWebPart element&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;On development environment manually activating features worked perfect, but when I followed steps below:&lt;br /&gt;• Create site based on custom site definition&lt;br /&gt;• Create paged based on custom layout&lt;br /&gt;I saw error like this:&lt;br /&gt;&lt;br /&gt;&lt;div  style=";background-color: rgb(255, 255, 153)"&gt;&lt;code&gt;&lt;span style="font-size:85%;"&gt;No parameterless constructor defined for this object.   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean&amp;amp; canBeCached, RuntimeMethodHandle&amp;amp; ctor, Boolean&amp;amp; bNeedSecurityCheck)&lt;br /&gt;at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)&lt;br /&gt;at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)&lt;br /&gt;at System.Activator.CreateInstance(Type type, Boolean nonPublic)&lt;br /&gt;at System.Activator.CreateInstance(Type type)&lt;br /&gt;at Microsoft.SharePoint.WebPartPages.SPWebPartSerializer.get_DefaultControl()&lt;br /&gt;at Microsoft.SharePoint.WebPartPages.BinaryWebPartSerializer.Serialize(PersonalizationScope scope)&lt;br /&gt;at Microsoft.SharePoint.WebPartPages.BinaryWebPartSerializer.get_Links()&lt;br /&gt;at Microsoft.SharePoint.WebPartPages.SPWebPartManager.AddWebPartToStore(WebPart webPart, Int32 viewId, String viewGuid)&lt;br /&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Semi-solution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;I found that removing template from “Master page and layout gallery” and adding it manually again fixed that problem. Of course this solution may be acceptable in development environment, but cannot be used in production environment with a lot of templates.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Solution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After few hours of investigation I found that site definition didn’t register web parts as safe controls. It wasn’t easy to discover and description of error didn’t make it easier.&lt;br /&gt;Now, you may ask why semi solution worked? After removing layout from gallery AllUsersWebPart section has been reseted and there were no default webparts on page.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-7566585387217267468?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/7566585387217267468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=7566585387217267468' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/7566585387217267468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/7566585387217267468'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/04/sharepoint-2007-creating-page-form.html' title='Sharepoint 2007: Creating page from layout with “No parameterless constructor defined for this object” exception'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ptUML5AvT6I/SAnpW52XP5I/AAAAAAAAAJg/vKx8QoUVeiM/s72-c/sharepoint_error.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-8310356312666578711</id><published>2008-04-04T13:13:00.007+02:00</published><updated>2008-12-10T22:25:24.284+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cruise control .net'/><category scheme='http://www.blogger.com/atom/ns#' term='msbuild'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>6 steps to successful Continuous Integration</title><content type='html'>In early seventies, when computer aided projects become bigger and bigger, more independent companies were involved. After few months of development there was a special phase called integration. It was the hardest task to integrate all solutions and it wasn’t uncommon that project was successful until it suddenly fails due to integration hell.&lt;br /&gt;&lt;br /&gt;Cure for this illness is Continuous Integration – everyone should integrate with rest not after months but continuously – each week, each day, each hour – as often as possible. This practice helps avoiding problems later. It’s so called “pay me now or pay me more later”.&lt;br /&gt;&lt;br /&gt;To increase chance for project it should be integrated continuously. In the rest of article I will introduce 6 practices, which should be used to integrate continuously. Depending on the project and your attitude you can choose only some of described practices, but I strongly advice to fulfill preceding practices before using next one.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/R_YOjukDEpI/AAAAAAAAAJQ/xRYGO3oYkyM/s1600-h/continous_integration_establishment_vs_maintanance.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/R_YOjukDEpI/AAAAAAAAAJQ/xRYGO3oYkyM/s400/continous_integration_establishment_vs_maintanance.GIF" alt="" id="BLOGGER_PHOTO_ID_5185348027921142418" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-style: italic;"&gt;Establish vs. maintain cost for Continuous Integration&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Chart above shows relationship between cost of initial phase and cost of work required to maintain.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;1. Use source code repository&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It’s first and required step to start using continuous integration. Initial cost is not so small because you need dedicated machine and backup policy for “heart of software company”. Also each developer must be aware of source control existence and install so on each machine. Consider using Svn, Cvs, Team System.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;2. Introduce check-in policy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Check in policy may be introduced after establishing source code repository – quite easy to introduce but some effort is required to follow. Firstly it involves storing all necessary files in repository. Secondly, each developer should check-in as often as possible completed part of work.  Frequently integration show problem early and either solution is easy or rollback is necessary for only small part of solution.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;3. Automate build&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next step is to perform build automatically – no more than one action should be required to start it. If developer must do more things than he forgets about it or deliberately skips it to save time. Perfect solution is when after each commit build is automatically triggered.  Cost of using it is very small after establishment so I strongly advice to use it in all projects.&lt;br /&gt;Consider using Nant, Msbuild for scripting build process and CruiseControl.Net, Microsoft Team System or Team City as Continous integration server.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;4. Create auto-deployable test environment&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After each build new version of binaries should be automatically deployed to test server. It helps improving better integration with customer – after each fix customer may verify new version. Some work is necessary at the beginning but it’s really worth this effort in later phases of project.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;5. Use code quality analysis&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Using code analysis is very important in big project with many developers. It helps preserving coding conventions and constantly monitors code. If any time some suspicious code is committed warning should be generated. It differs from code reviews, because here approval or disapproval must be done automatically. Trigger for generating warning should be adjusted for each project or for whole company. Consider using NDepend, FxCop, Simian&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;6. Use unit test&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Although I’m a big fan of XP and TDD I must admit that this is the most time consuming practice. At the beginning it requires reasonably small amount of effort, but when project goes on more and more time developers spend on writing, refactoring and verifying test cases. Only some companies can reach this level of maturity and can enjoy all advantages of continuous integration. Consider using NUnit, Team System Testing, TypeMock, RhinoMock, NMock and many others.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-8310356312666578711?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/8310356312666578711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=8310356312666578711' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/8310356312666578711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/8310356312666578711'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/04/6-steps-to-successful-continuous.html' title='6 steps to successful Continuous Integration'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ptUML5AvT6I/R_YOjukDEpI/AAAAAAAAAJQ/xRYGO3oYkyM/s72-c/continous_integration_establishment_vs_maintanance.GIF' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-2294686707226081902</id><published>2008-03-18T15:13:00.012+01:00</published><updated>2008-12-10T22:25:25.653+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Spam'/><category scheme='http://www.blogger.com/atom/ns#' term='aspnet'/><title type='text'>Encoding mailto in hyperlink against spam bots</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Scanning pages for hyper link with mail&lt;/span&gt;&lt;br /&gt;On almost each html tutorial you can find how to create link, which openes user's default mail application.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_YLFjrR-I/AAAAAAAAAII/TGPSGewzBqM/s1600-h/unsafe_mail.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_YLFjrR-I/AAAAAAAAAII/TGPSGewzBqM/s400/unsafe_mail.GIF" alt="" id="BLOGGER_PHOTO_ID_5179095781481334754" border="0" /&gt;&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Don't do that&lt;/span&gt; - after creating such page, your mailbox will contain almost only spam e-mails.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Solution 1 - creating dummy human readable address&lt;/span&gt;&lt;br /&gt;You can paste on your page:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_ZU1jrR_I/AAAAAAAAAIQ/ogtaWWXrzkY/s1600-h/mail_with_removeit.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_ZU1jrR_I/AAAAAAAAAIQ/ogtaWWXrzkY/s400/mail_with_removeit.GIF" alt="" id="BLOGGER_PHOTO_ID_5179097048496687090" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Yes - it's working, but do you really want place it on your company site. Imagine link on home page: "Contact our consultant - send sales_at_professional_REMOVE_IT_company.com" - actually it does not seems to be very professional.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Solution 2 - encode with javascript&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Concept&lt;/span&gt;: Instead of creating&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_YLFjrR-I/AAAAAAAAAII/TGPSGewzBqM/s1600-h/unsafe_mail.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_YLFjrR-I/AAAAAAAAAII/TGPSGewzBqM/s400/unsafe_mail.GIF" alt="" id="BLOGGER_PHOTO_ID_5179095781481334754" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Create on output page with javascript encoded email - spam-bots does not parse javascript, so email will not be properly generated:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ptUML5AvT6I/R9_b9ljrSAI/AAAAAAAAAIY/6GbotkkdVm8/s1600-h/email_javascript.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_ptUML5AvT6I/R9_b9ljrSAI/AAAAAAAAAIY/6GbotkkdVm8/s400/email_javascript.GIF" alt="" id="BLOGGER_PHOTO_ID_5179099947599611906" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In browsers supporting javascirpt it will be displayed as earlier, because &lt;span style="font-style: italic;"&gt;document.write&lt;/span&gt; is processed immediately after loading:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/R9_dLVjrSBI/AAAAAAAAAIg/8DIWO8aIq7k/s1600-h/email_onpage.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/R9_dLVjrSBI/AAAAAAAAAIg/8DIWO8aIq7k/s400/email_onpage.GIF" alt="" id="BLOGGER_PHOTO_ID_5179101283334440978" border="0" /&gt;&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;Steps&lt;/span&gt;:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Import javascript &lt;span style="font-style: italic;"&gt;decode64 &lt;/span&gt;function&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_etFjrSCI/AAAAAAAAAIo/t7juz2zs-XI/s1600-h/decode64.bmp"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R9_etFjrSCI/AAAAAAAAAIo/t7juz2zs-XI/s400/decode64.bmp" alt="" id="BLOGGER_PHOTO_ID_5179102962666653730" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Create ASP.NET filter which finds all occurences of hyperlinks with mailto:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_fN1jrSDI/AAAAAAAAAIw/eJI3YZcQMpI/s1600-h/htmlmailtosfilter.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_fN1jrSDI/AAAAAAAAAIw/eJI3YZcQMpI/s400/htmlmailtosfilter.GIF" alt="" id="BLOGGER_PHOTO_ID_5179103525307369522" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Replace all occurrences with javascript which produces &lt;span style="font-style: italic;"&gt;document.write&lt;/span&gt; in javascript&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_fi1jrSEI/AAAAAAAAAI4/Z2Rjr8n3bp8/s1600-h/encode.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/R9_fi1jrSEI/AAAAAAAAAI4/Z2Rjr8n3bp8/s400/encode.GIF" alt="" id="BLOGGER_PHOTO_ID_5179103886084622402" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Add configuration in web.config&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/R9_f2VjrSFI/AAAAAAAAAJA/lJISOstGOHU/s1600-h/inwebconfig.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/R9_f2VjrSFI/AAAAAAAAAJA/lJISOstGOHU/s400/inwebconfig.GIF" alt="" id="BLOGGER_PHOTO_ID_5179104221092071506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-2294686707226081902?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/2294686707226081902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=2294686707226081902' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/2294686707226081902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/2294686707226081902'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/03/encoding-mailto-in-hyperlink-against.html' title='Encoding mailto in hyperlink against spam bots'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ptUML5AvT6I/R9_YLFjrR-I/AAAAAAAAAII/TGPSGewzBqM/s72-c/unsafe_mail.GIF' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-3717110828119020460</id><published>2008-03-17T12:13:00.033+01:00</published><updated>2009-02-03T16:55:28.793+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='aspnet'/><category scheme='http://www.blogger.com/atom/ns#' term='msbuild'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Publishing Web Application with MsBuild</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/R95aTFjrR9I/AAAAAAAAAIA/FZ7_CJW2pZo/s1600-h/Visual_Studio_Publish.PNG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/R95aTFjrR9I/AAAAAAAAAIA/FZ7_CJW2pZo/s400/Visual_Studio_Publish.PNG" alt="" id="BLOGGER_PHOTO_ID_5178675905478477778" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Visual  Studio 2005 has features that can used to publish a website in a production server  or a staging server.When you compile files using  the Publish Web Site utility the source code is removed. The marked up code in  the .aspx files can also be removed optionally. After compilation the .aspx pages  point to the compiled versions. With this feature the source code of your pages  are safe. Accessing your code by others is difficult. This is one of the features  that are more useful for those who want their code to be safe.&lt;br /&gt;&lt;br /&gt;Question is how to automate it?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;1. Add target to .csproj file&lt;/span&gt;&lt;br /&gt;Ensure your web application project contains :&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R95ZyVjrR8I/AAAAAAAAAH4/a2I4rWrifEc/s1600-h/ImportProjectTarget.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R95ZyVjrR8I/AAAAAAAAAH4/a2I4rWrifEc/s400/ImportProjectTarget.GIF" alt="" id="BLOGGER_PHOTO_ID_5178675342837761986" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;2. Call hidden task _CopyWebApplication&lt;/span&gt;&lt;br /&gt;Call task from msbuild:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ptUML5AvT6I/R95Ya1jrR6I/AAAAAAAAAHo/gl_doPv1Jt0/s1600-h/CopyWebApplicationTask.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_ptUML5AvT6I/R95Ya1jrR6I/AAAAAAAAAHo/gl_doPv1Jt0/s400/CopyWebApplicationTask.GIF" alt="" id="BLOGGER_PHOTO_ID_5178673839599208354" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;3. Resolve references to other projects&lt;/span&gt;&lt;br /&gt;Unfortunatelly task _CopyWebApplicaiton does not copy output from referenced project so you have to do it  manually by calling &lt;span style="font-weight: bold;"&gt;ResolveReferences&lt;/span&gt; task&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R95ZTVjrR7I/AAAAAAAAAHw/nzwnYqmFIEA/s1600-h/ResolveReferencesTask.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R95ZTVjrR7I/AAAAAAAAAHw/nzwnYqmFIEA/s400/ResolveReferencesTask.GIF" alt="" id="BLOGGER_PHOTO_ID_5178674810261817266" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Update: Publishing Websites with cascade dependency (2009.02.03)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's quite common that you have project structure like on image below. Web site on top of Business Logic Layer, which is on top of Data Access Layer.&lt;br /&gt;&lt;br /&gt;Unfortunately, solution posted above does not solve second degree references (references of references) and therefore. When project is built with code above only first degree are propery copied to output directory.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ptUML5AvT6I/SYhopWbrowI/AAAAAAAAAPc/30V-SB-nPLU/s1600-h/project_structure.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 187px; height: 150px;" src="http://3.bp.blogspot.com/_ptUML5AvT6I/SYhopWbrowI/AAAAAAAAAPc/30V-SB-nPLU/s320/project_structure.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5298600021206999810" /&gt;&lt;/a&gt;&lt;br /&gt;In order to configure all degree references to be copied you have to specify both&lt;ul&gt;&lt;li&gt;&lt;b&gt;WebProjectOutputDir&lt;/b&gt; - where website will be published&lt;/li&gt;&lt;li&gt;&lt;b&gt;OutDir&lt;/b&gt; - where all assemblies should be placed (bin folder&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In MsBuild script you can use snippet as below:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Courier New; font-size: 10pt; color: black; background: white;"&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515;"&gt;MSBuild&lt;/span&gt;&lt;span style="color: blue;"&gt; &lt;/span&gt;&lt;span style="color: red;"&gt;Projects&lt;/span&gt;&lt;span style="color: blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue;"&gt;WebApplication\WebApplication.csproj&lt;/span&gt;&amp;quot;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;&lt;/span&gt;&lt;span style="color: red;"&gt;Properties&lt;/span&gt;&lt;span style="color: blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue;"&gt;Configuration=Release;WebProjectOutputDir=..\build\Release\Web\;OutDir= ..\builda\Release\Web\bin\&lt;/span&gt;&amp;quot;&lt;/p&gt;&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;&lt;/span&gt;&lt;span style="color: red;"&gt;Targets&lt;/span&gt;&lt;span style="color: blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue;"&gt;ResolveReferences;_CopyWebApplication&lt;/span&gt;&amp;quot;&lt;span style="color: blue;"&gt; /&amp;gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;Thanks to Maciej Grzyb for solution &lt;a href="http://maciek79.secondbrain.com/"&gt;http://maciek79.secondbrain.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-3717110828119020460?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/3717110828119020460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=3717110828119020460' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/3717110828119020460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/3717110828119020460'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/03/publishing-web-application-with-msbuild.html' title='Publishing Web Application with MsBuild'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ptUML5AvT6I/R95aTFjrR9I/AAAAAAAAAIA/FZ7_CJW2pZo/s72-c/Visual_Studio_Publish.PNG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6817808781210919491.post-2872118089860131458</id><published>2008-02-29T10:58:00.028+01:00</published><updated>2008-12-10T22:25:27.300+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cruise control .net'/><category scheme='http://www.blogger.com/atom/ns#' term='email notification'/><category scheme='http://www.blogger.com/atom/ns#' term='msbuild'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Configuring Cruise Control .Net with MsBuild and email notification</title><content type='html'>&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Best scenario should&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Easy to maintain - no extra developer effort should be required&lt;/li&gt;&lt;li&gt;Easy to extend - time to time it happens that custom action must be performed during build process&lt;/li&gt;&lt;li&gt;Build failure results should be accessible - via mail and on dashbord to decrease time spent looking for error&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Possible build scenarions&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;There are few build scenarios, that could be used with CCNET:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Visual studio batch mode&lt;/li&gt;&lt;li&gt;Nant compilation&lt;/li&gt;&lt;li&gt;MsBuild compilation&lt;/li&gt;&lt;/ul&gt;I'm going to go fast through first two, and later focus on most suitable ble for me sollution - using msbuild.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Using Visual Studio batch mode&lt;/span&gt;&lt;br /&gt;The easiest and fastest to establish way to compile projects is usage of Visual Stuido command line&lt;br /&gt;Adventages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Easy to establish&lt;/li&gt;&lt;li&gt;No developer effort required after enviroment configuration&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Disadventages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Error report is generated only on local disk, so it's hard for developer to check why it does not compile in build enviroment&lt;/li&gt;&lt;li&gt;Standard mail contains only changed files listing&lt;/li&gt;&lt;/ul&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ptUML5AvT6I/R86MOAzFmwI/AAAAAAAAAG4/I9tgPgZ1AHs/s1600-h/5.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_ptUML5AvT6I/R86MOAzFmwI/AAAAAAAAAG4/I9tgPgZ1AHs/s400/5.GIF" alt="" id="BLOGGER_PHOTO_ID_5174227194255612674" border="0" /&gt;&lt;/a&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-style: italic;"&gt;Standard  DevEnv configuration&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R86J6wzFmsI/AAAAAAAAAGY/0b4QwnUoFkc/s1600-h/1.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R86J6wzFmsI/AAAAAAAAAGY/0b4QwnUoFkc/s400/1.GIF" alt="" id="BLOGGER_PHOTO_ID_5174224664519875266" border="0" /&gt;&lt;/a&gt;&lt;span style="font-style: italic;"&gt;Mail for devenv task&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Using Nant&lt;/span&gt;&lt;br /&gt;Nant is another common tool used for building solution, by for me it's biggest disatventage is so uge, that it discards it.&lt;br /&gt;Adventages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Very extensible with plenty of custom task available&lt;/li&gt;&lt;li&gt;Tested in Java enviroment&lt;/li&gt;&lt;li&gt;Possible to include results in mail&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Disadventages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Each developer has to update build script after adding&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;project&lt;/li&gt;&lt;li&gt;file to project (it's possible to include **/*.cs but what about *.xml files or not included files)&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Not so easy to configure first time&lt;/li&gt;&lt;/ul&gt;For me, forcing developer to update build script is both unsecure and errorprone. In big build enviroments only build master should be resposible for build process. Also it's not obligatory for every developer (like CSS front side developer) to have knowledge about Nant syntax. Mistakes made in build script will fail build process what we should avoid like fire.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Using MsBuild&lt;/span&gt;&lt;br /&gt;For me it's most suitable solution and I would strongly recomend it for teams which have quite common build scenarios. In case of some sofisticated build process extension is also possible.&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Adventages:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Possible to include build results in mail&lt;/li&gt;&lt;li&gt;Extensible&lt;/li&gt;&lt;li&gt;Easy to maintain - no extra developer effort is required&lt;/li&gt;&lt;/ul&gt;Disadventages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Takes some time to configure&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;MsBuild configuration&lt;/span&gt;&lt;br /&gt;Follow the instructions from &lt;a href="http://confluence.public.thoughtworks.org/display/CCNETCOMM/Improved+MSBuild+Integration"&gt;ThoughtWorks blog.&lt;/a&gt; They have introduced improved MsBuild logger.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Copy &lt;a href="http://confluence.public.thoughtworks.org/download/attachments/6253/Rodemeyer.MsBuildToCCnet.dll?version=5"&gt;Rodemeyer.MsBuildToCCnet.dll&lt;/a&gt; to your \CruiseControl.NET\server folder&lt;/li&gt;&lt;li&gt;Copy &lt;a href="http://confluence.public.thoughtworks.org/download/attachments/6253/msbuild2ccnet.xsl?version=1"&gt;msbuild2ccnet.xsl&lt;/a&gt; to your \CruiseControl.NET\webdashboard\xsl and \CruiseControl.NET\servrt\xsl folder&lt;/li&gt;&lt;li&gt;Copy &lt;a href="http://confluence.public.thoughtworks.org/download/attachments/6253/cruisecontrol.css?version=1"&gt;cruisecontrol.css&lt;/a&gt; to your \CruiseControl.NET\webdashboard folder  &lt;ul&gt;&lt;li&gt;If you have modified the dashboards stylesheet, just copy the &lt;em&gt;section-project&lt;/em&gt;, &lt;em&gt;section-error&lt;/em&gt;, &lt;em&gt;section-warning&lt;/em&gt;, &lt;em&gt;error&lt;/em&gt; and &lt;em&gt;warning&lt;/em&gt; style.&lt;/li&gt;&lt;/ul&gt;  &lt;/li&gt;&lt;li&gt;In your &lt;a style="font-style: italic;" href="http://confluence.public.thoughtworks.org/display/CCNET/Configuring+the+Server" title="Configuring the Server"&gt;ccnet.config file&lt;/a&gt; locate the &lt;a style="font-style: italic;" href="http://confluence.public.thoughtworks.org/display/CCNET/MsBuild+Task" title="MsBuild Task"&gt;&lt;msbuild&gt; task&lt;/msbuild&gt;&lt;/a&gt;. Change the &lt;logger&gt; node to &lt;/logger&gt;&lt;div class="code panel" style="border-width: 1px;"&gt;&lt;div class="codeContent panelContent"&gt; &lt;span class="code-tag"&gt;&lt;logger&gt;&lt;/logger&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;c:\Program Files\CruiseControl.NET\ server\Rodemeyer.MsBuildToCCNet.dll&lt;/span&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;I highly suggest that you set the verbosity level to quiet or minimal to avoid excessive large log files!&lt;/p&gt; &lt;div class="code panel" style="border-width: 1px;"&gt;&lt;div class="codeContent panelContent"&gt; &lt;pre class="code-xml"&gt;&lt;span class="code-tag"&gt;&lt;buildargs&gt;&lt;/buildargs&gt;&lt;/span&gt;/v:quiet /noconlog /p:Configuration=Debug&lt;span class="code-tag"&gt;&lt;/span&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;In your &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Configuring+the+Web+Dashboard" title="Configuring the Web Dashboard"&gt;dashboard.config&lt;/a&gt; file locate the &lt;buildreportbuildplugin&gt; section. Remove the &lt;em&gt;compile-msbuild.xsl&lt;/em&gt; and &lt;em&gt;msbuild.xsl&lt;/em&gt; if present. Theses transformations are not compatible with the MsBuildToCCNet logger. Add the &lt;em&gt;msbuild2ccnet.xsl&lt;/em&gt; transformation instead.&lt;/buildreportbuildplugin&gt;&lt;span class="code-tag"&gt;&lt;buildreportbuildplugin&gt;&lt;/buildreportbuildplugin&gt;&lt;/span&gt;   &lt;span class="code-tag"&gt;&lt;xslfilenames&gt;&lt;/xslfilenames&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="code panel" style="border-width: 1px;"&gt;&lt;div class="codeContent panelContent"&gt;&lt;pre class="code-xml"&gt;       &lt;span class="code-tag"&gt;&lt;xslfile&gt;&lt;/xslfile&gt;&lt;/span&gt;xsl\header.xsl&lt;br /&gt;&lt;span class="code-tag"&gt;&lt;xslfile&gt;&lt;/xslfile&gt;&lt;/span&gt;xsl\msbuild2ccnet.xsl&lt;br /&gt;&lt;span class="code-tag"&gt;&lt;xslfile&gt;&lt;/xslfile&gt;&lt;/span&gt;xsl\modifications.xsl&lt;br /&gt;[...]&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;Modify &lt;span style="font-style: italic;"&gt;\CruiseControl.NET\server\ccnet.exe.config &lt;/span&gt;and &lt;span style="font-style: italic;"&gt;\CruiseControl.NET\server\ccservice.exe.config  &lt;/span&gt;add following lines   to add mail sending functionality&lt;/li&gt;&lt;li&gt;Configure project&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ptUML5AvT6I/R86KugzFmvI/AAAAAAAAAGw/7THkFNr2HIY/s1600-h/4.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_ptUML5AvT6I/R86KugzFmvI/AAAAAAAAAGw/7THkFNr2HIY/s400/4.GIF" alt="" id="BLOGGER_PHOTO_ID_5174225553578105586" border="0" /&gt;&lt;/a&gt;&lt;span style="font-style: italic;"&gt;Sample project configuration&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ptUML5AvT6I/R86KFQzFmtI/AAAAAAAAAGg/_kCgvdQbHco/s1600-h/2.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_ptUML5AvT6I/R86KFQzFmtI/AAAAAAAAAGg/_kCgvdQbHco/s400/2.GIF" alt="" id="BLOGGER_PHOTO_ID_5174224844908501714" border="0" /&gt;&lt;/a&gt;&lt;span style="font-style: italic;"&gt;Mail for MsBuild task&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ptUML5AvT6I/R86JOwzFmrI/AAAAAAAAAGQ/TT0_4P663_c/s1600-h/3.GIF"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_ptUML5AvT6I/R86JOwzFmrI/AAAAAAAAAGQ/TT0_4P663_c/s400/3.GIF" alt="" id="BLOGGER_PHOTO_ID_5174223908605631154" border="0" /&gt;&lt;/a&gt;&lt;span style="font-style: italic;"&gt;Webdashboard output&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6817808781210919491-2872118089860131458?l=blog.m.jedynak.pl' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.m.jedynak.pl/feeds/2872118089860131458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=6817808781210919491&amp;postID=2872118089860131458' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/2872118089860131458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6817808781210919491/posts/default/2872118089860131458'/><link rel='alternate' type='text/html' href='http://blog.m.jedynak.pl/2008/02/configuring-cruise-control-net-with.html' title='Configuring Cruise Control .Net with MsBuild and email notification'/><author><name>Mirosław Jedynak</name><uri>http://www.blogger.com/profile/03407217246259905745</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14119921345649383559'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ptUML5AvT6I/R86MOAzFmwI/AAAAAAAAAG4/I9tgPgZ1AHs/s72-c/5.GIF' height='72' width='72'/><thr:total>1</thr:total></entry></feed>