PactNet contract test failing with HttpRequestException and SocketException - c#

I'm trying to write a contract test using PactNet for the following method:
public async Task<IEnumerable<Models.RefData.Instrument> GetInstruments(string issuerCountry, string instrumentType)
{
ValidateNotNullOrWhiteSpaceParameter(issuerCountry, nameof(issuerCountry));
ValidateNotNullOrWhiteSpaceParameter(instrumentType, nameof(instrumentType)); ;
var queryString = $"instruments?issuerCountry={HttpUtility.UrlEncode(issuerCountry)}&instrumentType={HttpUtility.UrlEncode(instrumentType)}";
int pageNo = 0;
int pageSize = 20;
_logger.LogDebug($"GetInstruments Request:{queryString}");
var httpResponseMessage = await _client.GetAsync(queryString + $"&page={pageNo}&size={pageSize}");
_logger.LogDebug($"GetInstruments Response Status Code:{httpResponseMessage.StatusCode}");
switch (httpResponseMessage.StatusCode)
{
case HttpStatusCode.OK:
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<GetInstrumentsResponse>(content);
// if there are no results, return Empty
if (result.Metadata.TotalElements == 0)
{
return Enumerable.Empty<Models.RefData.Instrument>();
}
var instruments = new List<Models.RefData.Instrument>();
instruments.AddRange(result.Embedded.Instruments);
for (pageNo = 1; pageNo < result.Metadata.TotalPages; pageNo++)
{
var innerQueryString = queryString + $"&page={pageNo}&size={pageSize}";
_logger.LogDebug($"GetInstruments Request Additional Page:{innerQueryString}");
var httpResponseMessage2 = await _client.GetAsync(innerQueryString);
if (httpResponseMessage2.StatusCode != HttpStatusCode.OK)
{
_logger.LogError($"The requested page number {pageNo} gets response error {httpResponseMessage2.StatusCode.ToString()}.");
throw new UnexpectedResponseException(httpResponseMessage.StatusCode);
}
var content2 = await httpResponseMessage2.Content.ReadAsStringAsync();
var result2 = JsonConvert.DeserializeObject<GetInstrumentsResponse>(content2);
if (result2.Embedded.Instruments != null && result2.Embedded.Instruments.Any())
{
instruments.AddRange(result2.Embedded.Instruments);
}
}
if (instruments.Count != result.Metadata.TotalElements)
{
_logger.LogWarning($"Total number of instruments is different from MetaData. MetaData states {result.Metadata.TotalElements}, however only {instruments.Count} instruments retrieved.");
}
_logger.LogDebug($"GetInstruments Result:{instruments.ToJson()}");
return instruments;
default:
throw new UnexpectedResponseException(httpResponseMessage.StatusCode);
}
}
I created the following ConsumerPactTests.cs and ConsumerPactClassFixture.cs using this as a guide.
public class ConsumerPactTests : IClassFixture<ConsumerPactClassFixture>
{
private IMockProviderService _mockProviderService;
private string _mockProviderServiceBaseUri;
public ConsumerPactTests(ConsumerPactClassFixture fixture)
{
_mockProviderService = fixture.MockProviderService;
_mockProviderService.ClearInteractions(); //NOTE: Clears any previously registered interactions before the test is run
_mockProviderServiceBaseUri = fixture.MockProviderServiceBaseUri;
}
[Fact]
public void ItHandlesInvalidDateParam()
{
// Arange
var invalidRequestMessage = "issuerCountry or instrumentType is not valid";
_mockProviderService.Given("There is data")
.UponReceiving("A invalid GET request for Date Validation with invalid date parameter")
.With(new ProviderServiceRequest
{
Method = HttpVerb.Get,
Path = "/api/v2",
Query = "issuerCountry=USA&instrumentType=foo"
})
.WillRespondWith(new ProviderServiceResponse
{
Status = 400,
Headers = new Dictionary<string, object>
{
{ "Content-Type", "application/json; charset=utf-8" }
},
Body = new
{
message = invalidRequestMessage
}
});
// Act
RefDataHttpService sut = new RefDataHttpServiceBuilder().Build();
var result = sut.GetInstruments("USA", "foo").GetAwaiter().GetResult();
var resultBodyText = result.GetEnumerator();
// Assert
Assert.NotNull(resultBodyText);
}
}
public class ConsumerPactClassFixture : IDisposable
{
public IPactBuilder PactBuilder { get; private set; }
public IMockProviderService MockProviderService { get; private set; }
public int MockServerPort { get { return 9222; } }
public string MockProviderServiceBaseUri { get { return String.Format("http://localhost:{0}", MockServerPort); } }
public ConsumerPactClassFixture()
{
var pactConfig = new PactConfig
{
SpecificationVersion = "2.0.0",
PactDir = #"..\..\..\..\..\pacts",
LogDir = #".\pact_logs"
};
PactBuilder = new PactBuilder(pactConfig);
PactBuilder.ServiceConsumer("Consumer")
.HasPactWith("Provider");
MockProviderService = PactBuilder.MockService(MockServerPort);
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// This will save the pact file once finished.
PactBuilder.Build();
}
disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion
}
When I run my test I get this error:
dotnet test --filter "FullyQualifiedName=Bond.Publisher.Tests.ContractTest.ConsumerPactTests.ItHandlesInvalidDateParam"
Test run for c:\Workspace\prod\test\Bond.Publisher.Tests\bin\Debug\netcoreapp3.1\Bond.Publisher.Tests.dll(.NETCoreApp,Version=v3.1)
Microsoft (R) Test Execution Command Line Tool Version 16.7.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:10.95] Bond.Publisher.Tests.ContractTest.ConsumerPactTests.ItHandlesInvalidDateParam [FAIL]
X Bond.Publisher.Tests.ContractTest.ConsumerPactTests.ItHandlesInvalidDateParam [4s 196ms]
Error Message:
System.Net.Http.HttpRequestException : No connection could be made because the target machine actively refused it.
---- System.Net.Sockets.SocketException : No connection could be made because the target machine actively refused it.
Stack Trace:
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Bond.Publisher.HttpMessageHandlers.UnoAuthorisationHeaderMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in c:\Workspace\usprod\src\Bond.Publisher\HttpMessageHandlers\UnoAuthorisationHeaderMessageHandler.cs:line 37
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Bond.Publisher.Services.RefDataHttpService.GetInstruments(String issuerCountry, String instrumentType) in c:\Workspace\prod\src\Bond.Publisher\Services\RefDataHttpService.cs:line 52
at Bond.Publisher.Tests.ContractTest.ConsumerPactTests.ItHandlesInvalidDateParam() in c:\Workspace\prod\test\Bond.Publisher.Tests\ContractTest\ConsumerPactTests.cs:line 52
----- Inner Stack Trace -----
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
Test Run Failed.
I suspect it may be some sort of authentication issue as UnoAuthorisationHeaderMessageHandler.cs deals with that. What have I done wrong with this?

For me the path was too long. When I moved the project to a folder closer to C:\ the test ran.

The System.Net.Sockets.SocketException : No connection could be made because the target machine actively refused it. usually happens when there is no server listening on the url you are sending.
Check if the ruby service is up and running (the test runner starts it), you should see it in Task Manager under Visual Studio
Alernatively, before you call pactBuilder.Build(), you should be able to do an HTTP request through PostMan to http://localhost:9222/instruments...

Related

Argument 1: cannot convert from 'string' to 'Microsoft.Bot.Builder.ITurnContext'

I'm using QNA maker in my chatbot project using Bot Framework and I want to take the question from the adaptive card and send it to the QNA maker but I'm getting an error says: Argument 1: cannot convert from 'string' to 'Microsoft.Bot.Builder.ITurnContext'
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
var value = turnContext.Activity.Value;
if (value != null)
{
var val = value.ToString();
var relationsJSON = JToken.Parse(val);
var text = relationsJSON["text"].ToString();
if (text == "car model")
{
var result = await QnAMaker.GetAnswersAsync(text);
await turnContext.SendActivityAsync(result[0].Answer, cancellationToken: cancellationToken);
}
}
}
the error message on: var result = await QnAMaker.GetAnswerAsnyc(text);
The QnAMaker.GetAnswerAsnyc method is always expecting the ITurnContext as a mandatory parameter. So you can't inject any other parameter in the respective method.
So try the below code here JSON text is assigned to turnContext.Activity.Text object. Because as per your code OnTurnAsync method is always validating turnContext.Activity.Value object.
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
var value = turnContext.Activity.Value;
if (value != null)
{
var val = value.ToString();
var relationsJSON = JToken.Parse(val);
turnContext.Activity.Text = relationsJSON["text"].ToString();
if (turnContext.Activity.Text == "car model")
{
var result = await QnAMaker.GetAnswersAsync(turnContext.Activity.Text);
await turnContext.SendActivityAsync(result[0].Answer, cancellationToken: cancellationToken);
}
}
}

Does ChannelReader<T>.ReadAllAsync throw any exceptions when being canceled by a CancellationToken?

Does ChannelReader<T>.ReadAllAsync throw any exceptions when being canceled by a CancellationToken? It doesn't seem to be throwing OperationCanceledException/TaskCanceledException?
I know if these two methods were called in a fire and forget manner, i.e. _ = SendLoopAsync(); _ = ReceiveLoopAsync();, it would've crashed the task with no displayed message/exception because they were not awaited, meaning that we're losing the exceptions.
I don't want it to crash that task without letting me know that it actually has crashed/been cancelled, which means I should probably wrap the whole SendLoopAsync in a try/catch instead of what's between ReadAllAsync's branches.
A small example representing its behavior will be appreciated.
var clientWebSocket = new ClientWebSocket();
await clientWebSocket.ConnectAsync(new Uri("wss://www.deribit.com/ws/api/v2"), CancellationToken.None).ConfigureAwait(false);
var client = new ChannelWebSocket(clientWebSocket);
for (var i = 1; i <= 10; i++)
{
client.Output.TryWrite($"Item: {i}");
}
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1));
await client.StartAsync(cts.Token).ConfigureAwait(false); // blocks the UI
Console.ReadLine();
public class ChannelExample
{
private readonly WebSocket _webSocket;
private readonly Channel<string> _input;
private readonly Channel<string> _output;
public ChannelExample(WebSocket webSocket)
{
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
_input = Channel.CreateUnbounded<string>(new UnboundedChannelOptions
{
SingleWriter = true
});
_output = Channel.CreateUnbounded<string>(new UnboundedChannelOptions
{
SingleReader = true
});
}
public ChannelReader<string> Input => _input.Reader;
public ChannelWriter<string> Output => _output.Writer;
public async Task StartAsync(CancellationToken cancellationToken)
{
var receiving = ReceiveLoopAsync(cancellationToken);
var sending = SendLoopAsync(cancellationToken);
var completedTask = await Task.WhenAny(receiving, sending).ConfigureAwait(false);
if (completedTask.Exception != null)
{
Console.WriteLine("Exception");
}
}
private async Task SendLoopAsync(CancellationToken cancellationToken)
{
await foreach (var message in _output.Reader.ReadAllAsync(cancellationToken))
{
Console.WriteLine($"Sending: {message}");
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
}
}
private async Task ReceiveLoopAsync(CancellationToken cancellationToken)
{
using var buffer = MemoryPool<byte>.Shared.Rent();
while (_webSocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
{
ValueWebSocketReceiveResult receiveResult;
do
{
receiveResult = await _webSocket.ReceiveAsync(buffer.Memory, cancellationToken).ConfigureAwait(false);
if (receiveResult.MessageType == WebSocketMessageType.Close)
{
return;
}
} while (!receiveResult.EndOfMessage);
}
}
}
I suspect that it would throw; of course, you can always test that, but - that is the general expected pattern in this scenario. So you would wrap it with a:
try
{
// ...
}
catch (OperationCancelledException) when (cancellationToken.IsCancellationRequested)
{
// treat as completion; swallow
}
Alternatively: you could pass CancellationToken.None into the channel read API, and just use the writer's completion to signify exit (making sure that you call .Complete(...) on the writer when exiting).
That said: ReadAllAsync probably isn't the preferred API here, since you don't really need it as IAsyncEnumerable<T> - so it may be preferable to use the native channel API, i.e.
while (await _output.Reader.WaitToReadAsync(cancellationToken))
{
while (_output.Reader.TryRead(out var message))
{
// ...
}
}
I am not sure what does the Task returned by the StartAsync represent:
public async Task StartAsync(CancellationToken cancellationToken)
{
var receiving = ReceiveLoopAsync(cancellationToken);
var sending = SendLoopAsync(cancellationToken);
var completedTask = await Task.WhenAny(receiving, sending).ConfigureAwait(false);
if (completedTask.Exception != null)
{
Console.WriteLine("Exception");
}
}
It seems that it represents the completion of any of the receiving and sending tasks, which is weird. Most probably this is an unintended consequence of trying to log the exceptions of the tasks. There are better ways to log task exceptions than this, with the simplest being to enclose all the code inside the asynchronous method in a try/catch block. Beyond that, the Exception property of a Task is not-null only when the task IsFaulted, not when it IsCanceled.

Guid is causing a format exception

I'm trying to set up RavenDb 3.5 and NServiceBus 6. After I senter the saga that I have set up in my NServiceBus endpoint, I enter a handler. Once this handler is finished, I get this error:
System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
My code:
public static class AutoFacConfig
{
public static IContainer ConfigureAutofac()
{
var builder = new ContainerBuilder();
var resourceManagerId = new Guid("6c9abcbb-c7ca-4a67-a149-5142f633f535");
var dtcRecoveryBasePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
var recoveryPath = Path.Combine(dtcRecoveryBasePath, "NServiceBus.RavenDB", resourceManagerId.ToString());
builder.Register(x =>
{
var store = new DocumentStore
{
ConnectionStringName = "RavenDB",
ResourceManagerId = resourceManagerId,
TransactionRecoveryStorage = new LocalDirectoryTransactionRecoveryStorage(recoveryPath)
};
store.DefaultDatabase = "MyDB";
store.Initialize();
store.Conventions.IdentityPartsSeparator = "-";
return store;
})
.As<IDocumentStore>()
.SingleInstance();
builder.Register<IFilesStore>(x =>
{
var fileStore = new FilesStore()
{
Url = "http://localhost:40000",
DefaultFileSystem = "MyFS",
}.Initialize();
return fileStore;
}).SingleInstance();
return builder.Build();
}
}
In the saga:
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<FileToOrderSagaData> mapper)
{
mapper.ConfigureMapping<StartFileToOrderSagaCommand>(m => m.DataId)
.ToSaga(s => s.DataId);
}
public async Task Handle(StartFileToOrderSagaCommand message, IMessageHandlerContext context)
{
// Do Validation ValidateXmlCommand
Data.DataId = message.DataId;
await context.Send<ValidateXmlCommand>( x => { x.Filename = message.Filename; x.CustomerId = message.CustomerId; });
}
Here's the stack trace:
at System.Guid.TryParseGuidWithNoStyle(String guidString, GuidResult& result)
at System.Guid.TryParseGuid(String g, GuidStyles flags, GuidResult& result)
at System.Guid..ctor(String g)
at Raven.Client.Converters.GuidConverter.ConvertTo(String value) in C:\Builds\RavenDB-Stable-3.5\Raven.Client.Lightweight\Converters\GuidConverter.cs:line 51
at Raven.Client.Document.GenerateEntityIdOnTheClient.SetPropertyOrField(Type propertyOrFieldType, Object entity, Action`1 setIdentifier, String id) in C:\Builds\RavenDB-Stable-3.5\Raven.Client.Lightweight\Document\GenerateEntityIdOnTheClient.cs:line 170
at Raven.Client.Document.GenerateEntityIdOnTheClient.TrySetIdentity(Object entity, String id) in C:\Builds\RavenDB-Stable-3.5\Raven.Client.Lightweight\Document\GenerateEntityIdOnTheClient.cs:line 143
at Raven.Client.Document.InMemoryDocumentSessionOperations.<GenerateDocumentKeyForStorageAsync>d__99.MoveNext() in C:\Builds\RavenDB-Stable-3.5\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 833
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Raven.Client.Document.InMemoryDocumentSessionOperations.<StoreAsyncInternal>d__96.MoveNext() in C:\Builds\RavenDB-Stable-3.5\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 803
Any help guys?
After removing
store.Conventions.IdentityPartsSeparator = "-";
the issue was fixed.
See HadiEskandari's comment above and this link for more info: Exception in RavenDB.SagaPersister.Save, "Guid should contain 32 digits with 4 dashes". Guid is empty in Raven

Retry on 408 Timeout from Azure Table Storage service

We are using Azure Table Storage and are getting occasional 408 Timeouts when performing an InsertOrMerge operation. In this case we would like to retry, but it appears that the retry policy is not being followed for these errors.
This is a class we use to handle the table interaction. The method GetFooEntityAsync tries to retrieve the entity from Table Storage. If it cannot, it creates a new FooEntity and adds it to the table (mapping to a FooTableEntity).
public class FooTableStorageBase
{
private readonly string tableName;
protected readonly CloudStorageAccount storageAccount;
protected TableRequestOptions DefaultTableRequestOptions { get; }
protected OperationContext DefaultOperationContext { get; }
public CloudTable Table
{
get
{
return storageAccount.CreateCloudTableClient().GetTableReference(tableName);
}
}
public FooTableStorage(string tableName)
{
if (String.IsNullOrWhiteSpace(tableName))
{
throw new ArgumentNullException(nameof(tableName));
}
this.tableName = tableName;
storageAccount = CloudStorageAccount.Parse(ConnectionString);
ServicePoint tableServicePoint = ServicePointManager.FindServicePoint(storageAccount.TableEndpoint);
tableServicePoint.UseNagleAlgorithm = false;
tableServicePoint.ConnectionLimit = 100; // Increasing connection limit from default of 2.
DefaultTableRequestOptions = new TableRequestOptions()
{
PayloadFormat = TablePayloadFormat.JsonNoMetadata,
MaximumExecutionTime = TimeSpan.FromSeconds(1),
RetryPolicy = new OnTimeoutRetry(TimeSpan.FromMilliseconds(250), 3),
LocationMode = LocationMode.PrimaryOnly
};
DefaultOperationContext = new OperationContext();
DefaultOperationContext.Retrying += (sender, args) =>
{
// This is never executed.
Debug.WriteLine($"Retry policy activated in {this.GetType().Name} due to HTTP code {args.RequestInformation.HttpStatusCode} with exception {args.RequestInformation.Exception.ToString()}");
};
DefaultOperationContext.RequestCompleted += (sender, args) =>
{
if (args.Response == null)
{
// This is occasionally executed - we want to retry in this case.
Debug.WriteLine($"Request failed in {this.GetType().Name} due to HTTP code {args.RequestInformation.HttpStatusCode} with exception {args.RequestInformation.Exception.ToString()}");
}
else
{
Debug.WriteLine($"{this.GetType().Name} operation complete: Status code {args.Response.StatusCode} at {args.Response.ResponseUri}");
}
};
Table.CreateIfNotExists(DefaultTableRequestOptions, DefaultOperationContext);
}
public async Task<FooEntity> GetFooEntityAsync()
{
var retrieveOperation = TableOperation.Retrieve<FooTableEntity>(FooTableEntity.GenerateKey());
var tableEntity = (await Table.ExecuteAsync(retrieveOperation, DefaultTableRequestOptions, DefaultOperationContext)).Result as FooTableEntity;
if (tableEntity != null)
{
return tableEntity.ToFooEntity();
}
var fooEntity = CalculateFooEntity();
var insertOperation = TableOperation.InsertOrMerge(new FooTableEntity(fooEntity));
var executeResult = await Table.ExecuteAsync(insertOperation);
if (executeResult.HttpStatusCode == 408)
{
// This is never executed.
Debug.WriteLine("Got a 408");
}
return fooEntity;
}
public class OnTimeoutRetry : IRetryPolicy
{
int maxRetryAttempts = 3;
TimeSpan defaultRetryInterval = TimeSpan.FromMilliseconds(250);
public OnTimeoutRetry(TimeSpan deltaBackoff, int retryAttempts)
{
maxRetryAttempts = retryAttempts;
defaultRetryInterval = deltaBackoff;
}
public IRetryPolicy CreateInstance()
{
return new OnTimeoutRetry(TimeSpan.FromMilliseconds(250), 3);
}
public bool ShouldRetry(int currentRetryCount, int statusCode, Exception lastException, out TimeSpan retryInterval, OperationContext operationContext)
{
retryInterval = defaultRetryInterval;
if (currentRetryCount >= maxRetryAttempts)
{
return false;
}
// Non-retryable exceptions are all 400 ( >=400 and <500) class exceptions (Bad gateway, Not Found, etc.) as well as 501 and 505.
// This custom retry policy also retries on a 408 timeout.
if ((statusCode >= 400 && statusCode <= 500 && statusCode != 408) || statusCode == 501 || statusCode == 505)
{
return false;
}
return true;
}
}
}
When calling GetFooEntityAsync(), occasionally the "Request failed" line will be executed. When inspecting the values args.RequestInformation.HttpStatusCode = 408. However:
Debug.WriteLine("Got a 408"); within the GetFooEntity method is never executed.
Debug.WriteLine($"Retry policy activated... within the DefaultOperationContext.Retrying delegate is never executed (I would expect this to be executed twice - is this not retrying?).
DefaultOperationContext.RequestResults contains a long list of results (mostly with status codes 404, some 204s).
According to this (rather old) blog post, exceptions with codes between 400 and 500, as well as 501 and 505 are non-retryable. However a timeout (408) is exactly the situation we would want a retry. Perhaps I need to write a custom retry policy for this case.
I don't fully understand where the 408 is coming from, as I can't find it in the code other than when the RequestCompleted delegate is invoked. I have been trying different settings for my retry policy without luck. What am I missing here? How can I get the operation to retry on a 408 from table storage?
EDIT: I have updated the code to show the custom retry policy that I implemented, to retry on 408 errors. However, it seems that my breakpoints on retry are still not being hit, so it appears the retry is not being triggered. What could be the reason my retry policy is not being activated?

Getting a FaultException when trying to work with a WCF service

EDIT: Here's my call stack.
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(System.ServiceModel.Channels.Message reply, System.ServiceModel.Channels.MessageFault fault, string action, System.ServiceModel.Channels.MessageVersion version, System.ServiceModel.Channels.FaultConverter faultConverter) + 0x124 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.HandleReply(System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, ref System.ServiceModel.Dispatcher.ProxyRpc rpc) + 0x147 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.EndCall(string action, object[] outs, System.IAsyncResult result) + 0xb2 bytes
System.ServiceModel.dll!System.ServiceModel.ClientBase.ChannelBase.EndInvoke(string methodName, object[] args, System.IAsyncResult result) + 0x1e bytes
PhoneClient.dll!PhoneClient.ServiceReference1.Service1Client.Service1ClientChannel.EndGetFirstAidGuides(System.IAsyncResult result) Line 420 C#
PhoneClient.dll!PhoneClient.ServiceReference1.Service1Client.PhoneClient.ServiceReference1.IService1.EndGetFirstAidGuides(System.IAsyncResult result) Line 284 + 0x7 bytes C#
PhoneClient.dll!PhoneClient.ServiceReference1.Service1Client.OnEndGetFirstAidGuides(System.IAsyncResult result) Line 292 + 0x2 bytes C#
System.ServiceModel.dll!System.ServiceModel.ClientBase.OnAsyncCallCompleted(System.IAsyncResult result) + 0x20 bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously) + 0x66 bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously, System.Exception exception) + 0xe bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.CallComplete(bool completedSynchronously, System.Exception exception) + 0x8 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.FinishSend(System.IAsyncResult result, bool completedSynchronously) + 0x99 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.SendCallback(System.IAsyncResult result) + 0x1a bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously) + 0x66 bytes
System.ServiceModel.dll!System.ServiceModel.AsyncResult.Complete(bool completedSynchronously, System.Exception exception) + 0xe bytes
System.ServiceModel.dll!System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.OnGetResponse(System.IAsyncResult result) + 0x52 bytes
System.Windows.dll!System.Net.Browser.ClientHttpWebRequest.InvokeGetResponseCallback.AnonymousMethod__8(object state2) + 0x1b bytes
mscorlib.dll!System.Threading.ThreadPool.WorkItem.WaitCallback_Context(object state) + 0x18 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x63 bytes
mscorlib.dll!System.Threading.ThreadPool.WorkItem.doWork(object o) + 0x47 bytes
mscorlib.dll!System.Threading.Timer.ring() + 0x70 bytes
And the error: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.
I'm currenntly working on a Windows Phone 7 application in which I am communicating with a WCF service. I've made it work within one method already. So I that it is possible.
Here is my class that calls the WCF service
public partial class FirstAidGuides : PhoneApplicationPage
{
public FirstAidGuides()
{
InitializeComponent();
ServiceReference1.Service1Client sc = new ServiceReference1.Service1Client();
sc.GetFirstAidGuidesCompleted += new EventHandler<ServiceReference1.GetFirstAidGuidesCompletedEventArgs>(sc_GetFirstAidGuidesCompleted);
sc.GetFirstAidGuidesAsync();
}
void sc_GetFirstAidGuidesCompleted(object sender, ServiceReference1.GetFirstAidGuidesCompletedEventArgs e)
{
FirstAidGuideText.Text = e.Result[0].Text;
}
}
Right now, I'm just trying to get some text written in a textblock, from my result.
This is the interface of the WCF service.
[ServiceContract]
public interface IService1
{
[OperationContract]
long CreateCall(string phoneNumber, double longtitude, double langtitude);
[OperationContract]
List<Model.FirstAidGuide> GetFirstAidGuides();
}
The method of my service class, that pulls data from a database.
public List<Model.FirstAidGuide> GetFirstAidGuides()
{
DataClasses1DataContext db = new DataClasses1DataContext();
var firstAidGuides = (from f in db.FirstAidGuides select f);
List<Model.FirstAidGuide> list = new List<Model.FirstAidGuide>();
foreach (var guide in firstAidGuides.ToList())
{
Model.FirstAidGuide fa = new Model.FirstAidGuide();
fa.FirstAidId = guide.FirstAidId;
fa.Title = guide.FirstAidTitle;
fa.Text = guide.FirstAidText;
fa.LastUpdated = (DateTime)guide.LastUpdated;
list.Add(fa);
}
return list;
}
And just for convenience. The FirstAidGuide class.
[DataContract]
public class FirstAidGuide
{
[DataMember]
private string _title;
[DataMember]
private string _text;
[DataMember]
private DateTime _lastUpdated;
[DataMember]
private long _firstAidId;
public long FirstAidId
{
get { return _firstAidId; }
set { _firstAidId = value; }
}
public DateTime LastUpdated
{
get { return _lastUpdated; }
set { _lastUpdated = value; }
}
public string Text
{
get { return _text; }
set { _text = value; }
}
public string Title
{
get { return _title; }
set { _title = value; }
}
}
I simply cannot get it to do anything. I'm getting a FaultException, which points me in the direction that it cannot handle the response from the WCF service.
Any help would be appreciated.
Can you try enabling tracing on your WCF Service and also inspect the trace to find out what the error is. Also set the below property to get a complete stack trace of the error
<serviceDebug includeExceptionDetailInFaults="true" />
What I tend to do over WCF is to wrap everything inside my [OperationContract] method in a Try...Catch block; unravel the stack trace of any caught exception and all inner exceptions and stick that as a string into the message of a FaultException that I then re-throw over the soap boundary. Something like this:
public static string GetDebugString(this Exception ex)
{
var builder = new StringBuilder();
GetDebugString(ex, builder);
while ((ex = ex.InnerException) != null)
{
GetDebugString(ex, builder);
}
return builder.ToString();
}
private static void GetDebugString(Exception ex, StringBuilder builder)
{
builder.AppendLine(ex.GetType().Name);
builder.AppendLine();
builder.AppendLine(ex.Message);
builder.AppendLine();
builder.AppendLine(ex.StackTrace);
builder.AppendLine();
}
[OperationContract]
public void Foo()
{
this.MakeSafeCall(() => this.UnsafeFoo());
}
public void Unsafe()
{
// do stuff
}
private void MakeSafeCall(Action action)
{
try
{
action();
}
catch (Exception ex)
{
throw new FaultException(ex.GetDebugString());
}
}
The problem was in this line:
foreach (var guide in firstAidGuides.ToList())
Apparently calling .ToList() made the whole thing crash.
Simply removing .ToList() fixed everything.

Categories

Resources