views:

73

answers:

5

I'm trying to make a simple class for my REST calls so I won't have the same code in multiple places in my application.

The problem is, I don't know how to notify an object that called the UploadStringAsync() method from inside the UploadStringCompleted() event handler.

Here is my class, I have added comments to mark the place where I want to notify the object from which the Post method has been called:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MyProject.Utils
{
    public class Rest
    {
        private WebClient client = new WebClient();

        private void Init()
        {
            client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted);
        }

        public void Post(string uri, string postRequest, object objectToNotify)
        {
            Init();
            client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
            client.UploadStringAsync(new Uri(uri,
                UriKind.RelativeOrAbsolute), "POST", postRequest, objectToNotify);
        }

        private void client_UploadStringCompleted(object sender,
           UploadStringCompletedEventArgs e)
        {
            // here I would like to notify an object from 
            // which the Post() method has been called
            // I don't know how to do this as this method has no idea
            // which object called the method
            // this doesn't work:
            // (e.UserState.GetType())e.UserState.RestCallback(e);
        }
    }
}

EDIT:

My modified code after Jacob's suggestions:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;

namespace MyProject.Utils
{
    public class Rest
    {
        private WebClient client = new WebClient();

        private void Init()
        {
            client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted);
        }

        public void Post(
            string uri, string postRequest,
            Action<UploadStringCompletedEventArgs> callback)
        {
            HtmlPage.Window.Alert("bbb");
            client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
            client.UploadStringAsync(new Uri(uri,
                UriKind.RelativeOrAbsolute), "POST", postRequest, callback);
        }

        private void client_UploadStringCompleted(object sender,
           UploadStringCompletedEventArgs result)
        {
            HtmlPage.Window.Alert("aaa");
            var callback = (Action<UploadStringCompletedEventArgs>)result.UserState;
            callback(result);
        }
    }
}

This is how I'm calling the Post method:

    string postRequest = "id=5":
    Rest restClient = new Rest();
    restClient.Post("http://mywebsite.com/", postRequest, RestCallback);

    .......................

    private void RestCallback(UploadStringCompletedEventArgs result)
    {
        if (result.Error != null)
        {
            ContactFormSuccess.Text = "There was an error sending email message.";
        }
        else
        {
            ContactFormSuccess.Text = "Email message successfully sent.";
        }
    }
A: 

client_UploadStringCompleted is an instance method. You can simply use this.

dtb
That will only work if each object creates its own Rest instance. My guess is that he doesn't plan to use it that way.
John Fisher
A: 

If it is a windows application, you can write your own event and then raise it so that the calling code can handle that event. Not sure if it is applicable to Silvelight though.

Vivek
How does that help him know which object to notify?
John Fisher
You notify the object by raising the event. If the calling object cares about completion of the Post method, it should subscribe to that event.
Vivek
+2  A: 

Pass the object which needs the notification to the Post() method. Then, pass that object as a parameter (using one of the other overloads) to the UploadStringAsync() method. Then, your client_UploadStringCompleted can use the event arguments information to get the object that needs notification.

John Fisher
Could you give some code snippet please? I am still not sure how to do it. I know that there are three versions of UploadStringAsync (from here: http://msdn.microsoft.com/en-us/library/system.net.webclient.uploadstringasync%28VS.80%29.aspx) but I am not sure how to modify the client_UploadStringCompleted event handler.
Richard Knop
UploadStringCompletedEventArgs has a **UserState** property which would hold the object you passed to the UploadStryngAsync method. Cast that object to the type of object you passed in, then use it in whatever way is appropriate.
John Fisher
Yes, but I don't know what type to cast that object to. I want this class to be general so I can use it in all other classes in my application. I don't want to hardcode the type the object should be casted to inside it.
Richard Knop
Check my updated question. I have edited the code to the latest version of the class I have so far.
Richard Knop
+1  A: 

You could modify your Rest class so that it's given a callback delegate rather than the object to notify. It could look something like this:

public void Post(
    string uri, string postRequest, 
    Action<UploadStringCompletedEventArgs> callback)
{
    Init();
    client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
    client.UploadStringAsync(
        new Uri(uri, UriKind.RelativeOrAbsolute), 
        "POST", postRequest, callback);
}

private void client_UploadStringCompleted(object sender,
   UploadStringCompletedEventArgs e)
{
    var callback = (Action<UploadStringCompletedEventArgs>)e.UserState;
    callback(e);
}

Then, when you make the request, the call would look something like this:

var restClient = new Rest();
restClient.Post("http://your/url", "stuff", handleResult);


// ...

private void handleResult(UploadStringCompletedEventArgs result)
{
}
Jacob
I have tried this but now the client_UploadStringCompleted is not getting called at all. I will post updated code at the bottom of my question.
Richard Knop
Was `client_UploadStringCompleted` getting called before? I don't see a reason why your event handler for `UploadStringCompleted` wouldn't be called.
Jacob
Sorry, it was just my typo. It works perfect :)
Richard Knop
+1  A: 

Following John's suggestion, why not define your own interface, like IDoSomethingAfterRest. Implement it on any object that uses your Rest class. When the Rest call completes, and control is in the client_UploadStringCompleted function, simply call the method in IDoSomethingAfterRest that you want to execute.

public interface IDoSomethingAfterPost
{
  void DoSomethingAfterPost(<some parameters here>);
}

On your object that is calling Post implement the interface:

public DoSomethingAfterPost(<some parameters here>)
{
  Console.WriteLine("Just finished posting something");
}

Now, in your Rest class, when it is notified that the upload is complete, you can call back on the interface:

private void client_UploadStringCompleted(object sender,        
       UploadStringCompletedEventArgs e)        
{
    IDoSomethingAfterPost dsap = e.UserState as IDoSomethingAfterPost;
    if (dsap != null)
    {        
      _caller.DoSomethingAfterPost(e);
    }
    else
    {
      //Whoops!  Someone needs to implement IDoSomethingAfterPost!
    }
}     
wageoghe