Showing posts with label Fluent NHibernate. Show all posts
Showing posts with label Fluent NHibernate. Show all posts

Thursday, September 10, 2009

Part 2 - Extending Sharp Architecture with the version 1.0 of Fluent NHibernate’s ManyToManyTableConvention

 

In my last post I showed how to override S#arp Architecture’s implementation of Fluent NHibernate’s auto-mapping conventions. In the text that follows we will show how you can easily continue following the default behavior of S#arp Architecture and use convention over configuration. We will add a convention mapping strategy to automatically handle ManyToMany relationships. In doing so S#arp Architecture will be enabled to work with M:M entity relationships by default out of the box.

Before we dive into the implementation of code changes to facilitate this functionality, we will review the steps that will be required to implement some of the new FNH Interface improvements and changes introduced with the version 1.0 release. Our current project is based off of S#arp Architecture 1.0 which uses the version preceding Fluent NHibernate 1.0. Our project was previously using NHibernate 2.1.0.3001 and the new version of Fluent NHibernate compiles to NHibernate 2.1.0.4000. There has been mention by others that the Castle stack being used in S#arp Architecture requires an update also. In my situation the only Castle component requiring updating was to down-grade the Castle byte code provider from version 2.1.0.5642 to 2.1.0.0.

The Detour – Housekeeping tasks

I used a great new tool named to ease the pain of upgrading versions of Fluent NHibernate and its interdependent parts. I have heard about Horn for a while but had not spent any cycles on it till now. There is a great thread on the S#arp Architecture Google group discussion where a frustrated individual lamented the pain of upgrading Open Source Software. Horn also contains a discussion group and has a contrib group. It’s still in the infancy stages but is definitely worth a look. After downloading the binary of Horn, build it and then issue the following command line statements:

Horn –install:fluentnhibernate

Horn –install:nhibernate.validator

Through trial and error when building S#arp Architecture with the new version of NHibernate the existing NHibernate Validator assembly did not work properly with version 2.1.0.4 of NHibernate. Horn will look-up FNH’s hard dependencies, retrieve the projects and build them within your local Horn Package Tree. As an example I recently installed the project by Jimmy Bogard. I issed the following command line arguments against Horn.exe:

Horn –install:automapper

and a  little over a minute the result folder is populated as follows:

Horn1

All updated assemblies and any required dependent assemblies are placed into this location. Take the rebuilt assemblies from this folder and add them to your S#arp Architecture’s lib folder location. Rebuild the binaries for S#arp Architecture and then copy these binaries together with the updated binaries for Fluent NHibernate, NHibernate, NHibernate Validator to your project’s lib folder. Rebuild your project solution and it will likely fail with the following error:

Server Error in '/' Application.

Could not load file or assembly 'NHibernate, Version=2.1.0.3001, Culture=neutral, PublicKeyToken=aa95f207798dfdb4' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

The fix for this is to add a dependent assembly binding entry for the NHibernate 2.1.0.4000 binary in your web.config file. This was likely required to get the S#arp Architecture binaries to fully compile with an app.config entry and is sometimes forgotten in the projects that use the core framework libraries. Place the following configuration settings in your web config file’s <runtime> tag:

<dependentAssembly>
<
assemblyIdentity name="NHibernate" publicKeyToken="AA95F207798DFDB4" culture="neutral"/>
<
bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="2.1.0.4000"/>
</
dependentAssembly>


wrote a detailed 3 part series on individual changes required to get the conventions to compile with FNH 1.0. I recommend you review and make the changes that make sense for your projects use of the FNH conventions.



Addition of new convention classes




The Fluent NHibernate convention classes are located within your data assembly and are organized within a folder named NHibernateMaps as follows:



FNHChanges



I have added CustomManyToManyTableNameConvention and HasManyToManyConvention classes to the Conventions folder. Also notice the exclusion of the mapping classes.



Implement a class to derive from the ManyToManyTableNameConvention base class



public class CustomManyToManyTableNameConvention : ManyToManyTableNameConvention
{
protected override string GetBiDirectionalTableName(IManyToManyCollectionInspector collection,
IManyToManyCollectionInspector otherSide)
{
return Inflector.Net.Inflector.Pluralize(collection.EntityType.Name) +
Inflector.Net.Inflector.Pluralize(otherSide.EntityType.Name);
}

protected override string GetUniDirectionalTableName(IManyToManyCollectionInspector collection)
{
return Inflector.Net.Inflector.Pluralize(collection.EntityType.Name) +
Inflector.Net.Inflector.Pluralize(collection.ChildType.Name);
}
}



Our custom implementation of the ManyToManyTableNameConvention base class includes overrides for the table names for either Bidirectional or Unidirectional associations. This is a recommended approach from James Gregory to avoid tables being created for either side of the respective associations. 



Implement a class deriving from the IHasManyToManyConvention



    public class HasManyToManyConvention : IHasManyToManyConvention
{
public void Apply(IManyToManyCollectionInstance instance)
{
instance.Cascade.SaveUpdate();
}
}


Implement A Class to Override Foreign Key Naming For M:M and M:O Associations



public class CustomForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(PropertyInfo property, Type type)
{
if (property == null)
return type.Name + "ID";
return property.Name + "ID";
}
}



In all there is very little code to implement this new convention. The convention is bootstrapped as follows in the AutoPersistenceModelGenerator class. Focus your attention to the GetConventions method.




public AutoPersistenceModel Generate()
{
var mappings = new AutoPersistenceModel();
mappings.AddEntityAssembly(typeof(AppUser).Assembly).Where(GetAutoMappingFilter);
mappings.Conventions.Setup(GetConventions());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return mappings;
}
private static Action<IConventionFinder> GetConventions()
{
return c => {
c.Add<PrimaryKeyConvention>();
c.Add<ReferenceConvention>();
c.Add<HasManyConvention>();
c.Add<HasManyToManyConvention>();
c.Add<TableNameConvention>();
c.Add<CustomManyToManyTableNameConvention>();
c.Add<CustomForeignKeyConvention>();




    };
}



Within my test runs I perform sanity checks on my NHibernate maps and save schema and HBM file changes. Here is an excerpt from the test run showing just the areas of interest from introducing the new convention classes. What becomes apparent and is real cool is that the default for collection associations is a bag. I presume this is so since I am using an IList to manage this collection in either side of the association and FNH uses reflection to auto set the relationship to the .Net equivalent of the bag which is an IList. By default the inverse of the relationship is added without any explicit code and to the correct side of the relationship! This is pretty freaking cool if you ask me. 



Role.hbm.xml


<hibernate-mapping 
xmlns="urn:nhibernate-mapping-2.2"
default-access="property"
auto-import="true"
default-cascade="none"
default-lazy="true"> …




<bag cascade="save-update" 
inverse="true"
name="AppUsers"
table="AppUsersRoles">
<
key>
<
column name="RoleID" />
</
key>
<
many-to-many
class="VirtualAltNetRTM.Core.AppUser,
VirtualAltNetRTM.Core,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=null
">
<
column name="AppUserID" />
</
many-to-many>
</
bag>



AppUser.hbm.xml


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
default-access="property"
auto-import="true"
default-cascade="none"
default-lazy="true"> …



<bag cascade="save-update" 
name="Roles"
table="AppUsersRoles">
<
key>
<
column name="AppUserID" />
</
key>
<
many-to-many
class="VirtualAltNetRTM.Core.Role,
VirtualAltNetRTM.Core,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=null
">
<
column name="RoleID" />
</
many-to-many>
</
bag>



Here is the Edit of a recently created user. Note the checked values for both roles that I added this user to.



UserEdit1



Remove the user from both roles.



UserEdit2



In the final post for this series I will show the front-end changes made to make this all come together.



Links







Sunday, August 30, 2009

S#arp Architecture – Part 1: Implementing the M:M mapping override

User Story

Should be able to add a user to more than one role. Should be able to remove the user from specific roles.

Convention over Configuration

This topic is garnering quite a bit of interest of late in the .Net community. James Kovacs recently appeared on an episode of rocks discussing Convention over Configuration. He spent a fair amount of time discussing the move from XML configuration for NHibernate by using the conventions of Fluent NHibernate. By default S#arp Architecture implements the auto-mapping convention. This means that you only need to alter the convention for various edge cases, one of those being M:M associations. Knowing in advance that you will need to perform the override is handy hence the reason for today’s discussion.

Review of the Model

I have the following two entities forming a many-to-many relationship.

Mapping Override using Class Auto Mapping

I created a sub-folder within my data assembly naming it NHibernateMaps and then proceeded to add the following two classes within it. I added using statements for Fluent NHibernate’s AutoMap and AutoMap Alteration namespaces. The key mapping attributes below for both sides of the relationship are the WithParentKeyColumn and WithChildKeyColumn values.

public class AppUserMap : IAutoMappingOverride<AppUser>
{
public void Override(AutoMap<AppUser> mapping)
{
mapping.Id(x => x.Id, "AppUserID")
.WithUnsavedValue(0)
.GeneratedBy.Identity();

mapping.WithTable("AppUsers");
mapping.SetAttribute("lazy", "false");
mapping.Map(x => x.LoginName).WithLengthOf(50);
mapping.Map(x => x.Password).WithLengthOf(255);
mapping.HasManyToMany(x => x.Roles)
.WithTableName("AppUserRoles")
.WithParentKeyColumn("AppUserID")
.WithChildKeyColumn("RoleID")
.AsBag();
}
}
public class RoleMap : IAutoMappingOverride<Role>
{
public void Override(AutoMap<Role> mapping)
{
mapping.Id(x => x.Id, "RoleID")
.WithUnsavedValue(0)
.GeneratedBy.Identity();

mapping.Map(x => x.Name, "RoleName");
mapping.SetAttribute("lazy","false");
mapping.HasManyToMany<AppUser>(x => x.AppUsers)
.WithTableName("AppUserRoles")
.Inverse()
.WithParentKeyColumn("RoleID")
.WithChildKeyColumn("AppUserID")
.AsBag();
}
}

The choice of using a Bag, List, Set, Map or array structure to handle the transient collection in memory depends on the context of the requirement at hand. In our domain model above we are representing the User and Role associations as an IList structure that is equivalent to NHibernate’s IBag object and therefore we set the mapping to use a Bag. who blogs over at CodeBetter.com discusses this point in the Relationships section in Part 6 of a great series of articles that formed his book called .

In the next article I hope to implement the same override but by using the Conventional approach. Therefore there will not be any need to use any mapping classes. I will wrap up the series by indicating the additional changes needed in the UI and controller layers.