I recently dived into DotNetNuke for some clients, to integrate an E-Commerce platform and develop some custom modules. One major downside to developing modules in DotNetNuke is that you cannot Unit Test them (due to the design of ASP.Net Webforms). This, of course, can be overcome with patterns like ‘Model View Controller’ and (for WebForms more appropriate) ‘Model View Presenter’.
I used the MVP pattern before in projects that use the Composite Web Guidance from Microsoft Pattern&Practices, so I was already familiar with the do’s and don’ts.
Btw: Phil Haack has a nice article on MVP in ASP.Net here, google if you need more information.
For my first custom DNN module I wanted to try to apply MVP with to the module. I can say it works pretty well, thanks to the transparent developing experience with DotNetNuke.
Because I will be developing a lot more DotNetNuke modules in the near future, I needed some kind of standard stack of frameworks and tools for developing these modules.
Here are some of the requirements:
- Auto registration of all views and presenters using an DI container (using Convention over Configuration)
- Container independent (trough CommonServiceLocator).
- Passive views: views don’t call the presenter directly. A presenter subscribes for events on the view.
- As less code as possible in the view. (automatic presenter instantiation. Again, using Convention over Configuration and auto registration)
- As less code as possible to setup the framework. We really don’t want to alter DotNetNuke core code too much. Configuring and initializing should be a matter of 1 or 2 lines (in globals.asax).
- I want to provide an alternative Data Access Layer than DotNetNuke does. There are a lot of good ORM solutions, I want one. I thought a moment about making the framework independent of this DAL implementation. But that is something I cannot do at the moment (deliver fast, code less).
I’m going to use NHibernate. I really like it and using FluentNHibernate you can do really neat stuff: Mapping your entities in code for easy refactoring / Auto mapping using Convention over Configuration / etc.
For NHibernate / FluentNH, I’ll let me inspire by the SharpArchitecture project. It’s a really nice base arhictecture for ASP.Net MVC applications (tough it can be used for other presentation frameworks too).
- I also want to provide some NUnit base fixtures and helper classes to aid in easy testing your views and presenters.
Now some code:
I have a very basic ASPX file:
1 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SampleModule.ascx.cs" Inherits="DotNetNukeMVP.SampleModule.SampleModule" %>
2 What is your name? <asp:TextBox runat="server" ID="tbName"></asp:TextBox>
3 <asp:Button runat="server" ID="btnSubmit" Text="Submit" /><br />
4 <asp:Literal runat="server" ID="ltMessage"></asp:Literal>
And the corresponding view:
24 public partial class SampleModule : PortalModuleView<SampleModulePresenter, ISampleModuleView>, ISampleModuleView
25 {
26 public event EventHandler<EventArgs> SampleButtonClick;
27
28 public string Name
29 {
30 get { return tbName.Text; }
31 set { tbName.Text = value; }
32 }
33
34 public string Message
35 {
36 get { return ltMessage.Text; }
37 set { ltMessage.Text = value; }
38 }
39
40 protected override void OnInit(EventArgs e)
41 {
42 base.OnInit(e);
43 btnSubmit.Click += new EventHandler(SampleButtonClick);
44 }
45 }
This view, inherits from PortalModuleView (DotNetNuke modules inherit from PortalModuleBase, that’s where the name comes from). Basically, what PortalModuleView does, is create the presenter, initialize it, setting some properties, etc.
Now the Unit test:
32 [Test]
33 public void Can_instantiate_view()
34 {
35 var view = new SampleModule();
36
37 Assert.That(view, Is.InstanceOfType(typeof(ISampleModuleView)));
38 Assert.That(view.Presenter, Is.Not.Null);
39 Assert.That(view.Presenter.View, Is.SameAs(view));
40 }
As you can see, the view is pretty minimalistic. It contains only the properties and events defining the view and an override of OnInit to bind the button event handler to the SampleButtonClick event.
The presenter (view.Presenter) gets automagically instantiated and initialized by PortalModuleView.
On to the presenter’s unit tests:
45 [Test]
46 public void SampleButtonClick_should_shout_Hello()
47 {
48 // setup view and create presenter
49 var view = Stub<ISampleModuleView>();
50 var presenter = CreatePresenter<SampleModulePresenter, ISampleModuleView>(view);
51
52 // We fill the textbox with "remco" (SampleModuleView.Name)
53 view.Name = "remco";
54
55 // Click the button!
56 view.Raise(x => x.SampleButtonClick += null, view, EventArgs.Empty);
57
58 // Assertions
59 Assert.That(view.Message, Is.EqualTo("Hello remco"));
60 }
(I’m using Rhino Mocks here). I think the comment are self explanatory.
Finally the presenter:
14 public class SampleModulePresenter : Presenter<ISampleModuleView>
15 {
16 public override void Initialize()
17 {
18 base.Initialize();
19 View.SampleButtonClick += View_SampleButtonClick;
20 }
21
22 private void View_SampleButtonClick(object sender, System.EventArgs e)
23 {
24 View.Message = "Hello " + View.Name;
25 }
26 }
In the next post I will show some more in-depth code of DotNetNukeMVP and out-line my ideas of an alternative Data Access Layer for DotNetNuke.