Embed
Email

ASP

Document Sample

Shared by: cuiliqing
Categories
Tags
Stats
views:
288
posted:
11/10/2011
language:
English
pages:
48
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. ;)



Related docs
Other docs by cuiliqing
11.1 Exploring Area and Perimeter
Views: 0  |  Downloads: 0
Volusia County
Views: 2  |  Downloads: 0
choosing_topics_and_y10
Views: 0  |  Downloads: 0
CLE Credit - rscrpubs.com
Views: 2  |  Downloads: 0
Meeting Minutes September 8 Final
Views: 0  |  Downloads: 0
nov2411
Views: 3  |  Downloads: 0
EKG Spreadsheet - Geocities.ws
Views: 0  |  Downloads: 0
Gift from Christ to the Church
Views: 0  |  Downloads: 0
By registering with docstoc.com you agree to our
privacy policy

You are almost ready to download!

You are almost ready to download!