I'm trying to use async from the asp.net mvc futures, using my own async delegate. Haven't figured out how to make it work. Here's the code:
public delegate String GetString();
public String getHello() { return "Hello"; }
public IAsyncResult BeginHello(AsyncCallback cb, Object state)
{
GetString dlgt = getHello;
return dlgt.BeginInvoke(cb, state);
}
public ActionResult EndHello(IAsyncResult asyncResult)
{
return View();
}
In EndHello, asyncResult.IsCompleted=True, but asyncResult.AsyncState==null. I expected to have AsyncState=="Hello".
What am I missing?
Also, does it even make sense to arrange it this way? Or does this cause it to use the same thread pool anyway? Basically my thought was to put a datareader in my asynchronous function, thinking that I could loop through the reader populating a collection of objects and only return when they're done. Is it better to use BeginExecuteReader and populate the objects on the main thread?
EDIT: To anyone reading this later, this is in fact the wrong way to do it, and won't help you at all. BeginExecuteReader is the way to go.
You will not get Hello in AsyncState.
Hello will be returned when you do an EndInvoke().
e.g.
Here's the code you should be using:
public String EndHello(IAsyncResult asyncResult)
{
GetString getStringDel = (asyncResult as AsyncResult).AsyncDelegate as GetString;
return getStringDel.EndInvoke(asyncResult);
}
You can refer to:
MSDN link Calling Synchronous Methods Asynchronously
SO answer to What is AsyncCallback?
Related
Hi I have a update method in webAPI and that is a void method and I want to perform unit testing on that method.How do I do that??
Not Found any solution.
Below is webapi controller method :-
[HttpPut]
public void UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO)
{
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
}
Below is the unit test case for above method
[TestMethod]
public void UpdatePushNotificationQueue_ShouldUpdate()
{
var item = GetDemoPushNotificationQueue();
var controller = new PushNotificationQueueController();
var result = controller.UpdatePushNotification(item) as ;
Assert.IsNotNull(result);
}
I want what do I write after as in
var result = controller.UpdatePushNotification(item) as ???
The controller method is void so there is no return type and nothing to cast it to.
I believe this to be an XY problem.
void controller actions will always return HTTP Status Code 200 OK at run-time except when an exception is thrown.
Based on the tags in original post the assumption is that the mentioned controller is an ApiController
which means that the controller can be refactored to
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
}
There is also the option to wrap it in a try-catch in case of errors
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
try {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
} catch (Exception ex) {
return InternalServerError(ex);
//OR
//return InternalServerError()
}
}
But that is more of a cross-cutting concern that can be handled by action filters.
This would then allow for an actual return type to be asserted
//...omitted for brevity
IHttpActionResult result = controller.UpdatePushNotification(item);
Assert.IsNotNull(result);
The PushNotificationQueueBO business object however, appears to be making a static member call.
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
This makes it difficult to unit test the encapsulated API method call in isolation and may result in undesired behavior.
It is suggested that the static business object call be encapsulated behind an abstraction and implementation that can be replaced by a mock when testing in isolation.
You can test a void function in different ways and it depends on what the void method does. For example, if a void method increments the numeric value of a property of its class, then you can use that property in your test. In your case, your void method performs this action;
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
Firstly, identify what this action does and what it affects. For example, if this method's action performs a manipulation on a queue object, then you can test this object as a result of the void method.
I am trying to get the $select portion of ODataQueryOptions working with my Business layer and I can't seem to noodle way (fyi: placing the EF query in my controller is not an option)..
Here is my ODataController method
public async Task<IHttpActionResult> Get(Guid propertyId, ODataQueryOptions<HighSchoolViewModel> odataOptions)
{
// validate the query.
try
{
odataOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
IEnumerable<HighSchoolViewModel> returnData = await _service.ODataSearchAsync(propertyId, odataOptions);
return Ok<IEnumerable<HighSchoolViewModel>>(returnData);
}
And here is my business layer/method
public async Task<IEnumerable<HighSchoolViewModel>> ODataSearchAsync(Guid propertyId, ODataQueryOptions<HighSchoolViewModel> queryOptions)
{
using (DbContext context = new LAMSContext())
{
var query = queryOptions.ApplyTo(context.PropertyHighSchools.Where(hs => hs.PropertyId == propertyId)
.Project().To<HighSchoolViewModel>());
return await ((IQueryable<HighSchoolViewModel>)query).ToListAsync();
}
}
This works great for everything except the $select (and likely the expand but that's "out of scope").
Obviously when the ApplyTo() runs it changes the IQueryable to returning an anonymous type, and there for blows up when the query is executed. So I tried returning an IEnumerable so the type wouldn't matter, but when I do that OData can't seem to handle that and starts spitting out 406 errors.
In your scenario, change your controller inherent form ApiController, and return
result.AsQueryable();
This may be a work around for $select.
I have an MVC web application, the controller action calls a code in a business layer, which in turn calls an API:
public async Task<ActionResult> TestAction(){
Info something = await businessLayer.GetInfoAsync();
return View(something);
}
in the business layer I have GetInfoAsync defined as follows:
public async Task<Info> GetInfoAsync(){
return await myLib.GetAsync<Info>();
}
in my library I have GetAsync defined as follows:
public async Task<T> GetAsync(){
//do something
var data = await aService.Get<T>();
return data;
}
As you can see above GetInfoAsync method just makes a call to myLib.GetAsync and returns whatever value myLib.GetAsync returns.
Could I just get rid of async/await for GetInfoAsync, and define it as follows :
public Task<Info> GetInfoAsync(){
return myLib.GetAsync<Info>();
}
Thanks !
Yes, you can (and should) get rid of async/await in the middle layer.
If your async method is just awaiting a single task (and that's all it does), then the async just adds overhead.
I want to use a custom action filter to handle specific exceptions from my service classes to populate the model state and then return the view.
For example, take my previous code:
public ActionResult SomeAction(SomeViewModel model)
{
try
{
_someService.SomeMethod(model);
}
catch (ServiceException ex)
{
ModelState.AddModelError(ex.Key, ex.ErrorMessage);
}
return View();
}
Basically, it would call a service, and if a ServiceException was thrown, it would know that there was an issue w/ the model data, and add the error to the ModelState, then just return the view. But I noticed some very un-DRY-like patterns, because I had this same try/catch code in every action method.
So, to DRY it up a bit, I basically created a new HandleServiceError action filter:
public class HandleServiceErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
((Controller)context.Controller)
.ModelState
.AddModelError(
((ServiceException)context.Exception).Key,
((ServiceException)context.Exception).ErrorMessage
);
context.ExceptionHandled = true;
}
}
Then simplified my action methods like so:
public ActionResult SomeAction(SomeViewModel model)
{
_someService.SomeMethod(model);
return View();
}
Problem is, once the action filter handles the error, it doesn't return to my action method. I sort of understand, under the hood, why this is happening. But I would still like to figure out a way to do what I'm trying to do.
Is this possible?
Thanks in advance.
UPDATE:
I tried the suggestions from the article Darin provided in his answer, but ran into issues trying to use constructor injection with the controller's model state.
For example, if you look at their Controllers\ProductController.cs code, they have the controller's empty constructor using a service locator to create the service, passing in the controller's ModelState at that point:
public ProductController()
{
_service = new ProductService(new ModelStateWrapper(this.ModelState),
new ProductRepository());
}
But if you look at the injected constructor, it assumes the ModelState will be injected into the constructor for the service:
public ProductController(IProductService service)
{
_service = service;
}
I don't know how to get CI to work with the current controller's ModelState. If I could figure this out, then this approach may work.
You could still return the corresponding view:
context.Result = new ViewResult
{
ViewName = context.RouteData.GetRequiredString("action")
};
You may also take a look at the following article for an alternative about how to perform validation at the service layer.
I want to be able to check the form inputs prior to launching a long running asynchronous task.
Two approaches that come to mind:
Check values on the Begin method and
throw an exception?
Post to a normal (synchronous method) which validates as per normal. redirects to the asynchonrous method if no errors found.
Throwing an exception i thought would be a simple solution. I can't return the view from the begin method, so the exception is handled on the end method. Only it isn't getting across to the end method (I thought this was the normal pattern)
Validating in a normal synchronous method is fine... but how do i transfer or redirect the request to the asynchronous method???
You don't need to use exceptions or a synchronous method, you can just pass back a different IAsyncResult (assuming that's the pattern you're using - if you're using the event or delegate patterns, you'd still be able to achieve something similar without exceptions).
Here's a simple example of this where we use a dummy delegate to return if there's an error (in this case an invalid ID):
public class MyAsyncController : AsyncController
{
public IAsyncResult BeginFoo(int id, AsyncCallback callback, object state)
{
Action errorDelegate = () => ViewData["errors"] = "Invalid ID";
// Here's our validation check, return the error delegate if necessary
if (id <= 0) return errorDelegate.BeginInvoke(callback, state);
var webRequest = WebRequest.Create("http://www.apple.com");
return webRequest.BeginGetResponse(callback, webRequest);
}
public ActionResult EndFoo(IAsyncResult asyncResult)
{
if (asyncResult.AsyncState is WebRequest)
{
var webRequest = (WebRequest) asyncResult.AsyncState;
var httpResponse = (HttpWebResponse) webRequest.EndGetResponse(asyncResult);
ViewData["status"] = httpResponse.StatusCode;
}
return View();
}
}