How to handle OdataException in DefaultODataPathHandler.Parse at global level? - c#

DefaultODataPathHandler.Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer)
is throwing ODataException when I try to send a wrong data type to a
OData controller function. For example, calling GetOrders(date = 20181001), (with an integer instead of a date (2018-10-01)), throws ODataException with message:
not able to cast Edm.Int32 to Emd.DateTime
How can I handle this exception at global level?

Related

Polly with IDistributedCache and IHttpClientFactory Policy

Using the following code compiles fine, but receives the runtime error below. Seems to be a conflict between the policy only supporting HttpResponseMessage when using IHttpClientFactory?
The end goal is to be able to use several policies like retry, timeout, etc. and if everything is OK cache the result with the cache policy...
Unable to cast object of type
'Polly.Caching.AsyncCachePolicy'1[System.String]' to type
'Polly.IAsyncPolicy'1[System.Net.Http.HttpResponseMessage]'.'
serviceCollection.AddStackExchangeRedisCache(options =>
{
options.Configuration = "...";
});
IPolicyRegistry<string> registry = serviceCollection.AddPolicyRegistry();
var cacheProvider = ServiceProvider.GetRequiredService<IDistributedCache>().AsAsyncCacheProvider<string>();
serviceCollection.AddSingleton(serviceProvider => cacheProvider);
AsyncCachePolicy<string> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
registry.Add("CachingPolicy", cachePolicy);
serviceCollection.AddHttpClient<IMyClient, MyClient>()
.AddPolicyHandlerFromRegistry(this.PolicySelector)
private IAsyncPolicy<HttpResponseMessage> PolicySelector(IReadOnlyPolicyRegistry<string> policyRegistry, HttpRequestMessage httpRequestMessage)
{
return policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("CachingPolicy");
}
As the error says you can not convert AsyncCachePolicy<string> to IAsyncPolicy<HttpResponseMessage>. Since all AsyncXYZPolicy implements the IAsyncPolicy interface that's why the problem is not coming from here. Rather than from the type parameter.
The AddHttpClient returns an IHttpClientBuilder. There are several extension methods on them like AddPolicyHandlerFromRegistry, AddTransientHttpErrorPolicy or AddPolicyHandler. In all cases you need to register a policy where the return type is HttpResponseMessage.
If you would try to register your cache policy directly via the AddPolicyHandler then it would cause compilation error rather than run-time error. But because you retrieve the policy dynamically from the registry that's why it throws exception at runtime.
How to fix it?
Rather than defining a policy as AsyncCachePolicy<string> you should define it as AsyncCachePolicy<HttpResponseMessage>. To do that you need to change the type parameter of the AsAsyncCacheProvider method.
var cacheProvider = ServiceProvider
.GetRequiredService<IDistributedCache>()
.AsAsyncCacheProvider<HttpResponseMessage>();
You also need to change the cachePolicy's type
AsyncCachePolicy<HttpResponseMessage> cachePolicy =
Policy.CacheAsync(
cacheProvider: cacheProvider,
TimeSpan.FromSeconds(30));
Side note: I would also suggest to store the policy registry key ("CachingPolicy") in a constant and refer to it when you register that and when you retrieve that.
UPDATE #1:
I'm not even sure that you have to call the AsAsyncCacheProvider method at all. Let me double check.
UPDATE #2
After reading the source code of the AsAsyncCacheProvider I've realized that it only supports byte[] or string as a type parameter. This indicates that you can't use the AddPolicyHandler methods here to automatically cache the response.
Rather you have to use the AsyncPolicyCache<string> directly in your MyClient implementation. You need to modify the constructor of the MyClient to recieve an IReadonlyPolicyRegister<string> parameter as well.
private readonly IAsyncPolicy<string> _cachePolicy;
public MyClient(HttpClient client, IReadOnlyPolicyRegistry<string> policyRegistry)
{
_cachePolicy = policyRegistry.Get<IAsyncPolicy<string>>("CachingPolicy");
// ...
}
And inside your exposed method you need to explicitly use ExecuteAsync
await _cachePolicy.ExecuteAsync(context => getXYZ(), new Context("XYZUniqueKey"));
The getXYZ needs to return a string (potentially the response body).

Get Exception properties from MassTransit Fault message

I would like to be able to access properties on the original exception that is thrown from a Consumer inside a Fault Consumer. For example, if the unhandled exception is a ValidationException with a collection of Errors, am I able to access that collection from a Fault Consumer?
The only thing I seem to have access to is the ExceptionType and the Message. I suppose I could parse the exception message to get the Errors collection, but is there a way to achieve this without parsing the message and generating the collection?
public async Task Consume(ConsumeContext<Fault<MyMessage>> context)
{
string exceptionType = context.Message.Exceptions[0].ExceptionType;
string exceptionMessage = context.Message.Exceptions[0].Message;
if (exceptionType == "FluentValidation.ValidationException")
{
// here I want to get the Errors collection on the exception of type ValidationException
}
}
MassTransit does not serialize Exception, it encapsulates the exception details in an ExceptionInfo type that is included with the Fault event.
There is no access to the original Exception type, and for good reason. Serializing exceptions as part of a message contract is just bad practice, in my opinion.

Why is my Azure function not recording the correct exception in App Insights?

Let's say my function is the following:
public static void Run([QueueTrigger(queueName, Connection = connection)]string message,
TraceWriter logger) {
throw new CustomException();
}
Here's what the log looks like:
SomeTime [Error] ExceptionNameSpace.CustomException ---> System.Exception
When I go to App Insights and view the exception breakdown, I find this failed request under the "Exception" type. I don't even see a CustomException type listed! Why is my exception being transformed into a generic exception?
For those of you who ran into the same issue:
I found the "solution" for this by being able to recover my original exception by querying for the outerType column in the exceptions table inside the Analytics part of App Insights. Strange that the generic exception shows up under the "type" column but "outerType" is my original exception.

How to fix Newtonsoft.Json.JsonConvert deserialize failure?

I have an asp.net WebAPI app. I'm returning files to the client in the Request.CreateResponse(,) second parameter as type Dictionary<string, List<System.Net.Http.StreamContent>>, where List<System.Net.Http.StreamContent>> contains all the files.
I'm deserializing the files at the client using the following:
var someContent = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<System.Net.Http.StreamContent>>>(result.Content.ReadAsStringAsync().Result);
This code throws the exception:
An unhandled exception of type
'Newtonsoft.Json.JsonSerializationException' occurred in
Newstonsoft.Json.dll.
Additional information: Unable to find a construction to use for type
System.Net.Http.StreamContent. A class should either have default
constructor, one constructor with arguments or a constructor marked
with the JsonConstructor attribute. Path 'myFiles[0].Headers'
Is there a different way to return actual files?

How do you mock IContextChannel in a WCF ServiceAuthorizationManager?

I am trying to write unit tests for a custom ServiceAuthorizationManager. The call to CheckAccessCore takes an OperationContext as a parameter. To instantiate an OperationContext, one must pass an IContextChannel to the constructor. Using MOQ, I've declared an IContextChannel:
private OperationContext _context;
private Mock<IContextChannel> _contextChannelMock;
Then I attempt to create the OperationContext:
_context = new OperationContext(_contextChannelMock.Object);
But this line throws an exception:
Result Message: Initialization method
Urs.EnterpriseServices.Providers.Tests.UrsServiceAuthorizationManager_Tests.SetUp
threw exception. System.InvalidOperationException:
System.InvalidOperationException: Invalid IContextChannel passed to
OperationContext. Must be either a server dispatching channel or a
client proxy channel..
How do I mock, a server dispatching channel?
You can't directly. See if WCFMock will help.

Categories