HttpContext is null in a method called inside Parallel.For - c#

Posting this question after trying a lot. Doing normal for is not an option because we need to do a large amount of processing in very less time.
I have GetDataFor() inside which HttpContext.Current is used.
The code looks like this:
public void SomeMethod()
{
var context = HttpContext.Current;
Parallel.For(0, 100, i =>
{
var data = GetDataFor(i, context);
});
}
public data GetDataFor(int i, HttpContext context)
{
Uri requestUri = null;
if (HttpContext.Current != null)
{
requestUri = HttpContext.Current.Request.Url;
sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
}
else
{
requestUri = context.Request.Url;
}
//do something
return data;
}
Everything works fine inside normal for loop. However, when I call it inside Parallel.For and pass HttpContext.Current, HttpContext.Current.Request, HttpContext.Current.Request.Url as method parameters:
HttpContext.Current cannot be serialized because it does not have a parameterless constructor
Passing HttpContextBase httpContext = null as parameter throws:
To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.Object) at all levels of their inheritance hierarchy. System.Web.HttpApplicationStateBase does not implement Add(System.Object).
Tried making a property:
public string[] httpContextData
{
get
{
string requestUrl = HttpContext.Current.Request.Url.ToString();
string sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
return new string[] { requestUrl, sCookie };
}
}
and using in method:
var contextData = httpContextData;
which throws:
System.Uri cannot be serialized because it does not have a parameterless constructor
I did all this to send it's reference and state but unable to understand why the problem is not solving.
How do I use HttpContext.Current inside Parallel.For? What am I doing wrong here?
Btw, the needed stuff are:
HttpContext.Current.Request.Url and HttpContext.Current.Request.Headers["cookie"]

HttpContext.Current is only available (not null) inside request-handling threads. Parallel.For creates multiple threads, none of which is has access to HttpContext.Current.
You have to pass all data that code in Parallel.For threads needs either through
local variables assigned before the loop or
TLocal instance used in Parallel.For<TLocal>.
In any event, code such as HttpContext.Current.... is out.

The code is proprietary hence I'm only posting the relevant parts:
Since passing the following objects:
HttpContext.Current
HttpContext.Current.Request
HttpContext.Current.Request.Url
as params to GetDataFor was throwing so many errors.
Also, my needs were only
request url which can be re-generated by passing url as string to it's constructor
and a request header's value which is essentially a string
I only passed string to GetDataFor() method:
public void SomeMethod()
{
string requestUrl = HttpContext.Current.Request.Url.ToString();
string sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
Parallel.For(0, 100, i =>
{
var data = GetDataFor(i,
requestUrl: requestUrl,
sCookie: sCookie);
});
}
public data GetDataFor(int i, string requestUrl = null, string sCookie = null)
{
Uri requestUri = null;
if (HttpContext.Current != null)
{
requestUri = HttpContext.Current.Request.Url;
sCookie = string.Format("{0}", HttpContext.Current.Request.Headers["cookie"]);
}
else
{
requestUri = new Uri(requestUrl);
}
//do something
return data;
}

Related

How to mock Server Variables using NSubstitute

I have the following method to get a user's IP address:
public string GetUserIpAddress(HttpContext context)
{
var ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(ipAddress))
{
string[] addresses = ipAddress.Split(',');
if (addresses.Length != 0)
{
return addresses[0];
}
}
return context.Request.ServerVariables["REMOTE_ADDR"];
}
I am trying to write a unit test for this but am struggling to do so (using NSubstitute and NUnit). I have read other articles and so far this is my attempt:
var request = Substitute.For<HttpRequestBase>();
request.ServerVariables.Returns(new System.Collections.Specialized.NameValueCollection
{
{ "HTTP_X_FORWARDED_FOR", "value here" }
});
var httpContext = Substitute.For<HttpContextBase>();
httpContext.Request.Returns(request);
var helper = new UserIpAddressHelper();
var result = helper.GetUserIpAddress(httpContext.ApplicationInstance.Context);
The error is that the result is returning null when I want it to be configured properly with the server variables above.
HttpRequestBase has been created specifically to be testable, because it is abstract and can therefore be overridden by a mocking framework.
If you want GetUserIpAddress to be testable, have it accept a HttpContextBase instance:
public string GetUserIpAddress(HttpContextBase context)
If it needs to be called from code that uses HttpContext that can be wrapped:
HttpContext context;
var ipAddress = GetUserIpAddress(new HttpContextWrapper(context));

Constructor arguments cannot be passed for interface mocks (Mock IFeedResponse)

I am trying to write unit tests for DocumentDBRepository paging code. Since there is continuation token involved in the FeedResponse, I need to mock the FeedResponse in order to put some value for FeedResponse.ContinuationToken. But the problem is that I got an error saying:
Message: System.ArgumentException : Constructor arguments cannot be
passed for interface mocks.
Does it mean I am not able to mock FeedResponse? Or maybe the way I use FeedResponse is wrong?
Here's my code:
var response = new Mock<IFeedResponse<T>>(expected);
response.Setup(_ => _.ResponseContinuation).Returns(It.IsAny<string>());
var mockDocumentQuery = new Mock<IFakeDocumentQuery<T>>();
mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
.Returns((Task<FeedResponse<T>>)response.Object);
When I debugged, the break point stops at var response = new Mock<IFeedResponse<T>>(expected); and then the error happened.
The error is because you were mocking the interface and trying to pass a constructor argument. That wont work as stated by the error message.
You can however use an actual instance of FeedResponse.
Given that the desired member is not virtual and is also read-only, you could consider stubbing the class and overriding the default behavior since FeedResponse<T> is not sealed.
For example
public class FeedResponseStub<T> : FeedResponse<T> {
private string token;
public FeedResponseStub(IEnumerable<T> result, string token)
: base(result) {
this.token = token;
}
public new string ResponseContinuation {
get {
return token;
}
}
}
and using the stub in the test
//...
var token = ".....";
var response = new FeedResponseStub<T>(expected, token);
//...
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
//...
Here is the way I work around it in Cosmonaut.
public static FeedResponse<T> ToFeedResponse<T>(this IQueryable<T> resource, IDictionary<string, string> responseHeaders = null)
{
var feedResponseType = Type.GetType("Microsoft.Azure.Documents.Client.FeedResponse`1, Microsoft.Azure.DocumentDB.Core, Version=1.9.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var headers = new NameValueCollection
{
{ "x-ms-request-charge", "0" },
{ "x-ms-activity-id", Guid.NewGuid().ToString() }
};
if (responseHeaders != null)
{
foreach (var responseHeader in responseHeaders)
{
headers[responseHeader.Key] = responseHeader.Value;
}
}
var arguments = new object[] { resource, resource.Count(), headers, false, null };
if (feedResponseType != null)
{
var t = feedResponseType.MakeGenericType(typeof(T));
var feedResponse = Activator.CreateInstance(t, flags, null, arguments, null);
return (FeedResponse<T>)feedResponse;
}
return new FeedResponse<T>();
}
}
You can pass your continuation token as a header key-value in the dictionary to set the FeedResponse value.
You can do that by setting the x-ms-continuation value to a token.
Keep in mind that the ResponseContinuation property of the FeedResponse also takes the useETagAsContinuation value into account. I default it to false in the reflection invoked constructor.
For any further reference check the project's code and how unit tests are written.

.NET Core: How to get a `Controller` instance (or just it's full name) from ViewLocationExpanderContext

In MVC5, I had my own VirtualPathProviderViewEngine and had the following:
string controllerAssemblyName = controllerContext.Controller.GetType().Assembly.FullName;
I used that for dynamically adding view locations.
Anyway, I am migrating to .NET Core and writing my own IViewLocationExpander and need to determine the controller type there to do the same thing. The method signature is as follows:
public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
All I have is an instance of ViewLocationExpanderContext, which only provides ControllerName and AreaName properties, but no actual instance of a Controller. Is there a way to get an instance or at least full type name of the controller using those 2 properties?
I also tried the following:
var controllerContext = new ControllerContext(context.ActionContext);`
That gives me an instance of ControllerContext, but unlike MVC5, it doesn't have a Controller property on it.
Inspect the ActionContext to access the desired information.
Should be able to drill down into the context to get the action controller type info and assembly full name
public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) {
var controllerActionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if(controllerActionDescriptor != null) {
var controllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo;
//From the type info you should be able to get the assembly
var controllerAssemblyName = controllerTypeInfo.AsType().Assembly.FullName.ToString();
}
//...
}
Thanks to #Nkosi. I have used his answer and my solution is as follows. I am not sure if the CreateControllerFactory() part is good, but it's my fallback in case controllerActionDescriptor is NULL:
string controllerAssemblyName = null;
var controllerActionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if (controllerActionDescriptor != null)
{
var controllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo;
controllerAssemblyName = controllerTypeInfo.AsType().Assembly.FullName;
}
else
{
var controllerContext = new ControllerContext(context.ActionContext);
var factory = CreateControllerFactory();
var controller = factory.CreateController(controllerContext);
controllerAssemblyName = controller.GetType().Assembly.FullName;
}
private static DefaultControllerFactory CreateControllerFactory()
{
var propertyActivators = new IControllerPropertyActivator[]
{
new DefaultControllerPropertyActivator(),
};
return new DefaultControllerFactory(
new DefaultControllerActivator(new TypeActivatorCache()),
propertyActivators);
}

Create generic async task function

I have created a function that returns an object using async/await. I would like to make the function generic so that it can return whatever object that I pass in. The code is boilerplate except for the objects being returned. I would like to be able to call GetAsync and have it return the correct object
public Patron getPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
Patron Patron = GetAsync(uri).Result;
return Patron;
}
private async Task<Patron> GetAsync(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
JavaScriptSerializer ser = new JavaScriptSerializer();
Patron Patron = ser.Deserialize<Patron>(content);
return Patron;
}
What about a generic method?
private async Task<T> GetAsync<T>(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
var serializer = new JavaScriptSerializer();
var t = serializer.Deserialize<T>(content);
return t;
}
Normally, you should place this method into another class and make it public, in order it can be used by methods in different classes.
Regarding the way you call this method, you could try the following:
// I capitalized the first letter of the method,
// since this is a very common convention in .NET
public Patron GetPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
var Patron = GetAsync<Patron>(uri).Result;
return Patron;
}
Note: In the above snippet I assumed that you haven't moved the GetAsync into another class. If you move it, then you have to make a slight change.
Update
I'm not following what you mean by your note. Do I need to make GetPatronById a task function as well - like Yuval has done below?
I mean something like this:
// The name of the class may be not the most suitable in this case.
public class Repo
{
public static async Task<T> GetAsync<T>(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
var serializer = new JavaScriptSerializer();
var t = serializer.Deserialize<T>(content);
return t;
}
}
public Patron GetPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
var Patron = Repo.GetAsync<Patron>(uri).Result;
return Patron;
}
Generic can be easily done with:
private async Task<T> GetAsync(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(content);
}
Things to note:
JavaScriptSerializer has been deprecated for ages, avoid using it. Try out Json.NET instead.
This:
Patron Patron = GetAsync(uri).Result;
is dangerous and can cause potential deadlocks, especially in Web API. You need to go "async all the way":
public Task<Patron> GetPatronByIdAsync(string barcode)
{
string uri = $"patrons/find?barcode={barcode}";
return GetAsync<Patron>(uri);
}
And only your top most level invoker need await on the Task. Possibly some controller action:
public async Task SomeAction()
{
await GetPatronByIdAsync("hello");
}

Object null in WebApi method after PostAsJsonAsync

I am posting an object to a WebApi method. I'm using PostAsJsonAsync to do this.
public async Task<HttpResponseMessage> PostAsync(string token, ServiceCall call)
{
var client = new HttpClient();
client.SetBearerToken(token);
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", call);
return response;
}
The object call that I'm passing is not null when I post it.
[HttpPost]
[Route("id/nestedcall")]
public async Task<IHttpActionResult> NestedCall([FromBody]ServiceCall call)
{
// call is null here
}
However it is null in my API method. I can't seem to work out why as all of the examples I've followed use this format.
Why isn't the call object being picked up by the web api?
Edit
Here is the ServiceCall object. It is in a separate class library and a reference is included in both the web application and the API.
public class ServiceCall
{
public ServiceCall(Service service, string grantType)
{
ClientId = service.Id;
ClientSecret = service.Secret;
Uri = service.Uri;
Scope = service.Scope;
GrantType = grantType;
}
public ServiceCall(string clientid, string clientsecret, string uri, string scope, string grantType)
{
ClientId = clientid;
ClientSecret = clientsecret;
Uri = uri;
Scope = scope;
GrantType = grantType;
}
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string Uri { get; set; }
public string Scope { get; set; }
public string GrantType { get; set; }
}
I have seen Object null in WebApi method after PostAsJsonAsync due to serialization.
Better to use PostAsync like below :
var obj = new MyClass()
{
MyProperty = 11
};
using (var client = new HttpClient())
{
string inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
HttpContent inputContent = new StringContent(inputJson, Encoding.UTF8, "application/json");
HttpResponseMessage response1 = client.PostAsync("http://localhost:60909/api/home/Test", inputContent).Result;
if (response1.IsSuccessStatusCode)
{
}
}
Using Prefix Stackify I was able to diagnose that the serialiser was throwing an exception:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Core.Models.ServiceCall. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'ClientId', line 1, position 12.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize
However, very helpfully, rather than tell me that an exception occurred the controller simply gave me a null object.
As hinted by the exception the solution is to add a default constructor (or at least one the serialiser can understand).
public ServiceCall()
{
}
looks like the JSON serialization may be failing. BTW, remove that [FromBody] and try without it like below. PostAsJsonAsync method serializes the ServiceCall object to JSON and then sends the JSON payload in a POST request.
public async Task<IHttpActionResult> NestedCall(ServiceCall call)
{
// your code
}
I run into exactly the same problem and had to do this to solve it:
using (var client = new HttpClient())
{
client.SetBearerToken(token);
var content = new StringContent(JsonConvert.SerializeObject(call), Encoding.UTF8, "application/json");
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", content);
return response;
}

Categories