Friday, March 4, 2011

MVP Design Patterns with WinForms

In this long awaited entry dear reader who is likely only me,  I’ll aim to educate thee with the above noted two design patterns. I’ll highlight Model View Presenter and Model View Controller the latter most typically deployed as web applications and show a few examples of them in action within a windows forms project.

Our focus will be on separating behavioral and synchronization code away from the view into presentation class objects following the Single Responsibility Principal (SRP) coined by Robert C. Martin closely.

Applying these principles will result in the ability for us to test the design in smaller granular increments and also test behavior in isolation. I would recommend reading the views of Fowler on both of these patterns. When all code is self-contained behind the form developers will commonly duplicate common logic throughout the application. This is a definite anti-pattern and a maintenance nightmare.

Model View Presenter

According to the definition in Wikipedia the Model View Presenter is a UI design pattern engineered to facilitate automated unit testing and to improve the separation of concerns in the presentation logic. Furthering on from Wikipedia here are the definitions of each term in the pattern.

  • Model – interface that defines the data that will be displayed and acted upon in the UI
  • View – interface that displays the data and routes user commands (events) to the presenter for it to act on
  • Presenter – acts upon the model and the view retrieving data from repositories (model), persists it and format it for display in the view. Known as the middle-man assuming the same role as served by the Controller in the MVC pattern

 

Pattern Variations

There are two main variants to this design pattern namely Passive View and Supervising Controller the latter appearing to be used mostly within web applications.  With the Passive View interaction with the Model (data) is handled exclusively by the Presenter. The View is updated exclusively by the Presenter. With Supervising Controller the View interacts with the Model for simple data binding and the View is updated by the Presenter via data-binding.

Example – Supervising Controller

Here is a look at the project within Visual Studio. I have moved assemblies into separate projects.

BlogMVPVSSetup

The separation allows us to let the front-end be completely agnostic. We can easily re-use the assemblies easily within with a web project which is flexible and a good thing! This also allows us to freely test the Presentation layer and domain layer for meeting requirements. This example will focus solely on the Supervising Controller. 

Here is a screenshot of what we are aiming for. This is not an overly complex UI.

BlogMVP1

We will send messaging upon the successful completion of a task. When the state is such that success occurs the edit panel will become hidden and the message panel will become visible to the user. This is all handled via a Boolean property contained within the view’s interface.

BlogMVPSave

Here is a simple class called Task that will become our Model.

    public class Task
    {
        public string Name { get; set; }
        public bool Completed { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime CompletionDate { get; set; }
        public void Save() {}
    }


Here is the interface that is passed into the constructor of our Presentation layer.



public interface ITaskScreen
    {
        string TaskName { get; }
        bool Completed { get; }
        DateTime StartDate { get; }
        DateTime CompletionDate { get; }
        bool IsTaskSaved { set; }
        event EventHandler<EventArgs> Save;
    }


Within the constructor we also wire up the data binding for our domain objects values stored in the form. The ViewSave method will set each of the properties in the Model to the values set in the form. After the save finishes we wire up our message and set the visibility of the two panels within the form to their respective required states.



    public class TaskScreenPresenter
    {
        private readonly ITaskScreen _view;
        public TaskScreenPresenter (ITaskScreen view)
        {
            _view = view;
            Initialize();
        }
        private void Initialize()
        {
            _view.Save += ViewSave;
        }
        private void ViewSave(object sender, EventArgs e)
        {
            Task task;
            try
            {
                task = new Task
                           {
                               Name = _view.TaskName,
                               StartDate = _view.StartDate,
                               Completed = _view.Completed,
                               CompletionDate = _view.CompletionDate
                           };
                task.Save();
                _view.IsTaskSaved = true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }
    }


The code in the view is very compact and easy to get your head around. Lets have a look at that. The view references only the Presentation layer. This provides us with clear separation of responsibility. The view implements the ITaskScreen interface through the Presentation layer. When the view is loaded we call into the constructor of the Presentation layer through a private instance. The IsTaskSaved property sends messaging back to the UI notifying a successful Save action result.



    public partial class TaskUpdate : Form, ITaskScreen
    {
        private TaskScreenPresenter presenter;
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            presenter = new TaskScreenPresenter(this);
        }
        public TaskUpdate()
        {
            InitializeComponent();
        }
        private void btnSaved_Click(object sender, EventArgs e)
        {
            if (Save != null)
            {
                Save(this, EventArgs.Empty);
            }
        }
        public string TaskName
        {
            get { return txtTaskName.Text; }
        }
        public bool Completed
        {
            get { return chkComplete.Checked; }
        }
        public DateTime StartDate
        {
            get { return Convert.ToDateTime(txtStartDate.Text); }
        }
        public DateTime CompletionDate
        {
            get { return Convert.ToDateTime(txtCompleteDate.Text); }
        }
        public bool IsTaskSaved
        {
            set
            {
                if (!value) return;
                pnlEdit.Visible = false;
                pnlMessage.Visible = true;
            }
        }
        public event EventHandler<EventArgs> Save;
    }

Friday, November 12, 2010

Craftsmanship

I am starting to re-flex the writing muscle. It's been a while. I listened to a great discussion on TDD this morning via Software Engineering Radio. Kent Beck delivers a very interesting view on the subject. I think this is required listening by all participants who value their contributions to the craft of software development. It's time to shed any prior dumbpiphany conversations regarding the evolution of a software development project. Enjoy your brew!

Tuesday, December 15, 2009

Using jQuery on the client with JSON data for smart client-side data filtering

 

Background

Recently I started using some of the more advanced features of jQuery via a FUBU MVC project and from this experience I have grown a deep love and appreciation for the power it brings to the web developers set of tools. jQuery is a full-featured library add-in to JavaScript that enables DOM traversal. To more easily grok what jQuery sets out to accomplish, think of traversing the browser DOM in a SQL query like manner. With jQuery I will demonstrate that you can easily parse through a JSON data set stored in the DOM. The solution requires client side filtering for three selectors that reside on a page that displays a grid of data. The users desire an AJAX like selection process. We will store the JSON data in a hidden field mimicking the initial data set. This hidden field will additionally contain fields for the client-side filtering that are not part of the grids initial display.

View Models

FUBU MVC is an opinionated approach to web development. One of the underlying concepts of the FUBU MVC architecture that I think is fundamentally strong is what the authors refer to as the “Thunderdome Principal”. One model in and one model out. As I mentioned at the outset above, our out model defined for the view will need a property containing the entire record set that’s initially sent to the view together with additional filter selectors. To aid in the transformation to and from jQuery to JSON we will utilize an additional jQuery plug-in called jQuery-JSON. Jason Grundy, one of the Elegant Coders provides an insightful discussion on this plug-in and complements the ideas I will discuss here together with applicable code for wiring up the solution. The idea is not that difficult. A word of caution, when your data set is in the thousands of rows this approach may not be the most sensible choice due to the amount of data passing over the wire. The first step is to utilize the JavaScriptSerializer class that is contained within the System.Web.Script.Serialization namespace. To serialize data to the client, simply call the Serialize method and conversely to fill a property with JSON data coming back from the client call the Deserialize method. The MapLocations property below is of type IEnumerable<T> and is initially mapped to a user control contained within our view that will render a list of mapping locations. We will include three drop-down selectors directly above the grid of map locations for implementing the client side filtering requirement. The property TargetMapLocationsListJson which is of type string, will contain the data in JSON format for the grid and also the fields required for filtering.

 outModel.MapLocations = locationList;
 outModel.TargetMapLocationsListJson = _javaScriptSerializer.Serialize( outModel.MapLocations);
 
 

View


 <div id="selectedMapLocations">
                <table class="data-grid">
                    <thead>
                        <tr>
                            <th>
                                Store Name
                            </th>
                            <th>
                                Sales
                            </th>
                            <th>
                                Number of Employees
                            </th>
                            <th>
                                Action
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <%=this.RenderPartial().Using<ListMapLocationsView>().WithDefault
                            ("No locations exist for these choices").WithoutItemWrapper().WithoutListWrapper()
                                                    .ForEachOf(Model.MapLocations)%>
                    </tbody>
                </table>
            </div>



Our view model that displays the grid of map locations passes into the filtered list view a strongly typed list of store locations that are rendered by a ForEachOf HTML helper extension included within the FUBU MVC’s core framework. Ryan Kelley of Elegant Code wrote a great series on FUBU MVC and he covers wiring up the view model with the view in greater detail than I will here. If you are committed to using the FUBU MVC architecture I highly recommend that you check out his 4 part series.


We set up a hidden control on the page to hold the serialized JSON data that will mimic what is displayed within the selectedMapLocations div that is populated with another FUBU MVC helper extension called the HiddenFor control.


<div id="mapLocationsList">

                <%= this.HiddenFor(x => x.TargetMapLocationsListJson).ElementId("targetMapLocationsListJSON")%>

</div>




 


JavaScript


And now we can move on to the JavaScript. There will not be any server involvement until the user clicks the submit button. You will note above in the HTML for the data-grid that we have included a column named Action. This column will contain a button click event allowing the user to add the various map locations to their profile. This will add the location selection to a hidden field that is converted to JSON so that we can Deserialize the selections on the server. We will then add each of the selections to IEnumerable<T> property that is contained within the Domain object.


On document ready we created a function that contains initialization methods, event handling routines and associated helper routines. We set three global Boolean properties to false. As previously noted we use three filters that are invoked by dropdown controls. On initialization each of these dropdown controls are set to null. Then each of the dropdown event handlers are instantiated. When a user changes a selection the event handler associated to the dropdown control fires as noted below for State.


            function initSelectedStateChangeEventHandling() {
                $("#list-maplocations-state").change(function() {
                    if (stateChanged == false && $(this).val() == "") return;
                    $("#list-maplocations-state option:selected").each(function() {
                        stateChanged = true;
                        findMapLocationsByFilter($(this).val(), 'state');
                    })
                });
            }





What should be evident from the above JavaScript snippet is that when the Boolean property is no longer false and the selection value is not null then we for each through the users selection choices and call into a function that performs the client-side filtering. Lets have a look at the findMapLocationsByFilter function.


            function findMapLocationsByFilter(criteria, filterType) {
                var originalList = $('tbody');
                originalList.find('tr,td').remove();
                var data = $("#targetMapLocationListJSON").val();
                if (data == "") return;
                $.each($.evalJSON(data), function() {
                    originalList.append(getSelectedFilteredMapsHtml(this, criteria, filterType));
                });
            }



This is a generic function that is used by all dropdown selectors. In this function we utilize jQuery selectors for the existing content within the tbody element. We then modify the selector to contain only the elements within the tr and td tags. We then call the remove method on the content contained within the elements. A new selector is instantiated with the content contained in the hidden field that is structured as JSON data. We utilize the jQuery for each method together with the jQuery-JSON plugin’s evalJSON method. Each data element in the hidden field is passed into another function that will append the filtered content back into the selector that we previously cleared. Lets now have a look and the getSelectedFilteredMapsHtml function.


    function getSelectedFilteredmapsHtml(mapData, criteria, filterType) {   
        if (filterHasData(mapData, criteria, filterType)) {   

return "<tr id=" + "maplocations-maplist-row-" + mapData.mapId + "" + " stateId=" + mapData.StateId

                + " countyId=" + mapData.CountyId + " zipId=" + mapData.ZipCodeId + ">"   
                + "<td id='storeName'>" + mapData.StoreName + "</td>"   
                + "<td id='sales'>" + mapData.Sales + "</td>"   
                + "<td id='numEmployees'>" + mapData.NumberOfEmployees + "</td>"   
                + "<td id='includemaps'>"  
                + "<span class='form-item'" + ">"  
                + "<input id=" + "save-maplocations-" + mapData.mapId + " type=button" +  " value='Include Location'" + "></input>"  
                + "</span>"  
                + "</td></tr>"; 

}

              }






Clearly there will not be any row returned if the filter contains no data. The filterHasData function compares the stateId within the JSON data to the value of the selectors changed stateId. If they are equal then we return and append content back to the div we cleared earlier. The filterHasData function is quite simple and shown below.


            function filterHasData(mapData, criteria, filterType) {
                switch (filterType) {
                    case "state":
                        $("#list-maplocations-county").val("");
                        $("#list-maplocations-zip").val(""); 
                        return (mapData.StateId == criteria);
                        break;
                    case "county":
                        $("#list-maplocations-state").val("");
                        $("#list-maplocations-zip").val("");                        
                        return (mapData.CountyId == criteria);
                        break;
                    case "zip":
                        $("#list-maplocations-state").val("");
                        $("#list-maplocations-county").val("");
                        return (mapData.ZipCodeId == criteria);
                        break;
                    default:
                        return null;
                }
            }



What has become quite evident to me from having dabbled in a couple of MVC frameworks is the importance of knowing how to use JavaScript correctly. Adding a few extensions to it really assists you in developing smarter and more powerful UI experiences. For added assistance I would recommend the following books:



  1. Manning – jQuery in Action, Bear Bibeault & Yehuda Katz
  2. O’Reilly – JavaScript: The Good Parts, Douglas Crockford

I hope to post a detailed solution with all code in the near future. I appreciate any comments or alternatives the approach I have taken.

Sunday, October 4, 2009

NH Profiler – Using Filters to find the needle in the haystack

The allows a developer to analyze the behaviors between the domain and data layer more effectively. If you are using NHibernate I think this piece of software should be in your arsenal of tools. Oh, and for you Java folks it works against Hibernate. image

Session Filtering

Before I start the analysis notice to the left we have an unfiltered view of our current sessions. If you look a bit closer, you’ll notice that the recent statements list contains some added noise. To remedy this we will apply a filter to all session statements containing a url path for image,css and JavaScript files.

Locate the Filter component, in the upper right window of NH Profiler. imageBy default filtering is Inactive. So lets define one custom to our session!

  1. Click on the filter icon and in the dropdown list of the Edit Filter window choose filtering for  “Sessions by URL.
  2. Click the Add button. image
  3. Choose the not containing operator.
  4. Enter the text for filtering. In my example I want to filter URL’s being sourced with the text '/Assets/’. 
  5. Click apply.

 

And voila! we now have a more concise session list.image

When the analysis is complex try to make it simpler with filters! In a future discussion we will look into how NH Prof can diff sessions and what insight we can gain from doing this.

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







Wednesday, September 9, 2009

VAN:S#arp Architecture Revisited – Advanced Techniques – November 4, 2009

Topic

Billy McCafferty will join us once again on the heels of releasing Service Pack 1 for S#arp Architecture version 1. He will spend some time reviewing feature improvements, changes and add more context to the framework where time was not permitted in the first meeting. If you have any specific questions you would like addressed during the evening please add a question to our group and we will make sure Billy is ready to answer it.

Bio

Who is and what makes this Billy McCafferty guy tick? Well he is a long time developer and a hopeless romantic when it comes to writing beautiful software. Billy currently leads a double life between helping to run the world's greatest IT training school at http://www.itsamuraischool.com/ and filling the part-time role of lead developer and architect with Parsons Brinckerhoff. Billy is enjoying getting a bit of his life back after the recent release S#arp Architecture 1.0 and is currently hard at work on the first quarterly release later in September of 2009.

What is VAN?

Virtual ALT.NET (VAN) is the online gathering place of the ALT.NET community. Through conversations, presentations, pair programming and programming dojo’s, we strive to improve, explore, and challenge the way we create software. Using net conferencing technology such as Skype and LiveMeeting, we hold regular meetings, open to anyone, usually taking the form of a presentation or an Open Space Technology-style conversation.

Please see the Calendar to find a VAN group that meets at a time
convenient to you, and feel welcome to join a meeting. Past sessions can be found on the Recording page.

To stay informed about VAN activities, you can subscribe to the Virtual ALT.NET (VAN) Google Group and follow the Virtual ALT.NET blog.

Meeting Details

Times below are Central Daylight Time
Start Time: Wed, November 4, 2009 8:00 PM UTC/GMT -5 hours
End Time: Wed, November 4, 2009 10:00 PM UTC/GMT -5 hours
Attendee URL: Attend the meeting (Live Meeting)

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.