Browsing Posts in .NET General

A great new feature of Visual Studio 2008 is multi-targeting, which allows VS 2008 to compile for .NET 2.0, 3.0, or 3.5 simply by changing a project property.

ApplicationDesignerWindowPaneControl

You might be thinking, now I don’t have to keep Visual Studio 2005 and 2008 installed. I can just use Visual Studio 2008 for all my projects! Well, yes, but with one big proviso. Code targeting .NET 2.0 and written in VS2008 may only compile in VS2008! The reality is that multi-targeting changes Intellisense, the project templates, and the assemblies that you’re offered, but your code is still compiled using the C# 3.0 or VB9 compilers regardless of which .NET Framework version you target. Compiling a project targeting .NET 2.0 using VS2008 results in this output:

—— Build started: Project: Multitargetting, Configuration: Debug Any CPU ——
C:\Windows\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Multitargetting.exe /target:exe Program.cs Properties\AssemblyInfo.cs

Compile complete — 0 errors, 0 warnings
Multitargetting -> c:\dev\Examples\Multitargetting\Multitargetting\bin\Debug\Multitargetting.exe
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

Note the path to csc.exe – C:\Windows\Microsoft.NET\Framework\v3.5. We’re using the C# 3.0 compiler that ships with .NET 3.5. (Don’t get me started about the renaming of WinFX to .NET 3.0 again. My friends at Microsoft already know how I feel about that one.) Generally this doesn’t matter because .NET 3.0 and .NET 3.5 are additive libraries to .NET 2.0. .NET 2.0, 3.0, and 3.5 all run on the CLR that shipped with .NET 2.0. (I originally read about this in Dustin Campbell’s C#2.5 post.)

So how do we get all the crazy goodness of lambda expressions, anonymous types, LINQ, extension methods, and more? In .NET 3.0, WCF, WPF, WF, and CardSpace were all just additive libraries on top of .NET 2.0. In .NET 3.5, the new features can be divided into two categories – additive libraries and compiler enhancements.

Feature Library Compiler
implicitly-typed locals (var)   X
lambda expressions   X
automatic properties   X
anonymous types   X
object and collection initializers   X
LINQ X X
Expression Trees X X
Extension Methods X* X

* See below

Any feature not requiring library support is just syntactic sugar provided by our compilers. If you look under the covers using Lutz Roeder’s Reflector, you’ll see that the compilers are generating the same old CLR 2.0-compatible MSIL as they always were.

Implicitly-Typed Locals

Let’s take a look at implicitly-typed locals using the “var” keyword:

var foo = "Hello, World!";
 
Reflector says! (Harkening back to Family Feud and Richard Dawson.)
 
string foo = "Hello, World!";
 
The compiler was able to infer the type of “foo” based on its usage. No magic MSIL instruction to infer type. It’s all in the compilers.

Lambdas

Let’s take a look at our new swanky lambda expressions:
 
Action<string> display = msg => Console.WriteLine(msg);
 
Reflector says!
 
Action<string> display = delegate(string msg) { Console.WriteLine(msg); }
 
Once again, it’s just compiler magic turning the terse lambda expression (that funky “params => body” syntax) into the anonymous delegate syntax that we’ve known and loved since .NET 2.0. (To be honest, that’s not quite what Reflector says. The C# compiler actually caches the anonymous delegate in a static field for performance reasons, but the code above is close enough for purposes of this discussion.)

Automatic Properties

What about automatic properties?

public string Hello { get; set; }

Reflector says!

[CompilerGenerated]
private string <Hello>k__BackingField;
[CompilerGenerated]
public string get_Hello()
{
    return this.<Hello>k__BackingField;
}
[CompilerGenerated]
public void set_Hello(string value)
{
    this.<Hello>k__BackingField = value;
}
Once again, more compiler magic.

Anonymous Types

var position = new { Lat=42, Long=42 };

Reflector says!

[CompilerGenerated, DebuggerDisplay(@"\{ Lat = {Lat}, Long = {Long} }", Type="<Anonymous Type>")]
internal sealed class <>f__AnonymousType0<<Lat>j__TPar, <Long>j__TPar> {
  // Compiler generated goo omitted for clarity
}

Compiler magic.

Object/Collection Initializers

Object and collection initializers allow us to create and initialize objects/collections in a single line. They only require that the object have a parameterless constructor.

Program program = new Program { Hello="World" };
 
Reflector says!
Program <>g__initLocal0 = new Program();
<>g__initLocal0.Hello = "World";
Program program = <>g__initLocal0;
 
The C# 3.0 compiler is changing our object initializer into a plain old object instantiation via the parameterless constructor followed by a bunch of property sets. Nothing else to see here. Move it along…

LINQ, Expression Trees, and Extension Methods

Each of these features requires library support from System.Core, a new assembly that ships with .NET 3.5. Without System.Core installed in the GAC, your code isn’t going to run. So you can’t use these features without .NET 3.5 installed on the client.

The one exception is extension methods. If you try to compile an extension method while targeting .NET 2.0, you will receive the following compile error:

Cannot define a new extension method because the compiler required type ‘System.Runtime.CompilerServices.ExtensionAttribute’ cannot be found. Are you missing a reference to System.Core.dll?

As noted by Jared Parsons here, you can simply define the attribute yourself:

namespace System.Runtime.CompilerServices {
    [AttributeUsage(AttributeTargets.Method)]
    public class ExtensionAttribute : Attribute {
    }
}

Extension methods now compile while targeting .NET 2.0.

N.B. If you target .NET 3.5 as well as .NET 2.0 with this code, you’ll end up with a duplicate definition warning under .NET 3.5 as noted by Dustin Campbell here. If you’re really going to use this trick, you should probably wrap the class in some conditional directives that omit the definition when compiled under .NET 3.5.

Multi-targeting Proviso

What about that proviso I mentioned at the beginning of this post? If everyone on your project is using VS 2008 and targeting .NET 2.0, then all is good. You can use all the new syntactic sugary goodness that the C# 3.0 compiler (and VB9 compiler – if you swing that way) provides for you. If some team members are still using VS 2005, you need to be careful to not introduce C# 3.0 language constructs as VS2008 will not provide feedback that you are using them even if you target .NET 2.0. The new constructs are sufficiently different enough that it’s unlikely that you’ll mistakenly use a lambda expression or automatic property, but it’s something to keep in mind. Besides your CruiseControl.NET server is using a NAnt script that compiles with the C# 2.0 compiler to keep everyone honest, right? If not, this is yet another reason you should set up a CI server for your project today…

A few weeks ago I was merrily working away on an application that uses NHibernate to query data from an Oracle database. I didn’t feel like installing a local Oracle instance as typically I do more work against SQL Server. So I develop locally against a SQL Server database and switch to Oracle by changing a few NHibernate configuration settings. Very nice. I deploy the application into the test environment and it fails with:

ORA-12571: TNS:packet writer failure

Examining the stack trace, the error was happening in the guts of the Oracle driver. Looking up the documentation, this error typically indicates a faulty network cable or similar hardware cause. So I try another query. This time it works. I try my queries one at a time. Only one query is failing. What are the chances that the network cable only comes loose when executing a certain query? Not terribly likely, IMHO.

So I reproduced the error in a simple application and started pulling it apart. I’ll save you the blow-by-blow, but suffice it to say, the only difference between the working queries and broken query was the query parameters. (Also note that the identical code worked fine against SQL Server.) Working queries had integer parameters. The broken query had a string parameter. As soon as I realized this, I made an educated guess that it was a Unicode/ASCII conversion problem. I modified my mapping file to include the datatype in the property definition:

<property name=”InvoiceNumber” column=”invoice_num” type=”AnsiString” not-null=”true”/>

Now both Oracle and SQL Server were happy to execute the query. (N.B. Usually NHibernate can properly infer the datatype based on the property type on the class. So it is not required.)

Come on now, Oracle! Loose network cable??? Not “unable to convert Unicode to ASCII” or “unexpected datatype” or something vaguely useful. Loose network cable??? Hardware failure??? Oracle has a long way to go in improving their error messages. My database of choice is still SQL Server because, most of the time, it just works.

In a domain model, you will sometimes encounter immutable data - objects that can only be read from a database, but never modified. The question is how to test your persistence layer to ensure that you can read them. One way is to hard code values into your tests and assume that the appropriate rows are in the database. This is rather fragile as you are counting on your database being in a known good state. This also prevents you (or another developer) from getting latest from source control onto a clean developer workstation and being able to run the unit tests immediately. You must populate a test database with known good data first. Here’s a little trick you can play with NHibernate. Let’s say you have the following class:

namespace JamesKovacs.Examples.ImmutableData {
    public class Invoice {
        private readonly int m_Id = 0;
        private readonly string m_InvoiceNumber;
        private readonly decimal m_Amount;
        private readonly DateTime m_InvoiceDate;

        public Invoice() {
        }

        public Invoice(string invoiceNumber, decimal amount, DateTime invoiceDate) {
            m_Amount = amount;
            m_InvoiceDate = invoiceDate;
            m_InvoiceNumber = invoiceNumber;
        }

        public int Id {
            get { return m_Id; }
        }

        public string InvoiceNumber {
            get { return m_InvoiceNumber; }
        }

        public DateTime InvoiceDate {
            get { return m_InvoiceDate; }
        }

        public decimal Amount {
            get { return m_Amount; }
        }
    }
}

And its corresponding NHibernate mapping file:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="JamesKovacs.Examples.ImmutableData"
                   assembly="JamesKovacs.Examples.ImmutableData"
                   default-access="field.pascalcase-m-underscore">
  <class name="Invoice" mutable="false">
    <id name="Id">
      <generator class="native" />
    </id>
    <property name="InvoiceNumber"/>
    <property name="Amount"/>
    <property name="InvoiceDate"/>
  </class>
</hibernate-mapping>

Note that the Invoice is lookup data only. So it is declared as immutable in the mapping file (mutable=”false”). NHibernate will not permit INSERT, UPDATE, or DELETE operations against the entity. The question is how do we test this? We could circumvent NHibernate and directly insert test data into the table via ADO.NET. The downside is that you’re coupling your test data generation code to a particular database schema. If the database schema changes, you must not only update the NHibernate mapping, but you must also modify your SQL statements for creating test data. A definite violation of DRY (Don’t Repeat Yourself) Principle. Let’s see if there is a better way…

NHibernate can generate the database schema from mapping files using NHibernate.Tool.hbm2ddl.SchemaExport. The problem is that you can’t insert data into the Invoice table since the type is mapped as immutable. However, you can read the NHibernate configuration and mapping files and modify the mapping at runtime. This is done by cfg.GetClassMapping(typeof(Invoice)).IsMutable = true. We’re switching our immutable entity to a mutable one on the fly for the purposes of populating test data! Note that you have to modify the mapping before creating your SessionFactory.

using System;
using JamesKovacs.Examples.ImmutableData;
using MbUnit.Framework;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace JamesKovacs.Examples.ImmutableData.Tests {
    [TestFixture]
    public class InvoiceRepositoryTests {
        private const string InvoiceNumber = "ABCD1234";
        // N.B. You should do this once before all your repository tests
        //      as creating a SessionFactory and executing DDL is time-consuming.
       [SetUp]
        public void Setup() {
            Configuration cfg = new Configuration().Configure();
            SchemaExport exporter = new SchemaExport(cfg);
            exporter.Execute(false, true, false, false);
            cfg.GetClassMapping(typeof(Invoice)).IsMutable = true;
            ISessionFactory sessionFactory = cfg.BuildSessionFactory();
            using(ISession session = sessionFactory.OpenSession()) {
                session.BeginTransaction();
                Invoice invoice = new Invoice(InvoiceNumber, 42m, DateTime.Today);
                session.Save(invoice);
                session.Transaction.Commit();
            }
        }

        [Test]
        public void CanRetrieveInvoice() {
            using(UnitOfWork.Start()) {
                IInvoiceRepository repo = new InvoiceRepository();
                Invoice invoice = repo.FindByInvoiceNumber(InvoiceNumber);
                Assert.IsNotNull(invoice);
                Assert.AreEqual(InvoiceNumber, invoice.InvoiceNumber);
            }
        }
    }
}

So we now have an elegant solution for populating immutable test data in a clean database without resorting to hand-coded SQL.

N.B. UnitOfWork.Start() sets up an NHibernate session that is used internally by the repositories. It is based on Oren Eini’s UnitOfWork implementation in Rhino Commons.

Here are the code samples from some recent presentations…

Alberta Architect Forum 2007 – Creating Flexible Software

Austin .NET User Group – The NHibernate Mafia and the Persistent Ignorant Domain Model

Edmonton Code Camp 2007 – Building Applications Using ALT.NET

Thanks to everyone for attending the sessions, asking questions, and providing feedback.

The Virtual PC download of Visual Studio 2008 “Orcas” has been great. No install. No chance of corrupting your current Visual Studio 2005 install. It just works and allows you to play with the early bits in a virtual environment. That’s one of the things that virtualized environments excel at. The Virtual PC images were timebombed to prevent their use in perpetuity. Now for the bad news… Microsoft originally announced that the timebomb date was March 15, 2008. In actual fact, it is November 1, 2007. After that, you’ve got a few options as detailed on Jeff Beehler’s blog post:

  • The VPC will reboot every 2 hours due to the Windows license being expired. Move your data somewhere else or make sure your demos are less than 2 hours.
  • Upgrade the Windows Server 2003 guest OS to use a non-trial license, such as from a MSDN Subscription. (Details in Jeff’s article above.)

None of these situations is ideal, especially for those without MSDN Subscriptions. Hopefully we’ll get the next Visual Studio 2008 beta in the near future.

Did you know that you can get Visual Studio Intellisense support when editing NHibernate’s configuration and mapping files?* Simply drop nhibernate-configuration.xsd and nhibernate-mapping.xsd from the NHibernate zip file into:

C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas

or

C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas

Once you’ve got the XML Schemas in the right location, you should see something like this when editing NHibernate configuration and mapping files…

NHibernateIntellisense

(The little light bulb is ReSharper 3.0 suggesting a Quick Fix to split the empty XML tag into start and end tags.)

* This XML Intellisense trick works for any XML Schema (XSD) file.

N.B. Dropping your XML Schema files in the similarly named C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\schemas\xml does absolutely nothing!

I created a few simple ReSharper templates to make working with NHibernate and Castle ActiveRecord easier. They currently default to SQL Server 2005 because that’s what I do most of my work against. Feel free to customize to your heart’s content.

You can grab them from here. Simply import the templates into ReSharper via ReSharper… Options… Templates… Live (or File) Templates… User Templates… Import Templates from File… You’ll want to add the file templates to your Quick access list.

The first file template is hibernate.cfg.xml. (N.B. ReSharper will try to name it hibernate.cfg1 and add the xml extension. Delete the “1″ and hit enter.) The template will prompt you for the database server, database name, and mapping assembly.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
    <property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="hibernate.connection.connection_string">Data Source=localhost;Initial Catalog=DATABASE;Integrated Security=True</property>
    <property name="hibernate.dialect">NHibernate.Dialect.MsSql2005Dialect</property>
    <property name="hibernate.use_outer_join">true</property>
    <property name="hibernate.show_sql">false</property>
    <property name="hibernate.query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
    <mapping assembly="SimpleConfiguration"/>
  </session-factory>
</hibernate-configuration>

The second file template is <Class>.hbm.xml. You will want to rename it <Class>.hbm as ReSharper will add the xml extension for you. The template will prompt you for the class name and primary key name. You’ll also want to modify the PK generator. You’ll note that default-access is “field.pascalcase-m-underscore”, which means that if you map a property called “Name”, NHibernate will access it by the corresponding field name, “m_Name”. Change your default-access to whichever naming convention you prefer. (See property access and naming strategies in the NHibernate documentation.)

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="SimpleConfiguration"
                   assembly="SimpleConfiguration"
                   default-access="field.pascalcase-m-underscore"
                   default-lazy="false"
                   default-cascade="none" >
  <class name="CLASS">
    <id name="PRIMARYKEY">
      <generator class="assigned" />
    </id>
  </class>
</hibernate-mapping>

Lastly we have the Live template for Castle ActiveRecord configuration. You can insert this into app.config or web.config. Simple position your cursor inside <configuration> and type arconfig<TAB>. If you already have other configuration sections, you’ll have to merge the activerecord section with the others.

<configSections>
    <section name="activerecord"
             type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />
</configSections>
 
<activerecord>
  <config>
    <add key="hibernate.connection.driver_class" 
         value="NHibernate.Driver.SqlClientDriver" />
    <add key="hibernate.dialect"                 
         value="NHibernate.Dialect.MsSql2005Dialect" />
    <add key="hibernate.connection.provider"     
         value="NHibernate.Connection.DriverConnectionProvider" />
    <add key="hibernate.connection.connection_string" 
         value="Data Source=DBSERVER;Initial Catalog=DATABASE;Integrated Security=SSPI" />
  </config>      
</activerecord>

Enjoy!

The ALT.NET Open Space Conference in Austin has come to a close. It was a fantastic experience. I had an opportunity to connect with many people whose books I have read and tools I use. Thanks to everyone who came for sharing their time and knowledge. Given that I’m used to wearing the presenter hat, it was a refreshing change to attend a conference where the purpose was to discuss and question our core tenets. Which brings me to…

What is ALT.NET? This continues to be a hotly debated topic – before, during, and after the conference. My own take… ALT.NET is about considering alternatives, it is not about being alternative. It is about taking the best frameworks and tools from Microsoft, third-parties, and the open source community to build an application that is maintainable. It is not about being anti-Microsoft. Scott Hanselman suggested that Pragmatic.NET might have been a better name. I would tend to agree. At heart, everyone there was a pragmatic developer. (If you have not read The Pragmatic Programmer: From Journeyman to Master by Andrew Hunt and David Thomas, you really should.) It’s about thinking outside the Microsoft MSDN canon when building software.

What I find truly amusing is that, as far as I can tell, Microsoft has not attempted to create a canon. The collective knowledge has been created by regular people like us and people are fallible. Ideas that seem good at first may not stand the test of time. (DataSets/DataAdapters are a great example of an idea that in hindsight isn’t such a great one.) Microsoft is very open to better ways of developing software, as evidenced by the fact that many Microsoft employees attended. What many perceive as hostility to the community is more often disconnection from the community. But the honest truth is that they want to engage with us. I had great conversations with Simon Guest (Architecture Strategy Team), Howard Dierking (Editor in Chief of MSDN Magazine), Scott Guthrie (DevDiv), Peter Provost (P&P Team) Scott Hanselman (newest member of the Borg), and many other ‘softies about the values of the ALT.NET community and how Microsoft technologies support or hinder the development of maintainable software. (My intended purpose isn’t name dropping, but to show you, dear reader, that it’s the movers-and-shakers in Microsoft who are interested in providing a better development experience. These are the people who shape the platform and its documentation.)

Some people, who didn’t attend, have claimed that the conference was cliquey; that it was a venue for like-minded developers to say, “I do TDD.” and another developer to say, “Hey, cool. I do TDD too. You’re awesome.” On the contrary, the purpose was to foster better understanding of new techniques, such as BDD, specifications, domain-specific languages, FIT, etc. The conversations all started with a mutual understanding of what we were trying to achieve – more maintainable software supported by appropriate test artifacts.

For example, there was a lot of discussion around specification languages (like NBehave) were valuable compared with a written story on an index card. A written story is approachable by a non-technical business stakeholder and can elicit a discussion that might not otherwise happen. A specification written in C# (or Ruby) sitting in a source control system is not approachable by the non-technical business stakeholder. The answer is it depends on the individuals involved in the project. Each technique causes friction and your choice depends on which type of friction results in less project risk.

Another topic that has been widely reported is Scott Guthrie’s first public demonstration of the new ASP.NET MVC Framework. There were some pleasant surprises, including:

  • Microsoft’s commitment to testability. Controllers interact with the ASP.NET pipeline through IHttpRequest/Response rather than HttpRequest/Response. That little “I” allows us to mock out the dependency on the HTTP pipeline and run controller tests without all the ASP.NET hosting infrastructure. (ScottGu is looking to port those interfaces back into ASP.NET WebForms too.)
  • Microsoft will provide easy integration with a variety of other frameworks. For instance, if you want to mock a dependency, you can use Rhino Mocks, NMock, or TypeMock. If you want to use an inversion of control (IoC) container, Microsoft will allow you to bolt in Castle Windsor, Spring.NET, StructureMap, or ObjectBuilder. The team is even planning on providing templates for these open source frameworks. Microsoft is working with the community rather than trying to compete against it.
  • ScottGu’s team is not trying to wedge MVC into the existing WebForms model. If you use MVC, you are opting out of viewstate, postbacks, and the dreaded ASP.NET Page lifecycle. This is a brave move to make. WebForms will still work as it always has, but if you choose MVC, you will use a new set of controls specifically designed for the MVC model.
  • The bi-directional routing engine looks awesome. It allows for very flexible and human-readable URLs. The use of lambdas to create URLs was very cool.
  • The default view engine is ASP.NET. So you get all the design-time, CSS, and Intellisense support, but without the complex ASP.NET Page lifecycle, as noted above. You can easily create typed view data to communicate between your controller and view. (In MonoRail, the controller and view communicate via an untyped property bag. So you don’t get Intellisense and refactoring support.)

Overall, the design of ASP.NET MVC looks a lot like Castle MonoRail and has aspects of Ruby on Rails and Django sprinkled in. I’m looking forward to watching this technology develop. Jeremy Miller echoes my sentiments when he says, “I’m more excited about an upcoming Microsoft technology than anything else since moving from VB6 to .Net.” If you know Jeremy, he’s a hard developer to impress. So that says a lot!

There was so much more. I’ll try to put my thoughts around other discussions in the coming days as I mentally digest everything that was discussed. I’ll leave you with some personal highlights:

  • I enjoyed finally meeting Jamie Cansdale (TestDriven.NET) and hearing that he and Microsoft had come to an amicable agreement over TestDriven.NET.
  • It was great to meet Jay Flowers and thank him for all his great work on CIFactory. I even convinced him to implement an option in CIFactory to flip from using the command-line, CCNet.exe, to the Windows Service, CCNetService.exe.
  • Rock climbing Saturday night with Scott Hanselman, Russell Ball, and others was a blast. I didn’t feel like “a monkey with fly paper gloves”, but it was still a lot of fun. Maybe I’ll take it up as a hobby… 
  • Talking to Martin Fowler over dinner about the challenges in parallel computing. He said, “Concurrency is a lot like a white fluffy bunny…” at which point he leapt up and down wildly yelling, “with fangs and a mean streak a mile wide!!!” (Fans of Monty Python’s The Holy Grail will understand the reference. If you don’t, go rent the movie right now.)

Scott Guthrie has announced that Microsoft will be releasing the source code and debug symbols (aka PDB files) for the .NET Framework assemblies when they release .NET Framework 3.5 later this year.* Initially this will include the .NET Base Class Libraries (System.IO, System.Collections, etc.), ASP.NET (System.Web), Windows Forms (System.Windows.Forms), ADO.NET (System.Data), XML (System.Xml), and WPF (System.Windows). Additional source code will be released in the coming months for WCF, WF, LINQ, and others. This is fantastic. Although Lutz Roeder’s Reflector allows you to view implementation, it is not produce full-fidelity source code as it has no way to infer comments, local variable names, or other details that get washed out by the compiler. Now we can finally debug directly into Microsoft’s code as well as our own when troubleshooting. Kudos to Scott and his entire team for taking this step to make the .NET platform even better.

* From what I can see, the release of source code and debug symbols is only for .NET Framework 3.5 and not for previous versions. This is still a huge step forward. Publisher web log

Since I’ll be in Austin for the ALT.NET Open Space Conference early next month, my friend, Jeffrey Palermo, asked me to give a talk at the Austin .NET User Group while I was in town. So without further ado…

How does NHibernate enable a persistence ignorant domain model? Why would you want a persistence ignorant domain model? Why does the NHibernate Mafia feel so strongly about persistence ignorant domain models? And who are the NHibernate Mafia anyway? This session will answer all these questions and more.

Date/time: Monday, October 8, 2007 starting at 5:30pm
Location: Microsoft MTC