7

Today I've run into a problem when creating a Web API using async ApiControllers. I'm using MongoDB and since the C# driver doesn't support async, I tried to implement it in my repository layer.

The resulting method in the Building repository looked like this:

public async Task<IEnumerable<Building>> GetAll()
{
    var tcs = new TaskCompletetionSource<IEnumerable<Building>>();

    await Task.Run(() => {
        var c = this.MongoDbCollection.FindAll();
        tcs.SetResult(c);
    });

    return await tcs.Task;
}

Now this works perfectly when testing the repository by itself using NUnit.

But when testing from the controller (using a HttpClient) it never proceeds to the "return" line after running tcs.SetResult(c). The test just keeps running until i abort it manually.

When I remove the Task.Run code and do everything synchronously then everything works as it should:

public async Task<IEnumerable<Building>> GetAll()
{
    var c = this.MongoDbCollection.FindAll();

    return c;
}

Does anyone have any idea why I experience different behaviors when testing the repository + database and when testing controller + repository + database?

The controller method looks like this: (buildingRepository is injected in the constructor using Ninject)

public async Task<HttpResponseMessage> Get()
{
    var result = await this.buildingRepository.GetAll();
    return Request.CreateResponse(HttpStatusCode.OK, result);
}

EDIT: Here are also the test methods. The first one is the one that's not working: (this.client is a HttpClient object with the accept-header set to "application/json")

[Test]
public void Get_WhenBuildingsExist_ShouldReturnBuilding()
{
    var task = this.client.GetAsync("/api/building/");

    var result = task.Result.Content.ReadAsStringAsync().Result;

    var o = JsonConvert.DeserializeObject<IEnumerable<Building>>(result);

    Assert.That(o.Any());
}

[Test]
public void Get_WhenBuildingsExist_ShouldReturnAtLeastOneBuilding()
{
    var buildings = this.buildingRepository.GetAll().Result;

    Assert.That(buildings.Any());
}
4

2 に答える 2

3

非同期タスクからの呼び出しが悪い考えである理由を説明する投稿を読んだことが.Resultsありますが、現時点では利用できません。基本的に、これを行うことで非同期処理を強制終了しています。次のようにテストを変更してみてください。

[Test]
public void Get_WhenBuildingsExist_ShouldReturnBuilding()
{
    var task = this.client.GetAsync("/api/building/");

    var resultTask = task.Result.Content.ReadAsStringAsync();
    resultTask.Wait();

    var result = resultTask.Result;
    var o = JsonConvert.DeserializeObject<IEnumerable<Building>>(result);

    Assert.That(o.Any());
}
于 2012-08-14T14:08:46.740 に答える