DNN Blog

Apr 12

Posted by: Philip Beadle
Monday, April 12, 2010 12:42 PM  RssIcon

This morning at the QA team meeting we discussed the Testable DNN Module and its use of Webforms MVP to make it testable.  During the conversation we decided we needed some step by step information on how it all ties together in a “Michael Washington” style tutorial.  Charles Nurse has written up a great post on the theory behind the use of Webforms MVP here, so this tutorial will be more along the lines of what to do not why you do it.  Please make sure you read Charles’ post because I am assuming you have.

As a quick reminder here’s the list of parts Charles talks about.  There are four components for each MVP Module Control.

  1. An Interface which defines the View
  2. A Model class which will be passed between the Presenter and the View
  3. A Concrete View class (the User Control which renders the content to the Response stream) and
  4. A Presenter Class which manages everything

Interfaces for Everything

The main reason for moving your code out of the code behind file and into the presenter is so you can test it because now it is just a class and does not rely on there being a httpContext etc that a code behind file needs.  In addition to the classes Charles mentions above, to make sure your presenter class is fully testable you need to make sure that any time it calls another class that class has an interface so you can mock it out.  Take a look at the OnLoad method in EditTestDrivenDNNModulePresenter, you’ll see that it calls the controller class which means you need a controller class interface as well.  Along the same lines take a look at TestDrivenDNNModuleController and you’ll see it uses an interface when calling the SqlDataProvider.  Having an interface for each touch point makes it very easy to use a mocking framework such as Moq to create mocks for your tests.

The last class that I have in the solution that is not mentioned in Charles’ post is the UIEventArgs class.  This class is used to pass information from the View to the Presenter.  Take a look at the Save method in the EditTestDrivenDNNModulePresenter class. 

   1:  Public Sub Save(ByVal sender As Object, ByVal e As UIEventArgs(Of TestDrivenDNNModuleInfo))

Doing this allows the presenter to be given the data it requires without having to resort ot ViewState in the view.  This means you can actually turn off ViewState on your controls and save on the payload.

Build a WebformsMVP DNN Module

The following will lead you through how to build a WebformsMVP DNN Module starting from the StarterKit Compiled module.  The end result will be a module similar to the Testable DNN Module .  This article assumes you are using DNN 5.4.  You can get the latest code from CodePlex here http://dotnetnuke.codeplex.com/SourceControl/list/changesets.  Part 1 gets you set up by modifying the existing files in the starter kit.

  1. Create a new Project and select the DotNetNuke Compiled Module option.
    image
  2. Follow the setup instructions for the module.
  3. Change the .Net Framework version to .Net 3.5
    image
  4. Add a reference to DotNetNuke.dll, DotNetNuke.Web.dll and WebFormsMVP.dll.
  5. In the Components folder add a folder for each of the following:
    1. Controller
    2. DataProvider
    3. Models
    4. Presenters
    5. Views
      image
  6. Move the WebformsMVPController.vb file into the Controller folder
  7. Move the SqlDataProvider.vb and DataProvider.vb into the DataProvider folder.
  8. If you have a tool such as Resharper extract the interface for the WebformsMVPController into a separate file.  You should get an interace like this:
       1:  Namespace YourCompany.Modules.WebformsMVP
       2:      Public interface IWebformsMVPController
       3:          Function GetWebformsMVPs(ByVal ModuleId As Integer) As List(Of WebformsMVPInfo)
       4:          Function GetWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer) As WebformsMVPInfo
       5:          Sub AddWebformsMVP(ByVal objWebformsMVP As WebformsMVPInfo)
       6:          Sub UpdateWebformsMVP(ByVal objWebformsMVP As WebformsMVPInfo)
       7:          Sub DeleteWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer)
       8:      end interface
       9:  End NameSpace
  9. If you don’t have a refactoring tool create the interface above manually and then implement it on your like so:
    I have only shown the first method implemented.
       1:      Public Class WebformsMVPController
       2:          Implements Entities.Modules.ISearchable
       3:          Implements Entities.Modules.IPortable
       4:          Implements IWebformsMVPController
       5:   
       6:  #Region "Public Methods"
       7:          Public Function GetWebformsMVPs(ByVal ModuleId As Integer) As List(Of WebformsMVPInfo) _
       8:              Implements IWebformsMVPController.GetWebformsMVPs
       9:              Return CBO.FillCollection(Of WebformsMVPInfo)(DataProvider.Instance().GetWebformsMVPs(ModuleId))
      10:          End Function
  10. Add two constructors to the controller.  The constructor with no arguments is used in production and the one with the IDataProvider passed in is used for testing. 
       1:          Private _DataProvider As IDataProvider
       2:   
       3:          Public Sub New()
       4:              _DataProvider = DotNetNuke.ComponentModel.ComponentFactory.GetComponent(Of IDataProvider)()
       5:              If _DataProvider Is Nothing Then
       6:                  ' get the provider configuration based on the type
       7:                  Dim defaultprovider As String = DotNetNuke.Data.DataProvider.Instance.DefaultProviderName
       8:                  Dim dataProviderNamespace As String = "YourCompany.Modules.WebformsMVP"
       9:                  If defaultprovider = "SqlDataProvider" Then
      10:                      _DataProvider = New SqlDataProvider
      11:                  Else
      12:                      Dim providerType As String = dataProviderNamespace + "." + defaultprovider
      13:                      _DataProvider = CType(Framework.Reflection.CreateObject(providerType, providerType, True), IDataProvider)
      14:                  End If
      15:                  DotNetNuke.ComponentModel.ComponentFactory.RegisterComponentInstance(Of IDataProvider)(_DataProvider)
      16:              End If
      17:          End Sub
      18:   
      19:          Public Sub New(ByVal dataprovider As IDataProvider)
      20:              _DataProvider = dataprovider
      21:          End Sub
  11. Now we need to create the IDataProvider interface.  Do this by changing the DataProvider class into an interface and removing the shared and static methods.  Then make the MustOverride functions into normal interface functions like so:
       1:      Public Interface IDataProvider
       2:   
       3:          Function GetWebformsMVPs(ByVal ModuleId As Integer) As IDataReader
       4:          Function GetWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer) As IDataReader
       5:          Sub AddWebformsMVP(ByVal ModuleId As Integer, ByVal Content As String, ByVal UserId As Integer)
       6:          Sub UpdateWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer, ByVal Content As String, ByVal UserId As Integer)
       7:          Sub DeleteWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer)
       8:   
       9:      End Interface
  12. Change the DataProvider file name to IDataProvider.
  13. Now we need to modify the SqlDataProvider class to use the new Interface.  Open the SqlDataProvider class and change the Inherits DataProvider to:
       1:      Public Class SqlDataProvider
       2:   
       3:          Implements IDataProvider
  14. Add a private variable for the ModuleQualifier:
       1:  #Region "Private Members"
       2:   
       3:          Private Const ModuleQualifier As String = "YourCompany_"
       4:   
       5:  #End Region
  15. Next we need to change the ReadOnly properties so that we can set the values.  We need to be able to set the values so that during testing we can specify the values.  The code below will retrieve the value for the web.config file if it is not set.  This means during testing the value is set but in production the value comes from the config file.
       1:  #Region "Properties"
       2:          Private _connectionString As String = String.Empty
       3:          Public Property ConnectionString() As String
       4:              Set(ByVal value As String)
       5:                  _connectionString = value
       6:              End Set
       7:              Get
       8:                  If _connectionString = String.Empty Then
       9:                      Return DotNetNuke.Data.DataProvider.Instance().ConnectionString
      10:                  Else
      11:                      Return _connectionString
      12:                  End If
      13:              End Get
      14:          End Property
      15:   
      16:          Private _databaseOwner As String
      17:          Public Property DatabaseOwner() As String
      18:              Set(ByVal value As String)
      19:                  _databaseOwner = value
      20:              End Set
      21:              Get
      22:                  If _databaseOwner = String.Empty Then
      23:                      Return DotNetNuke.Data.DataProvider.Instance().DatabaseOwner
      24:                  Else
      25:                      Return _databaseOwner
      26:                  End If
      27:              End Get
      28:          End Property
      29:   
      30:          Private _objectQualifier As String
      31:          Public Property ObjectQualifier() As String
      32:              Set(ByVal value As String)
      33:                  _objectQualifier = value
      34:              End Set
      35:              Get
      36:                  If _objectQualifier = String.Empty Then
      37:                      Return DotNetNuke.Data.DataProvider.Instance().ObjectQualifier
      38:                  Else
      39:                      Return _objectQualifier
      40:                  End If
      41:              End Get
      42:          End Property
      43:   
      44:  #End Region
  16. Remove the Overrides from each of the public methods and add the Implements to each one so that the IDataProvider interface is properly implemented:
       1:  #Region "Public Methods"
       2:   
       3:          Public Function GetWebformsMVPs(ByVal ModuleId As Integer) _
       4:          As IDataReader Implements IDataProvider.GetWebformsMVPs
       5:              Return CType(SqlHelper.ExecuteReader(ConnectionString, _
       6:                     GetFullyQualifiedName("GetWebformsMVPs"), ModuleId), IDataReader)
       7:          End Function
       8:   
       9:          Public Function GetWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer) _
      10:          As IDataReader Implements IDataProvider.GetWebformsMVP
      11:              Return CType(SqlHelper.ExecuteReader(ConnectionString, _
      12:                      GetFullyQualifiedName("GetWebformsMVP"), ModuleId, ItemId), IDataReader)
      13:          End Function
      14:   
      15:          Public Sub AddWebformsMVP(ByVal ModuleId As Integer, ByVal Content As String, ByVal UserId As Integer) _
      16:          Implements IDataProvider.AddWebformsMVP
      17:              SqlHelper.ExecuteNonQuery(ConnectionString, _
      18:              GetFullyQualifiedName("AddWebformsMVP"), ModuleId, Content, UserId)
      19:          End Sub
      20:   
      21:          Public Sub UpdateWebformsMVP(ByVal ModuleId As Integer, _
      22:                                       ByVal ItemId As Integer, ByVal Content As String, ByVal UserId As Integer) _
      23:          Implements IDataProvider.UpdateWebformsMVP
      24:              SqlHelper.ExecuteNonQuery(ConnectionString, _
      25:              GetFullyQualifiedName("UpdateWebformsMVP"), ModuleId, ItemId, Content, UserId)
      26:          End Sub
      27:   
      28:          Public Sub DeleteWebformsMVP(ByVal ModuleId As Integer, ByVal ItemId As Integer) _
      29:          Implements IDataProvider.DeleteWebformsMVP
      30:              SqlHelper.ExecuteNonQuery(ConnectionString, _
      31:              GetFullyQualifiedName("DeleteWebformsMVP"), ModuleId, ItemId)
      32:          End Sub
      33:   
      34:  #End Region
  17. Lastly in the Controller class change the DataProvider.Instance() to _DataProvider.
  18. Build your project now. 

Part 1 showed you how to modify the files from the starterkit ready to be able to test the SqlDataProvider and the Controller classes.  Part 2 will show you how to set up the classes required for using WebFormsMVP.

Tags:
Categories:
Location: Blogs Parent Separator Philip Beadle

6 comment(s) so far...


Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Thanks Phil, this is exactly what I think we needed to get going with this.

By Chris Paterra on   Monday, April 12, 2010 3:23 PM
Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Thanks Chris, very nice way to go

By Ernst Peter Tamminga on   Tuesday, April 13, 2010 3:01 PM
Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Does this work with 5.3.1? Not everybody wants to pull down the latest build.
Thanks.

By Robert on   Tuesday, April 13, 2010 3:01 PM
Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Robert,
Part 1 will but part 2 uses the new classes we had to change in 5.4 to make writing tests better.

By Philip Beadle on   Tuesday, April 13, 2010 3:02 PM
Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Hi Phil,

Question: Is the DotNetNuke.Web.Mvp.dll actually in 5.4.1? I've downloaded the latest of the Install, Starter Kit, Upgrade, etc, and none of them seem to have this library. Can you give a bit of guidance (or did it not actually make the final distribution for that build?).

Thanks!

By Daniel Gilleland on   Monday, May 10, 2010 9:20 AM
Gravatar

Re: Step by Step Webforms MVP and DotNetNuke – Part 1.

Hi Daniel,
The dll youre looking for is called DotNetNuke.Web.dll and the other one is WebFormsMvp.dll. Theya re both in the distro sicne 5.3.0.

By Philip Beadle on   Monday, May 10, 2010 9:21 AM
Attend A Webinar
Free Demo Site
Download DotNetNuke Professional Edition Trial
Have Someone Contact Me
Have Someone Contact Me
Charlotte DoDNN

Like Us on Facebook Join our Network on LinkedIn Follow DNN Corporate on Twitter Follow DNN on Twitter

Advertisers

r2integrated
Telerik JustCode Free
Exact Target Exec Alert

Sponsors

DotNetNuke Corporation

DotNetNuke Corp. is the steward of the DotNetNuke open source project, the most widely adopted Web Content Management Platform for building web sites and web applications on Microsoft. Organizations use DotNetNuke to quickly develop and deploy interactive and dynamic web sites, intranets, extranets and web applications. The DotNetNuke platform is available in a free Community and subscription-based Professional and Enterprise Editions with an Elite Support option. DotNetNuke Corp. also operates the DotNetNuke Store where users purchase third party apps for the platform.