Archive
Monthly
Go
|
|
DNN Blog
Apr
12
Posted by:
Philip Beadle
Monday, April 12, 2010 12:42 PM
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. - An Interface which defines the View
- A Model class which will be passed between the Presenter and the View
- A Concrete View class (the User Control which renders the content to the Response stream) and
- 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.
- Create a new Project and select the DotNetNuke Compiled Module option.
- Follow the setup instructions for the module.
- Change the .Net Framework version to .Net 3.5
- Add a reference to DotNetNuke.dll, DotNetNuke.Web.dll and WebFormsMVP.dll.
- In the Components folder add a folder for each of the following:
- Controller
- DataProvider
- Models
- Presenters
- Views
- Move the WebformsMVPController.vb file into the Controller folder
- Move the SqlDataProvider.vb and DataProvider.vb into the DataProvider folder.
- 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
- 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
- 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
- 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
- Change the DataProvider file name to IDataProvider.
- 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
- Add a private variable for the ModuleQualifier:
1: #Region "Private Members"
2:
3: Private Const ModuleQualifier As String = "YourCompany_"
4:
5: #End Region
- 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
- 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
- Lastly in the Controller class change the DataProvider.Instance() to _DataProvider.
- 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.
6 comment(s) so far...
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
|
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
|
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
|
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
|
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
|
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
|
|