<?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-4580967133432571992</id><updated>2012-01-19T09:10:26.814Z</updated><category term='Security Trimming'/><category term='Crystal Reports'/><category term='Continuous Integration'/><category term='ASP.Net Membership'/><category term='TDD'/><category term='Barcamp'/><category term='Agile'/><category term='Powershell'/><category term='asp:Panel'/><category term='TFS'/><category term='NHibernate'/><category term='Hudson'/><category term='Blackpool'/><category term='ASP.Net'/><category term='ASP.Net MVC'/><category term='UrlRewriting.Net'/><category term='Url Rewriting'/><category term='Legacy Code'/><category term='FireFox'/><title type='text'>Alex Lea's Blog</title><subtitle type='html'>.Net Development &amp;amp; other short stories ...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.alexlea.me/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4580967133432571992.post-98005895270117915</id><published>2011-01-05T22:10:00.000Z</published><updated>2011-01-05T22:10:21.166Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Development Ideas For Small Companies &amp; Teams</title><content type='html'>&lt;p&gt;Having worked predominantly in small companies and teams this is a brain dump of issues and considerations:&lt;/p&gt; 
&lt;ol&gt;
&lt;li&gt;Understand your biggest constraint is the only one that matters. Find it, fix it, then find the next one.&lt;/li&gt;
&lt;li&gt;Only solve problems you actually have.&lt;/li&gt;
&lt;li&gt;Understand what constitutes "Done" for a piece of work.&lt;/li&gt;
&lt;li&gt;Reduce process overhead. A board and cards will probably do. How often do you use the audit trail you might think you need? CI server + tests will probably handle enough of this for you.&lt;/li&gt;
&lt;li&gt;Don't be too rigid with your process. If it doesn't suit then adjust it.&lt;/li&gt;
&lt;li&gt;Mandate quality appropriate to the software at hand (controversial I know!)&lt;/li&gt;
&lt;li&gt;Stakeholders may not always be available. Understand their expectations. You may need to play analyst too. Communicate by frequently demonstrating software or screenshots if you have to. Limit the size and scope of misunderstanding.&lt;/li&gt;
&lt;li&gt;If you are a major stakeholder then get on with it but don't ignore others.&lt;/li&gt;
&lt;li&gt;If stakeholders are always available then great!&lt;/li&gt;
&lt;li&gt;Plan work with a simple familiar system such as a queue. Consider limiting this to a small number of next features. Get enough detail to roughly size the features then analyse them in detail as Just-In-Time as is possible/tolerable.&lt;/li&gt;
&lt;li&gt;Avoid multitasking development.&lt;/li&gt;
&lt;li&gt;Talking does not build software. Decide, Do, and Review over a short timeframe.&lt;/li&gt;
&lt;li&gt;Timebox unpredictable tasks if you have an inferior but more deterministic fallback plan.&lt;/li&gt;
&lt;li&gt;Question people if you don't understand.&lt;/li&gt;
&lt;li&gt;Avoid email for in-depth discussions about how something should work. If you have to then try using stories and/or tables to communicate unambiguously then talk it through with a phonecall.&lt;/li&gt;
&lt;li&gt;Adopt industry standard terminology for your domain.&lt;/li&gt;
&lt;li&gt;Understand your development capacity and be honest about what you can achieve.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Okay so that's a long list of things and there's probably more. Feel free to contribute ...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-98005895270117915?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/98005895270117915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=98005895270117915' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/98005895270117915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/98005895270117915'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2011/01/development-ideas-for-small-companies.html' title='Development Ideas For Small Companies &amp; Teams'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-736211682830181119</id><published>2011-01-05T22:07:00.000Z</published><updated>2011-01-05T22:07:10.199Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Agile Aspirations and Misunderstandings</title><content type='html'>&lt;p&gt;It struck me today whilst talking to a colleague how the term "Agile" can easily be misunderstood.

&lt;blockquote style="font-weight:bold"&gt;Colleague: "Agile is SCRUM, a load of stakeholders get together in a meeting and decide by committee what development will happen the next week"&lt;/blockquote&gt;
&lt;/p&gt;

&lt;p&gt;But the point is "Agile" is not SCRUM or really any other "Agile" methodology. Methodologies are just tools in the box after all, and as any DIY enthusiast knows choosing the appropriate tool for the job is paramount in any life or death shelf hanging scenario.&lt;/p&gt;

&lt;p&gt;In fact "Agile", in my opinion, has become over-user and under-understood owing mainly to the aspirational nature of the word. We've seen numerous CVs with a heavy dusting of "Agile" dust only to find the candidate has little or no understanding of "Agile" development practices. (It's also a very easy word for agents to substitute in before others such as project, team, architecture. Agile Architecture?!)&lt;/p&gt;

&lt;h3&gt;So what's your point?&lt;/h3&gt;
&lt;p&gt;The goal is not to be "Agile" but discover a software development method that works for you. If you don't have experience of a similar organisational situation then your foundation must be an understanding of the principles behind Agile methodologies (see &lt;a href="http://agilemanifesto.org/"&gt;The Agile Manifesto&lt;/a&gt; and &lt;a href="http://agilemanifesto.org/principles.html"&gt;The Twelve Principles&lt;/a&gt;) and continuous improvement. &lt;p&gt;
&lt;p&gt;If you can understand (and not just quote) these principles then think about how you might apply them to your organisation. Look at popular methodologies and how others have applied them to their organisations.&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-736211682830181119?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/736211682830181119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=736211682830181119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/736211682830181119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/736211682830181119'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2011/01/agile-aspirations-and-misunderstandings.html' title='Agile Aspirations and Misunderstandings'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-3558796623894884775</id><published>2010-11-12T18:24:00.002Z</published><updated>2010-11-12T18:28:51.818Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='NHibernate'/><title type='text'>Using a Guid as a version column in NHibernate</title><content type='html'>&lt;p&gt;One of the great features of &lt;a href="http://nhibernate.org"&gt;NHibernate&lt;/a&gt; is the almost infinite control it gives you over mapping to existing database structures with its custom type mapping. Turns out custom types can also be applied to version columns for managing concurrency. Thanks to Justice for the pointer on &lt;a href="http://stackoverflow.com/questions/4134751/using-a-guid-version-column-in-nhibernate"&gt;my stackoverflow post&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;The basic method is to simply implement a &lt;em&gt;NHibernate.UserTypes.IUserVersionType&lt;/em&gt; and implement the &lt;em&gt;Seed&lt;/em&gt; and &lt;em&gt;Next&lt;/em&gt; methods to return Guids. The rest is just Guid type mapping.&lt;/p&gt;

&lt;p&gt;The custom type can then be registered in &lt;a href="http://fluentnhibernate.org"&gt;FluentNHibernate&lt;/a&gt; using this code:&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; AnimalMap()&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{                       &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    Id(x =&amp;gt; x.Id);    &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    Map(x =&amp;gt; x.SpeciesId);&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    Version(x =&amp;gt; x.ConcurrencyId).CustomType(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(GuidVersionType));&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    Map(x =&amp;gt; x.Name);    &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    Map(x =&amp;gt; x.NumberOfLegs); &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The full code is below:&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; GuidVersionType : NHibernate.UserTypes.IUserVersionType&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; SqlType[] SqlTypes&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;        get&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        {                &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;            var types = &lt;span class="kwrd"&gt;new&lt;/span&gt; SqlType[1];&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;            types[0] = &lt;span class="kwrd"&gt;new&lt;/span&gt; SqlType(DbType.Guid);&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; types;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; System.Type ReturnedType&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        get { &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Guid); }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; Equals(&lt;span class="kwrd"&gt;object&lt;/span&gt; x, &lt;span class="kwrd"&gt;object&lt;/span&gt; y)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (x == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; x.Equals(y);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; GetHashCode(&lt;span class="kwrd"&gt;object&lt;/span&gt; x)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; x.GetHashCode();&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; NullSafeGet(IDataReader rs, &lt;span class="kwrd"&gt;string&lt;/span&gt;[] names, &lt;span class="kwrd"&gt;object&lt;/span&gt; owner)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;        var &lt;span class="kwrd"&gt;value&lt;/span&gt; = NHibernateUtil.Guid.NullSafeGet(rs, names[0]); &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt; == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; (Guid) &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; NullSafeSet(IDbCommand cmd, &lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;, &lt;span class="kwrd"&gt;int&lt;/span&gt; index)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt; == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;            NHibernateUtil.Guid.NullSafeSet(cmd, &lt;span class="kwrd"&gt;null&lt;/span&gt;, index);&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;        }           &lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;        NHibernateUtil.Guid.NullSafeSet(cmd, &lt;span class="kwrd"&gt;value&lt;/span&gt;, index);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; DeepCopy(&lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt; == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;        var copy = ((Guid) &lt;span class="kwrd"&gt;value&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; copy;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsMutable&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;        get { &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;; }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Replace(&lt;span class="kwrd"&gt;object&lt;/span&gt; original, &lt;span class="kwrd"&gt;object&lt;/span&gt; target, &lt;span class="kwrd"&gt;object&lt;/span&gt; owner)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; original;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Assemble(&lt;span class="kwrd"&gt;object&lt;/span&gt; cached, &lt;span class="kwrd"&gt;object&lt;/span&gt; owner)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; cached;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Disassemble(&lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Compare(&lt;span class="kwrd"&gt;object&lt;/span&gt; x, &lt;span class="kwrd"&gt;object&lt;/span&gt; y)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; ((Guid) x).CompareTo((Guid) y);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Seed(ISessionImplementor session)&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; Guid.NewGuid();&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Next(&lt;span class="kwrd"&gt;object&lt;/span&gt; current, ISessionImplementor session)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; Guid.NewGuid();&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This works in FluentNHibernate 1.1.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-3558796623894884775?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/3558796623894884775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=3558796623894884775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/3558796623894884775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/3558796623894884775'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/11/using-guid-as-version-column-in.html' title='Using a Guid as a version column in NHibernate'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-961049636095694202</id><published>2010-10-20T21:10:00.000+01:00</published><updated>2010-10-20T21:10:53.305+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>HOWTO: Set the Hudson next build number programatically</title><content type='html'>Programatically settings the next build number in Hudson is a useful thing to do if you have a build pipeline. After a bit of research it seems there are 3 main approaches that could be useful in different situations:
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Set the nextBuildNumber.txt in your Hudson job's directory&lt;/li&gt;
&lt;li&gt;Use environment variables for build number passthrough (see &lt;a href="http://twitter.com/ruby_gem"&gt;@ruby_gem&lt;/a&gt;'s post &lt;a href="http://lancsrubygem.wordpress.com/2010/09/22/hudson-pass-the-build_number/"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Next+Build+Number+Plugin"&gt;nextBuildNumber Hudson plugin&lt;/a&gt; and HTTP POST the build number to it &lt;/li&gt;
&lt;/ol&gt;
In this post I'll be looking at option 3 - using the &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Next+Build+Number+Plugin"&gt;nextBuildNumber Hudson plugin&lt;/a&gt;.&lt;br /&gt;
&lt;h3&gt;
Using the nextBuildNumber Hudson plugin&lt;/h3&gt;
&lt;p&gt;
Firstly install the &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Next+Build+Number+Plugin"&gt;nextBuildNumber plugin&lt;/a&gt;. You can now set the next build number for a job manually from the plugin link that appear on the left.&lt;br /&gt;
To use this programatically you simulate the manual for submission with a web client. I tested it with FireFox ReST client.
&lt;br /&gt;
To set the next build number to 999 simply formulate an HTTP POST to your Hudson URL:
&lt;br /&gt;
&lt;pre&gt;e.g. http://myhudsonserver:8080/job/myhudsonjobname/nextbuildnumber/submit&lt;/pre&gt;
With the form data
&lt;br /&gt;
&lt;pre&gt;nextBuildNumber=999&lt;/pre&gt;
And the HTTP Content-Type request header
&lt;br /&gt;
&lt;pre&gt;Content-Type: application/x-www-form-urlencoded&lt;/pre&gt;
&lt;br/&gt;
If your Hudson is secured you'll need to authenticate your web client too.
&lt;/p&gt;
&lt;p&gt;
That's it! Finally it's worth noting that build numbers in Hudson can only go up!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-961049636095694202?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/961049636095694202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=961049636095694202' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/961049636095694202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/961049636095694202'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/10/howto-set-hudson-next-build-number.html' title='HOWTO: Set the Hudson next build number programatically'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-7958877323474477959</id><published>2010-09-16T08:42:00.000+01:00</published><updated>2010-09-16T08:42:36.191+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Powershell'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Error Handling CI Deployments Using Powershell</title><content type='html'>&lt;p&gt;PowerShell's a great tool for use in remote deployments from a CI server. 
However, it's default error handling does not cause a failure on error which can leave failed deployments reporting success!
&lt;/p&gt;
&lt;p&gt;To make PowerShell scripts fail on non-terminating errors add the following line to the top of the script after any &lt;em&gt;params&lt;/em&gt;:
&lt;pre&gt;
$ErrorActionPreference = "Stop"
&lt;/pre&gt;
This will cause the script to fail on terminating &lt;strong&gt;and&lt;/strong&gt; non-terminating errors hence preventing the Powershell script succeeding when elements of it have failed.
&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-7958877323474477959?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/7958877323474477959/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=7958877323474477959' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/7958877323474477959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/7958877323474477959'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/09/error-handling-ci-deployments-using.html' title='Error Handling CI Deployments Using Powershell'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-4669429439359919965</id><published>2010-08-26T21:40:00.008+01:00</published><updated>2010-09-01T21:42:39.871+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Better TDD By Writing Your Asserts First</title><content type='html'>&lt;p&gt;Writing unit tests with an Arrange, Act, Assert structure definitely helps but the order often seems at odds with the mental process of TDD. Best practice is to write your asserts first. Here's why:&lt;/p&gt;
&lt;h4&gt;Arrange First?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Arrange&lt;/em&gt;-ing first is virtually impossible. You have no idea what to &lt;em&gt;Arrange&lt;/em&gt;  until you've written the &lt;em&gt;Act&lt;/em&gt; or &lt;em&gt;Assert&lt;/em&gt;. This is particularly important when Mocking/Stubbing as the &lt;em&gt;Arrange&lt;/em&gt; requires knowledge of the implementation which is exactly what you don't have at this point! &lt;strong&gt;* see below&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;Act First?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Act&lt;/em&gt;-ing first seems more reasonable but requires something to first be arranged which can lead you down the dark path of arranging first. If there's any uncertainty about the purpose of the test, which there often is, then this can result in wasted effort.&lt;/p&gt;
&lt;h4&gt;Assert First&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Assert&lt;/em&gt;-ing first forces you to clarify the purpose of your test early. Have you ever had that paired conversation of "What are we really trying to do here?", well asserting first is having that conversation. Sure you may not be able to write the entire assert standalone but it will help you to drive the rest of the test and implementation with a clearer mind.&lt;/p&gt;

&lt;h4&gt;* but I &lt;em&gt;do&lt;/em&gt; have knowledge of the implementation&lt;/h4&gt;
&lt;p&gt;Really? For a trivial implementation great! But if you have an existing external dependency then how do you know that this implementation should call it directly? You don't! Sure it will eventually get called but that may not be the concern of the class under test. Switch back to top down design and let the &lt;em&gt;Assert&lt;/em&gt; drive out the interface you require the external dependency to have. Then, and only then, either wrap the dependency to make it expose the interface you require or take the well considered decision to use it directly.&lt;/p&gt;

&lt;p&gt;Assert first is really helpful for all but the most trivial tests and can help you achieve that holy grail of loosely coupled, highly cohesive code. Try and see if it works for you!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-4669429439359919965?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/4669429439359919965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=4669429439359919965' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/4669429439359919965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/4669429439359919965'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/08/better-tdd-with-assertfirst.html' title='Better TDD By Writing Your Asserts First'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-3446792573649956817</id><published>2010-08-26T00:33:00.004+01:00</published><updated>2010-08-26T00:46:46.109+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='Legacy Code'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Breaking The Legacy Bind : A Tale Of Build Automation</title><content type='html'>&lt;p&gt;I've recently moved teams and following a brief honeymoon on a greenfield web app we're now swimming in the murky waters of the "Legacy Codebase". Imagine a world with &lt;em&gt;no continuous integration, patchy test coverage, untamed dependencies, COM+, a long manual build and deployment process and to top it all off, Visual Source Safe&lt;/em&gt;. &lt;br/&gt;
If this doesn't make you shudder then best stop reading now :)&lt;/p&gt;

&lt;p&gt;Granted, there are probably many worse legacies out there and much of our underlying code is good but it's still a significant departure from the instant builds and frequent releases we had been making.&lt;/p&gt;

&lt;h3&gt;Science Friction&lt;/h3&gt;
&lt;p&gt;The initial obstacle faced here is friction in the development process. Friction resulting mainly from the repetitive manual build process. The manual build takes time; lengthening the feedback cycle. It's also tedious and error prone. These two facts alone can prevent even the most diligent of developers from making the refactorings necessary to keep the codebase in good health. Our first job - automate.&lt;/p&gt; 

&lt;h3&gt;Automation, Automation, Automation&lt;/h3&gt;
&lt;p&gt;Automating the build process to run on check-in removes the tedium of doing it manually and is relatively easy to do with any CI server. Fortunately our code already has a well organised suite of NAnt scripts so we simply configured Hudson, our CI server, to run them and display the test output. A relatively quick win.&lt;/p&gt;
&lt;p&gt;If you're not so lucky you could start with automating one part at a time. Just note down your manual process and automate each step in turn.&lt;/p&gt;

&lt;h3&gt;Migration From VSS To TFS&lt;/h3&gt;
&lt;p&gt;Using Hudson mandated the source code be migrated from Visual Source Safe to a system more suitable for use with a CI server - in our case Team Foundation Server. The migration itself is fairly straightforward with the Microsoft migration tool and instructions but can be time consuming so plan carefully, especially if your VSS repository is in constant use.&lt;/p&gt;

&lt;p&gt;I've previously done this from VSS to SVN and there are probably tools out there to move between most source control systems, but beware, not all features are supported by all systems e.g. VSS pinning, SVN externals. Of course, if you're not concerned about the history, tags etc. you can always just check-in a fresh copy from your old source control to the new one and get going straight away.&lt;/p&gt;

&lt;h3&gt;Verify The Build&lt;/h3&gt;
&lt;p&gt;The first build is by far the biggest risk and taking the time to verify it is very worthwhile if you care about the quality of your software. This cost us a couple of days due to build/env issues but it's either that or the entire thing is a no-go so don't be tempted to scrimp here!&lt;/p&gt;

&lt;p&gt;Comparison tools such as WinMerge and BeyondCompare can help here to compare the checkouts from each source tree or even build artefacts.&lt;/p&gt;

&lt;h3&gt;Building For The Future&lt;/h3&gt;
&lt;p&gt;We're not done here! An automated build is a huge leap forward and allows you to concentrate on skilled activities that deliver more value but it's by no means the end.&lt;/p&gt;
&lt;p&gt; Build time &lt;em&gt;is&lt;/em&gt; important and reducing it is next on our hitlist. Time spent waiting for a build is often time wasted or forces you into the undesirable world of multitasking. If your build takes longer than it does for you to make a cup of tea then there may well be room for improvement.&lt;/p&gt;

&lt;h3&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Dealing with unfamiliar or hard to work with code can be daunting. In my (limited) experience it's all about confidence - get stuck in, and little by little you'll find a path forwards. If you're struggling then I'd highly recommend Michael Feathers' &lt;em&gt;Working Effectively With Legacy Code (ISBN 0131177052)&lt;/em&gt; for some inspiration.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-3446792573649956817?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/3446792573649956817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=3446792573649956817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/3446792573649956817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/3446792573649956817'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/08/breaking-legacy-bind-tale-of-build.html' title='Breaking The Legacy Bind : A Tale Of Build Automation'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-8754358335683761207</id><published>2010-07-09T00:00:00.009+01:00</published><updated>2010-07-11T16:26:05.583+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Barcamp'/><category scheme='http://www.blogger.com/atom/ns#' term='Blackpool'/><title type='text'>Barcamp Blackpool 2010</title><content type='html'>&lt;p&gt;There's been a US and UK election since my last post but it's time to saddle up and ride this ol' blog horse once more. Besides, this event &lt;em&gt;really does&lt;/em&gt; deserve a mention.&lt;/p&gt;

&lt;p style="font-size:120%; color:#EC00AC"&gt;&lt;strong&gt;&lt;span style="font-size: 2.0em;vertical-align: top"&gt;Q: &lt;/span&gt;What do you get when you take several hundred sticks of rock, a wizard, 120 technology people, a pieman, a sprinkling of speakers, a free bar and leave them all in a casino ballroom for a day?&lt;/strong&gt;&lt;/p&gt;
&lt;p style="font-size:120%"&gt;&lt;strong&gt;&lt;span style="font-size: 2.0em;vertical-align:top"&gt;A: &lt;/span&gt;&lt;a href="http://barcampblackpool.com"&gt;Barcamp Blackpool&lt;/a&gt; (&lt;a href="http://twitter.com/bcblackpool"&gt;@bcblackpool&lt;/a&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For anyone who's not attended such an event before it's a free developer and technology unconference.&lt;/p&gt;

&lt;h3&gt;Unconference!? What's that?&lt;/h3&gt;
&lt;p&gt;Talks are arranged on the day, simply turn up with one and stick a post-it with your talk name in one of the available slots. Delightfully low tech and the ensuing speaker scrumdown is a marvellous spectacle. Only the strongest survive. If Attenborough made people documentaries ...&lt;/p&gt;

&lt;p&gt;Once talks are posted you're then free, after a quick ice-breaker, to browse the line-up and drop in on anything that takes your fancy.&lt;/p&gt;

&lt;h3&gt;Talk, Talk, Talk&lt;/h3&gt;
&lt;p&gt;Talks covered a variety of subjects both technical and non-technical subjects from British Sign Language to &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; to Ferret Keeping although it's fair to say the majority of talks have an IT bias.&lt;/p&gt;

&lt;p&gt;Excepting the free pie for lunch notable highlights included sessions on &lt;strong&gt;45 Second Elevator Pitches&lt;/strong&gt;, &lt;strong&gt;IT Recruitment&lt;/strong&gt; (by a recruiter and nice chap I should say), &lt;strong&gt;Sociograms&lt;/strong&gt; (that's connections between groups to you and me), &lt;strong&gt;IT Education&lt;/strong&gt; and more.&lt;/p&gt;

&lt;p&gt;Educational part over it's on to evening entertainment including free food, free bar and magic show before a careless plunge into the murky depths of Blackpool proper. Did I mention there's some free stuff?&lt;p&gt;

&lt;p&gt;A special mention is also deserving of the organiser, and my pink haired ferret-loving &lt;a href="http://twitter.com/esendex"&gt;@esendex&lt;/a&gt; colleague Gemma Cameron (&lt;a href="http://twitter.com/ruby_gem"&gt;@ruby_gem&lt;/a&gt;) whose tireless enthusiasm, merry band of helpers and fondness of wine made the event hugely entertaining.!&lt;p/&gt; 

&lt;h3&gt;The Rollercoaster Of Barcamp Love&lt;/h3&gt;
&lt;p&gt;
&lt;ul&gt;
&lt;li&gt;The Big One @ Blackpool Pleasure Beach&lt;/li&gt;
&lt;li&gt;Inspirational Speakers&lt;/li&gt;
&lt;li&gt;My first, and I suspect last ever, game of Werewolf*&lt;/li&gt;
&lt;li&gt;Karaoke Fun Bar - Blackpool's 8-Mile styled rap dungeon complete with MCs&lt;/li&gt;
&lt;li&gt;Pies&lt;/li&gt;
&lt;li&gt;Squirty Gertie&lt;/li&gt;
&lt;li&gt;Exchanging Ideas&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;* When Barcamp became Band-Camp. Ask &lt;a href="http://twitter.com/neilkilbride"&gt;@neilkilbride&lt;/a&gt; for his cup truly overfloweth.&lt;/p&gt;

&lt;h3&gt;Barcamp comes to Nottingham&lt;/h3&gt;
&lt;p&gt;Celebrate! Barcamp is coming to Nottingham. Join the discussion at the &lt;a href="http://groups.google.com/group/bcnott/"&gt;Google Group 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/4580967133432571992-8754358335683761207?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/8754358335683761207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=8754358335683761207' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8754358335683761207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8754358335683761207'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2010/07/barcamp-blackpool-2010.html' title='Barcamp Blackpool 2010'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-8323150557749350208</id><published>2009-07-08T14:35:00.045+01:00</published><updated>2010-07-08T22:27:45.179+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Security Trimming'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.Net'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.Net MVC'/><title type='text'>Security Trimming in ASP.Net MVC</title><content type='html'>&lt;p&gt;
We've been searching for a good way to security trim our links since the MVC Beta. After raising an issue on CodePlex MS assured us this would be made easier in the RTM. Turns out they were right!&lt;p&gt;
&lt;p&gt;Our security trimming requirements were quite simple, to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the existing ASP.Net MVC role based authorization offered by the Authorize attribute.&lt;/li&gt;
&lt;li&gt;Support the trimming of Action Links, Action Urls and custom scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's a couple of examples:&lt;/p&gt;
&lt;span class="left-indent-caption"&gt;
&lt;p&gt;Untrimmed Page - user has full access&lt;/p&gt;
&lt;a href="http://1.bp.blogspot.com/_Hu1Kgh-edq8/So6-s5AViLI/AAAAAAAAAFQ/pWWnxiCOqEI/s1600-h/Untrimmed.jpg" imageanchor="1"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_Hu1Kgh-edq8/So6-s5AViLI/AAAAAAAAAFQ/pWWnxiCOqEI/s400/Untrimmed.jpg" /&gt;&lt;/a&gt;&lt;/span&gt;
&lt;div style="clear:both"&gt;&lt;/div&gt;
&lt;p&gt;Notice the number of links in the dropdown above and the create link.&lt;/p&gt;
&lt;span class="left-indent-caption"&gt;
&lt;p&gt;Trimmed Page - actions are restricted&lt;/p&gt;
&lt;a href="http://2.bp.blogspot.com/_Hu1Kgh-edq8/So6_D0taREI/AAAAAAAAAFY/WP0oDEL3SYU/s1600-h/Trimmed.jpg" imageanchor="1"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_Hu1Kgh-edq8/So6_D0taREI/AAAAAAAAAFY/WP0oDEL3SYU/s400/Trimmed.jpg" /&gt;&lt;/a&gt;
&lt;/span&gt;
&lt;div style="clear:both"&gt;&lt;/div&gt;
&lt;p&gt;Now most of those links have been trimmed away.&lt;/p&gt;
&lt;h3&gt;How Does It Work?&lt;/h3&gt;
&lt;p&gt;
ASP.Net MVC 1.0 introduced the &lt;i&gt;ReflectedControllerDescriptor&lt;/i&gt; and &lt;i&gt;ReflectedActionDescriptor&lt;/i&gt; classes which can be used to identify the target controller and action method for a given action.&lt;/p&gt;
&lt;p&gt;
The code below shows how a &lt;b&gt;HasActionPermission&lt;/b&gt; method can be implemented to decide whether a user is authorized to perform a particular action according to the use of the &lt;i&gt;Authorize&lt;/i&gt; attribute at the controller or action level. From this method it's fairly trivial to implement a raft of security trimmed extension methods such as SecureActionLink to complement their non-trimmed counterparts. We use this for Sharepoint style pop-up command lists and it works very nicely.&lt;/p&gt;
&lt;p&gt;
&lt;b&gt;Note:&lt;/b&gt; This is example code, not production code :)
&lt;/p&gt;
&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasActionPermission (&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper helper, &lt;span class="kwrd"&gt;string&lt;/span&gt; actionName, &lt;span class="kwrd"&gt;string&lt;/span&gt; controllerName)&lt;/pre&gt;
&lt;pre&gt;{            &lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// Get the controller&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;    IController controller = GetController(helper, controllerName);&lt;/pre&gt;
&lt;pre class="alt"&gt;    ControllerContext controllerContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; ControllerContext (helper.RequestContext, (ControllerBase)controller);        &lt;/pre&gt;
&lt;pre&gt;    &lt;/pre&gt;
&lt;pre class="alt"&gt;    ReflectedControllerDescriptor controllerDescriptor = &lt;span class="kwrd"&gt;new&lt;/span&gt; ReflectedControllerDescriptor(controller.GetType());            &lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;    ActionDescriptor actionDescriptor = controllerDescriptor.FindAction (controllerContext, actionName);&lt;/pre&gt;
&lt;pre&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (actionDescriptor == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;; &lt;span class="rem"&gt;// action does not exist so say yes - should we authorise this?!&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;    &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;pre&gt;        AuthorizationContext authContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; AuthorizationContext(controllerContext);&lt;/pre&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        &lt;span class="rem"&gt;// run each auth filter until on fails&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;// performance could be improved by some caching&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (IAuthorizationFilter authFilter &lt;span class="kwrd"&gt;in&lt;/span&gt; actionDescriptor.GetFilters().AuthorizationFilters)&lt;/pre&gt;
&lt;pre class="alt"&gt;        {&lt;/pre&gt;
&lt;pre&gt;            authFilter.OnAuthorization(authContext);&lt;/pre&gt;
&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (authContext.Result != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;
&lt;pre class="alt"&gt;        }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;    }                        &lt;/pre&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; IController GetController(UrlHelper helper, &lt;span class="kwrd"&gt;string&lt;/span&gt; controllerName)&lt;/pre&gt;
&lt;pre&gt;{&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// Get the controller type&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;    controllerName = controllerName ?? helper.RequestContext.RouteData.GetRequiredString(&lt;span class="str"&gt;"controller"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span class="rem"&gt;// Instantiate the controller and call Execute&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;    IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();&lt;/pre&gt;
&lt;pre&gt;    IController controller = factory.CreateController(helper.RequestContext, controllerName);&lt;/pre&gt;
&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (controller == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; InvalidOperationException(&lt;/pre&gt;
&lt;pre&gt;            String.Format(&lt;/pre&gt;
&lt;pre class="alt"&gt;                CultureInfo.CurrentUICulture,&lt;/pre&gt;
&lt;pre&gt;                &lt;span class="str"&gt;"Controller factory {0} controller {1} returned null"&lt;/span&gt;,&lt;/pre&gt;
&lt;pre class="alt"&gt;                factory.GetType(),&lt;/pre&gt;
&lt;pre&gt;                controllerName));&lt;/pre&gt;
&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;pre&gt;    &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; controller;&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-8323150557749350208?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/8323150557749350208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=8323150557749350208' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8323150557749350208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8323150557749350208'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2009/07/aspnet-mvc-security-trimming-using.html' title='Security Trimming in ASP.Net MVC'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Hu1Kgh-edq8/So6-s5AViLI/AAAAAAAAAFQ/pWWnxiCOqEI/s72-c/Untrimmed.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4580967133432571992.post-8030819101225136909</id><published>2008-07-01T14:14:00.014+01:00</published><updated>2009-08-21T13:50:48.298+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Url Rewriting'/><category scheme='http://www.blogger.com/atom/ns#' term='UrlRewriting.Net'/><title type='text'>Practical ASP.Net Url Rewriting with UrlRewriting.Net</title><content type='html'>&lt;p&gt;
       This post describes briefly how I implemented url rewriting with the &lt;b&gt;UrlRewriting.Net&lt;/b&gt;
       package.
   &lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;My objectives were simple, to:&lt;/strong&gt;
   &lt;/p&gt;
   &lt;ol&gt;
       &lt;li&gt;Use simple readable urls wherever possible e.g. &lt;em&gt;http://domain.com/app&lt;/em&gt; &lt;/li&gt;
       &lt;li&gt;Honour querystring parameters passed to pages e.g. &lt;em&gt;http://domain.com/app?param=value&lt;/em&gt;
       &lt;/li&gt;
       &lt;li&gt;Map several Urls to one implementation page e.g. &lt;em&gt;http://domain.com/red&lt;/em&gt;
           =&gt; &lt;em&gt;http://domain.com/colours.aspx?name=red&lt;/em&gt; &lt;/li&gt;
       &lt;li&gt;Not have to create physical folders for all virtual folders e.g. &lt;em&gt;/red&lt;/em&gt; does not actually
           exist on disk! &lt;/li&gt;
       &lt;li&gt;Ensure all &lt;strong&gt;ASP.Net&lt;/strong&gt; functionality remains intact e.g. postbacks,
           themes, authentication, AJAX &lt;/li&gt;
       &lt;li&gt;Must work for apps rooted at the domain e.g. &lt;em&gt;http://domain.com/&lt;/em&gt; and in
           subdirectories &lt;em&gt;e.g. http://localhost/devapp/&lt;/em&gt;.&lt;/li&gt;
   &lt;/ol&gt;
   &lt;p&gt;
       &lt;strong&gt;This was based on the following setup&lt;/strong&gt;:
   &lt;/p&gt;
   &lt;ul&gt;
       &lt;li&gt;An &lt;strong&gt;IIS 5.5/6&lt;/strong&gt; web server&lt;/li&gt;
       &lt;li&gt;&lt;strong&gt;IIS&lt;/strong&gt; wilcard (.*) request mapping&lt;/li&gt;
       &lt;li&gt;&lt;strong&gt;UrlRewriting.Net&lt;/strong&gt; package&lt;/li&gt;
       &lt;li&gt;&lt;strong&gt;FormRewriter control adapter&lt;/strong&gt; &lt;span style="font-size:85%;"&gt;(see
           ScottGu's article &lt;a href="http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx"&gt;
               here&lt;/a&gt;)&lt;/span&gt; &lt;/li&gt;
       &lt;li&gt;An existing large site with default pages named default.aspx&lt;/li&gt;
   &lt;/ul&gt;
   &lt;p&gt;
       I chose &lt;b&gt;UrlRewriting.Net&lt;/b&gt; as it seemed fullest featured package out there,
       and the only one I tested that would work correctly with &lt;b&gt;FormsAuthentication&lt;/b&gt;
       and &lt;b&gt;ASP.Net AJAX&lt;/b&gt;.
   &lt;/p&gt;
   &lt;p&gt;
       With this running 'out of the box' I found a couple of problems.
   &lt;/p&gt;
   &lt;h2&gt;
       Issue 1: Trailing slashes&lt;/h2&gt;
   &lt;p&gt;
       This can be an issue with themes and is also required to get default pages working
       sensibly. The idea is to remove the trailing slash on urls except those on the authority.&lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;For example, we want:&lt;/strong&gt;&lt;/p&gt;
   &lt;ul&gt;
       &lt;li&gt;&lt;em&gt;http://domain.com/&lt;/em&gt; to map to &lt;em&gt;http://domain.com/&lt;/em&gt; i.e. no change&lt;/li&gt;
       &lt;li&gt;but &lt;em&gt;http://domain.com/dir/&lt;/em&gt; to map to &lt;em&gt;http://domain.com/dir&lt;/em&gt; i.e.
           slash removed&lt;/li&gt;
   &lt;/ul&gt;
   &lt;p&gt;
       My solution is based on the one by Fabrice &lt;a href="http://weblogs.asp.net/fmarguerie/archive/2006/10/20/Solving-URL-rewriting-problems-with-themes-and-trailing-slashes.aspx"&gt;
           here&lt;/a&gt; but is modified to exclude the authority part of the Url (e.g. &lt;em&gt;http://domain.com/&lt;/em&gt;)
       so we don't end up in an endless loop of redirects. This rule has to be a redirect since &lt;strong&gt;UrlRewriting.Net&lt;/strong&gt; will not process
       multiple rewrites. In any case you don't want users or search engines seeing &lt;em&gt;http://domain.com/dir/&lt;/em&gt;
       as different from &lt;em&gt;http://domain.com/dir&lt;/em&gt; so a permanent redirect to the
       slashless url makes good sense here, giving a single identity to multiply addressable
       resources.&lt;/p&gt;
       &lt;p&gt;The rule looks like this:&lt;/p&gt;
&lt;pre&gt;&amp;lt;add name="RemoveTrailingSlash"
virtualUrl="^~/(.*)/(\?.*)?$"
destinationUrl="~/$1$2"
rewriteUrlParameter="ExcludeFromClientQueryString"
redirectMode="Permanent"
redirect="Application"
ignoreCase="true" /&amp;gt;&lt;/pre&gt;
   &lt;h2&gt;
       Issue 2: The Default Page&lt;/h2&gt;
   &lt;p&gt;
       The default page is set as an attribute in the &lt;b&gt;UrlRewriting&lt;/b&gt; configuration
       and is a workaround for the default page being lost when the &lt;strong&gt;IIS wildcard mapping&lt;/strong&gt;
       is added. However, &lt;strong&gt;UrlRewriting&lt;/strong&gt; default pages do not work as in
       &lt;strong&gt;IIS&lt;/strong&gt; - the default page is actually appended to all directory requests.&lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;For example:

       &lt;/strong&gt;&lt;em&gt;http://mydomain.com&lt;/em&gt; becomes &lt;em&gt;http://mydomain.com/default.aspx&lt;/em&gt;
   &lt;/p&gt;
   &lt;p&gt;
       In projects where branding and SEO are primary considerations this is unlikely to
       be acceptable.
   &lt;/p&gt;
   &lt;p&gt;
       Switching off the default page functionality (by removing the &lt;em&gt;defaultPage&lt;/em&gt;
       configuration attribute) prevents this happening but then we have to add rules for
       every single instance in which we would like &lt;em&gt;http://mydomain.com&lt;/em&gt; to map to the file
       &lt;em&gt;http://mydomain.com/default.aspx&lt;/em&gt; .&lt;/p&gt;
   &lt;p&gt;
       The workaround here is to add the following rule last in the rewrite configuration:&lt;/p&gt;
   &lt;pre&gt;&amp;lt;add name="Default"
virtualUrl="^([a-zA-Z0-9_\-/]*)(\?.*)?$"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="$1/Default.aspx$2"
ignoreCase="true"/&amp;gt;&lt;/pre&gt;
   &lt;p&gt;
       This has the effect of re-instating default page functionality. Note the expression &lt;em&gt;[a-zA-Z0-9_\-/]&lt;/em&gt; does not include the period
       (.) character (i.e. to skip mapping requests for actual pages, style sheets or other
       resources which usually contain a period) but should include any other characters
       that may appear in your url. This rule is designed to only map requests such as
       &lt;em&gt;http://domain.com/test&lt;/em&gt; to &lt;em&gt;http://domain.com/test/default.aspx&lt;/em&gt;
       and relies on the prior redirect to remove the trailing slash (except at the authority
       level). &lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;NB 1.&lt;/strong&gt; This rule is by no means exhaustive. Modify it to suit the
       naming structure of your site.&lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;NB 2.&lt;/strong&gt; This rule relies on the trailing slash rule being present
       first, otherwise it would map requests such as &lt;em&gt;/test/&lt;/em&gt; to &lt;em&gt;/test//default.aspx&lt;/em&gt;
       . &lt;/p&gt;
   &lt;h2&gt;
       Summary&lt;/h2&gt;
   &lt;p&gt;
       In summary I have two core rules, the trailing slash rule and the default rule.
       I also have a number of site specific rules in the middle to map top level directories
       to a single template implementation page. My config rules looks something like this:&lt;/p&gt;
   &lt;pre&gt;&amp;lt;add name="RemoveTrailingSlash"
virtualUrl="^~/(.*)/(\?.*)?$"
destinationUrl="~/$1$2"
rewriteUrlParameter="ExcludeFromClientQueryString"
redirectMode="Permanent"
redirect="Application"
ignoreCase="true" /&amp;gt;

&amp;lt;!-- Begin app specific rules --&amp;gt;

&amp;lt;!-- Category mapping rule example - does not map querystring --&amp;gt;

&amp;lt;add destinationurl="~/Categories/CategoryHome.aspx?cn=$1"
ignorecase="true" name="CategoryHome" rewriteurlparameter="ExcludeFromClientQueryString"
virtualurl="^~/(Travel|Entertainment|Family|Health|Lifestyle|Care|Food|Fashion|Home)(\?.*)?$"&amp;gt;
&amp;lt;/add&amp;gt;

&amp;lt;!-- End app specific rules --&amp;gt;

&amp;lt;add name="Default"
virtualUrl="^([a-zA-Z0-9_\-/]*)(\?.*)?$"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="$1/Default.aspx$2"
ignoreCase="true"/&amp;gt;&lt;/pre&gt;
   &lt;h2&gt;
       Conclusion&lt;/h2&gt;
   &lt;p&gt;
       Whilst &lt;strong&gt;UrlRewriting.Net&lt;/strong&gt; has it's limitations it can be used to
       create a maintainable url rewritten site relatively
       quickly and cleanly .&lt;/p&gt;
   &lt;p&gt;
       &lt;strong&gt;Disclaimer:&lt;/strong&gt; Incorrect use of Url rewriting can have catastrophic
       consequences for your site. One miswritten rule can bring your whole site down.
       Use with caution! :)
   &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-8030819101225136909?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/8030819101225136909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=8030819101225136909' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8030819101225136909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/8030819101225136909'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2008/07/practical-aspnet-url-rewriting-with.html' title='Practical ASP.Net Url Rewriting with UrlRewriting.Net'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-298675729623185756</id><published>2007-08-08T16:15:00.003+01:00</published><updated>2010-07-08T22:40:03.602+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Reports'/><title type='text'>Reading the CrystalReportViewer report connection string  from the Web.config in Crystal Reports</title><content type='html'>&lt;p&gt;Again, another common requirement when working with Crystal Reports. So just where is the official documentation? ...&lt;/p&gt;

&lt;p&gt;When using the CrystalReportViewer control &lt;b&gt;it is&lt;/b&gt; possible to override the report's default database connection information with your own. To do this with SQL Server is well catered for, but what about us poor developers lumbered with ODBC? Perhaps we want to store and deal with connection strings from the &lt;i&gt;web.config&lt;/i&gt; too!
&lt;/p&gt;

&lt;p&gt;Thankfully it is possible to get Crystal Reports to use a custom connection string for ODBC as this code snippet shows:
&lt;/p&gt;

&lt;pre class="csharpcode"&gt;
&lt;span class="rem"&gt;// Get document       &lt;/span&gt;
ReportDocument doc = &lt;span class="kwrd"&gt;this&lt;/span&gt;.CrystalReportSource1.ReportDocument;

&lt;span class="rem"&gt;// Set connection string from config in existing LogonProperties &lt;/span&gt;
doc.DataSourceConnections[0].LogonProperties.Set(&lt;span class="str"&gt;"Connection String"&lt;/span&gt;,
  ConfigurationManager.AppSettings[&lt;span class="str"&gt;"connectionString"&lt;/span&gt;]);

&lt;span class="rem"&gt;// Add existing properties to a new collection&lt;/span&gt;
NameValuePairs2 logonProps = &lt;span class="kwrd"&gt;new&lt;/span&gt; NameValuePairs2();
logonProps.AddRange(doc.DataSourceConnections[0].LogonProperties);

&lt;span class="rem"&gt;// Set our new collection to be the defaults&lt;/span&gt;
&lt;span class="rem"&gt;// This causes Crystal Reports to actually use our changed properties&lt;/span&gt;
doc.DataSourceConnections[0].SetLogonProperties(logonProps);    
&lt;/pre&gt;
&lt;h3&gt;How Does It Work?&lt;/h3&gt;
&lt;p&gt;The key section is the call to &lt;i&gt;SetLogonProperties&lt;/i&gt; which causes Crystal Reports to use the new connection properties. Despite being modifiable the existing LogonProperties of a DataSourceConnection are actually read only.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-298675729623185756?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/298675729623185756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=298675729623185756' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/298675729623185756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/298675729623185756'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2007/08/howto-read-crystalreportviewer-report.html' title='Reading the CrystalReportViewer report connection string  from the Web.config in Crystal Reports'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4580967133432571992.post-870324992399965179</id><published>2007-08-08T16:01:00.001+01:00</published><updated>2009-08-21T14:38:44.041+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Reports'/><title type='text'>Customising the CrystalReportViewer export button default filename</title><content type='html'>&lt;p&gt;Crystal Reports is generally pretty useful, it's a shame some of the more common development tasks have not been exemplified by the creators. It's easy to think many things simply aren't possible, but with a bit of research you soon find most things are.
&lt;/p&gt;
&lt;p&gt;
Setting the default filename used by the CrystalReportViewer web control is one of those things. The filename used defaults to the ID property of the viewer control, so to change it simply change the ID. You can do this declaratively:
&lt;/p&gt;
&lt;pre class="csharpcode"&gt;
&amp;lt;CR:CrystalReportViewer ID="MyReportName" .... /&amp;gt;
&lt;/pre&gt;

or programattically:

&lt;pre class="csharpcode"&gt;
this.CrystalReportViewer1.ID = "MyReportName";
&lt;/pre&gt;

Otherwise you're into manually coding export functions etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-870324992399965179?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/870324992399965179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=870324992399965179' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/870324992399965179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/870324992399965179'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2007/08/howto-set-crystalreportviewer-export.html' title='Customising the CrystalReportViewer export button default filename'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-733288484354337403</id><published>2007-08-04T00:23:00.001+01:00</published><updated>2009-08-21T14:40:50.100+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.Net Membership'/><title type='text'>ASP.Net Membership: How To Log A User In By Email Address</title><content type='html'>Great as ASP.Net 2.0 Membership and Login Controls are sometimes they fall short in functionality. 

A common requirement is to allow users to log-in by email address. No problem with this snippet:

&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;void&lt;/span&gt; LogUserIn (&lt;span class="kwrd"&gt;string&lt;/span&gt; email, &lt;span class="kwrd"&gt;string&lt;/span&gt; password)
{
   &lt;span class="kwrd"&gt;string&lt;/span&gt; username = Membership.GetUserNameByEmail(email);
   &lt;span class="kwrd"&gt;if&lt;/span&gt; (username != &lt;span class="kwrd"&gt;null&lt;/span&gt;
       &amp;amp;&amp;amp; Membership.ValidateUser(username, password))
   {      
       &lt;span class="rem"&gt;// successfully authenticated&lt;/span&gt;
       FormsAuthentication.RedirectFromLoginPage(username, &lt;span class="kwrd"&gt;false&lt;/span&gt;);
   }
   &lt;span class="kwrd"&gt;else&lt;/span&gt;
   {
       &lt;span class="rem"&gt;// Display login error&lt;/span&gt;
   }
}&lt;/pre&gt;

Remember - this only makes sense if you have set the MembershipProvider's &lt;em&gt;RequiresUniqueEmail&lt;/em&gt; property to true. Otherwise &lt;em&gt;Membership.GetUserNameByEmail&lt;/em&gt; will simply return the first username for the email address.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-733288484354337403?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/733288484354337403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=733288484354337403' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/733288484354337403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/733288484354337403'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2007/08/howto-log-user-in-by-email-address.html' title='ASP.Net Membership: How To Log A User In By Email Address'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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-4580967133432571992.post-6167576235950419423</id><published>2007-08-03T19:32:00.001+01:00</published><updated>2009-08-21T14:41:47.323+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='asp:Panel'/><category scheme='http://www.blogger.com/atom/ns#' term='FireFox'/><title type='text'>Making asp:Panel DefaultButton work for a LinkButton in FireFox</title><content type='html'>The DefaultButton property of an asp:Panel is great, until you want to use a LinkButton or ImageButton. It just doesn't work in FireFox. This is because FireFox does not have a &lt;em&gt;click&lt;/em&gt; method on it's HTML anchors.

Fortunately a solution is at hand. Using this JavaScript you can add a click method to anchors that are missing one i.e. those in FireFox.

&lt;pre class="jscode"&gt;function AddClickMethod (clientId)
{   
      var anchor = document.getElementById (clientId);
      // define a click function for firefox
      if (typeof(anchor.click) == "undefined")
      {
           anchor.click = function ()
           {
               eval(this.href.replace("javascript:", ""));
           }
      }
}&lt;/pre&gt;

You can then link the function to your default link button using:

&lt;pre class="jscode"&gt;AddClickMethod ("&lt;%= myLinkButton.ClientID %&gt;");&lt;/pre&gt;

... and presto, a working asp:Panel default button!

&lt;h4&gt;Notes&lt;/h4&gt;

&lt;b&gt;Caveat:&lt;/b&gt; This does not work if you have validation controls with client script enabled. You can disable client script on a control with &lt;em&gt;enableClientScript=false&lt;/em&gt;.

I haven't tried it but I imagine it works with asp:ImageButton too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4580967133432571992-6167576235950419423?l=www.alexlea.me' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.alexlea.me/feeds/6167576235950419423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4580967133432571992&amp;postID=6167576235950419423' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/6167576235950419423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4580967133432571992/posts/default/6167576235950419423'/><link rel='alternate' type='text/html' href='http://www.alexlea.me/2007/08/howto-make-asppanel-defaultbutton-work.html' title='Making asp:Panel DefaultButton work for a LinkButton in FireFox'/><author><name>Alex Lea</name><uri>http://www.blogger.com/profile/07491382584939864847</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></feed>
