Determine if Server.Transfer[Request] was executed - c#

I was debugging two things and discovered that when an action executed:
Server.TransferRequest(url);
return new EmptyResult();
that my Application_Error would fire and catch the exception "The SessionStateTempDataProvider class requires session state to be enabled."
(I do have session state enabled, specifically StateServer, and for the sake of discussion let's imagine I do not need to commit changes from this request back to the session)
I'd like to control how this scenario gets logged. Obviously I can detect the exception type and message text, but if I wanted to not do special logging when the Request was .TransferRequest'd (or .Transfer'd), how can I do this?
I have already examined Response.IsRequestBeingRedirected - it was false.

Each Server.Transfer will add an unsafe request completion call to the stack, therefore, if there are more than 1 of this, you may think that the request is internally redirected by one of the Server.Transfer or Server.TransferRequest methods
Here is a boolean function which you can add to your global.asax file, which will return true if it finds more than one instances of request completion frames in the current stack trace.
The new StackTrace() is an expensive operation, but, because this is executing in an Exceptional context here, it's overhead "may" be ignored.
private bool IsRequestTransferred()
{
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
int requestCompletionCount = 0;
foreach (var stackFrame in stackTrace.GetFrames())
{
System.Reflection.MethodBase methodBase = stackFrame.GetMethod();
if (methodBase.DeclaringType.Name == "UnsafeIISMethods" && methodBase.Name == "MgdIndicateCompletion")
{
if (++requestCompletionCount == 2)
{
return true;
}
}
}
return false;
}
void Application_Error(object sender, EventArgs e)
{
bool isRequestTransferred = IsRequestTransferred();
}

Server.TransferRequest accepts headers collection. You can add a special technical header and then look for it later.
Server.TransferRequest(url,
true,
method: "GET",
headers: new NameValueCollection() { { "transferred", "true" } });
//and then
bool isTransferred = (Request.Headers["transferred"] != null);

Related

Call .fail(error) without throwing exception

Using SignalR, is there any possibility to call .fail instead of .done when specific values are returned by hub method?
Perhaps using the SignalR pipeline?
public bool Delete(int addressId)
{
// User should not be able to delete default address
if(AddressService.IsDefaultAddressOfCustomer(addressId))
return false; // Should call .fail() on client
AddressService.Delete(addressId);
return true; // Should call .done() on client
}
The alternative would be to throw an exception but I would like to avoid that since the error is not really a server fault, but a user fault.
Assuming you are really convinced an exception is not the right tool for you, you could use some custom attribute you would define to mark methods where a false return value must be translated into an error, and then intercept any incoming call with BuildIncoming from HubPipelineModule:
http://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubs.hubpipelinemodule.buildincoming(v=vs.118).aspx
From inside there you can intercept the call to your original method, inspect if it's marked with your attribute and if it returned false, if it's the case you can throw an exception from there. The bottom line is, you would still throw an exception to make it call .fail() client-side, but that exception would not bloat your business logic. Something like this:
public class FailPipelineModule : HubPipelineModule
{
public override Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
{
return base.BuildIncoming(context =>
{
var r = (bool)(invoke(context)).Result;
if (context.MethodDescriptor.Attributes.Any(a => typeof(FailAttribute) == a.GetType()) && !r)
throw new ApplicationException("false");
return Task.FromResult((object)r);
});
}
}
You'll need to define FailAttribute, use it to mark your hub's method and register FailPipelineModule at startup.
As Wasp indicated, with the SignalR JavaScript client, the promise is only rejected if hubResult.Error is set, which only happens when an exception is thrown while the request is processed. There's no way to modify that using the hub pipeline.
In general, I'd probably stick with using exceptions in that case, but if you're looking for another alternative, you could also modify the client-side jquery.signalr-*.js code, specifically the hubProxy prototype invoke method. It has a condition to decide whether to resolve or reject the promise:
if (result.Error) {
// code to reject ...
} else {
connection.log("Invoked " + that.hubName + "." + methodName);
d.resolveWith(that, [result.Result]);
}
and you could modify the else block:
if (result.Error) {
// code to reject ...
} else {
connection.log("Invoked " + that.hubName + "." + methodName);
if (typeof result.Result === "boolean" && !result.Result) {
d.rejectWith(that, [result.Result]);
} else {
d.resolveWith(that, [result.Result]);
}
}
Then all hub methods which return false would reject the promise.
The advantage over Wasp's solution would be not having to create attributes and overall less code. The drawback is that it might be less maintainable since you're manually editing the SignalR code (so if you do that, it should definitely be documented somewhere, and you'd have to minify the script yourself instead of using the packaged one).
A more maintainable client-side alternative would be to wrap the hub API and return your own deferred from that, e.g. like this:
var myHub = {
server: $.connection.myHub.server,
init: function() {
for (var methodName in this.server) {
if (this.server.hasOwnProperty(methodName)) {
this[methodName] = function() {
var deferred = $.Deferred();
this.server[methodName].apply(this.server, arguments)
.done(function(result) {
// reject if server hub method returned false
if (result === false) deferred.reject(result);
deferred.resolve(result);
});
return deferred.promise();
};
}
}
}
};
myHub.init();
Then instead of calling
$.connection.myHub.server.someMethod("hello", "world")
.done(function(result) { })
.fail(function(result) { });
you would call
myHub.someMethod("hello", "world")
.done(function(result) { })
.fail(function(result) { });
That way, you'd have full control over how return values are interpreted.

How to refactor code with try-catch-finally

I have to create a bunch of methods that look like this. The things that change will be the method name, the return type and the lines marked in the middle - the rest will be the same. Is there a clean way to refactor this so that I don't repeat myself?
private bool CanPerform(WindowsIdentity identity, string applicationName, int operation)
{
IAzApplication3 application = null;
IAzClientContext3 context = null;
try
{
application = this.store.OpenApplication(applicationName, null) as IAzApplication3;
ulong token = (ulong)identity.Token.ToInt64();
context = application.InitializeClientContextFromToken(token, null) as IAzClientContext3;
// lines that change go here
}
catch (COMException e)
{
throw new SecurityException(string.Format("Unable to check operation '{0}'", operation), e);
}
finally
{
Marshal.FinalReleaseComObject(context);
Marshal.FinalReleaseComObject(application);
}
}
I realise this is probably basic stuff but I work alone so there's no one else to ask.
It sounds like a delegate would be appropriate here, with a generic method to cover the return type changing:
private T ExecuteWithIdentity<T>(WindowsIdentity identity,
string applicationName, int operation,
Func<IAzApplication3, IAzClientContext3, T> action)
{
IAzApplication3 application = null;
IAzClientContext3 context = null;
try
{
application = this.store.OpenApplication(applicationName, null) as IAzApplication3;
ulong token = (ulong)identity.Token.ToInt64();
context = application.InitializeClientContextFromToken(token, null) as IAzClientContext3;
return action(application, context);
}
catch (COMException e)
{
throw new SecurityException(
string.Format("Unable to check operation '{0}'", operation), e);
}
finally
{
Marshal.FinalReleaseComObject(context);
Marshal.FinalReleaseComObject(application);
}
}
Then you put the code for each check in a separate method, or even just use a lambda expression:
bool check = ExecuteWithIdentity(identity, "Foo", 10,
(application, context) => context != null);
or
string check = ExecuteWithIdentity(identity, "Foo", 10, SomeComplexAction);
...
private static string SomeComplexAction(IAzApplication3 application,
IAzClientContext3 context)
{
// Do complex checks here, returning whether the user is allowed to
// perform the operation
}
You may want to change the delegate type of course - it's not clear what operation is meant to be used for, for example.
I would also strongly consider casting instead of using as. If the application or context is returned from OpenApplication/InitializeClientContextFromTokenas a non-null value which just isn't the right type, do you really want to handle that the same was as a null value being returned?
You could do your error handling slightly higher up the stack, so rather than catching and rethrowing the exception inside the method you could do it where the method is called?
If your method calls are all wrapped in a Manager class that might save a bit of time. If they're just ad-hoc called everywhere then naturally maybe not :)
I hope that might help.

Stop method execution

I have a class that tries to get information from web service few times:
public TResult Try(Func<TResult> func, int maxRetries)
{
TResult returnValue = default(TResult);
int numTries = 0;
bool succeeded = false;
while (numTries < maxRetries)
{
try
{
returnValue = func();
succeeded = true;
}
catch (Exception ex)
{
Log(ex,numTries);
}
finally
{
numTries++;
}
if (succeeded)
{
return returnValue;
}
else
{
if (numTries == maxRetries)
{
//ask user what to do
}
}
}
Now after 'if (numTries == maxRetries)' user will be able to chose if he wants to continue trying to get data from web service or cancel.
I want to just open new form when user cancels and stop executing method that called above method. I can't just return null or new object because the method that run this retrier will continue to work and in many cases cause problems.
So basically this looks like this:
someValue = retry.Try(() => webService.method(),maxRetries));
//if user canceled after app wasn't able to get data stop execution as already another form is opened
I could of course check if returned value was null like:
someValue = retry.Try(()=>webService.method(),maxRetries));
if (someValue == null)
return;
But this would mean a lot of changes in the code and I want to avoid it and it would be best if I could do it from Try method.
I can think of two things. You could make sure that TResult is of an Interface type that has a Boolean field that represents a successful request (IsSuccessful or IsValid, etc). If you cannot modify TResult, the other option is to use an Out parameter on your try method to pass another value out.
There is no way to just stop the execution of a method. The first thing that comes to mind though is to throw an exception and catch it from the calling method.
Keep in mind though that you shouldn't ever rely on exceptions to control flow like that.. but if you really can't rewrite the calling method, this may be your only option.
The other option is perhaps having your returned result set a flag (via an interface) to notify the caller that it completed successfully.

Why does the synchronized wrapper for ArrayList not work?

I have generated the proxy classes for a web service in Visual Studio with 'Add Web Reference'. The generated RTWebService class has a method SetValueAsync. I extended this class and added a SetValueRequest which keeps track of the requests and cancels all pending requests when an error occurs. With every request I store the userState object in an ArrayList I created as follows:
requests = ArrayList.Synchronized(new ArrayList());
I created a method:
public void CancelPendingRequests() {
lock (requests.SyncRoot) {
if (requests.Count > 0) {
foreach (object request in requests) {
this.CancelAsync(request);
}
requests.Clear();
}
}
}
I call this method when a request returns on the SetValueCompleted event:
private void onRequestComplete(
object sender,
Service.SetValueCompletedEventArgs args
) {
lock (syncResponse) {
if (args.Cancelled) {
return;
}
if (args.UserState != null) {
requests.Remove(args.UserState);
}
if (args.Error != null) {
CancelPendingRequests();
}
}
}
To start a new request I call:
public void SetValueRequest(string tag, string value) {
var request = new object();
this.SetValueAsync(tag, value, request);
requests.Add(request);
}
Everytime I make a request and at the same time a response returns with an error, I get a TargetInvocationException in the CancelPendingRequests. The inner exception is an InvalidOperationException on an ArrayList in the CancelPendingRequests method saying:
Collection was modified; enumeration operation may not execute.
So it seems SetValueRequest has modified the requests object while I was enumerating it. I thought this was impossible because I used the synchronized wrapper for ArrayList and use the SyncRoot to synchronize the enumeration. I'm a bit stuck on this so if anyone has an idea?
never use SyncRoot it's inherently broken. (if you share the list you just invite a deadlock)
Don't use ArrayList, it should be marked "Deprecated".
ArrayList.Synchronized return's something that works more slowly but is not thread safe, i.e. it's not thread safe during a set of operations.
you can either use something from System.Collection.Concurrent, or use ReaderWriterLockSlim
ORIGINAL ANSWER
I worked around the problem by removing the enumeration. I now use:
public void CancelPendingRequests() {
lock (requests.SyncRoot) {
if (requests.Count > 0) {
for (int i = 0; i < requests.Count; i++) {
this.CancelAsync(requests[i]);
}
requests.Clear();
}
}
}
This seems to do the trick. I'm still a bit worried that this lock (requests.SyncRoot) didn't work on the enumeration so why would it work here? Anyway, I am now unable to reproduce the exception like i could before so I consider this problem as solved. I can't waste any more time on this.
EDIT
Forget my silly answer above. I was working on a project and needed to make progress. I tracked down the problem now:
So it appeared this bug was not multithreading related at all. All code was executed on the same thread, I didn't need those locks. The problem lies in the fact that I was canceling the requests in my enumeration. The CancelAsync method raises the SetValueCompleted event which in turn calls requests.Remove, thus modifying the requests inside the enumeration. I Learnt some pitfall with events today.
I solved the problem by enumerating over a local copy of the requests object which I created with the ToArray method.
public void CancelPendingRequests()
if (requests.Count > 0) {
for (object request in requests.ToArray()) {
this.CancelAsync(request);
}
}
}
Trying adding a local variable to your CancelPendingRequests method for each request object like this:
public void CancelPendingRequests() {
lock (requests.SyncRoot)
{
if (requests.Count > 0)
{
foreach (object request in requests)
{
object currentRequest = request; //Add this
this.CancelAsync(currentRequest);
}
requests.Clear();
}
}
}

Workaround for HttpContext.HideRequestResponse being internal? Detect if HttpContext.Request is really available?

We're migrating an application to use IIS7 integrated mode. In library code that is designed to work either within the context of an HTTP request or not, we commonly have code like this:
if (HttpContext.Current != null &&
HttpContext.Current.Request != null) {
// do something with HttpContext.Current.Request
} else {
// do equivalent thing without HttpContext..
}
But in IIS7 integrated mode the check for HttpContext.Current.Request throws an exception whenever this code is called from Application_Start.
protected void Application_Start(object sender, EventArgs e)
{
SomeLibrary.DoSomethingWithHttpContextCurrentDetection();
}
Results in:
System.Web.HttpException: Request is not available in this context
How can I detect whether the request is really available without wrapping these calls in an exception handler and taking action based on whether an exception is generated or not.
Looking at HttpContext in Reflector I see it has an internal bool HideRequestResponse field but it's internal so I can only get to it with reflection and that's fragile. Is there a more official/approved way to determine if it's ok to call HttpContext.Request?
This blog post about the subject says not to use HttpContext, but how, in generic library code, can you determine if it's ok to use HttpContext?
http://mvolo.com/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-applicationstart/
I'm using the work-around mentioned there which is to use Application_BeginRequest and an initialized field to only initialize once as part of BeginRequest, but that has to be done in every calling application whereas I'd prefer to make the library code more robust and handle this situation regardless of where it's called from.
I would refactor your code to this:
if (IsRequestAvailable())
{
// do something with HttpContext.Current.Request...
}
else
{
// do equivalent thing without HttpContext...
}
public Boolean IsRequestAvailable()
{
if (HttpContext.Current == null)
return false;
try
{
if (HttpContext.Current.Request == null)
return false;
}
catch (System.Web.HttpException ex)
{
#if DEBUG
// Testing exception to a magic string not the best practice but
// it works for this demo.
if (ex.Message == "Request is not available in this context")
return false;
throw;
#else
return false;
#endif
}
return true;
}
Your question asked not to use exception handling (I assume for performance reasons) and my answer does. However, by changing your code from using "If (HttpContext.Current != null && HttpContext.Current.Request != null)" to "If (IsRequestAvailable())" you only have one place to change the code when you find an answer how not to use exception handling.
I'm afraid the answer is that you can't get what you want - Microsoft sees this case as an 'exceptional circumstance' and so it will throw an exception.
You can use reflection as you describe in your answer but you don't want to and so are limited by the API that Microsoft have provided, for better or for worse.
If you do decide to use reflection, of note is the HttpApplication.InitInternal method which is what sets the HideRequestResponse flag.
Hope that helps. I would suggest you file a report with Microsoft Connect.
You should not even use Request (or Response) in the Application_Start since application could be started without a request. So in the future your application won't even run when other parts of framework stop providing the Request object.
If you want to just hack it temporarily, you could use Reflection (if you have above-medium trust) or catching an exception (even though you don't want to) and store the result in a static variable or possibly use a static HttpContext wrapper:
Also you could use HttpRuntime.UsingIntegratedPipeline.
So the best approach is remove the dependance of your classes on HttpContext when they are being initialized or not initalize them in appstart.
What is your reasoning to use Request in the app start anyway? For statistics? Or just telling the user he woke the application?
Edited with code to explain better:
public static class ContextWrapper
{
public static HttpRequest Request
{
get
{
HttpContext context = HttpContext.Current;
if (context == null) return null;
if (HttpRuntime.UsingIntegratedPipeline)
{
try { return context.Request; }
catch (HttpException e) { /* Consume or log e*/ return null; }
// Do not use message comparison - .NET translates messages for multi-culture environments.
}
return context.Request;
}
}
}
And in code:
if (ContextWrapper.Request != null) //...
Or a user-controlled faster way:
public static class ContextWrapper2
{
public static bool IsIis7IntegratedAppStart { get; set; }
public static HttpRequest Request
{
get
{
if (ContextWrapper2.IsIis7IntegratedAppStart) return null;
HttpContext context = HttpContext.Current;
if (context == null) return null;
return context.Request;
}
}
}
And in app start:
protected void Application_Start(object sender, EventArgs e)
{
yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = true;
//...
yourLibraryNamespace.yourClass.Init();
//...
yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = false;
}
You could note this behaviour in your documentation and all should be well. AppStart-like context should be the only place where you get such an exception.
You could also implement IDisposable on a member and use it in appStart with the using statement so you do not forget to set IsIis7IntegratedAppStart = false.
I think I have the solution for you. I maintain a logging library and have the same issue as you. If it is a web request I am grabbing some data from the HttpContext. But depending on how the logging library is used this same scenario can happen. So here is my solution. The key fix for me was checking if the Handler was null or not.
if (System.Web.Hosting.HostingEnvironment.IsHosted
&& System.Web.HttpContext.Current != null
&& System.Web.HttpContext.Current.Handler != null
&& System.Web.HttpContext.Current.Request != null)
{
//access the Request object here
}
Depending on what you are trying to accomplish, you may be able to get some of the properties and settings around the web app from System.Web.Hosting.HostingEnvironment
I added a comment, but it gets auto-hidden.
I think it's more important to have an idea of what it is that you need from the request.
For instance, the link you provided which provides a workaround is looking for Request.ApplicationPath.
If that's actually what you're looking for (for, say, loading the web.config vs the app.config), you could do this:
if (HttpRuntime.AppDomainAppId != null)
return WebConfigurationManager.OpenWebConfiguration(HttpRuntime.AppDomainAppVirtualPath);
else
return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
If this (or HttpRuntime.ApplicationPath) isn't what you're actually looking for, it would be helpful to know which properties of the Request you are actually looking for. Maybe there's a better, safer way to get there.

Categories

Resources