I just want everyones feedback about the following Async Controller using the Web Api HttpClient. This looks very messy is there a way to make it cleaner? Does anyone have a good wrapper around chaining multiple async tasks together?
public class HomeController : AsyncController
{
public void IndexAsync()
{
var uri = "http://localhost:3018/service";
var httpClient = new HttpClient(uri);
AsyncManager.OutstandingOperations.Increment(2);
httpClient.GetAsync(uri).ContinueWith(r =>
{
r.Result.Content.ReadAsAsync<List<string>>().ContinueWith(b =>
{
AsyncManager.Parameters["items"] = b.Result;
AsyncManager.OutstandingOperations.Decrement();
});
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult IndexCompleted(List<string> items)
{
return View(items);
}
}
You seem to be using a bit to many async calls and AsyncManager.OutstandingOperations.Decrement(). The following code is enough to load the Flickr photo information asynchronously using YQL.
public class HomeController : AsyncController
{
public void IndexAsync()
{
var uri = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20flickr.photos.recent";
var httpClient = new HttpClient(uri);
AsyncManager.OutstandingOperations.Increment();
httpClient.GetAsync("").ContinueWith(r =>
{
var xml = XElement.Load(r.Result.Content.ContentReadStream);
var owners = from el in xml.Descendants("photo")
select (string)el.Attribute("owner");
AsyncManager.Parameters["owners"] = owners;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult IndexCompleted(IEnumerable<string> owners)
{
return View(owners);
}
}
You may take a look at http://pfelix.wordpress.com/2011/08/05/wcf-web-api-handling-requests-asynchronously/.
It contains an example based on the task iterator technique ( http://blogs.msdn.com/b/pfxteam/archive/2009/06/30/9809774.aspx ) for chaining async operations.
Related
So I have a controller that is using HttpClient to call a webservice like so:
public class DemoController : Controller
{
HttpClient client;
string baseUrl = "http://localhost:90/webservice";
public DemoController()
{
client = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
}
// GET: DemoInfo
public async Task<ActionResult> Index()
{
HttpResponseMessage response = await client.GetAsync(baseUrl + "vehicle/menu/year");
string content = "";
MenuItems result = null;
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
}
return View("Index", result);
}
}
My unit test for this action is as follows:
[TestMethod]
public async Task Test_Index()
{
// Arrange
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
So obviously I would like to avoid making the web service call every time the test is run. Would I be on the right track in opting for an IoC container like Unity so that HttpClient would be injected into the controller? Is that overkill for what I'm trying to achieve? I'm aware that there is a lot of history with people struggling with properly mocking httpclient in there unit tests through this github issue. Any help would be greatly appreciated in giving some insight into how to write the controller to make a service call while still being testable.
All dependencies which makes tests slow should be abstracted.
Wrap HttpClient with an abstraction, which you can mock in your tests.
public interface IMyClient
{
Task<string> GetRawDataFrom(string url);
}
Then your controller will depend on that abstraction
public class DemoController : Controller
{
private readonly IMyClient _client;
private string _baseUrl = "http://localhost:90/webservice";
public DemoController(IMyClient client)
{
_client = client;
}
public async Task<ActionResult> Index()
{
var rawData = _client.GetRawDataFrom($"{_baseUrl}vehicle/menu/year");
using (var reader = new StringReader(rawData))
{
var result =
(MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(reader);
return View("Index", result);
}
}
}
Then in tests you can mock your abstraction to return expected data
public class FakeClient : IMyClient
{
public string RawData { get; set; }
public Task<string> GetRawDataFrom(string url)
{
return Task.FromResult(RawData);
}
}
[TestMethod]
public async Task Test_Index()
{
// Arrange
var fakeClient = new FakeClient
{
RawData = #"[
{ Name: "One", Path: "/one" },
{ Name: "Two", Path: "/two" }
]"
};
DemoController controller = new DemoController(fakeClient);
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult)result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
Actual implementation will use HttpClient
public class MyHttpClient : IMyClient
{
public Task<string> GetRawDataFrom(string url)
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
}
}
An alternative approach to testing HttpClient calls without service wrappers, mocks, or IoC containers is to use Flurl, a small wrapper library around HttpClient that provides (among other things) some robust testing features. [Disclaimer: I'm the author]
Here's what your controller would look like. There's a few ways to do this, but this approach uses string extension methods that abstract away the client entirely. (A single HttpClient instance per host is managed for you to prevent trouble.)
using Flurl.Http;
public class DemoController : Controller
{
string baseUrl = "http://localhost:90/webservice";
// GET: DemoInfo
public async Task<ActionResult> Index()
{
var content = await baseUrl
.AppendPathSegment("vehicle/menu/year")
.GetStringAsync();
var result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
return View("Index", result);
}
}
And the test:
using Flurl.Http;
[TestMethod]
public async Task Test_Index()
{
// fake & record all HTTP calls in the test subject
using (var httpTest = new HttpTest())
{
// Arrange
httpTest.RespondWith(200, "<xml>some fake response xml...</xml>");
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
}
Flurl.Http is available on NuGet.
I got two console applications which calls my webapi the same time and I get back in the console application the follow response from my api:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
So they call at the same time my webapi and then something inside the webapi cannot handle those 2 async calls so this error is returned.
I checked all my code on the webapi project and all methods are async and got await so I cannot see why I get this.
Here is the code of the webapi.
Controller:
public class FederationsController : ApiController
{
private readonly IFederationRepository _federationRepository;
public FederationsController(IFederationRepository federationRepository)
{
_federationRepository = federationRepository;
}
[HttpGet]
[Route("federations", Name = "GetFederations")]
public async Task<IHttpActionResult> GetFederations()
{
var federations = await _federationRepository.GetAllAsync();
return Ok(federations.ToModel());
}
}
Repository
public class FederationRepository : IFederationRepository, IDisposable
{
private Models.DataAccessLayer.CompetitionContext _db = new CompetitionContext();
#region IQueryable
private IQueryable<Models.Entities.Federation> FederationWithEntities()
{
return _db.Federations.Include(x => x.Clubs)
.Where(x => !x.DeletedAt.HasValue && x.Clubs.Any(y => !y.DeletedAt.HasValue));
}
#endregion IQueryable
public async Task<IEnumerable<Models.Entities.Federation>> GetAllAsync()
{
return await FederationWithEntities().ToListAsync();
}
}
Mapper
public static class FederationMapper
{
public static List<Federation> ToModel(this IEnumerable<Models.Entities.Federation> federations)
{
if (federations == null) return new List<Federation>();
return federations.Select(federation => federation.ToModel()).ToList();
}
public static Federation ToModel(this Models.Entities.Federation federation)
{
return new Federation()
{
Name = federation.Name,
FederationCode = federation.FederationCode,
CreatedAt = federation.CreatedAt,
UpdatedAt = federation.UpdatedAt
};
}
}
DbContext
public class CompetitionContext : DbContext
{
public CompetitionContext() : base("ContextName")
{
}
public DbSet<Federation> Federations { get; set; }
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IFederationRepository, FederationRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Thank you for all the advices/help.
In your repository you are creating a single CompetitionContext and reusing it. I'm assuming that IoC setup is registring the repository as some kind of single instance, so the same repository is getting used every time. If that's the case you should create a new CompetitionContext for each method call.
Also, probably should make sure it's closed with a using statement.
I'm also not clear from your code snippets why you are returning an IQueryable from that FederationWithEntities, method, do you have other things that are using it?
Anyway, I'd probably change that GetAllMethod to be something like this:
public async Task<IEnumerable<Models.Entities.Federation>> GetAllAsync()
{
using (Models.DataAccessLayer.CompetitionContext _db = new CompetitionContext())
{
return _db.Federations.Include(x => x.Clubs)
.Where(x => !x.DeletedAt.HasValue && x.Clubs.Any(y => !y.DeletedAt.HasValue))
.ToListAsync();
}
}
I'm creating an Instagram API client on ASP MVC using HttpClient, I'm trying to make a get request but it fails without throwing exception or responding and doesn't respond to my timeout. Here is my code:
public class InstagramService
{
private HttpClient Client = new HttpClient {
BaseAddress = new Uri("https://api.instagram.com/v1/"),
Timeout = TimeSpan.FromMilliseconds(500)
};
public async Task<InstagramUser> GetInstagramUser(long? userId = null)
{
InstagramUser User = null;
string Parameter = (userId == null) ? "self" : userId.ToString();
try {
var response = await Client.GetAsync("users/" + Parameter + "/" + GetAccessToken());
if (response.IsSuccessStatusCode)
{
User = await response.Content.ReadAsAsync<InstagramUser>();
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}
return User;
}
private string GetAccessToken()
{
return "?access_token=" + DB.config_det_sys.Single(i => i.codigo == "ACCESS_TOKEN_INSTAGRAM" && i.estado == true).Valor;
}
}
EDIT
Here I add how I call my service on the Home Controller, I will still test changing the controller to async Task
public class HomeController : Controller
{
private InstagramService IGService = new InstagramService();
public ActionResult About()
{
var apiCall = IGService.GetInstagramUser();
var model = apiCall.Result;
return View(model);
}
}
I tested on Postman trying to make the API call and it indeed worked, so where I'm failing to catch errors?
Your problem is here:
var model = apiCall.Result;
As I describe on my blog, you shouldn't block on asynchronous code. It can cause a deadlock.
Instead of Result, use await:
var model = await apiCall;
Adding to Stephen's answer, update the controller's action to be async all the way.
public class HomeController : Controller {
private InstagramService IGService = new InstagramService();
public async Task<ActionResult> About() {
var model = await IGService.GetInstagramUser();
return View(model);
}
}
I write web application using ASP.NET MVC WebAPI and I want to transform current synchronous code to asynchronous for optimization. Problem is that I fill ViewModel with multiple objects taken from repository. These calls from repository should be async.
Let's asume I have signature for repository calls respecting this interface
public interface ICompanyRepository
{
IEnumerable<Company> GetCompanies();
IEnumerable<Address> GetAddresses();
}
ViewModels definition
public class CompaniesFullViewModel
{
public IEnumerable<Company> Companies { get; set; }
public IEnumerable<Address> Addresses { get; set; }
}
And controller:
public class CompanyController
{
public readonly ICompanyRepository Repository { get; private set; }
public CompanyController(IRepository repository)
{
Repository = repository;
}
[ResponseType(typeof(CompaniesFullViewModel))]
public HttpResponseMessage Get()
{
var companies = Repository.GetCompanies();
var addresses = Repository.GetAddresses();
HttpStatusCode statusCode = companies.Any()
? HttpStatusCode.OK
: HttpStatusCode.PartialContent;
return
Request.CreateResponse(
statusCode,
new CompaniesFullViewModel
{
Companies = companies,
Addresses = addresses
});
}
}
Furthermore I have tests implemented to the controller:
[TestClass]
public sealed class CompanyTestController : BaseTestController
{
#region Fields
private static Mock<ICompanyRepository> _repositoryMock;
private static CompanyController _controller;
#endregion
[ClassInitialize]
public static void Initialize(TestContext testContext)
{
// Mock repository
_repositoryMock = new Mock<ICompanyRepository>();
DependencyResolver.Default.Container.RegisterInstance(_repositoryMock.Object);
// Create controller
_controller =
DependencyResolver.Default.Container.Resolve<CompanyController>();
// Init request
_controller.Request = new HttpRequestMessage();
_controller.Request.SetConfiguration(new HttpConfiguration());
}
[ClassCleanup]
public static void Cleanup()
{
_controller.Dispose();
}
[TestMethod]
public void Get_ActionExecutes_ReturnsEmptyCompaniesViewModel()
{
var companies = new List<Company>();
var addresses = new List<Address>();
// Setup fake method
_repositoryMock
.Setup(c => c.GetCompanies())
.Returns(companies);
_repositoryMock
.Setup(c => c.GetAddresses())
.Returns(addresses);
// Execute action
var response = _controller.Get();
// Check the response
Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
}
}
How can I convert the controller to async, if the repository is async and the signature looks like this:
public interface ICompanyRepository
{
Task<IEnumerable<Company>> GetCompaniesAsync();
Task<IEnumerable<Address>> GetAddressesAsync();
}
What you need to do is change the Controller action to be async as well, and change the return type to Task<>. You can then await your asynchronous repository calls:
[ResponseType(typeof(CompaniesFullViewModel))]
public async Task<HttpResponseMessage> Get() // async keyword.
{
var companies = await Repository.GetCompaniesAsync(); // await
var addresses = await Repository.GetAddressesAsync(); // await
HttpStatusCode statusCode = companies.Any()
? HttpStatusCode.OK
: HttpStatusCode.PartialContent;
return
Request.CreateResponse(
statusCode,
new CompaniesFullViewModel
{
Companies = companies,
Addresses = addresses
});
}
By convention, you can also change the name of the controller action to end in Async as well, although if you are using RESTful conventions and / or Routing attributes, the actual name of the controller action isn't really important.
Testing
I use XUnit and NUnit, but it seems MSTest also supports testing of asynchronous methods, and Moq also provides Async versions of the setups:
[Test]
public async Task Get_ActionExecutes_ReturnsEmptyCompaniesViewModel() // async Task
{
var companies = new List<Company>();
var addresses = new List<Address>();
// Setup fake method
_repositoryMock
.Setup(c => c.GetCompaniesAsync())
.ReturnsAsync(companies); // Async
_repositoryMock
.Setup(c => c.GetAddressesAsync())
.ReturnsAsync(addresses); // Async
// Execute action
var response = await _controller.Get(); // Await
// Check the response
Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
_repositoryMock.Verify(m => m.GetAddressesAsync(), Times.Once);
_repositoryMock.Verify(m => m.GetCompaniesAsync(), Times.Once);
}
As an aside, it seems you are using Setter Dependency injection. An alternative is to use Constructor injection, which has the benefit of ensuring that the class is always in a valid state (i.e. there is no transient state while it is waiting for the dependencies to be set). This also allows the dependencies (your repository in this case) to be made readonly.
Imagine we have asynchronous action defined in controller:
public class PortalController : AsyncController {
public void NewsAsync(string city) {
AsyncManager.OutstandingOperations.Increment();
NewsService newsService = new NewsService();
newsService.GetHeadlinesCompleted += (sender, e) =>
{
AsyncManager.Parameters["headlines"] = e.Value;
AsyncManager.OutstandingOperations.Decrement();
};
newsService.GetHeadlinesAsync(city);
}
public ActionResult NewsCompleted(string[] headlines) {
return View("News", new ViewStringModel
{
NewsHeadlines = headlines
});
}
When I call it from browser - everything works. But I want to call this action (News) from another synchronous controller. Is there any way to call action and get the result?
public class PortalController: Controller {
public ActionResult News(string city) {
NewsService newsService = new NewsService();
ViewStringModel headlines = newsService.GetHeadlines(city);
return View(headlines);
}
}
you should have looked at the rest of the code you got From MSDN you can't have a view without an ActionResult of some sort to get the data.