Mahesh Jasti
Mahesh Jasti

Reputation: 572

webapi odata update savechanges issue - Unable to connect to remote server

In my mvc webapplication, I am using webapi to connect to my database through odata.

Both MVC WebApp and Odata WebApi are on different ports of Azure cloud service webrole endpoints.

MVC WebApp - 80
Odata WebApi - 23900

When I do a odataproxy updateobject and call savechanges like

odataProxy.UpdateObject(xxx);
odataProxy.SaveChanges(System.Data.Services.Client.SaveChangesOptions.PatchOnUpdate);

I am getting a weird exception on savechanges method call - unable to connect to remote server.

When I tried to look into inner exceptions, It says that - No connection could be made as the target machine actively refused it 127.0.0.1:23901

So if you observe the port number in the exception, it shows as 23901 and obviously this error should come as the request is supposed to hit 23900.

I am facing this exception only when running on azure cloud solution. Whenever I do an update request, it fails by hitting a wrong port (added by 1).

Another thing is, apart from this updateobject -> savechanges, rest all works like fetching data and adding data.

Upvotes: 0

Views: 672

Answers (2)

lc.
lc.

Reputation: 116458

FWIW, I've just run across this same thing. Darn near annoying and I really hope it doesn't happen in production. I'm surprised no other people have come across this though.

The idea of creating a new context, attaching the object(s) and calling SaveChanges really repulsed me because not only does it practically break all forms of testing, it causes debug code and production code to be fundamentally different.

I was however able to work around this problem in another way, by intercepting the request just before it goes out and using reflection to poke at some private fields in memory to "fix" the port number.

UPDATE: It's actually easier than this. We can intercept the request generation process with the BuildingRequest event. It goes something like this:

var context = new Context(baseUri);

context.BuildingRequest += (o, e) =>
{
    FixPort(e);
};

Then the FixPort method just needs to test the port number and build a new Uri, attaching it back to the event args.

[Conditional("DEBUG")]
private static void FixPort(BuildingRequestEventArgs eventArgs)
{
    int localPort = int.Parse(LOCAL_PORT);

    if (eventArgs.RequestUri.Port != localPort)
    {
        var builder = new UriBuilder(eventArgs.RequestUri);
        builder.Port = localPort;

        eventArgs.RequestUri = builder.Uri;
    }
}

Here's the original method using reflection and SendingRequest2, in case anyone is still interested.

First we create a context and attach a handler to the SendingRequest2 event:

var context = new Context(baseUri);

context.SendingRequest2 += (o, e) =>
{
    FixPort(e.RequestMessage);
};

The FixPort method then handles rewriting the URL of the internal request, where LOCAL_PORT is the port you expect, in your case 23900:

[Conditional("DEBUG")]
private static void FixPort(IODataRequestMessage requestMessage)
{
    var httpWebRequestMessage = requestMessage as HttpWebRequestMessage;
    if (httpWebRequestMessage == null) return;

    int localPort = int.Parse(LOCAL_PORT);

    if (httpWebRequestMessage.HttpWebRequest.RequestUri.Port != localPort)
    {
        var builder = new UriBuilder(requestMessage.Url);
        builder.Port = localPort;

        var uriField = typeof (HttpWebRequest).GetField("_Uri",
            BindingFlags.Instance | BindingFlags.NonPublic);
        uriField.SetValue(httpWebRequestMessage.HttpWebRequest, builder.Uri);
    }
}

Upvotes: 1

Mahesh Jasti
Mahesh Jasti

Reputation: 572

I have found the root cause and a temporary workaround.

Cause: When you hit WebApi through some port :23900 in Azure compute emulator and do an update or delete operation, somehow the last request is blocking the port and because of the port walking feature in Azure emulator, it is jumping to next port where there is no service available which is causing the issue.

Even this issue is found only in development emulators.

Temp Workaround: Use a different proxy to attach to updated context object and then save from the other proxy object.

var odataProxy1 = xxx;
var obj = odataProxy1.xyz.FirstOrDefault();
obj.property1="abcd";
...//Other update assignments

var odataProxy2 = xxx;
odataProxy2.AttachTo("objEntitySet",obj);
odataProxy2.UpdateObject(obj)
odataProxy2.SaveChanges(ReplaceOrUpdate);

Upvotes: 0

Related Questions