The following code snippet is causing a compilation error that I am hard to understand.
Error 1 Cannot await System.Collections.Generic.List'<BusinessLogic.News>'
Any suggestions?
public class NewsController : Controller
{
public async Task<ActionResult> Index(int page=1)
{
NewsNavigator News = new NewsNavigator();
await News.Load(page);
...
return View(News);
}
}
public List<News> Load(int page = DefaultPage, int pageSize = DefaultPageSize, string filter = DefaultFilter)
{
//DBLayer_News
...
return LoadedNews;
}
Await is applied to the result of a method call that returns a Task.
You cannot call it on News because News isn't a Task. Create a Task and pass your News.Load method to it.
NewsNavigator News = new NewsNavigator();
var newsLoadTask = Task.Factory.StartNew(() => News.Load(page));
await newsLoadTask;
...
Related
I would like to use ODATA. Unfortunately I get neither count nor odata.context back. Select, filter, orderBy are working though. Does anyone have an idea where my error is?
[AllowAnonymous]
[ApiController]
[Route("api/v1/wea")]
[Produces("application/json")]
[ODataRouteComponent("api/v1/wea")]
public class WeatherForecastODataController : ODataController
{
[HttpGet]
[EnableQuery]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogWarning("Get Forecast");
return _forecasts;
}
}
public static IMvcBuilder AddODataOptions(this IMvcBuilder builder)
{
builder.AddOData(options => {
var defaultBatchHandler = new DefaultODataBatchHandler();
defaultBatchHandler.MessageQuotas.MaxNestingDepth = 2;
defaultBatchHandler.MessageQuotas.MaxOperationsPerChangeset = 10;
defaultBatchHandler.MessageQuotas.MaxReceivedMessageSize = 100;
var model1 = EdmBuilder.BuildV1();
options.Select().Filter().OrderBy().Count().SetMaxTop(2).Expand();
options.AddRouteComponents("api/v1", model1, defaultBatchHandler);
});
return builder;
}
I am working on a Web project that gets data from an API endpoint. The API layer sits on top of Service Layer and Repository Layer at the bottom. I have written unit testing for the Service and API Layers. I am using Moq framework to mock dependencies.
Now i want to test the MVC controller. I am using a Request Manager class which is derived from HttpClient to get data from the API endpoints. So how do i test this controller. I have written a unit test but the test is getting data directly from my Database.
public class UserController : Controller
{
private RequestManager requestManager = new RequestManager();
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
public class UserControllerTest
{
public UserController controller;
[OneTimeSetUp]
public void InIt()
{
controller = new UserController();
}
[Test]
public async Task TestIndex()
{
var view = await controller.Index() as ActionResult;
Assert.That(view is ViewResult);
Assert.That(view.Model is List<UserViewModel>);
}
}
You should decouple controller and manager. Extract interface from RequestManager and inject it into controller. That should not be a problem, Ioc container can do that for you.
public class UserController : Controller
{
private RequestManager _requestManager;
public UserController(IRequestManager requestManager)
{
_requestManager = requestManager;
}
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await _requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
Then in your unit tests you can mock IRequestManager.
public class UserControllerTest
{
[Test]
public async Task TestIndex()
{
//arrange
Mock<IRequestManager> requestManager = new Mock<IRequestManager>();
//setup what you want here...
UserController sut = new UserController(requestManager.Object);//suggest to use AutoMoqer for this.
//act
var view = await sut.Index() as ActionResult;
//assert
Assert.That(view is ViewResult);
Assert.That(view.Model is List<UserViewModel>);
}
}
Try this one..
public class UserController : Controller
{
private RequestManager requestManager = new RequestManager();
Mock<RequestManager> mockRepository = new Mock<RequestManager>();
Mock<UserViewModel> mockUserViewModel = new Mock<UserViewModel>();
ViewResult viewResult;
// GET: User
public async Task<ActionResult> Index()
{
List<UserViewModel> allUsers = await requestManager.GetUsers();
if(allUsers == null)
{
throw new HttpException(404, "No Users Found");
}
return View(allUsers);
}
}
public class UserControllerTest
{
public UserController controller;
[OneTimeSetUp]
public void InIt()
{
controller = new UserController();
}
[Test]
public async Task TestIndexWhenAllUsersNULL()
{
var view = await controller.Index() as ActionResult;
List<mockUserViewModel> listofusermodel = new List<mockUserViewModel>();
//add some dummy data in your List so it will not getting data directly from your Database
mockRepository.Setup(x => requestManager.GetUsers()).Returns(listofusermodel);
Assert.That(view is ViewResult);
Assert.That(view.Model is List<mockUserViewModel>);
}
}
I am using an ORM to connect to the database it is called dapper. The issue with it is that it's database calls are synchronous and I recently found a way to make it asynchronous by following this short tutorial http://www.joesauve.com/async-dapper-and-async-sql-connection-management/ . My question is how can I bring this BaseRepository into my Controller class ? This is the code on that website and it's the same one I have
BaseRepository- by the way there is no issue in this code
public abstract class BaseRepository
{
private readonly string _ConnectionString;
protected BaseRepository(string connectionString)
{
_ConnectionString = connectionString;
}
protected async Task<T> WithConnection<T>(Func<IDbConnection, Task<T>> getData)
{
try {
using (var connection = new SqlConnection(_ConnectionString)) {
await connection.OpenAsync(); // Asynchronously open a connection to the database
return await getData(connection); // Asynchronously execute getData, which has been passed in as a Func<IDBConnection, Task<T>>
}
}
catch (TimeoutException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
}
catch (SqlException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
}
}
}
and now he brings it in like this
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
The part I am having a problem with is this public class PersonRepository : BaseRepository because Asp.Net Controllers start with public class HomeController: Controller , I need access to the WithConnection method to get this working. My controller looks like this
public class HomeController : Controller
{
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<ActionResult> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return View(topfive);
});
}
}
I obviously can not cover my ActionResult method with the BaseRepository because it gives all types of errors any suggestions ?
Why are you using inheritance instead of composition? What about something like:
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<List<TopFileClass>> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return topfive;
});
}
public class HomeController : Controller
{
private readonly PersonRepository _repo;
public HomeController(PersonRepository repo)
{
_repo = repo;
}
public async Task<ActionResult> TopFive()
{
var top5 = await _repo.topfive();
return View(top5);
}
}
If you are not familiar how to make the repository automatically get injected into the constructor, read up on dependency injection in MVC 6.
you have to intehirt the "BaseRepository" from "Controller". i think that will work for you. then just go with below code:
public abstract class BaseRepository : Controller
{
// do you work
}
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
public class HomeController : Controller
{
public ActionResult Index()
{
var t1= Test1().Result;
return View();
}
private async Task<HttpResponseMessage> Test1()
{
string strUrl = "http://localhost:52033/api/values";
var instanceClient = new HttpClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Get, strUrl);
var httpRespons = await instanceClient.SendAsync(requestMessage);
return httpRespons;
}
}
When I was in the Index Action called Test1 (). The Result; Will happen when the current thread deadlock program has been no response, do not bring the Result calls suffixes can normal operation!
You shouldn't call Result or Wait in an ASP.NET context; one reason is that it could cause deadlocks, as I explain on my blog.
Instead, you should use await, like this:
public async Task<ActionResult> Index()
{
var t1 = await Test1();
return View();
}
My controller action returns a custom ActionResult that executes either a success or failure result depending on some validation logic. This happens within ExecuteResult.
My question is, how can I check the result?
Here's the test so far:
[TestFixture]
public class FormActionResultTests
{
TestController controller;
[SetUp]
public void SetUp()
{
ObjectFactory.Initialize(cfg =>
{
cfg.For<IFormHandler<TestModel>>().Use<TestModelHandler>();
});
controller = new TestControllerBuilder().CreateController<TestController>();
}
[Test]
public void Valid_input_returns_success_result()
{
var result = controller.Test(new TestModel { IsValid = true, IsValid2 = true })
.AssertResultIs<FormActionResult<TestModel>>();
var context = controller.ControllerContext;
result.ExecuteResult(context);
// how to verify result?
}
}
public class TestController : Controller
{
public ActionResult Test(TestModel model) {
return new FormActionResult<TestModel>(model, this.Content("Success"), View(model));
}
}
public class TestModel {
public bool IsValid { get; set; }
public bool IsValid2 { get; set; }
}
public class TestModelHandler : IFormHandler<TestModel>
{
public void Handle(TestModel form, IValidationDictionary validationDictionary)
{
}
}
Update
Here's what worked for me in the end (using NSubstitute):
[Test]
public void Valid_input_returns_success_result()
{
var result = new FormActionResult<TestModel>(new TestModel { IsValid = true, IsValid2 = true },
new ContentResult { Content = "Success" }, new ContentResult { Content = "Failed" });
var sb = new StringBuilder();
var response = Substitute.For<HttpResponseBase>();
response.When(x => x.Write(Arg.Any<string>())).Do(ctx => sb.Append(ctx.Arg<string>()));
var httpContext = Substitute.For<HttpContextBase>();
httpContext.Response.Returns(response);
var controllerContext = new ControllerContext(httpContext, new RouteData(), new TestController());
result.ExecuteResult(controllerContext);
sb.ToString().ShouldEqual("Success");
}
Controller should be tested that they return correct ActionResult in your case, and the Success or Failure of ActionResult should be tested by ActionResultTest and it has nothing to do with controller. Unit test means single unit test - but you test both controller and ActionResult in the same test, that is incorrect. To test ActionResult, first imagine that generally all that ActionResult does, is writing result to HttpResponse. Let's rewrite your code to use Moq to supply StringWriter for ControllerContext.HttpContext.HttpResponse.Output
[Test]
public void Valid_input_returns_success_result()
{
var result = controller.Test(new TestModel { IsValid = true, IsValid2 = true })
.AssertResultIs<FormActionResult<TestModel>>();
var context = controller.ControllerContext;
Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
StringWriter actionResultOutput = new StringWriter();
mockHttpContext.Setup(x => x.Response.Output).Returns(actionResultOutput);
context.HttpContext = mockHttpContext.Object;
result.ExecuteResult(context);
// how to verify result? Examine actionResultOutput
}
All is left to examine actionResultOutput. For example, if your action result is designed to return string "Success" when validation is ok and "Error" when validation failed, compare these strings to actionResultOutput.ToString(). If your result view's generated html is more complex, you can use HtmlAgilityPack to examine output more deeply
You should write a simple unit test of the Test-action, asserting on the returned action result. You shouldn't depend on the MVC framework in your test. Simply create a new instance of TestController and call the Test method.