Version 1.0 Beta
Billy McCafferty
S#arp Architecture
A platform for developing domain driven projects using
ASP.NET MVC best practices with NHibernate
Version 1.0 Beta (rev 337)
Introduction to S#arp Architecture ..................................................................................... 4
What is S#arp Architecture? ........................................................................................... 4
Acknowledgements ......................................................................................................... 4
Preparing the Development Environment........................................................................... 5
Coding & Naming Conventions...................................................................................... 5
Installing and Configuring Prerequisites ........................................................................ 5
Configuring IIS 7 for ASP.NET MVC ........................................................................... 6
Configuring IIS 6 for ASP.NET MVC ........................................................................... 6
Visual Studio Templates and Code Generation .................................................................. 6
S#arp Architecture Project Template for Visual Studio 2008 ........................................ 6
CRUD Scaffolding .......................................................................................................... 8
Developing Custom Scaffolding ................................................................................... 10
Examining the Northwind Example Project and S#arp Architecture Class Libraries ...... 11
Setting up the Northwind Example Project .................................................................. 11
Examining the Project Tiers.......................................................................................... 12
Northwind.Core: The Domain Layer and the “M” in MVC .................................... 14
Northwind.Web.Controllers: The “C” in MVC ....................................................... 14
Northwind.Data: The Data Layer ............................................................................ 16
Northwind.Tests: The Unit Testing Layer ............................................................... 16
Northwind.TestsUsingDevelopmentDatabase .......................................................... 17
Northwind.Web: The “V” in MVC .......................................................................... 17
Examining the SharpArch Class Libraries .................................................................... 17
SharpArch.Core ........................................................................................................ 17
SharpArch.Core.NHibernateValidator ...................................................................... 18
SharpArch.Data ......................................................................................................... 19
SharpArch.Testing .................................................................................................... 19
SharpArch.Testing.NUnit ......................................................................................... 19
SharpArch.Web ......................................................................................................... 20
Tutorial for Developing with S#arp Architecture ......................................................... 20
Test Driven Development Cycle ............................................................................... 20
Developing the Staff Member Domain Model ......................................................... 21
Enabling the Domain Object for Comparison .......................................................... 23
Developing the Controller to Retrieve and Display Results ..................................... 25
Retrieving Results via a Custom DAO ..................................................................... 30
Showing the Results in a View ................................................................................. 38
So What Was Accomplished? ................................................................................... 40
A Few S#arp Architecture Best Practices ......................................................................... 41
Within the Web Layer ................................................................................................... 41
When using ASPX views .......................................................................................... 41
Configuring web.config ............................................................................................ 41
Managing Routes ...................................................................................................... 41
Within the Controllers Layer ........................................................................................ 42
Within the Tests Layer .................................................................................................. 42
Managing Solution Items .............................................................................................. 42
NHibernate .................................................................................................................... 42
Microformats................................................................................................................. 42
Common Development Problems & Exceptions .............................................................. 43
Exception Troubleshooting ........................................................................................... 43
Appendix A: Resources ................................................................................................... 45
Support for S#arp Architecture ..................................................................................... 45
Referenced Works ......................................................................................................... 45
Appendix B: Not So Frequently Asked Questions .......................................................... 46
Introduction to S#arp Architecture
What is S#arp Architecture?
Pronounced "Sharp Architecture," this is a solid
architectural foundation for rapidly building
maintainable web applications leveraging the
ASP.NET MVC framework with NHibernate. The
primary advantage to be sought in using any architectural framework is to decrease the
code one has to write while increasing the quality of the end product. A framework
should enable developers to spend little time on infrastructure details while allowing
them to focus their attentions on the domain and user experience. Accordingly, S#arp
Architecture adheres to the following key principles:
Focused on Domain Driven Design
Loosely Coupled
Preconfigured Infrastructure
Open Ended Presentation
The overall goal of this is to allow developers to worry less about application "plumbing"
and to spend most of their time on adding value for the client by focusing on the business
logic and developing a rich user experience.
Good background material reading includes
http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx. Although
there are major infrastructural changes from the referenced article when compared to the
current S#arp Architecture, the general structure is very similar and the background
reading is very helpful in understanding many of the ideas behind this development
foundation.
Acknowledgements
S#arp Architecture attempts to represent the combined wisdom of many software
development giants. Included patterns and algorithms reflect best practices described by
the GoF, Martin Fowler, Robert “Uncle Bob” Martin, Steve McConnell, many gurus in
the blogosphere and other industry leaders. Many have been personally involved with
helping to shape S#arp Architecture‟s current form including Frank Laub, Kyle “the
coding hillbilly” Baley, Simone Busoli, Luis Abreu (mostly for being an effective pain in
the ass ;), Jay Oliver and Lee Carter…along with many others who have asked WTF at
all the right times. A special thanks to Roy Bradley who was brave crazy enough to
commission me to develop the first version 0.1. Finally, none of this would have been
possible and/or applicable without the tireless efforts of the teams behind NHibernate,
Fluent NHibernate, NHibernate Validator, and the Castle project.
Preparing the Development Environment
Coding & Naming Conventions
Although not having a direct impact on the development environment, I‟d like to convey
a few coding conventions which are used throughout S#arp Architecture, the sample
project, the Visual Studio project template and the CRUD scaffolding generator (which is
completely customizable). The coding style and conventions generally follow the
guidelines described by Brad Abrams at
http://blogs.msdn.com/brada/articles/361363.aspx, especially when it comes to the use of
PascalCasing and camelCasing for naming conventions. E.g., member variables are
camelCase with no leading “_” and methods, properties and classes are PascalCase. The
conventions for braces are described as follows in the VS settings.
Optionally, if you‟d like to set your VS development environment to reflect some of the
coding style within S#arp Architecture code,
1. Within VS 2008, set auto-formatting options appropriately:
a. Go to Tools / Options and check “Show all settings”
b. Expand Text Editor / C# / Formatting / New Lines and uncheck the
following:
Place open brace on new line for methods
…for anonymous methods
…for control blocks
…for anonymous types
…for object initializers
…for lambda expressions
c. Expand Text Editor / HTML:
Under Validation, set target to “XHTML 1.1”
Under Miscellaneous:
1. Uncheck “Auto ID elements on paste in Source view”
2. Check “Format HTML on paste”
Installing and Configuring Prerequisites
1. Install NUnit 2.4.7 for .NET 2.0
2. Install the T4Toolbox from http://www.codeplex.com/t4toolbox. This enables
Visual Studio with templating capabilities. (This is needed for the CRUD
scaffolding generator.)
3. Optionally (but recommended to have ASP.NET MVC item templates in VS)
install ASP.NET MVC Beta (see the ASP.NET MVC Release Notes if you‟re
upgrading from a previous version). This should not be installed on production
environments as only the DLLs need to be deployed. If you need to have two
versions of MVC installed side by side, you can find further direction at
http://dylanbeattie.blogspot.com/2008/11/working-on-aspnet-mvc-beta-and-
preview.html.
4. Optionally, after unzipping the S#arp Architecture zip, copy
“\src\SharpArch\Solution Items\*.xsd” to "C:\Program Files\Microsoft Visual
Studio 9.0\Xml\Schemas" to enable NHibernate intellisense if you need to
create/edit HBMs. (You will probably not be editing HBMs directly; instead,
you‟ll surely want to use Fluent NHibernate.)
Configuring IIS 7 for ASP.NET MVC
The easiest way to configure IIS 7 for the ASP.NET MVC sample project is to modify
IIS 7 to behave in classic mode for the configured virtual directory. To do so, make sure
your virtual directory has been given an application name (assuming it‟s not a root
website), then set the application pool for it to the “classic mode” application pool.
Configuring IIS 6 for ASP.NET MVC
Contract to earlier versions of ASP.NET MVC, it‟s not trivially simple to configure IIS 6
to work nicely with the beta release and later. If you‟re developing on Windows XP, it‟s
easiest to develop and test code via F5 debugging rather than trying to hack IIS 5.1 or 6.0
to run it in an XP development env‟t. If you do need to host your ASP.NET MVC site on
IIS 6.0, Phil Haack has a great tutorial at http://haacked.com/archive/2008/11/26/asp.net-
mvc-on-iis-6-walkthrough.aspx.
Visual Studio Templates and Code Generation
S#arp Architecture Project Template for Visual Studio 2008
If setup manually, there are a number of steps that are necessary to start coding your own
project based on S#arp Archiecture. To assist, a Visual Studio 2008 template has been
included in the downloadable release to greatly simplify this process and get you going
very quickly with everything you need. Accordingly, a ready-to-use Visual Studio
template and wizard has been made available for immediate use. (If you‟d like to see
how this was done, you can review the source under
/src/SharpArch/SharpArch.VsSharpArchTemplate.) To get going, follow these steps to
create your own S#arp Architecture project:
1. Close all instances of Visual Studio 2008
2. If you haven‟t already done so, install the T4Toolbox from
http://www.codeplex.com/t4toolbox. This enables Visual Studio with templating
capabilities. (This is needed for the CRUD scaffolding generator.)
3. Optionally install the T4 Editor Community Edition from
http://www.t4editor.net/downloads.html which will add basic coloring to the
templates from within Visual Studio.
4. Copy “\VisualStudioTemplate
\SharpArchApplicationTemplate.zip” to "C:\Documents and Settings\\My Documents\Visual Studio
2008\Templates\ProjectTemplates\Visual C#\Web\". (Other locations are
available for organizing your templates; see http://msdn.microsoft.com/en-
us/library/y3kkate1.aspx for details.)
5. From the same source folder, copy SharpArchApplicationWizard.dll to
"C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\"
6. You‟re now ready to create your S#arp Architecture project:
a. Open Visual Studio 2008
b. Select File / New / Project
c. Select the project type Visual C# / Web
d. Select "S#arp Architecture Application" under "My Templates"
e. Enter a project name with no spaces; e.g., Northwind, WickedCool, or
OtherProjectName
f. Enter the location that will serve as the parent folder of the project; e.g.,
C:\MyProjects, E:\Dev\AllProjects
g. Keep "Create directory for solution" unchecked (it'll work if this is
selected but will add an extraneous parent folder)
h. Click OK
7. The generated project does not depend on ASP.NET MVC being installed on the
development machine. While this is good for compatibility with multiple
versions of MVC, the drawback is that ASP.NET MVC item templates are not
available within Visual Studio by default. If you have ASP.NET MVC installed
and you‟d like to have the (rather useless) item templates available, do the
following:
a. Using Windows Explorer, browse to the generated project and open
.Web.csproj with Notepad.
b. Insert the following immediately after the opening
tag, and before the other GUIDs: “{603c0e0b-db56-11dc-be95-
000d561079b0};”
c. Save and close the csproj file; you‟ll be prompted to reload it in VS.
8. Create your empty project database, if it doesn‟t already exist, and set the
connection string within .Web/Hibernate.cfg.xml
9. Right click the .Web project and select "Set as StartUp
Project"
10. Click F5 to build and run it! (The generated content is discussed further below
under “Examining the Northwind Example Project.”)
11. Optionally, if you‟d like to include Fluent NHibernate‟s convention-over-
configuration for auto-mapping your classes to the database, follow the steps
found in Appendix B, accordingly.
The generated project has the following directory structure:
/MyProject – Contains MyProject.sln.
/app - Contains the core project layers.
/MyProject.Core
/MyProject.Data
/MyProject.Web
/MyProject.Web.Controllers
/lib - Contains the solution items for the deployable application.
/db - Contains database schema information; e.g., the result of scaffolding and/or
NHibernate's schema export.
/docs - Project documents.
/logs - Output location for log files.
/tests
/MyProject.Tests
/tools
/build - Empty folder for housing build related stuff.
/lib - Contains the solution items for the tests project and all other non-deployable
assemblies.
/CrudScaffolding - Customizable CRUD, scaffolding generation code.
If you use the built in CRUD scaffolding generator, or one supplied by the community,
it‟s important that your directory structure reflect this.
While the generated folder structure makes it convenient to organize project related
resources such as dependencies, database scripts, and docs, it is the solution structure
itself which is of most interest. The details of the solution structure itself is discussed
later while examining the Nothwind example project.
CRUD Scaffolding
CRUD Scaffolding is fully available and can be used within any project generated using
the Visual Studio project template. Note that there is currently an incompatibility with
T4 Toolbox and VisualSVN on the same machine; consequently, you cannot have
VisualSVN installed on your development machine when using the scaffolding
generation. This does not preclude you from having SVN client applications on your
development machine.
Running the scaffolding generates the following resources and moves them to the
appropriate project folder.
\MyProject.Core\.cs: A class inheriting from Entity
having the properties designated within the generator command file.
\MyProject.Tests\ MyProject.Core\Tests.cs: A NUnit test
class for testing the domain object itself. After generation, you‟ll need to take a
couple of steps, described within this test, to get it passing.
\MyProject.Web.Controllers\Controller.cs: A
controller for doing full CRUD capabilities for the new domain object.
\MyProject.Tests\MyProject.Web\Controllers\Contr
ollerTests.cs: A test fixture containing comprehensive unit tests for the controller.
There‟s one test that‟s ignored at first which will need your attention.
\MyProject.Data\NHibernateMaps\Map.cs: An
NHibernate class map for the domain object which assumes that the table is a
plural version of the domain object name and that the column names are the same
as the property names.
\MyProject.Web\Views\\For
m.ascx: A form for collecting domain object information for both the create and
edit actions.
\MyProject.Web\Views\\Create.aspx: A container
page for the creation of a new domain object record. It uses the previously
mentioned form to collect the information.
\MyProject.Web\Views\\Edit.aspx: A container
page for the updating of an existing domain object record. It uses the previously
mentioned form to collect the information.
\MyProject.Web\Views\\Index.aspx: Shows all of
the domain object instances in the database. This page also provides links for
viewing, editing and deleting existing records as well as a link to adding a new
one.
\MyProject.Web\Views\\Show.aspx: Shows the
details of a particular domain object record.
Note that this scaffolding generator moves the files to the appropriate location and adds
the files to the project file itself; therefore, after generating the files, Visual Studio will
prompt you to reload the project files. Also note that this scaffolding generator does
NOT overwrite any files with the same name. Consequently, you never have to worry
about it overwriting your preexisting files.
To run the scaffolding generator for your own S#arp Architecture project, do the
following:
1. Ensure that you‟ve installed the T4Toolbox from
http://www.codeplex.com/t4toolbox as instructed in the previous section. This
enables Visual Studio with templating capabilities.
2. Open your S#arp Architecture project within Visual Studio 2008
3. Be sure to Save All (Ctrl+Shift+S) before running the CRUD scaffolding
generator. If you do not do this and you have pending changes to any *.csproj,
the generator may not successfully add the generated files to the project – you‟ll
then have to “Include in Project” manually and files that got left out.
4. Within the solution explorer, expand the “Code Generation” solution folder and
modify the contents of CrudScaffolding/ScaffoldingGeneratorCommand.tt as
described in the steps below. DO NOT SAVE THIS FILE UNTIL YOU‟VE
DONE ALL OF THE FOLLOWING:
a. Set the name of your domain object. It should be singular and in
PascalCase with no spaces. You may include a namespace hierarchy in
the domain object name; folders and appropriate namespace settings will
be generated, respectively. Example domain object names include
“Employee” and “Financial.Costs.LedgerEntry” to include the
LedgerEntry domain object within the namespace
YourProject.Core.Financial.Costs.
b. Add name/value pairs to the “properties” NameValueCollection
representing the public properties that you‟d like added to your domain
object along with their C# data type. Obviously, delete the two existing
properties included for demonstration purposes. Also, don’t include an
ID property as it’ll be included automatically.
c. Optionally, you can provide a fifth parameter to the ScaffoldingGenerator
constructor. This optional parameter is an array of ArtifactToGenerate
enum values to designate exactly which artifacts will be generated.
Allowable enum values are found within
CrudScaffolding/ScaffoldingEnums.tt. An example usage to just generate
the domain object, the unit tests for the domain object and the class map
is: “new ArtifactToGenerate[] { ArtifactToGenerate.DomainObject,
ArtifactToGenerate.ClassMap }”
d. Save the file. The file generation will occur automatically.
e. Review MyProject/logs/CrudScaffoldingog to see what files were created
and where they were placed. Look to make sure that all the expected files
are showing up in VS; if they‟re not, it‟s likely because you had pending
changes to a *.csproj. In that case, show the hidden files via VS and
include the respective file(s) manually into the project.
f. If, and only if, you included a namespace (anything before the domain
object name), then you must be sure that the “area” for the namespace
has been included in
MyProject/MyProject.Web.Controllers/RouteRegistrar.cs. It is
critically important to note that namespace areas with the greatest number
of “tiers” need to be declared before namespace areas with fewer tiers.
For example, the namespace “Organization/Department” needs to be
declared before the namespace “Organization.”
g. Compile the solution and run the unit tests with NUnit to view instruction
on manual steps which must be performed to get ignored tests passing.
Developing Custom Scaffolding
This is a more advanced subject better tackled by those already familiar with the structure
of S#arp Architecture projects. Therefore, if this is your first reading of this document,
you may want to skip down to the next major section, “Examining the Northwind
Example Project and S#arp Architecture Class Libraries.”
S#arp Architecture scaffolding uses the Visual Studio T4 templating technology for
providing a manageable approach to scaffolding generation. Accordingly, for the
enterprising developer, it is possible to provide your own scaffolding generator. As
mentioned within the previous section, it is necessary to install the T4Toolbox (and the
recommended T4 Editor) before developing your own scaffolding generator.
The following steps will guide you through customizing your T4 template generator for
your S#arp Architecture project. (Please let Billy McCafferty know if you do so, as I‟d
love to hear about the approaches that others are taking.)
To customize the templates for your needs, t‟s simplest to just modify the existing
templates found under the CrudScaffolding/Templates folder within Visual Studio. Care
needs to be taken if you add any new templates, as described below.
Adding New Templates
It‟s advisable that each template be added to its own template class file. To create your
templates:
1. Right click the folder you‟d like to add the template to and select Add / New
Item…
2. In the Add New Item dialog, select Visual C# Items / Code Generation /
Template.
3. Provide a name for your template that describes its purpose; something similar to
the final name is appropriate. For instance, if the rendered controller will be
called CustomerController.cs, then a good name for the template is
DomainObjectControllerTemplate.tt.
4. Unfortunately, Visual Studio attempts to render template files as soon as they‟re
added. Since it‟s preferred to leave the rendering of the template up to the
generator (discussed next), you‟ll want to detach the template from the built-in
template renderer. To do so, right click the newly added template and select
Properties. Set the value of the “Custom Tool” property to a blank field and save
the change.
5. Change the template class to inherit from BaseTemplate and add a constructor to
accept the appropriate arguments and pass them to the base template‟s
constructor. It‟s simplest to follow an already existing example; take a look at the
templates provided within the SharpArch.Scaffolding project for good examples
to learn from.
Examining the Northwind Example Project and S#arp
Architecture Class Libraries
Setting up the Northwind Example Project
1. Unzip the SharpArchitecture release zip which will include the example project;
this will create the following folders:
o bin: Contains all of the assemblies used by SharpArch and the Northwind
sample code,
o src: Contains the NorthwindSample and SharpArch sample code,
o VisualStudioTemplate: This was discussed previously.
2. Open the VS 2008 solution file at \src\NorthwindSample\Northwind.sln
3. Modify the connection string in Northwind.Web/Hibernate.cfg.xml to point to a
Northwind database. (It's currently configured for working with SQL Server
2005.)
4. Compile the solution.
5. Open NUnit and have it open \src\NorthwindSample\tests\Northwind.Tests\bin\Debug\Northwind.Test
s.dll and run the tests. They should all pass.
6. In VS 2008, click F5 (after making sure that Northwind.Web is set as the start up
project) to open http://localhost:####/ to see all of the project elements in action
including controller injection of data access object and NHibernate lazy loading.
7. Use the links from the homepage to view other actions such as listing data,
performing lazy loading actions, and running through full CRUD of employee
objects with validation.
Examining the Project Tiers
The Northwind example project is identical in structure to any project generated with the
Visual Studio project template. While a number of folders exist for the sake of
organizing the solution resources, it is the solution itself which is of primary interest
when discussing what defines a S#arp Architecture project. Before digging into some of
the more interesting aspects of the individual project layers, let‟s take a high level look at
the overall solution from a bird‟s eye view. The following diagram shows each project
layer as box within the solution. The direction of dependencies is illustrated as well.
What follows are light examinations of each project layer, focusing on specified areas of
interest in each. There are many more details of each layer discussed in the tutorial
discussed later.
Northwind.Core: The Domain Layer and the “M” in MVC
This class library contains all of the domain objects along with interfaces for any custom
repositories.
Concerning Custom Collections
Without the proper approach, custom collections can be tricky with NHibernate. As
NHibernate can only communicate with interfaces when loading a collection onto an
object, it‟s not possible to trivially bind to e.g. MyCustomerCollection which implements
an IList. Instead, it‟s simplest to bind a collection loaded by NHibernate directly to an
IList and then to use extension methods, as described below, to extend the collection‟s
functionality with custom methods.
Also note, by looking at Customer.Orders, that collections bound by NHibernate should
be initialized to a new list within the constructor and have a protected setter on the
property itself. This serves two benefits:
The collection will never be null. Because the collection is initialized during
construction and cannot be set directly, checking for a null value is unnecessary;
this avoids any “null object reference” exceptions and keeps the code clean.
If NHibernate loads the collection onto the object, it will keep track of the
reference to the collection. Accordingly, the reference to the collection itself
should not be replaced to avoid problems. It‟s fine to use methods such as Clear()
to manipulate the collection, but it shouldn‟t be replaced altogether; therefore,
having the setter be protected takes a step to ensure this.
Northwind.Core.OrdersExtensions is an example of adding a custom method to a
NHibernate bound collection; e.g., IList. This removes the complexity of other
alternatives to managing custom collection with NHibernate. (Some more complicated
alternatives, which are compatible with .NET 2.0 and do not require extension methods,
are described at http://devlicio.us/blogs/billy_mccafferty/archive/2007/12/03/custom-
collections-with-nhibernate-part-i-the-basics.aspx.)
Northwind.Web.Controllers: The “C” in MVC
This layer contains all of the controller classes which handle HTTP requests before
displaying a view. It also contains a route registration class, RouteRegistrar.cs, for
mapping routes; this is invoked from Global.asax. With S#arp Architecture, there are
two key points to note in the controllers layer: controllers depend only on service object
interfaces, and database transactions are managed with a transaction attribute.
(Transactions may be managed manually but are greatly simplified with the use of the
transaction attribute.)
Dependency Injection of Service Objects
A controller class should never have a direct dependency on a service object; e.g., a data
access object. Instead, controller classes should only depend on service object interfaces.
A dependency injection mechanism is used to inject the concrete instances into the
controller at runtime. S#arp Architecture uses the mature, dependency injection library
Castle Windsor (http://www.castleproject.org/container/) for handling this responsibility.
To maintain a clean separation to the dependency injection mechanism, the controller
classes are unaware what tool is performing the dependency injection; they only need to
specify what dependencies are required via the constructor parameters; i.e., the
Northwind.Web.Controllers assembly has no reference to the Castle Windsor library. As
an example, the Northwind.Web.Controllers/CategoriesController.cs class defines the
needed dependency, in this case IRepository, within the constructor.
Windsor handles figuring out the appropriate concrete object to pass to the constructor at
runtime. The primary benefits include promotion of a loosely coupled design and
facilitation of easy unit testing with test doubles.
The snippet below demonstrates defining a controller‟s dependencies via its constructor:
public class CategoriesController : Controller
{
public CategoriesController(
IRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
private readonly IRepository categoryRepository;
}
Transaction Management
A challenge with ASP.NET MVC is wrapping multiple data manipulation activities into a
single transaction within an action method. A possible solution is to programmatically
start the transaction at the beginning of the action method and to commit/rollback the
transaction at the end of the action method. The drawbacks to this include making the
controller aware of the data access mechanism (we‟d rather it be completely agnostic)
and introducing data communication logic into the controllers layer. This latter point
breaks the separation of concerns that we work so hard to maintain. To accommodate
transactions while keeping the controllers layer data access agnostic, an ASP.NET MVC
action filter attribute is provided to open a transaction at the beginning of an action
method and to commit/rollback the transaction at the end of the transaction. An example
usage is found above the method
Northwind.Web.Controllers.CategoriesController.Create(). Note that by default you
must use a transaction attribute above any controller method which will cause changes to
the database. Furthermore, for best performance, it is recommended to use a transaction
attribute above every method which interacts with the database.
The following shows an example of using the transaction attribute on a controller action:
[Transaction]
public ActionResult Create(string categoryName) {
Category category = new Category(categoryName);
category = categoryRepository.SaveOrUpdate(category);
return View(category);
}
The transaction attribute is provided within the SharpArch.Web library. Although the
transaction attribute is very handy, it is not required for use. Alternatively, every
IRepository exposes an IDbContext with the following methods:
CommitChanges to flush all pendinig changes to the DB,
BeginTransaction to begin a new transaction,
CommitTransaction to commit the transaction, and
RollbackTransaction to…well, you can probably figure it out.
Why are the controllers in a separate assembly?
Deciding to put the controllers into a physically separate assembly was a very deliberate
action. For a more thorough discussion of this approach, please see
http://devlicio.us/blogs/billy_mccafferty/archive/2009/01/09/an-argument-for-moving-
asp-net-mvc-controllers-to-a-separate-assembly.aspx.
Northwind.Data: The Data Layer
When compared to the article that inspired S#arp Architecture, NHibernate Best Practices
with ASP.NET (see “NBP” in the references Appendix), these updated architectural
guidelines remove much complexity; there's no DaoFactory in S#arp Architecture and it's
much simpler to add new Repositories and extend them with custom methods. (Note that
in this updated architecture, DAOs are now known as Repositories.) To make it even
easier, no action needs to be taken to support “out of the box” data access functionality
for any persistent domain object. For example, to have full Repository support for a
Category object, you‟d simply create a new Repository instance without
having to develop a custom “CategoryRepository” class.
But there are times when you‟ll need to add additional capabilities to the default
Repository behavior; such as adding a custom Repository method. Look at
Northwind.Core/ISupplierRepository.cs to see the interface of a custom Repository for
doing just that. The implementation of this custom Repository is
Northwind.Data/SupplierRepository.cs. These classes simply inherit from
IRepository and Repository, respectively, to include the “out
of the box” Repository functionality with the new, custom Repository method.
In the incredibly rare (and discouraged) circumstance wherein your object requires an
assigned ID, then instead inherit from RepositoryWithTypedId. Look at
Northwind.Core/ICustomerRepository.cs as an example of this. The implementation of
the custom Repository class is Northwind.Data/CustomerRepository.cs. Notice that this
class inherits from RepositoryWithTypedId because Customer
has an ID type other than int. This particular Repository was included to demonstrate an
example of supporting an object with an assigned ID. (Please note that assigned IDs are
the spawn of the devil and should be avoided.)
Northwind.Tests: The Unit Testing Layer
This class library contains all of the unit tests for testing the layers of your custom
application. Browse these tests to see examples of how the various other layers are
tested. You‟ll see that there is a separate folder for testing each layer of the application.
Of particular interest are the tests within Northwind.Tests/Northwind.Data. Almost all of
these tests leverage SQLite for running database tests against an in-memory database.
This greatly speeds the unit tests and removes the need to keep a development database in
synch with the unit tests. Obviously, you‟ll also want to make sure that your NHibernate
mappings are compliant with the “live” database. The unit test at
Northwind.Tests/Northwind.Data/NHibernateMaps/MappingIntegrationTests.cs ensures
that this is the case. (This integration test is automatically included when using the S#arp
Architecture Visual Studio template.)
Northwind.TestsUsingDevelopmentDatabase
This testing library has been provided to demonstrate how to develop unit tests against a
development database. As demonstrated in the Northwind.Tests library, it is
recommended to use SQLite for data communication tests. But if you feel more
comfortable testing against a persistent database, then this class library should serve to be
a good example for you.
Northwind.Web: The “V” in MVC
This is the web layer of the sample application which ties everything together via
configuration and exposes the presentation pages.
Items of particular interest are the Global.asax.cs class and the component registration
class, /CastleWindsor/ComponentRegistrar.cs.
Also note that there is an assembly redirect in web.config for the NHibernate assembly
due to other dependencies using an earlier version of this library.
Hosting WCF Services via IIS
An example of doing this will be coming before the 1.0 release of S#arp Architecture.
Examining the SharpArch Class Libraries
SharpArch.Core
Contains basic utilities, e.g., a Design by Contract class, and persistence agnostic data
access support.
Of particular note:
/CommonValidator/*.cs: Defines validator interfaces used by the SharpArch
class libraries. This works similarly to the Common Service Locator wherein you
can register your own, custom validator implementation if you do not want to use
the provided NHibernate Validator adapter, included in
SharpArch.Core.NHibernateValidator.
/DomainModel/Entity.cs: Acts as a base class for all entity classes. This class
provides validation functionality, persistence support with an ID property and a
standardized means of performing object comparisons with Equals(). An
interface exists for this class in case you‟d like to roll your own. Assumptions of
this object include:
o Equals and GetHashCode should behave independently from each other.
The current mechanism is the result of hours of conversation with many
people over a long period of time. Some enlightening reading with respect
to this subject may be found at http://groups.google.com/group/sharp-
architecture/browse_thread/thread/f76d1678e68e3ece.
o If two objects inherit from Entity, and are
compared, then it is assumed that the comparison between the two is
solely dependent on the properties marked with the attribute
[DomainSignature]. If no properties are decorated with the
DomainSignature attribute, then the comparison will throw a Design-by-
Contract exception;
o Two Entity objects which point to the same memory location (i.e., are the
same object in memory) are always equal;
o Two DomainModel/Entity objects which have
the same ID are always equal;
o If two Entity objects are compared and one of them is transient
while the other is persistent, they will always be different from a
standpoint from equality;
o Any Entity properties which are not decorated with the
[DomainSignature] attribute will not be included in the comparison of
two Entity object; i.e., ONLY the properties
decorated with the [DomainSinature] attribute will have an impact on
comparison.
/DomainModel/ValueObject.cs: This object is a good base class for value
objects; it has similar comparison capabilities to Entity but does not have an ID or
validation support.
/PersistenceSupport/IRepository.cs: Provides a base Repository interface to be
used by all Repository objects.
DesignByContract.cs: A design-by-contract (DbC) utility originally written by
Kevin McFarlane and described in detail at
http://www.codeproject.com/KB/cs/designbycontract.aspx. The modified version
included within S#arp Architecture does not support turning off DbC enforcement
with compile time flags.
SharpArch.Core.NHibernateValidator
Contains basic utilities, e.g., a Design by Contract class, and persistence agnostic data
access support.
Of particular note:
/CommonValidatorAdapter/Validator.cs & ValidationResult.cs: These objects
provide a concrete implementation of IValidator, found within
SharpArch.Core/CommonValidator, using NHibernate Validator. The Visual
Studio template wires the Validator class as the service for the IoC IValidator
interface. You can write your validator implementation if you do not want to use
NHibernate Validator.
HasUniqueDomainSignatureValidator.cs: This is a very handy validator for
verifying the uniqueness of an entity‟s domain signature. To use, simply add the
attribute [HasUniqueDomainSignature] above an entity‟s class declaration. This
won‟t take care of every situation but easily covers the 80/20 rule of situations.
The validator will let you know if it encounters a property that it doesn‟t know
how to work with; at that point, you can either write your own class-specific
validator and/or open a new issue on the S#arp Architecture development site.
SharpArch.Data
Provides “out of the box” data access functionality using NHibernate.
Of particular note:
/NHibernate/Repository.cs: The “out of the box” data access object that may be
used directly, with a generic qualifier, or as a base class for all concrete
Repositories.
/NHibernate/NHibernateSession.cs: Provides a means for getting an NHibernate
session. It also builds the NHibernate session factory upon creation.
SharpArch.Testing
Provides reusable testing utility classes.
Of particular note:
PersistentObjectIdSetter.cs: Used to set the ID property of an Entity object.
Although I highly recommend you never set this property outside of a unit testing
scenario, it‟s a very useful utility when returning results from a mock repository.
SharpArch.Testing.NUnit
Provides NUnit specific testing utility classes.
Of particular note:
BehaviorSpecificationTestsBase.cs: Provides a base class for writing unit tests
following a behavior driven approach. More information about this approach may
be found at http://flux88.com/blog/the-transition-from-tdd-to-bdd/.
/NHibernate/DatabaseRepositoryTestsBase.cs: Provides a base class for unit tests
which act upon a live database. A transaction is begun before each test and is
then rolled back upon completion of the test, regardless of success or failure.
Consequently, database changes made within a unit test will not have a permanent
effect on the persisted data. It is suggested that data communication tests are
performed using an in-memory SQLite database. This base class is provided for
those of you that would rather test against a persistent, development database.
The preferred base classes for testing database communications are
RepositoryTestsBase and RepositoryBehaviorSpecificationTestsBase.
/NHibernate/RepositoryTestsBase.cs: Facilitates running database unit tests using
an in-memory SQLite database.
/NHibernate/RepositoryBehaviorSpecificationTestsBase.cs: Facilitates running
database unit tests using an in-memory SQLite database in a behavior driven
manner.
SharpArch.Web
Includes classes for supporting use of SharpArch within an ASP.NET MVC context.
Of particular note:
/NHibernate/TransactionAttribute.cs: An ActionFilterAttribute for wrapping
MVC actions within a transaction.
/NHibernate/WebSessionStorage.cs: An ISessionStorage implementation for
storing the NHibernate session in a web context. (This is not specific to MVC
and may be leveraged in a classic ASP.NET environment.)
Tutorial for Developing with S#arp Architecture
The following is more than just a lab exercise! There are many gems of insight into how
S#arp Architecture is structured, many motivations behind why it is designed the way it
is, and insight into how best it can be leveraged.
Almost always, you would use the CRUD scaffolding generator as a starting point, but
the lab which follows helps illustrate how the various layers work together by going
through the process manually. The example details all steps necessary for creating a new
domain object and showing associated records from a database onto a web page. It
covers creating the domain object, creating and testing the controller with mock objects,
extending a data access object with a custom method, configuring repositories for
dependency injection and displaying the results on a web page.
Test Driven Development Cycle
The following test-driven cycle will be used:
1) Write your test as if the target objects and API already exists.
2) Compile the solution and see it break.
3) Write just enough code to get it to compile.
4) Run the test and see if fail.
5) Write just enough code to get it to pass.
6) Run the test and see it pass!
7) Refactor if necessary!
For the purposes of this example, the user story is as follows: Users may search for staff
members matching a name filter. The results should include any staff members with a
first name, last name, or employee number containing the provided filter.
Assume that we haven‟t yet tackled another user story defined as “User may create staff
member having a unique employee number” yet; so although not necessary yet, because
it‟s not dictated in the current user story that we‟re tackling, let‟s keep in mind that the
staff member‟s employee number is what makes it unique. With that said, let‟s start with
the domain model to support the staff member.
Developing the Staff Member Domain Model
1) Go to Northwind.Tests/Northwind.Core and add a new tests class called
StaffMemberTests.cs with an initial test of CanCreateStaffMember(). (Note that the test
is written as a question.) The class should look as follows:
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Northwind.Core;
namespace Tests.Northwind.Core
{
[TestFixture]
public class StaffMemberTests
{
[Test]
public void CanCreateStaffMember() {
string employeeNumber = "ABC123";
string firstName = "Karel";
string lastName = "Čapek";
StaffMember staffMember =
new StaffMember(employeeNumber) {
FirstName = firstName,
LastName = lastName
};
Assert.That(staffMember.EmployeeNumber,
Is.EqualTo(employeeNumber));
Assert.That(staffMember.FirstName,
Is.EqualTo(firstName));
Assert.That(staffMember.LastName,
Is.EqualTo(lastName));
}
}
}
Note that the constructor of StaffMember only takes an employee number. This is the
minimal amount of information to create a valid, unique staff member, as specified by the
requirements.
2) Compile the build and notice that it breaks due to the missing StaffMember class.
3) Go to Northwind.Core and add a StaffMember.cs class file to develop the StaffMember
domain object, as follows: (Note that we‟ve only added just enough code to get the test
to compile.)
namespace Northwind.Core
{
public class StaffMember
{
public StaffMember(string employeeNumber) {}
public string EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
4) Open NUnit and load the tests project by going to File / Open Project and open
Northwind.Tests/bin/Debug/Northwind.Tests.dll. Double click the
CanCreateStaffMember test to have it run. You should see it fail because
EmployeeNumber is null.
5) Go back to the StaffMember domain object and alter the constructor as follows:
public StaffMember(string employeeNumber) {
EmployeeNumber = employeeNumber;
}
6) Compile and run the test and again to see it go green…woohoo!
7) Time to refactor! In the constructor of StaffMember, it‟s possible to set the
employeeNumber parameter to a null or empty string object. This is bad. What we‟d
like to do is enforce that a valid employee number is provided. Let‟s use the design-by-
contract utility to enforce this with a Check.Require. This is good. Furthermore, to
protect the employee number in the future, since it shouldn‟t change, let‟s protect the
setter of EmployeeNumber. Be sure to run the unit tests again to make sure nothing
broke while refactoring.
When implementing this refactoring, a test driven approach should be followed, as was
performed in the previous six steps. For brevity, the unit test below demonstrates step
one of the refactoring process followed by code which gets the test to pass in step six of
the process. (Note that we can‟t unit test proving that the EmployeeNumber‟s setter is
protected as it will not compile if we attempt to set it.)
using SharpArch.Core;
…
[Test]
[ExpectedException(typeof(PreconditionException))]
public void CannotCreateStaffMemberWithInvalidEmployeeId() {
new StaffMember(" ");
}
using SharpArch.Core;
using System;
namespace Northwind.Core
{
public class StaffMember
{
public StaffMember(string employeeNumber) {
Check.Require(!string.IsNullOrEmpty(employeeNumber)
&& employeeNumber.Trim() != String.Empty,
"employeeNumber must be provided");
EmployeeNumber = employeeNumber;
}
public string EmployeeNumber { get; protected set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
Enabling the Domain Object for Comparison
So now we have our basic domain model in place to support creating staff members from
a domain perspective; the staff member is our “entity” in domain driven development
speak. Within the requirements, as was discussed, the employee number is the basis for
staff member uniqueness; therefore, we‟ll want to be able to compare staff members for
equality for this field. The employee number is the “domain signature” of the entity.
Here‟s where we begin leveraging the S#arp Architecture framework to support
consistent comparisons.
1) Within StaffMemberTests.cs, add a new test called CanCompareStaffMembers().The
method should look as follows:
[Test]
public void CanCompareStaffMembers() {
string employeeNumber = "ABC123";
StaffMember staffMember = new StaffMember(employeeNumber);
Assert.That(staffMember, Is.EqualTo(
new StaffMember(employeeNumber)));
}
2) Compile the build; it‟ll compile because objects are always comparable. So we‟ve also
taken care of the write-just-enough-code-to-get-it-to-compile step.
3) Within NUnit, double click the CanCompareStaffMembers test to have it run. It will fail
because the comparison is using the native .NET Equals() method.
4) To enable consistent comparisons of domain objects, alter StaffMember to inherit from
Entity, and add a DomainSignature attribute above the property(s) which makes the
staff member unique, as follows:
using SharpArch.Core.DomainModel;
public class StaffMember : Entity
{
…
[DomainSignature]
public string EmployeeNumber { get; protected set; }
…
}
Inheriting from Entity and adding the attribute has had a few effects on the StaffMember
object:
Equals() and GetHashCode() have been provided by default for consistent
behavior. Contrast to the original inspiration from
http://devlicio.us/blogs/billy_mccafferty/archive/2007/04/25/using-equals-
gethashcode-effectively.aspx, Equals and GetHashCode now work independently
of each other; consequently, in theory, two objects can have the same hashcode
without being equal. This is very much by design. Additional differences
between the current architecture and what was described in the blog post include:
GetHashCode() has been moved up to Entity so only the “domain signature” has
to be defined via the inclusion of attributes. Furthermore, as shown in the
example above, the comparison behavior has been abstracted into a Entity class
so as to serve as an optional base class for non-persistent objects. To read more
about the importance of providing Equals and GetHashCode for NHibernate, see
http://www.hibernate.org/hib_docs/nhibernate/html/persistent-
classes.html#persistent-classes-equalshashcode.
For calculating the HashCode, the “domain signature” gets automatically created
by performing bitwise XORs, and some other fancy math, with all the domain
signature properties resulting in an int HashCode defining what makes an object
unique. For checking objects for equality, each domain signature property is
compared, one by one. Alternatively, you can forego using attributes and define
the domain signature comparison functionality yourself by overriding
HasSameObjectSignatureAs(BaseObject compareTo). Take a look at
Northwind.Core/Order for an example of doing so.
To reiterate the easier alternative, you could simply add the [DomainSignature]
attribute above both the respective properties which has the same behavior as the
example. To learn more about the difference between an entity and a value
object, as well as a more in depth analysis of what defines an entity‟s domain
signature, I highly recommend reading Domain Driven Design by Eric Evans.
So what if the domain signature happens to include a complex reference type (a
non-primitive) as part of its domain signature? For example, assume that a
“HomeAddress” object is part of what makes a StaffMember unique. The
property comparison will invoke Equals between the properties, so it‟s important
to have the composite objects implement Equals as well, either by inheriting
from Entity, ValueObject (which uses all of the object‟s properties for
comparison) or providing your own Equals method.
The following illustrative example - i.e., don’t modify the tutorial code
you’re working on to reflect this – that shows creating a multi-part signature
using the DomainSignature across multiple properties. It‟s alright if the
properties which make up an object‟s domain signature are value or reference
types; the base class will check for nulls.
using SharpArch.Core;
using SharpArch.Core.PersistenceSupport;
using System;
namespace Northwind.Core
{
public class StaffMember
{
...
[DomainSignature]
public string FirstName { get; set; }
[DomainSignature]
public string LastName { get; set; }
[DomainSignature]
public Address HomeAddress { get; set; }
}
}
5) Compile and run the test and again to see it go green…gotta love the green!
Developing the Controller to Retrieve and Display Results
We now have the basics in place for our domain model. We have two options to proceed, we can
develop the controller or we can build the mechanism to retrieve results from the database. As a
general rule, there are two things that we want to delay as long as possible: writing any
presentation pages and writing any data access code against a live database. Delaying these keeps
our attention focused on the domain and the interactions with the behavior of the domain.
Therefore, let‟s proceed with developing the controller and we‟ll fake our interactions with a live
database.
1) As always, let‟s start with a unit test…surprised? Under
Northwind.Tests/Northwind.Web/Controllers, add a new class called
StaffMembersControllerTests which contains a single test. An explanation of what is
happening follows the code.
using NUnit.Framework;
using Northwind.Core;
using MvcContrib.TestHelper;
using SharpArch.Core.PersistenceSupport;
using Rhino.Mocks;
using System.Collections.Generic;
using NUnit.Framework.SyntaxHelpers;
using Northwind.Web.Controllers;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
namespace Tests.Northwind.Web.Controllers
{
[TestFixture]
public class StaffMembersControllerTests
{
[Test]
public void CanListFilteredStaffMembers() {
StaffMembersController controller =
new StaffMembersController(
CreateMockStaffMemberRepository());
ViewResult result =
controller.ListStaffMembersMatching("martin")
.AssertViewRendered()
.ForView("ListStaffMembersMatchingFilter");
Assert.That(result.ViewData, Is.Not.Null);
Assert.That(result.ViewData.Model as
List, Is.Not.Null);
Assert.That((result.ViewData.Model as
List).Count, Is.EqualTo(4));
}
///
/// In most cases, we'd simply return
/// IRepository, but since we're
/// leveraging a custom Repository method, we need a
/// custom Repository interface.
///
public IStaffMemberRepository
CreateMockStaffMemberRepository() {
MockRepository mocks = new MockRepository();
IStaffMemberRepository mockedRepository =
mocks.StrictMock();
Expect.Call(mockedRepository.FindAllMatching(null))
.IgnoreArguments()
.Return(CreateStaffMembers());
mocks.Replay(mockedRepository);
return mockedRepository;
}
private List CreateStaffMembers() {
List staffMembers =
new List();
staffMembers.Add(new StaffMember("ABC123"));
staffMembers.Add(new StaffMember("DEF456"));
staffMembers.Add(new StaffMember("GHI789"));
staffMembers.Add(new StaffMember("Abracadabera"));
return staffMembers;
}
}
}
That‟s quite a mouthful! Here‟s what the code is trying to verify… It‟s making sure that
if we call the controller‟s ListStaffMembersMatching() method, then the controller will
invoke the IStaffMemberRepository.FindAllMatching() method to retrieve the results and
put them into the ViewData.
(Incidentally, if you feel limited in ASP.NET MVC‟s ability to support robust unit testing
of controllers in an HTTP context, take a look at the MvcContrib project at
http://www.codeplex.com/MVCContrib; the assemblies for it are already included with
S#arp Architecture.)
Notice that the constructor of the controller accepts the return value from
CreateMockStaffMemberRepository(), which is a concrete instance of
IStaffMemberRepository. This doesn‟t exist yet, but we know that the controller is going
to need it…this is the first D in TDD. By passing IStaffMemberRepository into the
constructor of the controller, we‟re performing “manual dependency injection,” enabling
ourselves to unit test the controller without a live database. For more information on
dependency injection, read
http://www.codeproject.com/KB/architecture/DependencyInjection.aspx - the “mock”
objects in this article are actually “stubs”…but I digress.
The CreateMockStaffMemberRepository() method itself is an example of creating a true
mock object, using an external library called Rhino Mocks
(http://www.ayende.com/projects/rhino-mocks.aspx). This mock object simulates
interaction with a live database. In this way, we‟ve foregone digging into the data access
layer until we have the business logic completely pinned down...no, it‟s not because
we‟re hopeless procrastinators putting off all the tedious work until the interns start, we
simply want to be able to focus on the domain logic until the details are worked out. For
more information on mocking the database (no pun intended), see
http://www.martinfowler.com/articles/mocksArentStubs.html.
2) Compile the solution and see the compilation break. It‟ll complain because both
StaffMembersController and IStaffMemberRepository don‟t yet exist.
3) To get the code to compile, create IStaffMemberRepository first since the controller
depends on it. To do so, go to Northwind.Core/DataInterfaces and add a new interface
called IStaffMemberRepository, as follows:
using SharpArch.Core.PersistenceSupport;
using System.Collections.Generic;
using Northwind.Core;
namespace Northwind.Core.DataInterfaces
{
public interface IStaffMemberRepository :
IRepository
{
List FindAllMatching(string filter);
}
}
As shown, this inherits from IRepository which resides in SharpArch.Core. This
general-use Repository interface declares a number of standard Repository methods such
methods as GetAll() and Delete(). All that needs to be done for now is to extend it with
one additional, custom method, FindAllMatching().
When using S#arp Architecture, IRepository should be used as the basis for
all custom repositories. If your object has an ID type other than int, then implement
IRepositoryWithTypedId, instead. (Incidentally,
IRepository inherits from IRepositoryWithTypedId.)
The key point is that the Northwind.Core assembly does not contain any data access
implementation details; it only declares, and has access to, data access interfaces. As
discussed later on, the concrete Repository, containing the implementation details, will be
added to Northwind.Data (but not until we need it). Having the interface separated from
the implementation details located in a different assembly is known, appropriately
enough, as “Separated Interface”
(http://www.martinfowler.com/eaaCatalog/separatedInterface.html). (Uncle Bob, aka
Robert Martin, originally coined this technique as “Dependency Inversion.”) For more
information concerning refactoring towards Separated Interface, see
http://devlicio.us/blogs/billy_mccafferty/archive/2008/10/30/refactoring-service-
dependencies-to-separated-interface.aspx.
Getting back to the task at hand, go to Northwind.Web.Controllers and add a
StaffMembersController class; it should have just enough code to compile:
using Northwind.Core;
using System.Collections.Generic;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
using SharpArch.Web;
namespace Northwind.Web.Controllers
{
public class StaffMembersController : Controller
{
public StaffMembersController(
IStaffMemberRepository staffMemberRepository) {}
public ActionResult ListStaffMembersMatching(
string filter) {
return null;
}
}
}
4) Build the solution, head back to NUnit, run the CanListFilteredStaffMembers test, and
see it fail it a fit of agonizing pain. So of a!!! (But wait, that‟s exactly what we wanted
to see at this point.)
5) We now simply have to add a couple lines of code to the controller to get the unit test to
pass:
using Northwind.Core;
using System.Collections.Generic;
using Northwind.Core.DataInterfaces;
using System.Web.Mvc;
using SharpArch.Core;
using SharpArch.Web;
namespace Northwind.Web.Controllers
{
public class StaffMembersController : Controller
{
public StaffMembersController(
IStaffMemberRepository staffMemberRepository) {
Check.Require(staffMemberRepository != null,
"staffMemberRepository may not be null");
this.staffMemberRepository = staffMemberRepository;
}
public ActionResult ListStaffMembersMatching(
string filter) {
List matchingStaffMembers =
staffMemberRepository.FindAllMatching(filter);
return View("ListStaffMembersMatchingFilter",
matchingStaffMembers);
}
private readonly IStaffMemberRepository
staffMemberRepository;
}
}
Arguably, adding the Check.Require within the constructor is a little more than “just
enough” code to get the test to pass. But with that said, adding Check.Require statements
all over the place should become habitual while coding. It only takes a moment to write
and will end up saving you hours in the debugger. And if you‟re worried about these
checks causing a performance hit, there are far larger fish to fry than these.
6) Now run the NUnit test again and you should be seeing green. We were able to do all of
this without yet defining our staff members table in the database. But we‟re not going to
get much further without doing that as well…
Retrieving Results via a Custom DAO
As requested, the requirements of this tutorial stipulate that the user may provide a filter
to retrieve staff members matching their first name, last name, and/or employee number.
The customized nature of this query won‟t allow us to use the out of the box Repository
very easily. Accordingly, we‟re going to have to extend the Repository with a custom
method. So first off, the test:
1) Go to Northwind.Tests/Northwind.Data and add a new class called
StaffMemberRepositoryTests; this will contain a single test to prove that our custom
Repository method is working as expected:
using NUnit.Framework;
using SharpArch.Testing.NUnit.NHibernate;
using Northwind.Core.DataInterfaces;
using Northwind.Core;
using System.Collections.Generic;
using NUnit.Framework.SyntaxHelpers;
using Northwind.Data;
namespace Tests.Northwind.Data
{
[TestFixture]
[Category("DB Tests")]
public class StaffMemberRepositoryTests : RepositoryTestsBase
{
protected override void LoadTestData() {
AddStaffMember("thistest_Filter", "Mike", "Park");
AddStaffMember("ABC123", "_test_FiLtEr_", "Vance");
AddStaffMember("GHI789", "Lynette", "Knackstedt");
AddStaffMember("DEF456", "Gerry",
"Lundquistest_filtER");
}
[Test]
public void CanLoadStaffMembersMatchingFilter() {
List matchingStaffMembers =
staffMemberRepository
.FindAllMatching("TEST_FiLtEr");
Assert.That(matchingStaffMembers.Count,
Is.EqualTo(3));
}
private void AddStaffMember(string employeeNumber,
string firstName, string lastName) {
StaffMember staffMember =
new StaffMember(employeeNumber) {
FirstName = firstName,
LastName = lastName
};
staffMemberRepository.SaveOrUpdate(staffMember);
FlushSessionAndEvict(staffMember);
}
private IStaffMemberRepository staffMemberRepository =
new StaffMemberRepository();
}
}
The benefit of inheriting from RepositoryTestsBase is twofold: during setup, an in-
memory SQLite database is created and the NHibernate session factory is initialized.
Within the LoadTestData, you can then insert data into this in-memory database which
will be used by the unit tests. Note that after each staff member is inserted into the
database, it is flushed and evicted. The flush commits the new staff member to the
database and the eviction removes the staff memory from NHibernate‟s radar. In this
way, when you retrieve a staff member from the database, NHibernate makes a trip to the
database without pulling the object from cache.
Also notice, at the top of the test class, an attribute has been included to indicate that this
test is within the “DB Tests” category. Categorizing this test enables you to disable the
running of these unit tests within NUnit. Why would you want to do that? Because unit
tests which connect to a database are slow, taking a few seconds to load and run. This
doesn‟t sound too bad until you have dozens or hundreds of DB tests to run. If all the
tests take more than a few seconds to run, then developers stop running them. And once
developers stop running them, tests start breaking and the quality of the code degrades.
So by turning the DB tests off while running the domain logic tests allows a developer to
run all of the fast tests very often and run the time consuming tests when appropriate.
The continuous integration server will also be running all of the longer running tests
every time a change is checked in, so they‟ll get checked sooner rather than later
anyway…assuming you‟re a good coder who checks in changes frequently! Testing
against SQLite greatly improves the performance, but it‟s still nice to be able to turn them
off.
Finally note, in the unit test, that we‟re asserting that we expect three matching staff
members.
2) Since we‟ve already created IStaffMemberRepository, but haven‟t created the
StaffMemberRepository concrete implementation, this unit test will not compile. At this
point, add a public StaffMemberRepository class to Northwind.Data and have it inherit
from Repository and implement IStaffMemberRepository. To get the
solution to compile, you‟ll need to also implement the FindAllMatching method from
IStaffMemberRepository, but just throw a NotImplementedException within it.
3) Running the CanLoadStaffMembersMatchingFilter test in NUnit fails, as it should. It
will complain – unsurprisingly enough – that StaffMember is an unknown entity. This is
because we haven‟t informed NHibernate how this object is stored in the database. So
let‟s start there first…
4) In writing just enough code to get this data dependent test to pass, we‟re going to have to
do a few tasks to get the minimal data access plumbing in place:
a. Although no action needs to be taken for this specific step, it’s important to note
that the StaffMember domain object inherits from Entity (which is, incidentally, a
subclass of BaseObject). Entity gives the object an ID property and extends the
comparison capabilities to take the ID property and state of transience into
consideration. By default, Entity assumes that the object has an ID of type int;
consequently, StaffMember now has an ID property of type int with a public
getter and protected setter. (Due to a variety of hard learned lessons, only the
hydration mechanism, e.g., Nhibernate, should have access to setting this ID
property.) If you need an ID having a type other than ID, inherit instead from
PersistenObjectWithTypedId. And if you really need to set the ID
property manually (and you don‟t), then implement the IHasAssignedId
interface. (Sometimes, it‟s useful to set the ID manually in your unit tests,
especially for results returned from a mock data repository; you can use
SharpArch.Testing.EntityIdSetter.SetIdOf().)
b. Create the Repository which implements IStaffMemberRepository. This class
will contain the implementation details of the custom Repository method that we
added to the base IRepository interface within IStaffMemberRepository. As was
done in a previous step, a new class, called StaffMemberRepository was added to
Northwind.Data which implements IStaffMemberRepository. Use the following
to build out the remaining details of this class:
using Northwind.Core.DataInterfaces;
using Northwind.Core;
using System.Collections.Generic;
using NHibernate;
using SharpArch.Data.NHibernate;
using NHibernate.Criterion;
namespace Northwind.Data
{
public class StaffMemberRepository :
Repository, IStaffMemberRepository
{
public List FindAllMatching(
string filter) {
ICriteria criteria =
Session.CreateCriteria(typeof(StaffMember))
.Add(
Expression.Or(
Expression.InsensitiveLike(
"EmployeeNumber",
filter,
MatchMode.Anywhere),
Expression.Or(
Expression.InsensitiveLike(
"FirstName",
filter,
MatchMode.Anywhere),
Expression.InsensitiveLike(
"LastName",
filter,
MatchMode.Anywhere))))
.AddOrder(
new NHibernate.Criterion.Order(
"LastName", true));
return criteria.List()
as List;
}
}
}
The above code implements and inherits from two objects:
IStaffMemberRepository which we‟ve already seen, and
Repository which comes from SharpArch.Data.NHibernate. The
Repository base class implements IRepository and provides most of the basic
Repository CRUD functionality that we‟ll ever need. The benefit to inheriting
from this base implementation in our custom Repository is that we only need to
provide details for our custom method, FindAllMatching(string filter). Writing
a custom Repository is usually the exception than the norm; more often than
not, Repository will provide all the data access functionality we need for a
given Entity. But you still have a few options to choose from when the need
arises; in fact, you have three options when it comes to a using a data repository:
o You can use the concrete “out of the box” repository
SharpArch.Data.NHibernate.Repository, implementing
SharpArch.Core.PersistenceSupport.IRepository, which includes Get,
GetAll, GetByProperties, GetUniqueByProperties, SaveOrUpdate, and
Delete - this will likely be the most common scenario. This also serves
as a good base class for your own custom repositories.
o You can use the more NHibernate specific interface
SharpArch.Core.PersistenceSupport.NHibernate.INHibernateRepository
which includes NHibernate specific additions to IRepository, including
Get with a lock mode, Load with and without a lock mode,
GetByExample, GetUniqueByExample, Save (for assigned IDs), Update
(also for assigned IDs), and Evict. The concrete implementation to be
used with this is SharpArch.Data.NHibernate.NHibernateRepository.
But since controllers only depend on the repository interfaces, and not
the underlying repository, the controllers‟ layer sees them as different
objects. Accordingly, for the benefit of keeping your layers loosely
coupled to NHibernate, you should try to avoid using this more
NHibernate specific interface if possible. But if you have an object with
an assigned ID, it‟s unavoidable as you‟ll need the separate Save and
Update methods.
o Alternatively, you can extend IRepository and the Repository class with
your own custom methods as we‟re about to do.
While the above discusses the concrete implementations that are available for out
of the box usage, it should be clarified that there are exactly four interfaces which
are the basis for all repository objects (including your own custom repositories):
o SharpArch.Core.IRepositoryWithTypedId: This provides
exposure to very basic generic repository methods; e.g., Get() and
SaveOrUpdate(). It accepts the domain object domain, and the type of
the domain object's ID property to communicate with the database.
o SharpArch.Core.IRepository: This is nothing more than
IRepositoryWithTypedId with a default ID type of int. This is expected
to be used almost exclusively and certainly more than any other
repository.
o SharpArch.Core.NHibernate.INHibernateRepositoryWithTypedId: This extends the basic repository methods with other methods
which are specific to NHibernate; e.g., GetByExample, Save, Update,
etc. This would be a good interface to add GetByPage, and other non-
trivial repository methods which have some insight about the internals of
NHibernate. As a general rule, the use of this repository should be
avoided unless absolutely necessary; e.g., if you're using a domain object
with an assigned ID (ack!).
o SharpArch.Core.NHibernate.INHibernateRepository: Like
IRepository, this is nothing more than
INHibernateRepositoryWithTypedId with a default ID type of int. And
similarly to NHibernateRepositoryWithTypedId, the use of this
repository should be avoided unless absolutely necessary.
In the StaffMemberRepository code that you just wrote, the querying mechanism
uses Hibernate Query Language (HQL); for more information about using HQL,
see the NHibernate reference documentation at
http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html. My only
(very big) complaint with HQL is that string literals are used instead of strongly
typed references; a few good solutions exist to solve this problem:
LINQ to NHibernate: This is still a work in progress, but it‟s coming
along very nicely. You can read more about this LINQ provider at
http://www.hookedonlinq.com/LINQToNHibernate.ashx.
Typesafe ICriteria using Lambda Expressions:
http://nhforge.org/blogs/nhibernate/archive/2009/01/07/typesafe-
icriteria-using-lambda-expressions.aspx. This is my personal favorite as
it gives you the expressiveness of HQL, which is quite powerful, while
still being strongly typed.
NHibernate Query Generator:
http://www.ayende.com/Blog/archive/7186.aspx
c. Alter the domain object to have virtual properties and a no-argument
constructor. When NHibernate loads objects, it does so “lazily” by default. This
means that if a request is made to load an object, NHibernate actually returns a
proxy to the object. Only when the object is used in some way is an actual trip to
the database made. This behavior can be modified on a case by case basis and
will improve performance in some cases. To facilitate StaffMember objects
being lazily loaded by NHibernate, its class properties and methods need to be
defined as virtual. So under Northwind.Core, modify StaffMember.cs as follows:
[DomainSignature]
public virtual string EmployeeNumber { get; protected set;}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
If you didn‟t do this, NHibernate would complain that the properties need to be
defined as virtual to work with the default, lazy behavior. (To learn more about
the proxy design pattern in general, see
http://www.dofactory.com/Patterns/PatternProxy.aspx.)
In addition to needing virtual properties and methods to lazily load an object,
NHibernate also requires that the domain object have a no-argument constructor
in order to create and hydrate the object with data from the database. As we do
not want anyone creating a StaffMember object without supplying an employee
number, we can simply add a protected no-argument constructor, as in the first
line below:
protected StaffMember() { }
public StaffMember(string employeeNumber) {
Check.Require(!string.IsNullOrEmpty(employeeNumber) &&
employeeNumber.Trim() != string.Empty,
"employeeNumber must be provided");
EmployeeNumber = employeeNumber;
}
In this way, our domain logic is enforced while allowing NHibernate to load the
object without difficulty.
d. Create the NHibernate mapping class. There are two popular means for
generating mapping files for NHibernate classes: HBMs which are based on
XML and the increasingly more popular Fluent NHibernate to describe mappings
in a strongly typed manner. The only drawback to Fluent NHibernate is that it‟s
only compatible with C# 3.0 and later. But after using it for only a short while,
I‟ll never go back to writing HBMs again. Anyway, go to
Northwind.Data/NHibernateMaps and add a new class called
StaffMemberMap.cs.
using Northwind.Core;
using FluentNHibernate.Mapping;
namespace Northwind.Data.NHibernateMappings
{
public class StaffMemberMap : ClassMap
{
public StaffMemberMap() {
WithTable("StaffMembers");
Id(x => x.ID, "ID")
.WithUnsavedValue(0)
.GeneratedBy.Identity();
Map(x => x.EmployeeNumber);
Map(x => x.FirstName);
Map(x => x.LastName);
}
}
}
e. Now compile and go run those unit tests! (I‟ll wait…still waiting.) While you
might be impressed to see the
StaffMemberRepositoryTests.CanLoadStaffMembersMatchingFilter() test now
passing, we‟ve now got a new one failing. What the?!?!? There‟s a unit test
that‟s included in the Northwind sample code, and one which comes
preconfigured with the S#arp Architecture Visual Studio template, called
Northwind.Data.NHibernateMaps.MappingIntegrationTests
.CanConfirmDataMatchesMappings(). This test initializes an NHibernate
session using the database connection information stored within
Northwind.Web/Hibernate.cfg.xml. So this isn‟t talking to SQLite; it‟s a
connection to your “live” development database. This test loops through every
persistent object and performing a zero-result select statement on each,
effectively validating that the database is configured in alignment with the
NHibernate mapping classes. So we have one final step to complete persistence
support for the StaffMember object; we need to add the table to our database.
AN ALTERNATIVE TO USING FLUENT NHIBERNATE IN THE PREVIOUS STEP:
Create the NHibernate mapping file (HBM). Like Fluent NHibernate, this XML
configuration file informs NHibernate how to translate the StaffMember object into a row
in the database and vice versa. To use an HBM instead of Fluent NHibernate, go to
Northwind.Data and add a new XML file called StaffMember.hbm.xml. (The “hbm” in
the name is crucial.) Immediately after adding the file, right click it and bring up its
properties; set the “Build Action” to “Embedded Resource.” Missing this step is
probably the most common NHibernate mistake a developer makes when working with
HBM files; seeing an error of “unknown entity” is a good indication that this file has not
been set to be an embedded resource. Setting it as an embedded resource embeds the
XML file directly into the compiled DLL so that it does not have to be explicitly
deployed to the deployment server. If you‟re going the HBM route – again, you
shouldn’t do this if you’ve already added the Fluent NHibernate map class – then the
StaffMember.hbm.xml would reflect the following:
This HBM is everything that is needed to inform NHibernate how to perform CRUD
operations on a StaffMember object using the StaffMembers table. What StaffMembers
table you ask? We‟ll get to that next.
AN ALTERNATIVE TO THE ALTERNATIVE TO USING FLUENT NHIBERNATE IN
THE PREVIOUS STEP: Use Fluent NHibernate Auto Persistence Mapping. The
developers of Fluent NHibernate have tried to make it as simple as possible to map
classes to the database. They‟ve even gone as far as allowing you, the pragmatic
developer that you are, to define conventions which will be automatically used by Fluent
NHibernate to map classes to tables. While the Visual Studio template project does not
set this up by default, steps are explained in detail in Appendix B of this document for
upgrading the “out of the box” functionality to include auto persistence mapping.
f. Create the table to support the mapping class. Many developers start with a
database model and allow it to be the driving factor in designing the application.
This is an aspect of “model driven development.” The ADO.NET Entity
Framework is a perfect example of a solid model driven design framework.
Another approach that others espouse, including me, is known as “domain driven
development” wherein the database becomes an afterthought to support the
domain model. For more information on domain driven design, see
http://www.infoq.com/minibooks/domain-driven-design-quickly which is a
concise summary of Eric Evans‟ classic book, Domain Driven Design. To add
the table, completing the final piece to the persistence layer, run the following
within the Northwind database (SQL was generated with SQL Server 2005):
USE Northwind
GO
CREATE TABLE [dbo].[StaffMembers](
[ID] [int] IDENTITY(1,1) NOT NULL,
[EmployeeNumber] [varchar](50) NOT NULL,
[FirstName] [varchar](50) NOT NULL CONSTRAINT
[DF_StaffMembers_FirstName] DEFAULT (''),
[LastName] [varchar](50) NOT NULL CONSTRAINT
[DF_StaffMembers_LastName] DEFAULT (''),
CONSTRAINT [PK_StaffMembers] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
5) With this table in place, you should now be able to run all of the unit tests and see them
pass. The CanLoadStaffMembersMatchingFilter() test successfully connected to the
SQLite database, created the database using schema information from our mapping
classes, populated the StaffMembers table with test data, ran the provided filtering query,
hydrated the objects with NHibernate‟s help, and returned the results to the unit test as a
strongly typed listing of StaffMember objects. No datasets…no ADO.NET…no
connection object management…no stored procedures…no thousands of lines of auto-
generated data access code! To go a step further to ensure it‟ll integrate smoothly with
our development database, the CanConfirmDataMatchesMappings() test then ensures our
mapping-to-database configuration is properly setup.
6) There‟s nothing to refactor at this point, but a five minute break is certainly warranted!
Showing the Results in a View
The domain‟s been created, the data access mechanism exists, the controller has been
developed...the only thing remaining is to populate the development database with some
test data and to throw together a little HTML to pull it altogether. By the time you get
here, the view is but a minor afterthought.
In this tutorial, we‟ll be using ASP.NET MVC pages to create the views. But ASP.NET
MVC is so extensible that you can use a variety of mechanisms for developing your view.
The MVC Contrib documentation discusses four options at
http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=Documentation.
With respect to unit testing, there are a variety of opinions on how much of the view layer
should be unit tested. On one hand, the view layer changes so frequently that a lot of
maintenance is required to manage tightly coupled view-oriented unit tests via a tool such
as NUnitAsp (http://nunitasp.sourceforge.net) or Selenium (http://selenium.openqa.org).
On the other hand, it‟s not a bad idea to create web “smoke tests” to at least make sure
the stated URL isn‟t blatantly breaking. For an example of a web smoke test, see
http://geekswithblogs.net/Billy/archive/2006/05/10/77820.aspx. There are even tools for
unit testing your JavaScript…the options never end!
For this example, we‟ll get right to the heart of the matter and create the view:
1) Go to Northwind.Web/Views and add a new directory called StaffMembers. The name
of this folder needs to be the first part of the name of the StaffMembersController
controller class that it is associated with; consequently, it‟s very important that it‟s named
this way.
2) Under the StaffMembers directory, add a new Web Content Form called
ListStaffMembersMatchingFilter.aspx and select Site.Master (in /Views/Shared) as the
master page. The ASPX should be modified to reflect the following:
Matching Staff Members:
The corresponding code-behind should then look as follows:
using System.Web.Mvc;
using Northwind.Core;
using System.Collections.Generic;
namespace Northwind.Web.Views.StaffMembers
{
public partial class ListStaffMembersMatchingFilter :
ViewPage>
{
}
}
The code-behind could have inherited from ViewPage>, since that‟s
what the controller is giving it, but inheriting from
ViewPage> makes it a bit more flexible and reusable by a
variety of controllers which may not always have a List available.
3) Populate the table with test data to support passing the test.
USE Northwind
GO
INSERT INTO StaffMembers
VALUES ('thistest_Filter', 'Mike', 'Park')
INSERT INTO StaffMembers
VALUES ('ABC123', '_test_FiLtEr_', 'Vance')
INSERT INTO StaffMembers
VALUES ('GHI789', 'Lynette', 'Knackstedt')
INSERT INTO StaffMembers
VALUES ('DEF456', 'Gerry', 'Lundquistest_filtER')
4) Give it a whirl! Hit F5 and change the URL to reflect
http://localhost:####/StaffMembers/ListStaffMembersMatching?filter=test_Filter. You
should be presented with the same three users that your unit test returned from the SQLite
database. This is completely incidental since we happened to populate our Northwind
database with the exact same data that the unit test populated SQLite with. Note that
there is absolutely no need to keep the live database in synch with the data used by
the unit tests with SQLite. This is a terrific benefit of using an in-memory SQLite
database for unit testing data access methods and avoids developing fragile tests which
start breaking as soon as the data within the database changes. Assume that our unit test
instead depended on the Northwind database; if the test data gets out of synch with the
tests, then the tests start breaking. And once they start breaking, and aren‟t fixed quickly,
then developers stop running them. So any test that depends on live data in a persistent
database should be considered a fragile test and avoided unless absolutely necessary.
So What Was Accomplished?
The above steps have taken us through a test driven approach of designing a domain
model, creating a controller which uses a custom Repository, testing with a mock
database, testing custom data access methods against an in-memory SQLite database,
interacting with a live database, and displaying results in an MVC view. There‟s plenty
more to learn, but this covers the basics.
Don‟t forget that the CRUD scaffolding generator is also provided to make the process of
creating the entity, controller and views with very little effort. While this tutorial did not
leverage the generator, for pedagogical reasons, the CRUD scaffolding generator is your
friend and is a great starting point when you need to add a new domain entity. Also don‟t
forget that the scaffolding templates are completely customizable to meet your projects
needs…enjoy!
A Few S#arp Architecture Best Practices
All of the best practices described below should be seen as general rules of thumb which
are certainly subject to excepted cases. But any exception to the rule should have a
justifiable reason, accordingly.
Within the Web Layer
When using ASPX views
Within a data bound control, avoid using Eval(“PropertyName”). Instead, convert
the data item to a domain object to leverage strongly typed accessors; e.g.:
Configuring web.config
In production environments, include the following setting:
Disable session state by default:
Disable view state by default:
If you find that you absolutely have to have it on a particular page or control,
enable it on the page or control only:
For pages:
For controls:
Declare customer server controls in web.config:
Managing Routes
Routes are a facet of control logic. Accordingly, I prefer to keep route management
contained within a class within the controllers layer called RouteRegistrar.cs. This
facilitates unit testing without requiring a reference to Global.asax.cs and introduction a
great separation of concerns between view and control.
Within the Controllers Layer
When passing data from a controller to a view, Data Transfer Objects (DTOs) should be
used to encapsulate multiple data elements to be strongly type passed to a view. For
example, assume you have a ProductsController with an action called List. The view
data DTO, if it contains more than one type of object, would be called ListViewData and
would have public properties to hold the data elements to be passed on to the view.
(Suggested by http://weblogs.asp.net/scottgu/archive/2007/12/06/asp-net-mvc-
framework-part-3-passing-viewdata-from-controllers-to-views.aspx.) For organization
purposes, the view data DTOs should be placed within the controllers which they
support. So the aforementioned example would be referenced via
ProductsController.ListViewData.
For creating and editing an item, the "Create" action should be invoked to initialize a
form. The form should then submit itself to the "Create" action via the POST method
which will handle capturing the form data and saving the data item. See the
EmployeesController for a full CRUD example.
Within the Tests Layer
Test classes should be organized under sub-folders, having one sub-folder for
each layer being tested. For example, a folder would be added to
ProjectName.Tests called ProjectName.Core. This folder would contain all the
test fixtures for testing the Projectname.Core assembly.
The default namespace of this class library should be changed to “Tests” to avoid
namespace ambiguity.
Managing Solution Items
Whenever possible, assembly dependencies should never be placed into the GAC.
Instead, application dependencies should be maintained in a folder called “Solution
Items” which resides in the root of the source directory. This facilitates multiple
applications on a single server using different versions of an assembly.
NHibernate
Set .IsInverse(), or “inverse=true” if you‟re using HBMs, on the parent in a bi-
directional parent/child relationship to avoid foreign key constraint violations
(NRD 6.5 Lazy Initialization).
For organization purposes, only declare one class per mapping file.
For consistency, regardless of using HBMs or Fluent NHibernate, keep the
mapping artifacts in MyProject.Data.
Microformats
Per http://microformats.org/wiki/Main_Page, “microformats are small bits of HTML that
represent things like people, events, tags, etc. in web pages. Microformats enable the
publishing of higher fidelity information on the Web, providing the fastest and simplest
way to support feeds and APIs for your website.”
To be more concise, microformats are industry accepted standards for facilitating
semantic XHTML. CSS should be used to alter look and layout of microformat output.
The following microformat standards should be followed when applicable:
hCard (http://microformats.org/wiki/hcard) for people, companies, organizations,
and places.
hCalendar (http://microformats.org/wiki/hcalendar) for calendaring and events.
hReview (http://microformats.org/wiki/hreview) for reviews of products, services,
business, events, etc.
XFN (http://www.gmpg.org/xfn/) for organizational information, relationships,
and social networks.
XOXO (http://microformats.org/wiki/xoxo) for outlines.
If another microformat is available for the information being displayed, use when
appropriate.
Common Development Problems & Exceptions
Exception Troubleshooting
Exception: “The controller for path „/SomeArea/SomeObjects‟ could not be found or it
does not implement IController.” Alternatively, this exception may show up as “The
resource cannot be found.”
Fix: This is likely occurring because you have a missing route in
YourProject.Web.Controllers/RouteRegistrar.cs.
Exception: Reference to unmapped class
Fix: It is usually because the NHibernate HBM is not configured to be an embedded
resource. Right click the newly added HBM, click properties, and then set its
compilation mode to “Embedded Resource.”
Exception: Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information. (If the page is refreshed, it may then
say “The controller for path … could not be found…”
Fix: This is actually a mask over another exception due to the ASP.NET MVC
framework. To see the actual exception, add the following code to
Global.Application_Error: Exception exception = Server.GetLastError().
Then, place a breakpoint on the closing tag of Global.Application_Error and run the
application in debug mode. The exception variable in the debug window will hold the
details of the underlying exception.
Problem: Your host will not support running the application in Full Trust.
Fix: If you must run in a Medium Trust environment, the following modifications must
be made to the NHibernate configuration:
…
managed_web
Additionally, all class mappings need to be set to lazy=”false”.
Appendix A: Resources
Support for S#arp Architecture
Support for S#arp Architecture projects may be found at the official discussion group at
http://groups.google.com/group/sharp-architecture. The most current code may be found
at http://code.google.com/p/sharp-architecture/.
Referenced Works
[NBP]
NHibernate Best Practices with ASP.NET, 1.2nd Ed.
http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx
Contains detailed background materials and theory related to S#arp Architecture. The
CodeProject article is the predecessor to the current architecture.
[NRD]
NHibernate Reference Documentation Version 1.2.0.
http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/.
The definitive reference guide to NHibernate.
Domain Driven Design by Eric Evans.
The classic work for developing domain driven software.
Appendix B: Not So Frequently Asked Questions
(Obviously, I intend for this appendix to grow a bit more.)
Q: How do I register an NHibernate IInterceptor?
A: To register an IInterceptor, simply invoke the registration after initializing
NHibernateSession in Global.asax.cs; e.g.,
NHibernateSession.Init(new WebSessionStorage(this),
new string[] { Server.MapPath("~/bin/MyProject.Data.dll") });
NHibernateSession.RegisterInterceptor(new MyAuditLogger());
Q: How do I “upgrade” my S#arp Architecture project to include Fluent
NHibernate auto persistence mapping?
A: Starting with S#arp Architecture beta, Fluent NHibernate‟s auto persistence mapping
is supported. If you have no idea what I‟m talking about, take a quick look at
http://www.chrisvandesteeg.nl/2008/12/02/fluent-nhibernates-autopersistencemodel-i-
love-it/. Although supported, this is not configured by default in the Visual Studio
project template nor CRUD scaffolding generator. What follows are the steps for
upgrading the out of the box behavior to include Fluent NHibernate‟s auto persistence
mapping.
Note that, admittedly, converting an existing mid to large size project over to include
Fluent NHibernate‟s auto persistence mapping support can be challenging (even if you‟re
already using Fluent NHibernate and not HBMs) and may not be worth the effort due to
the possibility probability of hard to track down configuration problems. Accordingly, be
sure to make a back up of all your work before trying to convert it over to auto-
persistence mapping…just in case.
1. From YourProject.Data add a reference to Inflector.Net which will be found in
your solution‟s /lib directory.
2. Add an implementation of IAutoPersistenceModelGenerator to
YourProject.Data/NHibernateMaps. An example generator is as follows:
using FluentNHibernate;
using FluentNHibernate.AutoMap;
using Northwind.Core;
using SharpArch.Core.DomainModel;
using SharpArch.Data.NHibernate.FluentNHibernate;
using System;
namespace Northwind.Data.NHibernateMaps
{
public class AutoPersistenceModelGenerator
: IAutoPersistenceModelGenerator
{
public AutoPersistenceModel Generate() {
AutoPersistenceModel mappings = AutoPersistenceModel
.MapEntitiesFromAssemblyOf()
.Where(GetAutoMappingFilter)
.WithConvention(GetConventions);
return mappings;
}
private bool GetAutoMappingFilter(Type t) {
return t.Namespace == "Northwind.Core";
}
private void GetConventions(Conventions c) {
c.GetPrimaryKeyNameFromType =
type => type.Name + "ID";
c.FindIdentity = type => type.Name == "ID";
c.GetTableName = type =>
Inflector.Net.Inflector.Pluralize(type.Name);
c.IsBaseType = IsBaseTypeConvention;
c.GetForeignKeyNameOfParent =
type => type.Name + "ID";
}
private bool IsBaseTypeConvention(Type arg) {
bool derivesFromEntity = arg == typeof(Entity);
bool derivesFromEntityWithTypedId = arg.IsGenericType
&& (arg.GetGenericTypeDefinition() == t
typeof(EntityWithTypedId));
return derivesFromEntity ||
derivesFromEntityWithTypedId;
}
}
}
3. Take necessary corrective steps to align your entity properties and/or the database
tables and columns to be in alignment with the conventions defined in your
AutoPersistenceModelGenerator class.
4. Have all of your class maps inherit from AutoMap instead. You‟ll need to add a
reference to using FluentNHibernate.AutoMap; within each affected class file.
5. Modify YourProject.Web/Global.asax.cs to load the auto persistence model and
feed it to the NHibernate initialization process. The following example code
demonstrates how this may be accomplished:
AutoPersistenceModel autoPersistenceModel =
new AutoPersistenceModelGenerator().Generate();
NHibernateSession.Init(new WebSessionStorage(this),
new string[] { Server.MapPath("~/bin/Northwind.Data.dll") },
autoPersistenceModel);
6. Likewise, within
YourProject.Tests/YourProject.Data/NHibernateMaps/MappingIntegrationTests
class, feed the auto-persistence model to the NHibernate initialization process:
[TestFixture]
[Category("DB Tests")]
public class MappingIntegrationTests
{
[SetUp]
public virtual void SetUp() {
string[] mappingAssemblies =
RepositoryTestsHelper.GetMappingAssemblies();
AutoPersistenceModel autoPersistenceModel =
new AutoPersistenceModelGenerator().Generate();
NHibernateSession.Init(new SimpleSessionStorage(),
mappingAssemblies, autoPersistenceModel,
"../../../../app/Northwind.Web/Hibernate.cfg.xml");
}
7. Compile and hope for the best. ;)