ASP.NET - Async - A system that syncs between two other systems - asp.net

I am trying to create a system that synchronizes between two other systems' data.
I work on ASP.NET Web Forms using VB (Cannot be changed)
The main system functions will be in a form of Web API which will be triggered by a scheduled task or other events, such as a website form or a landing page.
Each of these functions call a REST API of System 1 and the REST API of System 2 , operates the data, and then calls each system REST APIs to update both sides.
I chose to use a ASP.NET Web Api 2 for the main system, and HttpClient to call the functions.
There will be hundreds of thousands of transactions per day. It is a lot! So I chose to use async/await methods for the HttpClient, (because of the many simultaneous long processing requests), but ASP.NET seems to have forced me to convert all the functions from bottom to top, including the Web Api functions and the database access to Async!
So I ended up changing all the functions to Async and in all the functions that call them I use Await for everything.
Everything seems to work. I don't know if what I did is correct, and I see there is no way to check if my functions are actually working asynchronically.
About the ConfigureAwait(False) I added it because I don't have operations that are context related. If there will be I will remove this statement. I read it is recommended to use it when it is possible. (Am I right?)
SMALL COMMENT:
Why is my solution good??? I am trying to avoid blocking the main thread by creating much more worker threads. Why is this a better solution? I am creating a lot of awaits which each one starts a worker. Isn't it a worse practice than the synchronic solution????
Can somebody tell me if what I am doing is correct and if not - please explain why or if there are other approaches to this scenario?
Here is the code for example (only the relevant parts). A little explanation of what you see:
The WebApi Controller has an Login function
The Login function calls the System1.DoSomething asynchronically
The System1.DoSomething calls the private function
System1.SetCredentials asynchronically
The System1.DoSomething also uses HttpHandler to get data
asynchronically from System1's API
System1.SetCredentials function calls MyLoginManager.GetCredentials asynchronically
MyLoginManager.GetCredentials calls the database asynchronically using DBHelper.ExecuteReaderAsync
The DBHelper.ExecuteReaderAsync function calls ExecuteReaderAsync
asynchronically and also opens the connection asynchronically using
OpenAsync() function
The WebAPI Controller
Public Class WebApiController
Inherits ApiController
Public Async Function Login() As Threading.Tasks.Task(Of IHttpActionResult)
Dim result As MyResult= Await System1.DoSomething().ConfigureAwait(False)
End Function
The System1 Class
Public Class System1
Public Shared Async Function DoSomething() As Task(Of MyResult)
Try
Using client As New HttpClient
client.BaseAddress = New Uri("blablabla")
Await SetCredentials(client).ConfigureAwait(False)
Dim response As HttpResponseMessage = Await client.GetAsync(urlParameters).ConfigureAwait(False)
... More code
End Using
Catch ex As Exception
End Try
End Function
Private Shared Async Function SetCredentials(client As HttpClient) As Task
Dim auth As BasicAuthenticationData = Await MyLoginManager.GetCredentials.ConfigureAwait(False)
Dim credentials As String = Cryptography.EncodeBase64(String.Format("{0}\{1}:{2}", auth.userName, auth.userPassword))
client.DefaultRequestHeaders.Add("Authorization", "Basic " & credentials)
... More code
End Function
End Class
The MyLoginManager Class
Public Class MyLoginManager
Public Shared Async Function GetCredentials() As Threading.Tasks.Task(Of BasicAuthenticationData)
Dim auth As New BasicAuthenticationData
Dim dbConn As String = DBConnection.GetConnection(True)
Dim q As String = "SELECT * FROM BlaBlaBla"
Using sdr As SqlDataReader = Await DBHelper.ExecuteReaderAsync(dbConn, q, Nothing).ConfigureAwait(False)
... More code
End Using
Return auth
End Function
End Class
The DBHelper Class
Public Class DBHelper
Public Shared Async Function ExecuteReaderAsync(ByVal dbConnection As String, ByVal commandText As String, ByVal params() As SqlParameter) As Threading.Tasks.Task(Of SqlDataReader)
Dim dbConnectionAsync As String = New SqlConnectionStringBuilder(dbConnection) With {
.AsynchronousProcessing = True
}.ToString
Dim objConn As New SqlConnection(dbConnectionAsync)
Dim oc As New SqlCommand(commandText, objConn)
Dim sdr As SqlDataReader
' Throws a custom exception if there is a problem
Try
Await oc.Connection.OpenAsync.ConfigureAwait(False)
sdr = Await oc.ExecuteReaderAsync(CommandBehavior.CloseConnection).ConfigureAwait(False)
Return sdr
Catch ex As Exception
End Try
End Function
End Class

You should add a call to ConfigureAwait(false) to any await behind any await that comes out of any controller action.
As Mister Epic said you'll loose things like HttpContext.Current if you call ConfigureAwait(false) and you shouldn't do that on controller action methods. But that also means that every time you don't you incur in context switching.
What you should do is extract the logic in controller action methods to their own methods (preferably on their own class) and pass every thing they need to do their work.
So, the only thing you did wrong was to call ConfigureAwait(false) in the controller action method.

Don't add ConfigureAwait in your web api project. Use it in library code.
The big gotcha is that when you call a method that uses ConfigureAwait, you'll lose your context. Your context includes important details like your session, so you'll need to ensure you capture any details from HttpContext you need before you call into library code that uses ConfigureAwait.

Related

How to mange API endpoints?

Configured the HttpClient in the startup.cs.
services.AddHttpClient("jsonPosts", client => {
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
On the Controller calling API:
// Obtaining _clientFactory by DI on the Controller constructor
var client = _clientFactory.CreateClient("jsonPosts");
var myContent = JsonConvert.SerializeObject(myObjectToSerialize);
HttpContent stringContent = new StringContent(myContent, Encoding.UTF8, "application/json");
HttpResponseMessage result = await client
.PostAsync(client.BaseAddress + "posts/1", stringContent)
.ConfigureAwait(false);
You can can see on the PostAsync method the API endpoint is being appended to the base address of the HttpClient.
Is this the recommended approach of managing different endpoints across an application?
Well, that depends on your application.
If you only have to do few things like authenticate, post something, exit application then there´s no reason to do the work and create a structure thatfor.
If you do multiple calls and especially want to do the same call at different points in your code you should create an api wrapper.
A common way is to create one generic method that takes an Type as generic argument, also give it the url, HTTP method and other data you might need.
The method will do the call with the arguments given, automatically Deserialize the JSON to an Object of the generic type and return it to you.
This way you can do something like this with only having to write one method and define classes for the Results. You might even use dynamics without defining classes but I personally don´t like dynamics.
ApiClient api = new ApiClient(baseUrl);
User user = api.get<User>("/user", new Query().add("user", "admin"));
EmailList emails = api.get<EmailList>("/user/emails");
Then you could still populate it into multiple methods if you don´t want to mess with the endpoints like
public User getUser(String username){
User user = api.get<User>("/user", new Query().add("user", "admin"));
return user;
}
And use it like
MyApiWrapper.getUser("admin");

How do QueryString parameters get bound to Action method parameters?

I have a webforms project, and am attempting to run some code that allows me to make a call to an MVC route and then render the result within the body of the web forms page.
There are a couple of HttpResponse/Request/Context wrappers which I use to execute a call to an MVC route, e.g.:
private static string RenderInternal(string path)
{
var responseWriter = new StringWriter();
var mvcResponse = new MvcPlayerHttpResponseWrapper(responseWriter, PageRenderer.CurrentPageId);
var mvcRequest = new MvcPlayerHttpRequestWrapper(Request, path);
var mvcContext = new MvcPlayerHttpContextWrapper(Context, mvcResponse, mvcRequest);
lock (HttpContext.Current)
{
new MvcHttpHandlerWrapper().PublicProcessRequest(mvcContext);
}
...
The code works fine for executing simple MVC routes, for e.g. "/Home/Index". But I can't specify any query string parameters (e.g. "/Home/Index?foo=bar") as they simply get ignored. I have tried to set the QueryString directly within the RequestWrapper instance, like so:
public class MvcPlayerHttpRequestWrapper : HttpRequestWrapper
{
private readonly string _path;
private readonly NameValueCollection query = new NameValueCollection();
public MvcPlayerHttpRequestWrapper(HttpRequest httpRequest, string path)
: base(httpRequest)
{
var parts = path.Split('?');
if (parts.Length > 1)
{
query = ExtractQueryString(parts[1]);
}
_path = parts[0];
}
public override string Path
{
get
{
return _path;
}
}
public override NameValueCollection QueryString
{
get
{
return query;
}
}
...
When debugging I can see the correct values are in the "request.QueryString", but the values never get bound to the method parameter.
Does anyone know how QueryString values are used and bound from an http request to an MVC controller action?
It seems like the handling of the QueryString value is more complex than I anticipated. I have a limited knowledge of the internals of the MVC Request pipeline.
I have been trying to research the internals myself and will continue to do so. If I find anything I will update this post appropriately.
I have also created a very simple web forms project containing only the code needed to produce this problem and have shared it via dropbox: https://www.dropbox.com/s/vi6erzw24813zq1/StackMvcGetQuestion.zip
The project simply contains one Default.aspx page, a Controller, and the MvcWrapper class used to render out the result of an MVC path. If you look at the Default.aspx.cs you will see a route path containing a querystring parameter is passed in, but it never binds against the parameter on the action.
As a quick reference, here are some extracts from that web project.
The controller:
public class HomeController : Controller
{
public ActionResult Index(string foo)
{
return Content(string.Format("<p>foo = {0}</p>", foo));
}
}
The Default.aspx page:
protected void Page_Load(object sender, EventArgs e)
{
string path = "/Home/Index?foo=baz";
divMvcOutput.InnerHtml = MvcWrapper.MvcPlayerFunctions.Render(path);
}
I have been struggling with this for quite a while now, so would appreciate any advice in any form. :)
MVC framework will try to fill the values of the parameters of the action method from the query string (and other available data such as posted form fields, etc.), that part you got right. The part you missed is that it does so by matching the name of the parameter with the value names passed in. So if you have a method MyMethod in Controller MyController with the signature:
public ActionResult MyMethod(string Path)
{
//Some code goes here
}
The query string (or one of the other sources of variables) must contain a variable named "Path" for the framework to be able to detect it. The query string should be /MyController/MyMethod?Path=Baz
Ok. This was a long debugging session :) and this will be a long response, so bear with me :)
First how MVC works. When you call an action method with input parameters, the framework will call a class called "DefaultModelBinder" that will try and provide a value for each basic type (int, long, etc.) and instance of complex types (objects). This model binder will depend on something called the ValueProvider collection to look for variable names in query string, submitted forms, etc. One of the ValueProviders that interests us the most is the QueryStringValueProvider. As you can guess, it gets the variables defined in the query string. Deep inside the framework, this class calls HttpContext.Current to retrieve the values of the query string instead of relying on the ones being passed to it. In your setup this is causing it to see the original request with localhost:xxxx/Default.aspx as the underlying request causing it to see an empty query string. In fact inside the Action method (Bar in your case) you can get the value this.QueryString["variable"] and it will have the right value.
I modified the Player.cs file to use a web client to make a call to an MVC application running in a separate copy of VS and it worked perfectly. So I suggest you run your mvc application separately and call into it and it should work fine.

VB Async/await not working with datatables

I'm having some problems converting this method from c# to VB, it's working flawless in C# :
private async void Test()
{
DataSet dt;
var client = new xxxSoapClient();
dt = await client.ToDoAsync();
}
I'm using Async/await in VS 2012, and in c# it's all ok, but when trying to do exactly the same in VB it returns an error indicating that system.void is not awaitable!!!
Private Async Sub Test()
Dim dt As DataTable
Dim Client As New xxxSoapClient
dt = Await Client.ToDoAsync()
End Sub
The webservice is just returning a simple datatable, ideas someone?
The WCF proxy generator before VS2012 would generate *Async methods that returned void and signaled their completion using events. As of VS2012, the WCF proxy generator by default will generate *Async methods that return Task<T>.
Try re-generating the proxy.
If that doesn't work, check your "advanced" options for the style of asynchronous methods to create. There are three styles: Asynchronous Programming Model (APM) uses Begin*/End*/IAsyncResult; Event-based Asynchronous Pattern (EAP) uses *Async/*Completed/AsyncCompletedEventArgs; Task-based Asynchronous Pattern (TAP) uses *Async/Task.
TAP works naturally with async/await, so that's the one you want.

Abstracting HttpContext Request and Session - thread safety

I have the following assemblies in my ASP.NET app:
Website - this is an ASP.NET website
ClassLib - this is just a class lib that contains all the business logic
Class Lib needs to interact with the HttpContext Session and Request objects. This is a code upgrade from an old ASP app, where I've hoovered all the VBScript that contained the logic and put it into VB.NET. We simply didn't have time to rewrite.
Instead of ClassLib interacting directly with HttpContext, which I thought was BAD and also prevented us from unit testing, I introduced the following abstraction layer:
Public Class Request
Private Shared _requestWrapper as IRequestWrapper
Public Shared ReadOnly Property RequestWrapper()
Get
If _requestWrapper Is Nothing Then
Throw New Exception("_requestWrapper is null. Make sure InitRequest() is called with valid parameters")
End If
Return _requestWrapper
End Get
End Property
Public Shared Sub InitRequest(ByRef requestWrapper As IRequestWrapper)
_requestWrapper = requestWrapper
End Sub
Public Shared Function GetVal(ByVal key As String) As Object
Return RequestWrapper.GetVal(key)
End Function
etc.
This means in the unit tests I can supply my own MockRequest object into this Request class, which is just a simple NameValue collection. The code in the ClassLib and the Website then simply use the Request class and are none the wiser that it isn't coming from the HttpContext, but rather this mock class.
When it comes to the real deal, I simply have the following (C#) class:
public class RealRequest : IRequestWrapper
{
public void Initialize(HttpContext context)
{
}
#region Implementation of IRequestWrapper
public object GetVal(string index)
{
return HttpContext.Current.Request[index];
}
etc.
This is initialised in Session_Start of global.asax in the Website, as follows:
protected void Session_Start(object sender, EventArgs e)
{
IRequestWrapper requestWrapper = new RealRequest();
WebSession.Request.InitRequest(ref requestWrapper);
}
I think this is similar to the Static Gateway pattern.
Now, I am aware of singletons and static vars in a multi threaded environment such as ASP.NET, but this is slightly different. When it gets down to the RequestWrapper.GetVal(), its actually going to the HttpContext for that running thread - and pulling the value from that.
Certainly, any concurrent tests that we do with multiple users hitting the same server have never shown up any strange behaviour.
I'm just looking for re-assurance that this is a sound design, and if not why not?
Thanks
Duncan
This is fine. We have a very similar case in our applications that either uses HttpContext if it exists or fake implementations otherwise.
The one thing to watch out for is that there is a very specific instance where HttpContext.Current will return a value but HttpContext.Current.Request will throw an exception when triggered by the Application_Start event. In framework code, you really don't know (or want to know) what triggered the call though.
Workaround for HttpContext.HideRequestResponse being internal? Detect if HttpContext.Request is really available?

Can Webservices as singletons cause issues with different users?

I am developing an ecommerce app that is using the UPS shipping webservice. I have read that it is good to create a singleton so there is only one instance of a webservice at any time. My code for that is below.
Public Class Ship
Private Shared sync As New Object()
Private Shared _Service As New ShipService
Public Shared ReadOnly Property Service As ShipService
Get
If _Service Is Nothing Then
SyncLock sync
If _Service Is Nothing Then
_Service = New ShipService
End If
End SyncLock
End If
Return _Service
End Get
End Property
Public Shared Function GetInstance() As ShipService
Return Service()
End Function
End Class
Here is a snippet from where it will be used.
Public Sub New(ByVal ToAddress As Address, ByVal WeightInLbs As String)
//Not relevant code
Ship.Service.UPSSecurityValue = Security
//More not relevant code
End Sub
Public Function ProcessShipment() As ShipmentResponse
Return Ship.Service.ProcessShipment(ShipmentRequest)
End Function
In the line above in the constructor I have to set the UPSSecurityValue of the service. Then later I will call the ProcessShipment function. My question is; Since the webservice is being traeted as a singleton could different instances of the app share that same UPSSecurityValue and could it change between when I set it and when I call ProcessShipment?
In the case of what you're doing, it could definately change between when you call New and set the Security value and when you actually process the shipment. The singleton is shared across all users of your application (within the same web app, that is - if you had multiple copies of this app on your server, they'd each use their own singleton), so all users will share the same data.
If multiple users run through the application at the same time (or User2 is only 1ms behind):
User1 User2
New (sets security code)
New (sets security code)
ProcessShipment
ProcessShipment
Both shipments will process with User2's security code, which isn't what you want. The way to do this safely could be to pass the security into the function when you ship the package, and then consume it immediately - if you store it for use later, even a single instruction later, you're setting yourself up for a race condition where users read each other's data.

Resources