-
Notifications
You must be signed in to change notification settings - Fork 688
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Renamed project files and assembly output names. * Imported ExampleAttribute.cs from StoryTeller project and removed the reference to it, as this was the only thing used from it. * Converted dependency on ShadeTree.Core to an assembly reference and added that to the tools. * Added first attempt at a logo git-svn-id: https://fluent-nhibernate.googlecode.com/svn/trunk@2 48f0ce17-cc52-0410-af8c-857c09b6549b
- Loading branch information
Showing
139 changed files
with
103,328 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
|
||
//------------------------------------------------------------------------------ | ||
// <auto-generated> | ||
// This code was generated by a tool. | ||
// Runtime Version:2.0.50727.1433 | ||
// | ||
// Changes to this file may cause incorrect behavior and will be lost if | ||
// the code is regenerated. | ||
// </auto-generated> | ||
//------------------------------------------------------------------------------ | ||
|
||
[assembly: AssemblyVersionAttribute("0.1.0.0")] | ||
[assembly: AssemblyCopyrightAttribute("Released by Jeremy D. Miller under the Apache 2.0 License")] | ||
[assembly: AssemblyProductAttribute("FluentNHibernate")] | ||
[assembly: AssemblyCompanyAttribute("FluentNHibernate")] | ||
[assembly: AssemblyConfigurationAttribute("debug")] | ||
[assembly: AssemblyInformationalVersionAttribute("0.1.0.0")] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using NUnit.Framework; | ||
|
||
namespace ShadeTree.Testing | ||
{ | ||
[TestFixture] | ||
public class Debugging | ||
{ | ||
[Test] | ||
public void Break() | ||
{ | ||
//Assert.Fail("Make it break"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> | ||
<html> | ||
<head> | ||
<title></title> | ||
</head> | ||
<body> | ||
<p>Yesterday, | ||
<a href="http://codebetter.com/blogs/david_laribee/archive/2008/06/17/test-your-nhibernate-mappings.aspx">David Laribee related some problems</a> he experienced with refactorings in his | ||
domain model leading to some breaking problems with NHibernate mappings. | ||
Specifically, the issues are:</p> | ||
<ol> | ||
<li>Changing the property names of a domain model can break the NHibernate mapping</li> | ||
<li>Changing the database fields can break the NHibernate mappings</li> | ||
</ol> | ||
<p> | ||
David went on to bemoan the absence of a quick way to validate NHibernate | ||
mappings. Ayende popped in with the suggestion that the presence of | ||
integrated tests around the NHibernate usage would spot mapping problems. | ||
Other folks mentioned that there's a new ReSharper plugin to validate and | ||
refactor NHibernate mappings. I'll circle back to the refactoring plugin | ||
in a while. First I want to talk about quick ways to validate NHibernate | ||
mappings. Ayende is right of course about the integrated tests against the | ||
individual queries, but I'm going to suggest that that isn't the most efficient | ||
answer. The bigger integration tests will tell you that something is | ||
wrong, but from experience they'll be harder to diagnose because there is so | ||
much more going on than simple property checking, and they provide a slow | ||
feedback cycle because of how much stuff is going on. What would be nice | ||
is a way to walk right up to a mapping and specify which properties on a class | ||
are supposed to be persisted and how.</p> | ||
<p> | ||
I thought I would come out of my blogging retirement and show an example of the | ||
<a href="http://www.lostechies.com/blogs/chad_myers/archive/2008/06/02/a-new-start.aspx">Chad and Jeremy</a> | ||
approach to testing NHibernate mappings:</p> | ||
<pre name="code" class="c-sharp"> | ||
|
||
[SetUp] | ||
public void SetUp() | ||
{ | ||
// Ensure that the StructureMap configuration is bootstrapped | ||
// In our case, this includes everything we need setup to | ||
// execute NHibernate (mappings + ISessionFactory configuration) | ||
// This will be pretty application specific here | ||
Bootstrapper.RestartStructureMap(); | ||
} | ||
|
||
[Test] | ||
public void SaveAndLoadCustomerContact() | ||
{ | ||
new PersistenceSpecification<CustomerContact>() | ||
.CheckProperty(x => x.Name, "Frank") | ||
.CheckProperty(x => x.Email, "Email") | ||
.CheckProperty(x => x.Extension, 123) | ||
.CheckProperty(x => x.FaxNumber, "111-111-1111") | ||
.CheckProperty(x => x.TelephoneNumber, "222-222-2222") | ||
.CheckProperty(x => x.Title, "Mr.") | ||
.VerifyTheMappings(); | ||
} | ||
</pre> | ||
<p> | ||
All this test does is ensure that the 6 properties of the CustomerContact class | ||
(Name, Email, Extension, FaxNumber, TelephoneNumber, Title) are mapped in | ||
NHibernate. We have some other methods for checking to many and many to | ||
one type relationships. </p> | ||
<p> | ||
Behind the scenes the PersistenceSpecification<T> class:</p> | ||
<ol> | ||
<li> Creates a new instance of T</li> | ||
<li>Uses the lambda expressions and suggested values in the calls to CheckProperty | ||
to load values into the new instance of T</li> | ||
<li>Grabs our IRepository out of StructureMap (of course), and saves the object</li> | ||
<li>Grabs a second IRepository out of StructureMap</li> | ||
<li>Fetches a second copy of the same T out of the second IRepository</li> | ||
<li>Verifies that all of the specified properties in the specification were saved | ||
and loaded. If any single property does not match between the first T and | ||
the second T, the test will fail.</li> | ||
</ol> | ||
<p>Here's the implementation of the PersistenceSpecification.VerifyTheMappings() method:</p> | ||
<pre name="code" class="c-sharp"> | ||
public void VerifyTheMappings() | ||
{ | ||
// Create the initial copy | ||
var first = new T(); | ||
|
||
// Set the "suggested" properties, including references | ||
// to other entities and possibly collections | ||
_allProperties.ForEach(p => p.SetValue(first)); | ||
|
||
// Save the first copy | ||
_repository.Save(first); | ||
|
||
// Get a completely different IRepository | ||
var secondRepository = createRepository(); | ||
|
||
// "Find" the same entity from the second IRepository | ||
var second = secondRepository.Find<T>(first.Id); | ||
|
||
// Validate that each specified property and value | ||
// made the round trip | ||
// It's a bit naive right now because it fails on the first failure | ||
_allProperties.ForEach(p => p.CheckValue(second)); | ||
} | ||
</pre> | ||
|
||
<p>The advantage of this testing is that it gives a (relatively) fast feedback cycle | ||
focused specifically on the mappings. Tools that check the hbm.xml | ||
mappings can only verify that what's there is correctly formed. The | ||
mapping tests above will catch missing mappings and desired behavior. At a | ||
bare minimum, you really should have at least one smoke test in your CI build | ||
that simply tries to create an NHibernate ISession object from your | ||
configuration. Let that test run and possibly fail before any integrated | ||
tests run.</p> | ||
<p>Now, the ReSharper plugin for NHibernate sounds pretty cool. I definitely | ||
want little or no friction in renaming or adding properties in my Domain Model | ||
classes (why I absolutely despise codegen your business object solutions). | ||
We beat the refactoring problem by eliminating HBM.XML. As part of my New | ||
Year's resolution to eliminate my exposure to angle bracket hell, we've created | ||
the beginning of a Fluent Interface API to express NHibernate mappings. | ||
Using copious amounts of Generics (I guess .Net code just "wants" to have lots | ||
of angle brackets) and lambda expressions, we're able to express NHibernate | ||
mappings in a completely compiler-checked, ReSharper-able way. Since we | ||
switched to the FI, we've experienced far less trouble with mapping problems. | ||
Here's a couple of examples:</p> | ||
|
||
<pre name="code" class="c-sharp"> | ||
// Simple class with properties and a single "to-many" relationship | ||
public class CustomerContactMap : ClassMap<CustomerContact> | ||
{ | ||
public CustomerContactMap() | ||
{ | ||
Map(x => x.Name); | ||
Map(x => x.Email); | ||
Map(x => x.Extension); | ||
Map(x => x.FaxNumber); | ||
Map(x => x.TelephoneNumber); | ||
Map(x => x.Title); | ||
References(x => x.Customer); | ||
} | ||
} | ||
|
||
// Class with a "Component" | ||
public class CustomerDeliveryAddressMap : ClassMap<CustomerDeliveryAddress> | ||
{ | ||
public CustomerDeliveryAddressMap() | ||
{ | ||
Map(x => x.Name); | ||
References(x => x.Customer); | ||
Component<Address>(x => x.Address, m => | ||
{ | ||
m.Map(x => x.AddressLine1); | ||
m.Map(x => x.AddressLine2); | ||
m.Map(x => x.AddressLine3); | ||
m.Map(x => x.CityName); | ||
m.Map(x => x.CountryName); | ||
m.References(x => x.State); | ||
m.References(x => x.PostalCode); | ||
}); | ||
} | ||
} | ||
|
||
// Class with some "has many" relationships | ||
public class CustomerMap : ClassMap<Customer> | ||
{ | ||
public CustomerMap() | ||
{ | ||
HasMany<CustomerContact>(x => x.Contacts).CascadeAll(); | ||
HasMany<CustomerJob>(x => x.Jobs).CascadeAll(); | ||
HasMany<CustomerDeliveryAddress>(x => x.DeliveryAddresses).CascadeAll(); | ||
|
||
Map(x => x.Name); | ||
Map(x => x.LookupName); | ||
Map(x => x.IsGeneric); | ||
Map(x => x.RequiresPurchaseOrder); | ||
Map(x => x.Retired); | ||
} | ||
} | ||
|
||
|
||
</pre> | ||
|
||
<p>With this approach, and backed up with the little PersistenceSpecification tests, | ||
we can happily change class names and property names with relative confidence. | ||
Besides, the simple usage of Intellisense plus compiler safe code cuts down on | ||
the number of mapping errors. We're more or less greenfield at the moment, so we can get away with generating | ||
the database from our NHibernate mappings on demand, but you can specify | ||
specific table and column names in the language above for brownfield scenarios. | ||
I'd very confidently say that we're faster with this approach than we would be | ||
with HBM.XML.</p> | ||
<p>If you're interested, the complete code for everything I talked about is in the ShadeTree.DomainModel project in the StoryTeller codebase (and effectively released under the Apache 2.0 license). The code is at | ||
<a href="http://storyteller.tigris.org/svn/storyteller/trunk">http://storyteller.tigris.org/svn/storyteller/trunk/</a>. Use the src\ShadeTree.sln for this stuff. I don't know that we'll ever get around to a fully supported release of this stuff, but I wanted to throw out the idea anyway. At this point I'm only extending this code when we need something new for our current project. | ||
A lot of the advantages of this approach are tied to application specific | ||
conventions and also by tieing the forward generation of the database structure | ||
to validation rules.</p> | ||
<p> </p> | ||
|
||
<h4>As for IoC container testing... </h4> | ||
<p>I'll overlook the fact that my friend David implicitly implied that an | ||
IoC container not named StructureMap was the de facto standard. | ||
<a href="http://weblogs.asp.net/bsimser/archive/2008/06/04/the-first-spec-you-should-write-when-using-castle.aspx">Bil Simser posted a little snippet of code to smoke test the configuration</a> of one of those other IoC containers. StructureMap has had quite a bit | ||
of diagnostic support since version 0.85, but StructureMap 2.5 will add the | ||
ability to do this:</p> | ||
<pre name="code" class="c-sharp"> | ||
[SetUp] | ||
public void SmokeTestStructureMapConfiguration() | ||
{ | ||
ObjectFactory.AssertConfigurationIsValid(); | ||
} | ||
</pre> | ||
<p>This will attempt to build every possible configured instance in StructureMap, perform any designated | ||
<a href="http://codebetter.com/blogs/jeremy.miller/archive/2006/04/06/142431.aspx">environment tests</a> (like trying to connect to a database), and generate a complete report of all errors encountered by StructureMap. | ||
If you're aggressive about putting all services into your IoC container, this | ||
test can help measure the correctness of an installation.</p> | ||
</body> | ||
</html> |
Oops, something went wrong.