One of the questions while migrating IWeb to WCF, was how to maintain the same header as previous versions of IWeb. This header is important due to the fact that it carries with it the portalId and user credentials for the request. We found a couple of possible ways to maintain this functionality. The option we did not choose involved using message inspectors, but this seemed very foreign to the IWeb way of things.
So, we decided to go with MessageContracts for input and output parameters for all methods. While this may appear to be a limitation at first, once you are accustom to the concept it is actually very intuitive and flexible! We will even be providing some generic contracts for you to use for simple method signatures, such as a function that returns a string and takes no parameters. There is also a base class, IWebRequest that we encourage you to inherit from when you have a need to create your own message contract. Below is a diagram of some of the message contracts used by the IWeb core.
As you can see, all of the requests derive from IWebRequest in order to get the header attached to the request for free. This holds true for the generic message contracts provided by the core as in the following diagram:
These classes were created as an ease of use feature for developers creating extension methods for IWeb. This allows you to pass up to two strong typed parameters to a method without requiring you to create your own message contract. We may expand upon this concept if the community speaks out and says "two parameters are not enough!" Even so, I strongly recommend creating your own message contracts once you cross the two parameter threshold. Imagine, a message with 15 parameters all named Parameter1 through Parameter15... do you smell that? Take the encryptionRequest class for example, very easy to swap the key for the text if they are name Parameter1 and Parameter2. Which is exactly why we created that message contract for encryption requests.
With that said, creating your own message contract for your extension method is actually very easy and will probably make your code more readable. Here is there code for the AddFolderRequest message contract:
Public Class AddFolderRequest
Private _NewFolderName As String = String.Empty
Public Property NewFolderName() As String
Set(ByVal value As String)
_NewFolderName = value
With this message contract in place the AddFolder signature then becomes: Public Sub AddFolder(ByVal msg As AddFolderRequest)
With that single parameter you are passing all of the information needed to authenticate, authorize and process the request. If you are thinking, "you could have used IWebRequest(Of T1) instead of creating the AddFolderRequest class!" You are correct! The method signature could very easily have been: Public Sub AddFolder(ByVal msg As IWebRequest(Of String)) Changing the signature to use the generic request would not change you would process the request beyond the name of the member you would call to get the name of the folder. NewFolderName versus Parameter1. Obviously, the first option is much more readable and the generics are there only for ease of use.
So far, I have shown you all about message requests, and that's great, right?! But what good is an SOA framework if you cannot get data back from it? Enter IWebResponse(Of T)! As you may have assumed this works in the opposite direction as the IWebRequest contracts. However, it is still simply a message contract. On the response side we only provide one generic return type. Similar to the request, I suggest any time you need to return a complex object, that you create your own Data Contract for the response type. If you are returning a value type then it should be acceptable to use IWebResponse(Of String) or IWebResponse(Of Integer) etc.
However, more complex types such as an object containing information about a user account will need to have a custom data contract as shown below:
If you look closely at the UserInfoDataContract class you may notice that it looks almost identical to the UserInfo class from the DotNetNuke framework. You are correct, and as a matter of fact we tried using UserInfo before creating our own data contract. In doing so, we learned an important lesson: the type specified in IWebResponse(Of T) must be serializable at a minimum, preferably marked as a data contract. This fact is the primary reason I recommend creating your our message and data contracts over using the generic classes we provide. While it make take slightly more effort, you will be explicit about your intent and it will lessen the chance of running into quirky behavior such as trying to return an unserializable object.
You may have also noticed that, the constructor of UserInfoDataContract takes a UserInfo object as a parameter. This is another ease of use feature. In order to turn the unserializable UserInfo object into a WCF compliant type, simply pass it to the construct of the data contract and away you go. The constructor simply maps the values of the original object into the members of the data contract.
Public Class UserInfoDataContract
Public Sub New(ByVal info As UserInfo)
Me.AffiliateID = info.AffiliateID
Me.DisplayName = info.DisplayName
Me.Email = info.Email
Me.FirstName = info.FirstName
Me.IsSuperUser = info.IsSuperUser
Me.LastName = info.LastName
Me.Password = info.Membership.Password
Me.PortalID = info.PortalID
Me.Roles = info.Roles
Me.UserID = info.UserID
Me.Username = info.Username
There is one more change to the process of creating IWeb methods. Given that we are moving to WCF, the WebMethodAttribute is going away. This is normally a good thing, but as you may know, we have been using the description field of that attribute to hold administrative meta data about each method. So, we have create the IWebMethodAttribute class that should now be used to specify the administrative data like so:
<IWebMethod(DefaultSecuritySetting:=IWebMethodAttribute.MethodSecurityLevels.Host, _ ,
Group2:="Portal" Group1:="IWEB Misc" Description:="Retrieves IWeb Version", _
Name:="GetVersion" Application:="IWeb" Company:="DotNetNuke")> _
Public Function GetVersion(ByVal msg As IWebRequest) As IWebResponse(Of String)
Leaving this attribute off of the method will prevent the method from being registered in the IWeb administration system.
This article has outline some of the more drastic changes we have made while migrating the server side of IWeb to WCF. There are more changes in the works, but we hope to shield the public api as much as possible from these changes. This is the first in a series of articles we will be posting regarding changes to IWeb. I hope to have the "Changes to IWeb windows client programming" very soon. Just as a teaser, the client changes should be drastically less, and you will be surprised how the things described in this article translate to the client proxy. It is truly an intuitive outcome...
Using Message Contracts: http://msdn2.microsoft.com/en-us/library/ms730255.aspx
Using Data Contracts: http://msdn2.microsoft.com/en-us/library/ms733127.aspx