<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Pat Gannon's blog</title>
        <link>http://patrickgannon-net.access.secure-ssl-servers.biz/Default.aspx</link>
        <description>.NET, Ruby, Agile</description>
        <language>en-US</language>
        <copyright>Pat Gannon</copyright>
        <managingEditor>PatrickJosephGannon@yahoo.com</managingEditor>
        <generator>Subtext Version 1.9.5.177</generator>
        <image>
            <title>Pat Gannon's blog</title>
            <url>http://patrickgannon-net.access.secure-ssl-servers.biz/images/RSS2Image.gif</url>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/Default.aspx</link>
            <width>77</width>
            <height>60</height>
        </image>
        <item>
            <title>XP vs Kanban: New Product Development</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/07/08/xp-vs-kanban-new-product-development.aspx</link>
            <description>&lt;h3&gt;Our Situation&lt;/h3&gt;  &lt;p&gt;&lt;a href="http://www.hsihealth.com/"&gt;My team and I&lt;/a&gt; are currently working on a new line-of-business web application, and we had a discussion today about what the best Agile process would be for our current endeavor. Up until last Friday, we have been practicing &lt;a href="http://www.extremeprogramming.org/"&gt;Extreme Programming&lt;/a&gt; (XP). We generally have week-long iterations, starting on Friday mornings and ending on Thursday evenings (so that end-of-iteration crunch time to meet our iteration commitment doesn’t fall on a Friday evening).  Friday mornings we have our stand-up, then our retrospective, and then we plan our next iteration.  Last Friday, we decided to try out a Lean/pull process for a week.  This morning at our stand-up, I was trying to pick between working on a rather large story that was at the top of our “Ready” queue, which I probably could not have finished by the end of the day, and a smaller story just below that is more manageable for today.  If we were to continue using Lean/Pull, having the story half-finished at the end of the day is not such a problem, but if we’re going back to XP and fixed-length iterations, I need to make sure there’s no work-in-progress by EOD.  This post is about which process we decided to use and why.&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h3&gt;Extreme Programming&lt;/h3&gt;  &lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 15px 0px 0px; display: inline; border-top: 0px; border-right: 0px" title="Motorcycle" border="0" alt="Motorcycle" align="left" src="http://www.patrickgannon.net/Images/XPvsKanbanNewProductDevelopment_D11E/Motorcycle.jpg" width="244" height="160" /&gt;An advantage of XP over Kanban is our perception that its easier to convey rough estimates of a batch of work to upper management.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.thefreakparade.com/"&gt;My boss&lt;/a&gt; (the CTO) routinely meets with the CEO and COO of our small company and they will discuss a strategic new product, or a fairly large module to be added to an existing product.  They will collaboratively write a set of stories, and then on the following Friday, the development team will meet to give a rough estimate to each of those stories (or each of the “must haves” if there are a lot of stories).  Once we’ve done that, my boss will divide the sum of estimates by our weekly velocity (the average amount of work we complete per week), then multiply by a risk-factor, to produce an estimate of when that chunk of work will be done, and conveys that back to upper management.  The estimate and the scope it represents are not set in stone, and everyone understands that. This still has value to upper management, because they can choose to cancel or defer that chunk of work if its expected value isn’t roughly commensurate with the required effort. Also, it serves as a signal to whether or not they should be brainstorming new strategic products/modules, or whether we’re busy enough that it’s not worth-while for the moment. Additionally, the iteration commitment in XP provides a bit of security in knowing that the work currently under construction will be done by the end of the week, which can be communicated to other stakeholders if asked.&lt;/p&gt;  &lt;p&gt;In an ideal world (eg. textbook Agile/XP), we would be able to deliver the most valuable features to production in one iteration, generate some value, and build from there. In the real-world (or in our world in any case), there is often a minimum marketable feature set that must be completed in order for a new application to deliver any value in its business context. In our business, our customers are busy and selective, so if you demonstrate something for them that doesn’t do the core functions that sparked their interest, at best: they’re not going to use it in its current state. At worst: they will lose interest in the new product and decline to dedicate any more attention to it in the future. Another factor that can result in a minimum marketable feature set like this is a marketing push. Given a fixed marketing budget, are you going to spend those valuable dollars running that full-page ad in a trade journal after the product has had just a week of development?  I think that would probably be unwise in most situations. All of this is certainly not to say we should return to waterfall and decide the scope up front, then do it ‘til it’s ‘done’, etc., but merely that like most things in life, selecting when to ‘launch’ a new application is a balance. You want to launch with the minimum set of functionality that will actually garner the attention of your intended audience, without doing so much up front that you need to do a ton of re-work after it’s used in production and the users tell you everything that’s wrong with it.  The trick is to implement A, B and C (if those are the features that are absolutely critical) without trying to deliver D, E and F (the nice-to-haves) at the same time. The problem is that the effort required to build A, B and C often takes more than one reasonably-sized iteration, in the case of a new application.&lt;/p&gt;  &lt;p&gt;To sum up, the fixed length iterations used in XP enable us to deliver valuable timeframe estimates to upper management, and I don’t see a way around that in our business context.&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h3&gt;Kanban&lt;/h3&gt;  &lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 15px 0px 0px; display: inline; border-top: 0px; border-right: 0px" title="Kanban" border="0" alt="Kanban" align="left" src="http://www.patrickgannon.net/Images/XPvsKanbanNewProductDevelopment_D11E/Kanban.jpg" width="504" height="379" /&gt; &lt;a href="http://www.agileproductdesign.com/blog/2009/kanban_over_simplified.html"&gt;Kanban&lt;/a&gt; is Japanese for “signboard” or “billboard”. “Kanban is a signaling system to trigger action. As its name suggests, kanban historically uses cards to signal the need for an item.” (&lt;a href="http://en.wikipedia.org/wiki/Kanban"&gt;Wikipedia&lt;/a&gt;)  We have actually been using a &lt;a href="http://agilezen.com/"&gt;virtual Kanban board&lt;/a&gt; for a little while now, as in it’s simplest usage, it’s effectively similar to the XP-style story board (index cards stuck on a ferrous whiteboard with colored magnets) we were using previously.  The process change I referred to previously is that we briefly tried out a pull system, where our stakeholders added and prioritized stories in the “Ready” queue at will, theoretically signaled by our completion of work.  We tried this rather than having a fixed-length iteration this week, prompted in part by it being a short week due to the 4th of July holiday recognized on Monday.&lt;/p&gt;  &lt;p&gt;What attracted us to a pull-system is the flexibility it gives stakeholders to introduce new cards or prioritize cards in the middle of an iteration. (Both are prohibited in XP: scope is fixed for the current iteration and the order of story implementation is selected by dev team members based on the logical flow of work occurring during the iteration.)  The other nice thing about it is that it avails us to interesting approaches (like &lt;a href="http://en.wikipedia.org/wiki/Value_stream_mapping"&gt;Value Stream Mapping&lt;/a&gt;) for maximizing the throughput of new features.  (In other words, minimizing the amount of time it typically takes a story to go from conception to production.) It definitely sounds nice in principal, and seems to have worked out well for Toyota.&lt;/p&gt;  &lt;p&gt;The down-side of using Kanban when developing a new application is that there’s no apparent way to do the broad estimates that we currently communicate to upper management.  Although you can use your calculated cycle time (the average amount of time it takes a feature to go from conception to production) to estimate when a particular feature will be delivered to production, what about the time required to deliver the minimum marketable set of features?  It doesn’t seem possible to use cycle time to calculate that, and you can’t use velocity if you don’t have fixed length iterations.  Furthermore, the chunks of work represented by the minimum set of marketable features doesn’t seem very compatible with a pull system.  The stakeholders aren’t adding a feature to the queue as we get work done, they’re telling us the minimum set of features that are needed before they’re willing to call a meeting with a key client to demonstrate a new application, and that seems logical given the circumstances.  Lastly, calculating cycle time and identifying bottle necks becomes much more difficult if the stories moving through the value stream are not similarly sized, and they commonly are not toward the beginning of an applications lifetime, since there is little or no infrastructure in place at the beginning.&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h3&gt;Decision&lt;/h3&gt;  &lt;p&gt;Since the value of being able to produce rough estimates for new applications to upper management exceeds the value of them being able to add and prioritize stories in the middle of an iteration, we decided to go back to using XP for our next iteration.  We will continue to use XP on this application (and all new functionality which can’t be fully launched after one iteration) until it is actually being used in production. We will deliver it to a production environment at regular intervals prior to that, and demonstrate it there for upper management. Once the intended users are actually using it in production and getting value from it, we have tentatively decided to try out a pull system again, since it will make more sense to have new features be pulled through the system in response to work being completed once we are getting feedback from the intended audience, and rough estimates will not need to be communicated back to upper management for that application.  Does ‘XP for brand new apps, Kanban for enhancements’ seem like a good modus operendi?  One thing that occurs to me as I’m reading back over my post is that if had decided to go with a pull system from the start, we could use some estimation methodology that is unrelated to our process (for example some flavor of &lt;a href="http://en.wikipedia.org/wiki/COCOMO"&gt;COCOMO&lt;/a&gt;, or &lt;a href="http://www.urbandictionary.com/define.php?term=scientific-wild-ass+guess"&gt;SWAG&lt;/a&gt;, or you name it), although I gather that the velocity-based estimates we have been using since we adopted XP have been a lot more accurate than estimates given before the company used an Agile process (although I guess that could have to do with adoption of other Agile practices keeping us focused).  If anybody reading this has implemented a new application using a pull system under similar constraints, how did you deal with estimation (was it anywhere near accurate?), and was your work queue much larger at the beginning of the project, or was it small throughout?&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:23d3bbdd-07be-430d-869d-924965537eb0" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/Agile" rel="tag"&gt;Agile&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Extreme+Programming" rel="tag"&gt;Extreme Programming&lt;/a&gt;,&lt;a href="http://technorati.com/tags/XP" rel="tag"&gt;XP&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Lean" rel="tag"&gt;Lean&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Lean+Software+Development" rel="tag"&gt;Lean Software Development&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Kanban" rel="tag"&gt;Kanban&lt;/a&gt;&lt;/div&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/19.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/07/08/xp-vs-kanban-new-product-development.aspx</guid>
            <pubDate>Fri, 09 Jul 2010 02:40:19 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/19.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/07/08/xp-vs-kanban-new-product-development.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/19.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Writing WPF apps using Kiddo/IronRuby and the MVVM pattern</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/06/02/writing-wpf-apps-using-kiddoironruby-and-the-mvvm-pattern.aspx</link>
            <description>&lt;p&gt;&lt;a href="http://www.ihaveaframework.com/2010/05/introducing-kiddo-a-ruby-dsl-for-building-simple-wpf-and-silverlight-applications/"&gt;Kiddo is a Ruby DSL for building simple WPF and Silverlight applications&lt;/a&gt;, inspired by the &lt;a href="http://wiki.github.com/shoes/shoes/okay-well-shoes"&gt;Shoes library&lt;/a&gt;.  It’s still an early alpha, but a very cool concept none-the-less.  I rather like the idea of being able to produce a slick looking WPF (or Silverlight) UI using a simple Ruby API.  The basic syntax looks like this (taken from the post linked to above):&lt;/p&gt;  &lt;p&gt;require "kiddo" &lt;/p&gt;  &lt;p&gt;Kiddo.start do    &lt;br /&gt;    &lt;br /&gt;  draw :stack do     &lt;br /&gt;    draw :text, "Text to reverse:", :font_size =&amp;gt; 18     &lt;br /&gt;    draw :edit_box, :named =&amp;gt; "input"     &lt;br /&gt;    draw :text, "Your string will show up here.", :named =&amp;gt; "result"     &lt;br /&gt;    draw :button, "Reverse It!", :named =&amp;gt; "fun_button"     &lt;br /&gt;  end     &lt;br /&gt;    &lt;br /&gt;  after :fun_button.is_clicked do     &lt;br /&gt;    :result.text = :input.text.reverse     &lt;br /&gt;  end     &lt;br /&gt;    &lt;br /&gt;end &lt;/p&gt;  &lt;p&gt;If you run this, you’ll get something like this on your screen: &lt;/p&gt;  &lt;p&gt;&lt;img alt="" src="http://imgur.com/hYIPj.png" /&gt;     &lt;br /&gt;You type in some text in the textbox, click the button, and the app will reverse the string and show it above the button.  I think the sample code is fairly self explanatory, but if you’re not sure what it’s doing, check out the link above. &lt;/p&gt;  &lt;p&gt;The only issue I had with Kiddo is that it wasn’t clear how one could test the logic that was occurring in one of the event handlers (like the string reversing logic in the “after” clause above).  I wasn’t able to figure out how I could plug in an &lt;a href="http://msdn.microsoft.com/en-us/magazine/dd419663.aspx"&gt;MVVM&lt;/a&gt; (Model View View-Model: the approximate thick-client equivalent of the Model View Controller pattern for web applications) framework without modifying Kiddo, so I went ahead and monkey patched it to support a very basic type of MVVM that would enable me to easily test the logic that handled UI events.  The MVVM extension would need enhancing to be used for probably anything significant, but I think that’s probably true of Kiddo also.  Here is the modified Kiddo example, hooking into the extensions I added… &lt;/p&gt;  &lt;p&gt;require "kiddo"    &lt;br /&gt;require "kiddo_mvvm"     &lt;br /&gt;require "main_view_model"     &lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;Kiddo.start(:view_model =&amp;gt; MainViewModel) do     &lt;br /&gt;  draw :stack do     &lt;br /&gt;    draw :text, "Text to reverse:", :font_size =&amp;gt; 18     &lt;br /&gt;    draw :edit_box, :named =&amp;gt; "input"     &lt;br /&gt;    draw :text, "Your string will show up here.", :named =&amp;gt; "result"     &lt;br /&gt;    draw :button, "Reverse It!", :named =&amp;gt; "fun_button"     &lt;br /&gt;  end  &lt;br /&gt;    &lt;br /&gt;  command :fun_button.is_clicked, :reverse_text     &lt;br /&gt;end     &lt;br /&gt;    &lt;br /&gt;There are three differences between this and the original example.  First, the updated example requires the file with the Kiddo extension I added (kiddo_mvvm.rb) and file with the ViewModel that has the logic that will respond to the button press (main_view_model.rb)  Next, the ViewModel class is associated with the sample UI by passing it in as an option on the Kiddo.start method call.  Finally, instead of having an “after” clause with the actual logic that responds to the button click, we have a call to the new “command” method which ties the button click event (:fun_button.is_clicked) to the method on the ViewModel that should be executed when the event fires (reverse_text). &lt;/p&gt;  &lt;p&gt;Here is the code for the ViewModel class containing the logic (main_view_model.rb): &lt;/p&gt;  &lt;p&gt;class MainViewModel    &lt;br /&gt;  attr_accessor :input     &lt;br /&gt;  attr_accessor :result     &lt;br /&gt;    &lt;br /&gt;  def reverse_text     &lt;br /&gt;    @result = @input.reverse     &lt;br /&gt;    @input = "thank you"     &lt;br /&gt;  end     &lt;br /&gt;end     &lt;br /&gt;    &lt;br /&gt;It defines public getter/setter properties that correspond to the text of the “input” textbox and the “result” text (label).  It also defines the “reverse_text” method that is invoked by the MVVM extensions to Kiddo in response to the button being clicked.  That method reverses the string in “input” and sets it into “result”.  To illustrate the advantage that this approach has to simply calling a method on a business object in an “after” clause (that the command can easily read from and write to several bound widgets), it also sets “input” to “thank you”.  Now, you can run the revised sample which behaves like the original sample except that it also puts “thank you” in the input box after you click the button. &lt;/p&gt;  &lt;p&gt;Now you can write a test directly against MainViewModel to test the logic without bringing the actual UI into play.  Here is a simple example of that (test_MVVM_custom.rb), using the very simple Test::Unit ruby testing framework: &lt;/p&gt;  &lt;p&gt;require "test/unit"    &lt;br /&gt;require "main_view_model"     &lt;br /&gt;    &lt;br /&gt;class MainViewModelTest &amp;lt; Test::Unit::TestCase     &lt;br /&gt;  def test_reversing     &lt;br /&gt;    vm = MainViewModel.new     &lt;br /&gt;    vm.input = "mytest"     &lt;br /&gt;    vm.reverse_text     &lt;br /&gt;    assert_equal "tsetym", vm.result     &lt;br /&gt;  end     &lt;br /&gt;end     &lt;br /&gt;    &lt;br /&gt;The fixture defines a single test that instantiates the ViewModel, sets the input to “mytest”, calls reverse_text and verifies that the result is “tsetym”.  Running a test/unit test is very simple, simply execute the ruby file! (ir test_MVVM_custom.rb)     &lt;br /&gt;Finally, here is the code for the MVVM extensions I added to Kiddo (kiddo_mvvm.rb):&lt;/p&gt;  &lt;p&gt;module Kiddo    &lt;br /&gt;  class App     &lt;br /&gt;    attr_accessor :view_model     &lt;br /&gt;  end     &lt;br /&gt;    &lt;br /&gt;  class &amp;lt;&amp;lt; self     &lt;br /&gt;    alias_method :original_start, :start     &lt;br /&gt;  end     &lt;br /&gt;    &lt;br /&gt;  def self.start(options={}, &amp;amp;block)     &lt;br /&gt;    view_model = options.delete(:view_model)     &lt;br /&gt;    if view_model     &lt;br /&gt;      App.instance.view_model = view_model.new     &lt;br /&gt;    end     &lt;br /&gt;    original_start options, &amp;amp;block     &lt;br /&gt;  end     &lt;br /&gt;    &lt;br /&gt;  def command(hash, view_model_method)     &lt;br /&gt;    view_model = App.instance.view_model     &lt;br /&gt;    if !view_model     &lt;br /&gt;      raise "Must pass a ViewModel instance to Kiddo.start in order to bind a command"     &lt;br /&gt;    end     &lt;br /&gt;    hash[:instance].send hash[:method] do |sender, args|     &lt;br /&gt;      #Copy data from widgets into ViewModel     &lt;br /&gt;      view_model.methods.each do |method|     &lt;br /&gt;        if method.index("=") == method.length - 1     &lt;br /&gt;          property = method.chop  &lt;br /&gt;                 widget = App.instance.locate property  &lt;br /&gt;    &lt;br /&gt;                 if widget and defined?(widget.text)  &lt;br /&gt;                   view_model.send method, widget.text  &lt;br /&gt;                 end     &lt;br /&gt;        end     &lt;br /&gt;      end     &lt;br /&gt;      view_model.send view_model_method     &lt;br /&gt;      #Copy data from ViewModel back into widgets     &lt;br /&gt;      view_model.methods.each do |method|     &lt;br /&gt;        if method.index("=") != method.length - 1     &lt;br /&gt;          property = method     &lt;br /&gt;          widget = App.instance.locate property     &lt;br /&gt;          if widget and widget.respond_to?("text=")     &lt;br /&gt;            new_text = view_model.send method     &lt;br /&gt;            widget.text = new_text     &lt;br /&gt;          end     &lt;br /&gt;        end     &lt;br /&gt;      end     &lt;br /&gt;    end     &lt;br /&gt;  end     &lt;br /&gt;end     &lt;br /&gt;    &lt;br /&gt;This code theoretically shouldn’t need to get modified on an app by app basis (except to enable new types of controls, etc.)  It essentially just adds in support for the “view_model” option on Kiddo.start and then defines the new “command” method.  That method loops through all the methods on the ViewModel that end in “=” (Ruby property setters) and tries to match them to UI widgets based on their name.  When a match is found, it sets the ViewModel property to the text of the corresponding widget.  (Obviously this approach won’t work for all types of widgets, but it’s a start!)  Next, it calls the method on the ViewModel that has the name passed in from the caller.  Finally, it loops through all the methods on the ViewModel that don’t end in “=” and tries to match those to widget names.  When a match is found, it sets the text on the widget to the corresponding property value from the ViewModel.  This enables the ViewModel class to effectively “bind” to widgets simply by creating a getter/setter property with the same name as the widget. &lt;/p&gt;  &lt;p&gt;I’m contemplating porting MongoDB Management Studio to use Kiddo, so if that comes to pass, then there will likely be a number of enhancements to this extension.&lt;/p&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/18.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/06/02/writing-wpf-apps-using-kiddoironruby-and-the-mvvm-pattern.aspx</guid>
            <pubDate>Wed, 02 Jun 2010 20:12:04 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/18.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/06/02/writing-wpf-apps-using-kiddoironruby-and-the-mvvm-pattern.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/18.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Introduction to IronRuby - presentation materials</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/04/22/introduction-to-ironruby---presentation-materials.aspx</link>
            <description>Attached is the Powerpoint slide deck and two sample applications (application plug-in and simple DSL) that I used for my presentation for NBNUG on Tuesday, 4/20/10.  &lt;p&gt;   &lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:a968b212-06e4-4880-802a-ad7ed61bd648" class="wlWriterSmartContent"&gt;&lt;p&gt; &lt;a href="http://www.patrickgannon.net/Images/IntroductiontoIronRubypresentationmateri_13273/IntroductiontoIronRuby.ppt" target="_blank"&gt;Slide deck&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;   &lt;p&gt;   &lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:7e262ed6-02a6-4bde-8482-11658947ac15" class="wlWriterSmartContent"&gt;&lt;p&gt; &lt;a href="http://www.patrickgannon.net/Images/IntroductiontoIronRubypresentationmateri_13273/DSL_sample.zip" target="_blank"&gt;DSL Plugin&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;   &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:83a747f2-4469-4918-ae66-8754533ada55" class="wlWriterSmartContent"&gt;&lt;p&gt; &lt;a href="http://www.patrickgannon.net/Images/IntroductiontoIronRubypresentationmateri_13273/AppPlugin_sample.zip" target="_blank"&gt;App Plugin&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/17.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/04/22/introduction-to-ironruby---presentation-materials.aspx</guid>
            <pubDate>Fri, 23 Apr 2010 04:49:48 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/17.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2010/04/22/introduction-to-ironruby---presentation-materials.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/17.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Troubleshooting Non-Reproducible Bugs in Visual Studio 2010</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/11/07/troubleshooting-non-reproducible-bugs-in-visual-studio-2010.aspx</link>
            <description>&lt;p&gt;There are two new features in Visual Studio 2010 that help developers troubleshoot bugs that can't be easily reproduced.  One is IntelliTrace, which seems like it will be of limited usefulness to me because it can’t be done remotely, but it might be more useful for teams that have a dedicated QA staff and/or don't practice TDD.  On the other hand, I watched an episode of Hanselminutes where Scott interviews Tess Fernandez about Debugging Crash Dumps in VS2010, and that technique seems like it could be really useful.&lt;/p&gt;  &lt;p&gt;IntelliTrace is only available in Visual Studio 2010 Ultimate.  It seems to collect the values of all in-scope variables whenever an IntelliTrace event occurs.  Those watched events are configurable (from a long, but pre-determined list), and include things like button clicks, writing to the Debug log, registry access, etc.  If you encounter some non-desirable functionality in an application while debugging (or running their test tool), you can break in the debugger, and then see what the state was whenever one of the watched events occurred.  There is also an option to basically capture the state whenever a method is called, but that entails a significant performance penalty, as one might expect.  I found an article on MSDN that says: “With IntelliTrace, you can debug applications launched from Visual Studio and log files that were created by IntelliTrace or the test tool Test and Lab Manager. You cannot use IntelliTrace with applications launched outside Visual Studio and attached using the Attach to command. IntelliTrace does not support remote debugging of applications that are running on other computers.”  That implies to me that there’s no real way to use this functionality to figure out what happened when a bug occurred on a user’s computer, unfortunately.  That article is here: &lt;a href="http://msdn.microsoft.com/en-us/library/dd264915%28VS.100%29.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd264915(VS.100).aspx&lt;/a&gt; .  Also note that although you can step backwards in the debugger (and step out of methods to see what the variables were in the calling method) in IntelliTrace, you can only step in increments of IntelliTrace events (which can include method calls if you configured VS that way), so it will step over big chunks of code if they don’t include watched events.&lt;/p&gt;  &lt;p&gt;Here are some posts about how to get started using IntelliTrace: &lt;a href="http://blogs.msdn.com/habibh/archive/2009/10/20/getting-started-with-visual-studio-2010-intellitrace-hello-intellitrace.aspx"&gt;http://blogs.msdn.com/habibh/archive/2009/10/20/getting-started-with-visual-studio-2010-intellitrace-hello-intellitrace.aspx&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/habibh/archive/2009/10/21/the-future-of-debugging-is-here-visual-studio-2010-now-supports-stepping-back-in-the-debugger.aspx"&gt;http://blogs.msdn.com/habibh/archive/2009/10/21/the-future-of-debugging-is-here-visual-studio-2010-now-supports-stepping-back-in-the-debugger.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Debugging crash dumps, however, does seem like something that can be done to troubleshoot undesirable functionality that happened on a user’s computer (or on a production server).  Apparently in Vista and Windows 7, there is functionality in Task Manager such that you can right-click on a process and choose “Create Dump File” which creates a file in “the temp directory”.  (Apparently there are separate apps you can download to generate such dump files with Windows XP, such as “DebugDiag”, but I'm not sure if that works with managed code.)  You can then open the resulting file in Visual Studio 2010 and, provided you have the source code for the application, debug it in “Mixed Mode” (meaning you can see what was happening in managed code as well as in Native code).  Although you can’t step in the debugger when you’ve loaded a dump file, you can see all the values of the variables in memory (by highlighting over them in code just like typical debugging), and using the “Parallel Stacks” window in VS2010, you can get a visualization of the code that all active threads were executing when the dump was generated, and click through to see what all the variables were set to in a given thread when the dump was generated.  It was implied in passing that this will work with applications compiled for .NET 3.5.  It seems to me this would be incredibly useful for troubleshooting a problem that can’t be reproduced.  When the problem happens, just tell the user to create a dump file, send it to development, and then see what was happening with their app at the time.  That screencast is here: &lt;a href="http://channel9.msdn.com/posts/Glucose/Hanselminutes-on-9-Debugging-Crash-Dumps-with-Tess-Ferrandez-and-VS2010/"&gt;http://channel9.msdn.com/posts/Glucose/Hanselminutes-on-9-Debugging-Crash-Dumps-with-Tess-Ferrandez-and-VS2010/&lt;/a&gt;  I'm excited about this functionality, and I'm hoping to be able to spend more time trying to debug Crash Dumps created from some of my company's WinForms applications, and seeing if it is feasible to do from Windows XP (which is installed on many of the workstations running our applications).  When and if that comes to fruition, I'll be writing another post on this topic.&lt;/p&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/16.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/11/07/troubleshooting-non-reproducible-bugs-in-visual-studio-2010.aspx</guid>
            <pubDate>Sat, 07 Nov 2009 08:16:08 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/16.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/11/07/troubleshooting-non-reproducible-bugs-in-visual-studio-2010.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/16.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Getting ActiveRecord and NHibernate Working in Medium Trust</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/23/getting-activerecord-and-nhibernate-working-in-medium-trust.aspx</link>
            <description>&lt;p&gt;I have spent the last couple of hours trying to figure out how to get &lt;a href="http://www.castleproject.org/activerecord/index.html"&gt;Castle ActiveRecord&lt;/a&gt; working in &lt;a href="http://help.godaddy.com/article/1039"&gt;medium trust&lt;/a&gt; (using &lt;a href="http://www.godaddy.com"&gt;GoDaddy&lt;/a&gt;), and I have finally succeeded (for my purposes).&lt;/p&gt;  &lt;p&gt;Here are the steps I had to take to get this working:&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;* Re-built ActiveRecord to allow partially trusted callers, which entails the following steps: (thanks to &lt;a href="http://forum.castleproject.org/viewtopic.php?t=1439"&gt;this thread on the Castle Project forums&lt;/a&gt;)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;1. Do an SVN Checkout on the Castle project trunk using &lt;a href="http://tortoisesvn.tigris.org/"&gt;TortoiseSVN&lt;/a&gt; or &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt;. The repository is here: &lt;a title="http://svn.castleproject.org:8080/svn/castle/trunk" href="http://svn.castleproject.org:8080/svn/castle/trunk"&gt;http://svn.castleproject.org:8080/svn/castle/trunk&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;2. Navigate to the root directory of the Castle project on your machine and enter the following command:&lt;/p&gt;    &lt;p&gt;SharedLibs\build\NAnt\bin\nant -t:net-3.5 -D:assembly.allow-partially-trusted-callers=true&lt;/p&gt;    &lt;p&gt;Naturally, the build fails (running unit tests), but I was none-the-less able to get the updated DLLs I needed from the build\net-3.5\debug directory under the Castle directory.  I updated all the ActiveRecord-related DLLs (and NHibernate, etc.) that my app was using from the release version of ActiveRecord to this newly-compiled version.  It also now needs Iese.Collections.dll and NHibernate.ByteCode.Castle.dll&lt;/p&gt;    &lt;p&gt;3. I added the following line to my ActiveRecord configuration file (appconfig.xml), which is required by newer versions of NHibernate (thanks to &lt;a href="http://nhforge.org/blogs/nhibernate/archive/2008/11/09/nh2-1-0-bytecode-providers.aspx"&gt;these instructions on NHForge&lt;/a&gt;):&lt;/p&gt;    &lt;p&gt;&amp;lt;add key="proxyfactory.factory_class" value="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle" /&amp;gt; &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;* Enable &lt;a href="http://msdn.microsoft.com/en-us/library/system.security.allowpartiallytrustedcallersattribute.aspx"&gt;AllowPartiallyTrustedCallers&lt;/a&gt; by adding the following code to AssemblyInfo.cs:&lt;/p&gt;  &lt;p&gt;[assembly: System.Security.AllowPartiallyTrustedCallers]&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;* Place this line after your call to ActiveRecordStarter.Initialize(...): (thanks to &lt;a href="http://nhforge.org/wikis/howtonh/run-in-medium-trust.aspx"&gt;these instructions on NHForge&lt;/a&gt;)    &lt;br /&gt;NHibernate.Cfg.Environment.UseReflectionOptimizer = false;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Apparently getting Lazy Loading to work in Medium Trust is even harder, but there is apparently a tool available called &lt;a href="http://blog.symbiotic-development.com/2008/03/22/nhibernate-proxy-generator/"&gt;NHibernate Proxy Generator&lt;/a&gt; that will enable that to work, although it entails generating proxies at compile-time because Reflection is not allowed under Medium Trust.  Here is the tale of an individual who was apparently able to get this to work: &lt;a title="http://blechie.com/WPierce/archive/2008/02/17/Lazy-Loading-with-nHibernate-Under-Medium-Trust.aspx" href="http://blechie.com/WPierce/archive/2008/02/17/Lazy-Loading-with-nHibernate-Under-Medium-Trust.aspx"&gt;http://blechie.com/WPierce/archive/2008/02/17/Lazy-Loading-with-nHibernate-Under-Medium-Trust.aspx&lt;/a&gt;  At present, I don't need lazy-loading in the application that I'm running in Medium Trust, so I haven't tried it.&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Good luck!  Leave a comment if this worked for you, or if you have any problems.&lt;/p&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/15.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/23/getting-activerecord-and-nhibernate-working-in-medium-trust.aspx</guid>
            <pubDate>Wed, 24 Jun 2009 05:31:18 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/15.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/23/getting-activerecord-and-nhibernate-working-in-medium-trust.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/15.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Updated Presentation Materials: Developing REST Web Services using WCF</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/16/updated-presentation-materials-developing-rest-web-services-using-wcf.aspx</link>
            <description>&lt;p&gt;Here are the presentation materials that were slightly revised for the second time I grave the WCF/REST presentation (on June 3rd in the South Bay).  Sorry for the delay!&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:c366043b-619f-42d0-ba0a-d726c75c3760" class="wlWriterSmartContent"&gt;&lt;p&gt; &lt;a href="http://www.patrickgannon.net/Images/UpdatedPresentationMaterialsDevelopingRE_11174/RESTDemo.zip" target="_blank"&gt;RESTDemo.zip&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/14.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/16/updated-presentation-materials-developing-rest-web-services-using-wcf.aspx</guid>
            <pubDate>Wed, 17 Jun 2009 02:28:13 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/14.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/16/updated-presentation-materials-developing-rest-web-services-using-wcf.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/14.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Presenting on REST/WCF in the South Bay</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/01/presenting-on-restwcf-in-the-south-bay.aspx</link>
            <description>&lt;p&gt;I am doing my &lt;a href="http://www.patrickgannon.net/archive/2009/04/18/presenting-at-nbnug-on-421-developing-rest-web-services-using.aspx"&gt;Developing REST Web Services using WCF&lt;/a&gt; presentation again for the South Bay chapter of the &lt;a href="http://www.baynetug.org/"&gt;Bay.NET&lt;/a&gt; user group.  It will be Wednesday, June 3rd at the Microsoft’s Silicon Valley conference center at 1065 La Avenida St, Mountain View, CA (Building 1 - room number is still TBD).&lt;/p&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/13.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/01/presenting-on-restwcf-in-the-south-bay.aspx</guid>
            <pubDate>Tue, 02 Jun 2009 05:10:33 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/13.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/06/01/presenting-on-restwcf-in-the-south-bay.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/13.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Developing REST Web Services using WCF - Presentation materials</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/22/developing-rest-web-services-using-wcf---presentation-materials.aspx</link>
            <description>&lt;p&gt;As promised in the presentation I did last night for NBNUG, here is my slide deck and sample code for my REST/WCF presentation.  As I mentioned in the presentation, the sample code is purposefully not well-factored, so as not to obscure the core concepts that the code is demonstrating.  The sample code is not at all meant to convey architectural best practices for WCF-based applications, it's essentially an illustration to get folks started working with this technology.&lt;/p&gt;  &lt;p&gt;   &lt;/p&gt;&lt;div class="wlWriterSmartContent" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:5cafb7ec-4bbe-4fb9-aa86-680635a378dc" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;&lt;p&gt; &lt;a href="http://www.patrickgannon.net/Images/DevelopingRESTWebServicesusingWCFPresent_13F95/DevelopingRESTWebServicesusingWCF.ppt" target="_blank"&gt;Slide Deck&lt;/a&gt;&lt;br /&gt; &lt;a href="http://www.patrickgannon.net/Images/DevelopingRESTWebServicesusingWCFPresent_13F95/RESTDemo.zip" target="_blank"&gt;Sample Code&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;   &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:62840abe-ef26-4e86-bfdc-33452523a6d3" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/WCF" rel="tag"&gt;WCF&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/REST" rel="tag"&gt;REST&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/.NET" rel="tag"&gt;.NET&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/Web%20Services" rel="tag"&gt;Web Services&lt;/a&gt;&lt;/div&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/12.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/22/developing-rest-web-services-using-wcf---presentation-materials.aspx</guid>
            <pubDate>Thu, 23 Apr 2009 05:48:21 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/12.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/22/developing-rest-web-services-using-wcf---presentation-materials.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/12.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Presenting at NBNUG on 4/21: Developing REST Web Services using WCF</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/18/presenting-at-nbnug-on-421-developing-rest-web-services-using.aspx</link>
            <description>&lt;p&gt;I am demonstrating how to create a REST-based web service using WCF at the upcoming NBNUG meeting.  Here is the abstract:&lt;/p&gt;  &lt;p&gt;Tuesday, 4/21/2009, meeting at 7:00 PM&lt;/p&gt;  &lt;p&gt;O'Reilly Media in Sebastopol &lt;/p&gt;  &lt;p&gt;&lt;a name="WhereWhenField1"&gt;&lt;/a&gt;1003-1005 Gravenstein Highway North, Sebastopol    &lt;br /&gt;Tarsier Conference Room (between Building B and Building C)    &lt;br /&gt;(8 miles west of Santa Rosa) &lt;/p&gt;  &lt;p&gt;&lt;a name="DescriptionField"&gt;&lt;/a&gt;The presenter will walk through a working REST-based web service (and client), implemented with WCF (Windows Communication Foundation). We will discuss the underlying principles of REST (Representational State Transfer), the motivations for it, and how to create that style of web service using WCF. We will discuss how you can secure these type of web services and finally, we will briefly touch on ADO.NET Data Services (formerly known as Astoria): a technology that automatically exposes a REST web service layer on top of a Linq data source.&lt;/p&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/11.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/18/presenting-at-nbnug-on-421-developing-rest-web-services-using.aspx</guid>
            <pubDate>Sun, 19 Apr 2009 00:51:15 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/11.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/18/presenting-at-nbnug-on-421-developing-rest-web-services-using.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/11.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Rendering a diff in a web page</title>
            <link>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/08/rendering-a-diff-in-a-web-page.aspx</link>
            <description>&lt;p&gt;Recently in my spare time, I have been working on a web site where developers can post code snippets which they would like to be reviewed by other developers on the web.  Once a code snippet has been posted, other developers can add comments and make a revised version of the code snippet, which will be rendered under the original code snippet as a diff (the difference between the original snippet and the revised snippet).  I have registered myself &lt;a href="http://www.codenuggets.net/"&gt;a domain&lt;/a&gt;, but the site is not ready to be launched yet.  This post is about how I rendered a line-oriented diff in an ASP.NET MVC web page.  I'll write another post to announce the site itself when I launch it.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.patrickgannon.net/Images/Renderingadiffinawebpage_14380/Diff.jpg" target="_blank"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="142" alt="The diffUI" src="http://www.patrickgannon.net/Images/Renderingadiffinawebpage_14380/Diff_thumb.jpg" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h3&gt;The Algorithm&lt;/h3&gt;  &lt;p&gt;After a brief period of trying to figure out my own algorithm for rendering a diff, I decided to do some research on how others have solved the problem.  It seems that it's not quite as easy of a problem to solve as it would seem on the surface, and there has actually been a fair amount of in-depth research done on how to solve it.  I wound up deciding to use &lt;a href="http://blog.menees.com/"&gt;Bill Menee&lt;/a&gt;'s &lt;a href="http://www.menees.com/"&gt;Menees diff library&lt;/a&gt;, which uses the popular Myers algorithm, described in the paper &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927"&gt;An O(ND) Difference Algorithm and its Variations&lt;/a&gt; by Eugene W. Myers.  The paper looks fascinating, and I might be interested in implementing it myself at some point, but for the moment I'm going to use the Menees library.  Here is the abstract to the paper, to give you an overview of how Myers solves the problem.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;The problems of finding a longest common subsequence of two sequences A and B and a shortest edit script        &lt;br /&gt;for transforming A into B have long been known to be dual problems. In this paper, they are shown to be         &lt;br /&gt;equivalent to finding a shortest/longest path in an edit graph. Using this perspective, a simple O(ND) time         &lt;br /&gt;and space algorithm is developed where N is the sum of the lengths of A and B and D is the size of the         &lt;br /&gt;minimum edit script for A and B. The algorithm performs well when differences are small (sequences are         &lt;br /&gt;similar) and is consequently fast in typical applications. The algorithm is shown to have O(N+D2 )         &lt;br /&gt;expected-time performance under a basic stochastic model. A refinement of the algorithm requires only         &lt;br /&gt;O(N) space, and the use of suffix trees leads to an O(NlgN +D2 ) time variation.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h3&gt;The Code&lt;/h3&gt;  &lt;p&gt;Here's the code that I came up with to render a diff created by the Menees library.&lt;/p&gt;  &lt;p&gt;The model and view-data:&lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Menees.DiffUtils;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; CodeReview.Models&lt;/pre&gt;

  &lt;pre&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;    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;    &lt;span class="rem"&gt;/// Represents a chunk of source code&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SourceCode&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Id { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _title = &lt;span class="kwrd"&gt;string&lt;/span&gt;.Empty;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Title &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;            get&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; _title;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;            set&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;                _title = &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&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; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Description { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Code { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; CreatedBy { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;Comment&amp;gt; Comments { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; _lines = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;        &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Lines&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;            get&lt;/pre&gt;

  &lt;pre&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; (_lines == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;                    _lines = GetLines(Code);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; _lines;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsValid&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; (GetRuleViolations().Count() == 0); }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; SourceCode(&lt;span class="kwrd"&gt;string&lt;/span&gt; code)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;            : &lt;span class="kwrd"&gt;this&lt;/span&gt;()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;            Code = code;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; SourceCode()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;            Comments = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;Comment&amp;gt;();&lt;/pre&gt;

  &lt;pre&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;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;Difference&amp;gt; Diff(SourceCode compareWith)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;            List&amp;lt;Difference&amp;gt; differences = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;Difference&amp;gt;();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;            IList a = Lines;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;            IList b = compareWith.Lines;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;            TextDiff diff = &lt;span class="kwrd"&gt;new&lt;/span&gt; TextDiff((&lt;span class="kwrd"&gt;int&lt;/span&gt;)HashType.HashCode, &lt;span class="kwrd"&gt;false&lt;/span&gt;, &lt;span class="kwrd"&gt;false&lt;/span&gt;, 0);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;            EditScript script = diff.Execute(a, b);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Edit edit &lt;span class="kwrd"&gt;in&lt;/span&gt; script)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;                differences.Add(ConvertEditToDifference(edit, compareWith));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; differences;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; GetLines(&lt;span class="kwrd"&gt;string&lt;/span&gt; code)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; code.Split(&lt;span class="str"&gt;'\n'&lt;/span&gt;).ToList();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&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;        &lt;span class="kwrd"&gt;private&lt;/span&gt; Difference ConvertEditToDifference(Edit edit, SourceCode compareWith)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Difference()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;                LineNumber = edit.StartA + 1,&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;                Text = GetChangeText(edit, compareWith),&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;                Type = ConvertEditTypeToDifferenceType(edit.Type),&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;                NumberOfLines = edit.Length&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;            };&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  98:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  99:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetChangeText(Edit edit, SourceCode compareWith)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 100:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 101:  &lt;/span&gt;            StringBuilder changeText = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 102:  &lt;/span&gt;            SourceCode textSource;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 103:  &lt;/span&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; lineNumberInTextSource;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 104:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 105:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (edit.Type == EditType.Insert)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 106:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 107:  &lt;/span&gt;                textSource = compareWith;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 108:  &lt;/span&gt;                lineNumberInTextSource = edit.StartB;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 109:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 110:  &lt;/span&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 111:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 112:  &lt;/span&gt;                textSource = &lt;span class="kwrd"&gt;this&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 113:  &lt;/span&gt;                lineNumberInTextSource = edit.StartA;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 114:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 115:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 116:  &lt;/span&gt;            &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; edit.Length; i++)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 117:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 118:  &lt;/span&gt;                changeText.Append(textSource.Lines[lineNumberInTextSource + i]);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 119:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 120:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 121:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; changeText.ToString();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 122:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 123:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 124:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; DifferenceType ConvertEditTypeToDifferenceType(EditType editType)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 125:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 126:  &lt;/span&gt;            &lt;span class="kwrd"&gt;switch&lt;/span&gt; (editType)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 127:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 128:  &lt;/span&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; EditType.Change:&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 129:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; DifferenceType.Change;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 130:  &lt;/span&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; EditType.Delete:&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 131:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; DifferenceType.Removal;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 132:  &lt;/span&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; EditType.Insert:&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 133:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; DifferenceType.Addition;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 134:  &lt;/span&gt;                &lt;span class="kwrd"&gt;default&lt;/span&gt;:&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 135:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentException(&lt;span class="str"&gt;"Unexpected edit type: "&lt;/span&gt; + editType, &lt;span class="str"&gt;"editType"&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 136:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 137:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 138:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 139:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; IEnumerable&amp;lt;RuleViolation&amp;gt; GetRuleViolations()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 140:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 141:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (String.IsNullOrEmpty(Title))&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 142:  &lt;/span&gt;                &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; RuleViolation(&lt;span class="str"&gt;"Title is required"&lt;/span&gt;, &lt;span class="str"&gt;"Title"&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 143:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 144:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (String.IsNullOrEmpty(Code))&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 145:  &lt;/span&gt;                &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; RuleViolation(&lt;span class="str"&gt;"Code is required"&lt;/span&gt;, &lt;span class="str"&gt;"Code"&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 146:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 147:  &lt;/span&gt;            &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;break&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 148:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 149:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 150:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Validate()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 151:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 152:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (!IsValid)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 153:  &lt;/span&gt;                &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException(&lt;span class="str"&gt;"Rule violations prevent saving"&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 154:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 155:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 156:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 157:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Difference&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 158:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 159:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; DifferenceType Type { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 160:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Text { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 161:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; LineNumber { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 162:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; NumberOfLines { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 163:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 164:  &lt;/span&gt;}&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 165:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 166:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; CodeReview.ViewData&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 167:  &lt;/span&gt;{&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 168:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ShowSourceCodeViewData&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 169:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 170:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; RenderedOriginal { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 171:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;CommentViewData&amp;gt; Comments { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 172:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 173:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; ShowSourceCodeViewData()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 174:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 175:  &lt;/span&gt;            Comments = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;CommentViewData&amp;gt;();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 176:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 177:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 178:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 179:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CommentViewData&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 180:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 181:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; RenderedOriginal { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 182:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; RenderedRevised { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 183:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Text { get; set; }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 184:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 185:  &lt;/span&gt;}&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 186:  &lt;/span&gt; &lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;The controller action and supporting methods:&lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Show(&lt;span class="kwrd"&gt;int&lt;/span&gt;? id)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;            SourceCode original = _repository.GetById(id.Value);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt;[] originalLines = original.Lines.ToArray();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;            ShowSourceCodeViewData viewData = &lt;span class="kwrd"&gt;new&lt;/span&gt; ShowSourceCodeViewData();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;            viewData.RenderedOriginal = ConvertFromPlainTextToHtml(original.Code);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Comment comment &lt;span class="kwrd"&gt;in&lt;/span&gt; original.Comments)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;                StringBuilder originalBuilder = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder(); &lt;span class="rem"&gt;//the buffer for displaying the original version (on the left side of the diff)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;                StringBuilder editedBuilder = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder(); &lt;span class="rem"&gt;//the buffer for displaying the revised version (on the right side of the diff)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;                &lt;span class="kwrd"&gt;string&lt;/span&gt;[] editedLines = comment.Revision.Lines.ToArray();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;                List&amp;lt;Difference&amp;gt; differences = original.Diff(comment.Revision);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; originalLineNumber = 0;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; editedLineNumber = 0;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;                CommentViewData commentViewData = &lt;span class="kwrd"&gt;new&lt;/span&gt; CommentViewData();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Difference difference &lt;span class="kwrd"&gt;in&lt;/span&gt; differences)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;                {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;                    &lt;span class="rem"&gt;//put all unchanged lines leading up to this difference into both buffers&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;for&lt;/span&gt; (; originalLineNumber &amp;lt; (difference.LineNumber - 1); originalLineNumber++, editedLineNumber++)&lt;/pre&gt;

  &lt;pre&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;                        originalBuilder.Append(RenderLineHtml(DifferenceType.NoChange, originalLineNumber, originalLines[originalLineNumber]));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;                        editedBuilder.Append(RenderLineHtml(DifferenceType.NoChange, editedLineNumber, editedLines[editedLineNumber]));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;                    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (difference.Type == DifferenceType.Addition)&lt;/pre&gt;

  &lt;pre&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;string&lt;/span&gt; differenceText = difference.Text;&lt;/pre&gt;

  &lt;pre&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;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (differenceText.EndsWith(&lt;span class="str"&gt;"\r"&lt;/span&gt;))&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;                            differenceText = differenceText.Substring(0, differenceText.Length - 1);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;string&lt;/span&gt;[] lines = differenceText.Split(&lt;span class="str"&gt;'\r'&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;                        &lt;span class="rem"&gt;//put added lines only into the buffer for the revised version, and add blank space to the buffer for the original version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; line &lt;span class="kwrd"&gt;in&lt;/span&gt; lines)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;                        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;                            editedBuilder.Append(RenderLineHtml(difference.Type, editedLineNumber, line));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;                            originalBuilder.AppendLine();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;                            editedLineNumber++;&lt;/pre&gt;

  &lt;pre&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;/pre&gt;

  &lt;pre&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;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (difference.Type == DifferenceType.Removal)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;                    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;string&lt;/span&gt; differenceText = difference.Text;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (differenceText.EndsWith(&lt;span class="str"&gt;"\r"&lt;/span&gt;))&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;                            differenceText = differenceText.Substring(0, differenceText.Length - 1);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;string&lt;/span&gt;[] lines = differenceText.Split(&lt;span class="str"&gt;'\r'&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;                        &lt;span class="rem"&gt;//put removed lines only into the buffer for the original version, and add blank space to the buffer for the revised version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; line &lt;span class="kwrd"&gt;in&lt;/span&gt; lines)&lt;/pre&gt;

  &lt;pre&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;                            originalBuilder.Append(RenderLineHtml(difference.Type, originalLineNumber, line));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;                            editedBuilder.AppendLine();&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;                            originalLineNumber++;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;                        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;                    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (difference.Type == DifferenceType.Change)&lt;/pre&gt;

  &lt;pre&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="rem"&gt;//put revised version of changed line into buffer for revised version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;                        editedBuilder.Append(RenderLineHtml(difference.Type, editedLineNumber, editedLines[editedLineNumber]));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;                        &lt;span class="rem"&gt;//put original version of changed line into buffer for original version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;                        originalBuilder.Append(RenderLineHtml(difference.Type, originalLineNumber, difference.Text));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;                        editedLineNumber++;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;                        originalLineNumber++;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;                    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;                }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;                &lt;span class="rem"&gt;//put all remaining unchanged lines into buffer for original version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;                &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = originalLineNumber; i &amp;lt; originalLines.Length; i++)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;                    originalBuilder.Append(RenderLineHtml(DifferenceType.NoChange, i, originalLines[i]));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;                &lt;span class="rem"&gt;//put all remaining unchanged lines into buffer for revised version&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;                &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = editedLineNumber; i &amp;lt; editedLines.Length; i++)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;                    editedBuilder.Append(RenderLineHtml(DifferenceType.NoChange, i, editedLines[i]));&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;                commentViewData.RenderedOriginal = ConvertFromPlainTextToHtml(originalBuilder.ToString());&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;                commentViewData.RenderedRevised = ConvertFromPlainTextToHtml(editedBuilder.ToString());&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;                commentViewData.Text = comment.Text;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;                viewData.Comments.Add(commentViewData);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; View(viewData);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ConvertFromPlainTextToHtml(&lt;span class="kwrd"&gt;string&lt;/span&gt; plainText)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; plainText.Replace(&lt;span class="str"&gt;"\r"&lt;/span&gt;, &lt;span class="str"&gt;"&amp;lt;br/&amp;gt;"&lt;/span&gt;).Replace(&lt;span class="str"&gt;"\n"&lt;/span&gt;, &lt;span class="str"&gt;""&lt;/span&gt;).Replace(&lt;span class="str"&gt;"  "&lt;/span&gt;, &lt;span class="str"&gt;"&amp;amp;nbsp;&amp;amp;nbsp;"&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;        }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt; &lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  98:  &lt;/span&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; RenderLineHtml(DifferenceType differenceType, &lt;span class="kwrd"&gt;int&lt;/span&gt; lineNumber, &lt;span class="kwrd"&gt;string&lt;/span&gt; lineText)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  99:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 100:  &lt;/span&gt;            &lt;span class="rem"&gt;//the line will be styled according to the type of change that was made&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 101:  &lt;/span&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;"&amp;lt;div class=\"{0}\"&amp;gt;{1}: {2}&amp;lt;/div&amp;gt;"&lt;/span&gt;, differenceType.ToString().ToLower(), lineNumber, lineText);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 102:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;/div&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;The view:&lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre&gt;&lt;div class="csharpcode"&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (CommentViewData comment &lt;span class="kwrd"&gt;in&lt;/span&gt; Model.Comments)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="comment_body"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        &lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;= comment.Text &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editingArea"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="originalCode"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;            Original:&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;            &lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;= comment.RenderedOriginal &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editedCode"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;            Edited:&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;            &lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;= comment.RenderedRevised &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;="height: 500px"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;&lt;pre&gt; &lt;/pre&gt;&lt;pre&gt;The CSS:&lt;/pre&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.originalCode { float: left; border: thin solid black; padding: 10px 10px 10px 10px; width: 450px; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.editedCode { float: right; border: thin solid black; padding: 10px 10px 10px 10px; position: absolute; left: 600px; width: 450px; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.editingArea { } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.addition { background-color: Black; color: green } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.removal { background-color: Black; color: red } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.change { background-color: Black; color: white } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.comment_body { font-style: italic; font-weight: bold; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.comment_header { font-weight: bold; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#Title { width: 700px; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#Description { width: 700px; height: 150px; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#Code { width: 700px; height: 300px; } &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is going to need some work before it's ready for prime time (the HTML conversion and CSS need work, and the controller method should be broken out into smaller methods, perhaps using a command pattern or something, etc.), but I figured it was enough to get you going if you're thinking about implementing something like this yourself.  (This approach could make for some very nice build notification emails, IMO.)  Did I leave anything important out?  Is there a better or easier way to do this?  Is there a glaring bug?  Leave a comment!&lt;/p&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:b54fe025-6f69-434c-9ade-d1b707ef7d50" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/.NET" rel="tag"&gt;.NET&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/asp.net" rel="tag"&gt;asp.net&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/mvc" rel="tag"&gt;mvc&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/algorithms" rel="tag"&gt;algorithms&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/diff" rel="tag"&gt;diff&lt;/a&gt;&lt;/div&gt;&lt;img src="http://patrickgannon-net.access.secure-ssl-servers.biz/aggbug/10.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Pat Gannon</dc:creator>
            <guid>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/08/rendering-a-diff-in-a-web-page.aspx</guid>
            <pubDate>Wed, 08 Apr 2009 07:05:06 GMT</pubDate>
            <wfw:comment>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/10.aspx</wfw:comment>
            <comments>http://patrickgannon-net.access.secure-ssl-servers.biz/archive/2009/04/08/rendering-a-diff-in-a-web-page.aspx#feedback</comments>
            <wfw:commentRss>http://patrickgannon-net.access.secure-ssl-servers.biz/comments/commentRss/10.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>