Saturday, March 20, 2010

Creating WCF proxy without reference

The problem : Creating WCF proxy in Silverlight without using the add service reference and it’s auto generated code

It was a debate that whether we can call a WCF service from Silverlight without using “Add Service Reference” menu item.In WPF or in the other .Net environments it is easy.We just need to refer the contracts dll and write a class which inherits from ClientBase.But this is Silverlight - A different runtime.

A Normal WCF service reference with all the generated code files

There were 3 opinions.Not at all possible,Possible with less amount of code,Possible if we create all those classes by our own.Just avoid the last one because that doesn’t give us any benefit as it is just making the process manual.

One of my Colleagues started research on that and was able to achieve the same.I am just sharing the idea.

Solution : We can achieve the same using contract interface,proxy class and a custom eventargs class

First of all there are no changes at the server side.Only 3 types we are going to write at client side which will be present in the reference.cs file if you create the proxy using “Add Service Reference” Dialog.

There will be a question for sure.Why are we writing the code which is being auto generated ? Why don’t we create a reference using the dialog and copy paste the reference.cs code?

The answer for both the questions is same.You can create reference using the dialog and get the code from reference.cs.That is enough.

Advantages

  • Can avoid huge amount of code which is auto generated.
  • This is acting as a separate layer and can encapsulate operations here in future.

Implementation steps

Lets take example of implementing a DataService with one method GetData which returns a string.Don’t bother about the server side.Use the same.Below are the steps to perform at client side ie Silverlight side.

  1. Create interface IDataService
    1. Decorate with ServiceContract attribute.
    2. Add method BeginGetData.Decorate it with OpertionContract and Set AsyncPattern to True.
    3. Add Method EndGetData.
      Imports System.ServiceModel

      <ServiceContract()> _
      Public Interface IDataService
      <OperationContract(AsyncPattern:=True)> _
      Function BeginGetData(ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
      Function EndGetData(ByVal result As System.IAsyncResult) As String
      End Interface





  2. Create eventArgs class deriving from System.ComponentModel.AsyncCompletedEventArgs


    1. Add required properties to pass values.

      Public Class GetDataCompletedEventArgs
      Inherits System.ComponentModel.AsyncCompletedEventArgs

      Private results() As Object

      Public Sub New(ByVal results() As Object, ByVal exception As System.Exception, ByVal cancelled As Boolean, ByVal userState As Object)
      MyBase.New(exception, cancelled, userState)
      Me.results = results
      End Sub

      Public ReadOnly Property Result() As String
      Get
      If Me.results IsNot Nothing Then
      Return CType(Me.results(0), String)
      End If
      Return Nothing
      End Get
      End Property
      End Class




  3. Create class DataServiceProxy derived from ClientBase which Implements IDataService.Write constructors.Implement methods.Write delegates and event which communicate back to the caller.



Public Class DataServiceProxy
Inherits ClientBase(Of IDataService)
Implements IDataService
#Region " Constructors"
Public Sub New()
MyBase.New()
End Sub

Public Sub New(ByVal endpointConfigurationName As String)
MyBase.New(endpointConfigurationName)
End Sub

Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As String)
MyBase.New(endpointConfigurationName, remoteAddress)
End Sub

Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
MyBase.New(endpointConfigurationName, remoteAddress)
End Sub

Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
MyBase.New(binding, remoteAddress)
End Sub
#End Region

#Region "GetData"
Private onBeginGetDataDelegate As BeginOperationDelegate
Private onEndGetDataDelegate As EndOperationDelegate
Private onGetDataCompletedDelegate As System.Threading.SendOrPostCallback
Public Event GetDataCompleted As System.EventHandler(Of GetDataCompletedEventArgs)

Public Function BeginGetData(ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult Implements ServiceContracts.IDataService.BeginGetData
Return MyBase.Channel.BeginGetData(callback, asyncState)

End Function

Public Function EndGetData(ByVal result As System.IAsyncResult) As String Implements ServiceContracts.IDataService.EndGetData
Return MyBase.Channel.EndGetData(result)
End Function

Private Function OnBeginGetData(ByVal inValues() As Object, ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
'Dim engID As String = CType(inValues(0), String)
'Dim libraryID As String = CType(inValues(1), String)
'Dim folderName As String = CType(inValues(2), String)
'Dim parentFolder As PathInfo = CType(inValues(3), PathInfo)
'Dim folderGeneralProperties As FolderGeneralProperties = CType(inValues(4), FolderGeneralProperties)
'Dim additionalProperties As System.Collections.Generic.Dictionary(Of String, String) = CType(inValues(5), System.Collections.Generic.Dictionary(Of String, String))
Return CType(Me, IDataService).BeginGetData(callback, asyncState)
End Function

'Private Function GetData() As String Implements IDataService.GetData
'Return MyBase.Channel.GetData()
'End Function

Private Function OnEndGetData(ByVal result As System.IAsyncResult) As Object()
Dim retVal As String = CType(Me, IDataService).EndGetData(result)
Return New Object() {retVal}
End Function

Private Sub OnGetDataCompleted(ByVal state As Object)
'If Not (GetDataCompleted Is Nothing) Then
Dim e As InvokeAsyncCompletedEventArgs = CType(state, InvokeAsyncCompletedEventArgs)
RaiseEvent GetDataCompleted(Me, New GetDataCompletedEventArgs(e.Results, e.Error, e.Cancelled, e.UserState))
'End If
End Sub

Public Overloads Sub GetDataAsync()
If (Me.onBeginGetDataDelegate Is Nothing) Then
Me.onBeginGetDataDelegate = AddressOf Me.OnBeginGetData
End If
If (Me.onEndGetDataDelegate Is Nothing) Then
Me.onEndGetDataDelegate = AddressOf Me.OnEndGetData
End If
If (Me.onGetDataCompletedDelegate Is Nothing) Then
Me.onGetDataCompletedDelegate = AddressOf Me.OnGetDataCompleted
End If
MyBase.InvokeAsync(Me.onBeginGetDataDelegate, New Object() {}, Me.onEndGetDataDelegate, Me.onGetDataCompletedDelegate, Nothing)
End Sub
#End Region
End Class



Uploaded a sample which does the same.

No comments: