There is a new app setting in asp.net 4.5
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
code like this can run in asp.net 4.0
protected void Button1_Click(object sender, EventArgs e)
{
CallAysnc();
}
public void CallAysnc()
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(Guid.NewGuid().ToString());
WebClient client = new WebClient();
client.DownloadStringCompleted += (object sender, DownloadStringCompletedEventArgs e) =>
{
asyncOp.PostOperationCompleted(CallCompleted, e.Result);
};
client.DownloadStringAsync(new Uri("http://www.google.com"));
}
private void CallCompleted(object args)
{
Response.Write(args.ToString());
}
But it doesn't work in asp.net 4.5,and when I remove the new appsetting,it works again!
So what's the meaning of "UseTaskFriendlySynchronizationContext" ?
Regarding UseTaskFriendlySynchronizationContext, from Microsoft Forums:
That tells ASP.NET to use an entirely new asynchronous pipeline which
follows CLR conventions for kicking off asynchronous operations,
including returning threads to the ThreadPool when necessary. ASP.NET
4.0 and below followed its own conventions which went against CLR guidelines, and if the switch is not enabled it is
very easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.
Also, I think AsyncOperationManager is intended for desktop applications. For ASP.NET apps you should be using RegisterAsyncTask and setting <%# Page Async="true", see here for more details.
So using the new c# keywords your example would be:
protected void Button1_Click(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(CallAysnc));
}
private async Task CallAysnc()
{
var res = await new WebClient().DownloadStringTaskAsync("http://www.google.com");
Response.Write(res);
}
The aim is to support the following by release but is not currently supported in the beta:
protected async void Button1_Click(object sender, EventArgs e)
{
var res = await new WebClient().DownloadStringTaskAsync("http://www.google.com");
Response.Write(res);
}
More details, quoted from ASP.NET 4.5.1 documentation for appSettings on MSDN:
aspnet:UseTaskFriendlySynchronizationContext
Specifies how asynchronous code paths in ASP.NET 4.5 behave.
...
If this key value is set to false [default], asynchronous code paths in ASP.NET 4.5 behave as they did in ASP.NET 4.0. If this key
value is set to true, ASP.NET 4.5 uses code paths that are optimized
for Task-returning APIs. Setting this compatibility switch is
mandatory for WebSockets-enabled applications, for using Task-based
asynchrony in Web Forms pages, and for certain other asynchronous
behaviors.
Related
We can now use the async/await key words in ASP.NET MVC 4.
public async Task<ActionResult> TestAsync()
{
WebClient client = new WebClient();
return Content(await client.DownloadStringTaskAsync("http://www.google.com"));
}
But how to use it in ASP.NET WebForms?
One easy way is to just make your event handlers async. First, add the Async="true" parameter to the #Page directive, and then you should be able to write async event handlers as such:
protected async void Page_Load(object sender, EventArgs e)
{
var client = new WebClient();
var content = await client.DownloadStringTaskAsync("http://www.google.com");
Response.Write(content);
}
I say "should be able to" because I haven't actually tried this out myself. But it should work.
Update: This does not work for Page_Load (see this MSDN forum thread), but should work for other events such as button clicks.
Update: This does work for Page_Load in ASP.NET 4.5. Also, they added checks if you improperly use an async event handler. See this video for details.
According to http://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx the only reliable way to use async in web forms is to call Page.RegisterAsyncTask.
The code to support simple things like async Page_Load is extremely
complicated and not well-tested for anything beyond basic scenarios.
Using async with voids is not stable or reliable. However, all you
have to do is call Page.RegisterAyncTask - it's not any trouble and
you'll be in a better more flexible place
.
public void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
public async Task LoadSomeData()
{
var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject>(await clientcontacts);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject(await clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
}
I have an ASP.NET web service based on SOAP built on top of Mono.
If I throw exceptions inside the service, the exceptions remain on the HTTP+HTML level. What I'd like to do is send always all exceptions as SOAP responses, i.e. I don't have any normal aspx pages (everything should work with SOAP).
I've tried handling all exceptions in the Global.asax.cs file within the Application_Error() method, but it seems to always send the exceptions as HTML.
What I see is the generic ASP.NET error page.
My SOAP client, when pointed to the HTML, informs me that it cannot parse HTML.
Sending SOAP from the server works nicely when no exceptions are thrown.
I've studied various web sources and learned that Application_Error shouldn't be used for SOAP exception handling from this resource:
Handling and Throwing Exceptions in XML Web Services
Do I have to implement my own HTTP Module or
ExceptionUtility Class or HTTP Handler?
I am running this on my development machine:
Version information: Mono Runtime Version: 2.10.5 (Debian 2.10.5-1); ASP.NET Version: 4.0.30319.1
I am testing this with MonoDevelop's built-in xsp HTTP server inside Ubuntu 11.10.
Here is my test code:
Global.asax.cs:
using System;
using System.Collections;
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;
using System.Web.Services.Protocols;
namespace SoapTaikina
{
public class Global : System.Web.HttpApplication
{
protected virtual void Application_Start (Object sender, EventArgs e)
{
Backend.Initialize();
}
protected virtual void Application_Error (Object sender, EventArgs e)
{
// This doesn't appear to be executed when Foo() throws an exception.
// I cannot catch the Foo() constructor exception here.
throw new SoapException("This is never reached.", null);
}
// These are not used in this example.
protected virtual void Session_Start (Object sender, EventArgs e) { }
protected virtual void Application_BeginRequest (Object sender, EventArgs e) { }
protected virtual void Application_EndRequest (Object sender, EventArgs e) { }
protected virtual void Application_AuthenticateRequest (Object sender, EventArgs e) { }
protected virtual void Session_End (Object sender, EventArgs e) { }
protected virtual void Application_End (Object sender, EventArgs e) { }
}
}
Foo.asmx.cs:
using System;
using System.Web;
using System.Web.Services;
namespace SoapTaikina
{
public class Foo : System.Web.Services.WebService
{
public Foo()
{
// This may throw an Exception which will not be caught in
// Application_Error().
//
// This is the problem spot.
Backend2.InitializeMoreStuff();
}
[WebMethod]
public bool DoStuff() {
// This works fine and returns a SOAP response.
return true;
}
}
}
First, theory
Application_Error is never fired according to .NET Framework requirements. This is because the pipeline that runs pages is different from the one that runs web services. Also, notice that throwing exceptions in Application_Error is a very bad idea.
I found that testing web services from the browser (where, and probably because, accept header is not set to application/soap+xml but to text/html) makes a plaintext message appear. If the client is a SOAP proxy (ie you generated in Visual Studio/MonoDevelop from the web service's WSDL) and inherits from SoapHttpClientProtocol then it is expected that the exception is always thrown as SOAP fault.
Second, practice.
You do var f = new Foo() from Application_Start. This is wrong because the web service skeleton class is instantiated fresh new on every HTTP/SOAP request, and should never be initialized in the special Application_Start method that is run on the very first request and without the request itself bein processed yet. Also, you should avoid to do complex things (that may throw exceptions) in web service's constructor. This is just a bad design, not a non-compiling or non-working solution.
Your problem probably occurs because the ASP.NET pipeline never reached the point where the request is mapped to a web service handler rather than a page handler, firing default behaviour. Tried to find the code in mono repository, no luck :)
I suggest you to first remove any WS initialization in Application_Start, and if you said you don't really need it, throw away Application_Error
I am looking at a legacy enterprise application, which written using ASP.NET. No controls or web forms. This is how it works:
EmployeeList.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="EmployeeList.aspx.cs" Inherits="EmployeeList" %>
EmployeeList.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
// Security Check
// Load Template, get version 1.4 of active employee list
StringBuilder template = GetTemplate("Employee", "ActiveList", "1.4", true);
// Get list from database
using(SqlDataReader objReader = GetListFromDB())
{
while(objReader.Read())
{
//fills data
TUtils.Replace(template, ROW, "%name%", objReader[0]);
}
}
// return response
Response.Write(template.ToString());
}
private StringBuilder GetTemplate(string x, string y, string v, bool z);
{
// returns template
}
private SqlDataReader GetListFromDB() {
// returns data reader
}
My question is, since we are not using web forms, is there a way to introduce NUnit in this event driven model (as shown above)?
Also, please avoid suggestions to move to ASP.NET MVC or other patterns, which we are considering, but wondering is there any way to convert this enterprise application testable.
This is absolutely possible. You should have a look on implementing MVP pattern with ASP.NET Webforms. There are several open source implementations but you can do a smaller specialized on your your own.
The basics are to move your code behind logic to a presenterclass. The presenter class has a reference to the page implementing an interface. The trick in your case will be to Mock the Page.Response object for your test. Thats why it´s hard to unit test it right way. The PageResponse Property contains a object deriving from HttpResponseBase and that´s the baseclass you should Mock in your tests and do your asserts on with your example. You could start with that and then extend your presenter with functionalty like Session, Request etc.
If you don´t have any markup at all probably you can just create the presenter in the view constructor and don´t bother of having and reference to the view.
To clarify: The big trick is to get the code out of the aspx.cs file. That beast is not testable.
Sample base class for Presenters:
public class Presenter<T> where T : class, IView
{
protected readonly T View;
protected Presenter(T view, ILogger logger)
{
View = view;
}
public virtual void page_PreRender(object sender, EventArgs e)
{
}
public virtual void page_Init(object sender, EventArgs e)
{
}
public virtual void page_Load(object sender, EventArgs eventArgs)
{
}
public HttpContextBase HttpContext { protected get; set; }
protected HttpRequestBase Request
{
get { return HttpContext.Request; }
}
}
Since most of your code is in the code-behind, I dont think that the usual testing approach with NUnit / Visual Studio testing framework will work well.
However, I think one possible approach is to use UI Testing frameworks like WATIN / Selenium. This will enable you to still write tests for the various functionality.
I have done something similar in the past by basically writing a test case for every UI action that results in a server postback. May not be ideal but it does allow you to automate your testing.
There is a HttpModule that change Server field in Response Headers. But it does not work in ASP.NET/IIS7 classic mode. what is the solution for remove or change Server field in reponse header?
public class CloakHttpHeaderModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.PreSendRequestHeaders += new EventHandler(context_PreSendRequestHeaders);
}
public void Dispose()
{
}
private void context_PreSendRequestHeaders(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
context.Response.Headers.Set("Server", "Apache 2.0");
//HttpContext.Current.Response.Headers.Set("Server", "WSGIServer/0.1 Python/2.6.1");
}
}
You can't do this unless you're running at least IIS7, Integrated Pipeline Mode and .NET 3.0. The documentation states this:
HttpResponse.Headers Property
The Headers property is only supported with the IIS 7.0 integrated
pipeline mode and at least the .NET Framework 3.0. When you try to
access the Headers property and either of these two conditions is not
met, a PlatformNotSupportedException is thrown.
I'm looking for the best way to configure NHibernate Validator on a classic ASP.net app that is using NHibernate Burrow to manage NH sessions.
How do I get it to register the interecptors automatically?
OK, here is what I ended up doing.
First I set up a SharedEngineProvider in global.asax Application_Start event.
protected void Application_Start(object sender, EventArgs e)
{
InitializeValidator();
}
private void InitializeValidator()
{
NHibernateSharedEngineProvider provider = new NHibernateSharedEngineProvider();
NHVConfigurationBase config = new NHVConfigurationBase();
config.Properties[Environment.ApplyToDDL] = "true";
config.Properties[Environment.AutoregisterListeners] = "true";
config.Properties[Environment.ValidatorMode] = ValidatorMode.UseAttribute.ToString();
config.Mappings.Add(new MappingConfiguration(Assembly.GetAssembly(typeof(User)).FullName, null));
provider.GetEngine().Configure(config);
Environment.SharedEngineProvider = provider;
}
Since Burrow intercepts requests for pages to start the NH Session I decided to hook on the PreInit event to integrate the validator with the current NH session. I have a custom base page (a good practice I think) so I added this method there:
protected void Page_PreInit(object sender, EventArgs args)
{
Configuration cfg = new BurrowFramework().BurrowEnvironment.GetNHConfig("PersistenceUnit1");
ValidatorInitializer.Initialize(cfg);
}
So far it seems to be working fine.
To get a reference to the validator engine I'm using:
validatorEngine = Environment.SharedEngineProvider.GetEngine();