I am looking for something like a background process in MVC.
Requirement is I should run a background process/thread in .Net MVC application, which will be async and doesn't wait for the action in the foreground/ UI. Similar to fire and forget.
I am trying to use HostingEnvironment.QueueBackgroundWorkItem for the same. Can this be used or anything else is recommended ? I am armature in MVC, Thanks in advance.
If you want to use background job in controller HostingEnvironment.QueueBackgroundWorkItem is normal solution.
For examle, start backgroud action and foget
public class HomeController : Controller
{
[HttpPost]
public ActionResult Index()
{
Action<CancellationToken> workItem = SomeBackGroundWork;
// start background work and forget
HostingEnvironment.QueueBackgroundWorkItem(workItem);
return RedirectToAction("Home");
}
private async void SomeBackGroundWork(CancellationToken cancellationToken)
{
await Task.Delay(2000,cancellationToken);
// or you can do http request to web site
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://google.com", new StringContent(""), cancellationToken);
}
}
}
Related
I think my web application is suffering from deadlock when I'm calling YouTube API services, so I want to know how to resolve this in the correct manner. I suspect it's a similar scenario to the following: Why does this async action hang?
Please can somebody advise, in very simple terms, why my web application hangs (see inline comments) and how it should be correctly resolved? Thanks!
public ActionResult Index()
{
YouTubeHelper yth = new YouTubeHelper();
bool unpublishVideo = yth.UpdateVideoOnYouTube(17, "public").Result;
}
public async Task<bool> UpdateVideoOnYouTube(int propertyId, string publishStatus)
{
.....
YouTubeService youtubeService = await GetYouTubeService(db);
.....
}
public async Task<YouTubeService> GetYouTubeService(ApplicationDbContext db)
{
....
if (!await credential.RefreshTokenAsync(CancellationToken.None)) //It hangs here!!
{
....
}
....
}
The deadlock is explained here. In summary, your asynchronous methods are needing the ASP.NET request context before they can complete, but the call to Result is blocking the ASP.NET request context until the asynchronous methods are already completed.
To avoid the deadlock, don't block on async code. Use await instead of Result:
public async Task<ActionResult> Index()
{
YouTubeHelper yth = new YouTubeHelper();
bool unpublishVideo = await yth.UpdateVideoOnYouTube(17, "public");
}
Been stuck for days, hoping someone can help me.
I have been trying to run the YouTube 'Search by keyword' example from Google's API examples for .net in a VS 2013 Express for Web MVC4 project, and the ExecuteAsync() calling the Google API never comes back.
I believe the example code works as I tested it in VS 2013 Express for Windows Desktop as a console application and it came back fine. Also the stats in google's developers console tell me the API request is being received.
Here is what I did:
I created a new VS 2013 Express for Web MVC4 project called GoogleTest and installed the 'Install-Package Google.Apis.YouTube.v3' package.
I then added the following model.
public class SearchYouTube
{
public int ID { get; set; }
public async Task RunYouTube()
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = " <MY DEVELOPER KEY HERE> ",
ApplicationName = this.GetType().ToString()
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = "googleapi examples"; // Replace with your search term.
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = await searchListRequest.ExecuteAsync();
List<string> videos = new List<string>();
List<string> channels = new List<string>();
List<string> playlists = new List<string>();
// Add each result to the appropriate list, and then display the lists of
// matching videos, channels, and playlists.
foreach (var searchResult in searchListResponse.Items)
{
switch (searchResult.Id.Kind)
{
case "youtube#video":
videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
break;
case "youtube#channel":
channels.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.ChannelId));
break;
case "youtube#playlist":
playlists.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.PlaylistId));
break;
}
}
Console.WriteLine(String.Format("Videos:\n{0}\n", string.Join("\n", videos)));
Console.WriteLine(String.Format("Channels:\n{0}\n", string.Join("\n", channels)));
Console.WriteLine(String.Format("Playlists:\n{0}\n", string.Join("\n", playlists)));
}
}
Then I call the above class in the Home controller like so:
public ActionResult Index()
{
ViewBag.Message = "MVC example";
SearchYouTube searchObject = new SearchYouTube();
searchObject.RunYouTube().Wait();
return View();
}
Running this in the debugger, the program steps into but never returns from this line in the SearchYouTube class above:
var searchListResponse = await searchListRequest.ExecuteAsync();
Can anyone help explain what I am doing wrong or what I am missing??
You seem to have a deadlock on your hands because you're doing "sync over async". When you use Task.Wait you're blocking and wasting a thread. After the inner async operation (i.e. await searchListRequest.ExecuteAsync();) completes it evidently needs that same thread to continue processing the rest of the method.
All that happens because of the SynchronizationContext present in ASP.Net which is captured when await is used so that the continuation would be posted to it. When you use ConfigureAwait(false) you're configuring the continuation to not run on the captured context and use the ThreadPool instead.
In console apps there is no SC and so every continuation runs on the ThreadPool. It's as if every await had ConfigureAwait(false).
To solve this deadlock you can use ConfigureAwait(false) or even better, make the MVC method async so you don't need to block synchronously (more on async in MVC):
public async Task<ActionResult> Index()
{
ViewBag.Message = "MVC example";
SearchYouTube searchObject = new SearchYouTube();
await searchObject.RunYouTube();
return View();
}
I have an ApiController and a Controller in the same ASP.NET project. The idea is that I want to expose a REST API to 3rd parties and build a website on top of the REST API that I'm exposing.
I want to consume the REST API (in ProfileApiController) from within my MVC controller (in ProfileController). Both controllers require to be authenticated and the response of the ProfileApiController depends on the User.Identity that is active.
How can I achieve this?
Code below:
namespace Controllers
{
[Authorize]
public class ProfileApiController : ApiController
{
[Route("api/profile/{param}")]
[HttpGet]
public async Task<IHttpActionResult> GetProfile(string param)
{
return this.Ok<IEnumerable<TransferObject>>( /* business logic */ );
}
}
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> GetProfile()
{
//Pseudocode -- this is what I'm looking for
var api = (reference_to_profileapicontroller);
api.Authenticate(User.Identity);
var m = api.GetProfile("myparameter");
//End Pseudocode
return View(m):
}
}
}
I have already tried two approaches:
Call the WebApi through the HttpClient
HttpClientHandler h = new HttpClientHandler();
var client = new HttpClient(h);
var response = client.GetAsync("http://localhost:4827/api/profile/param/").Result;
var m = await response.Content.ReadAsAsync<List<TransferObject>>();
return View(m);
but here I'm stuck with passing the identity from the Controller to the ApiController
Invoke the Controller directly
var pc = DependencyResolver.Current.GetService<ProfileController>();
var r = await pc.GetTenseProfile("param");
var rr = await r.ExecuteAsync(System.Threading.CancellationToken.None);
var m = await rr.Content.ReadAsAsync<List<TransferObject>>();
return View(m);
but this turns into a mess as pc.Configuration and pc.Request need to be configured. This shouldn't be this hard?
I would go one of 3 routes, in this order.
Move your logic that is common to both the Controller and ApiController into a class then use that class in your controller.
[Authorize]
public class ProfileApiController : ApiController
{
[Route("api/profile/{param}")]
[HttpGet]
public async Task<IHttpActionResult> GetProfile(string param)
{
// have all business logic in this class
ProfileClass = newClass = new ProfileClass();
IList<TransferObject> vm = newClass.GetData(); // from bus rules
return this.Ok<IList<TransferObject>>(vm);
}
}
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> GetProfile()
{
// have all business logic in this class
ProfileClass = newClass = new ProfileClass();
IList<TransferObject> vm = newClass.GetData(); // from bus rules
return View(vm):
}
}
Consume your API via AJAX. This is more server round trips, but uses your API as it was designed. Use the parameter in the view to make the AJAX call to the API controller.
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> GetProfile()
{
return View("myparameter"):
}
}
Use Claims based authentication which includes headers in your requests. If you're securing your API, then you're probably already doing this. Use HttpClient as you've listed above, then just add the bearer token in the header based on the user in MVC.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
This may also help: http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
Lots of redundant code in options 2 and 3. It's better for your controllers to be agnostic of the business logic and have your code consume that. I don't think it's a good practice to have to create HttpRequests all over the place in your MVC code in every Action. That's going to lead to alot of headaches down the road when you have to refactor things.
I have a custom ActionResult that sets HttpResponse.BufferOutput = false and then writes data to the response stream. I noticed that when the action result comes from a task-based asynchronous action method in ASP.NET MVC, writing to the response stream blocks. This happens only when Glimpse plugin is enabled in web.config. Glimpse is very useful to me, I really want to have it enabled, at least during development and testing. BufferOutput property should remain false, because the content length can be quite large and I don't want to buffer it in memory.
This is the shortest code that could reproduce this exact behaviour:
public sealed class CustomResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
var resp = context.HttpContext.Response;
resp.BufferOutput = false;
resp.ContentType = "text/plain";
resp.Output.Write(DateTime.UtcNow.ToString());
resp.Flush();
}
}
public sealed class DownloadController : Controller
{
// the client nevers gets the response from this action
public async Task<ActionResult> Async()
{
await Task.Yield();
return new CustomResult();
}
// this works
public ActionResult Sync()
{
return new CustomResult();
}
}
I tested this with the latest Glimpse.Mvc4 package (version 1.3.2).
Am I doing something wrong, is there a workaround to this issue or is this a Glimpse bug and I should report it?
I want to know is there any open source action result code available for asp.net mvc in which i can pass a URL and it will response out the content.
Just like there are some inbuilt Action results
FileResult
FileStreamResult
I want to build a custom Action Result in which i can pass a URL (basically an mp3 url) which will get downloaded in memory and the content will be streamed to the current executing response.
It should also support resume download if supported by server and client. Thats a must for me.
public UrlActionResult DownloadUrl(string url)
{
return new UrlActionResult("http://www.example.com/audiofile.mp3");
}
For a basic scenario you could use something like this:
public class DownloadResult : ActionResult
{
private readonly FileStreamResult _fileStreamResult;
public DownloadResult(string url, string contentType)
{
using (var myWebClient = new WebClient())
{
var myStream = myWebClient.OpenRead(url);
_fileStreamResult = new FileStreamResult(myStream, contentType);
}
}
public override void ExecuteResult(ControllerContext context)
{
_fileStreamResult.ExecuteResult(context);
}
}
However if you want to do the resume download it becomes a lot more complicated. Here is a great article with example code.
If you don't need to hide the url just use a redirect. Using a redirect with spare you server bandwidth:
public ActionResult DownloadUrl(string url)
{
return new Redirect("http://www.example.com/audiofile.mp3");
}