AlumCloud.Com AlumCloud.Com - 2 months ago 15
C# Question

Get object from Task and return from non-async method

I've got a lower level library that one of the methods I've designed to be async and to return a Task.

I know it is not a good idea to have async methods in lower level libraries, but there is no way around this one, because of how heave the processing is.

In between my lower level library I've got a main library that exposes the lower level library method in question. "Just like a business layer".

The entry point of my app is a WEB Api it then calls the mid level library and the the mid level calls the lowest level library.

My relevant question is would it be better to unpack the result from the lowest level library where the async chain begin inside my mid library and then expose just a static method to the consumer, or should I just offer the consumer a async method and just offer async methods from the lowest to the highest?

From the entry into the app the first method in question is:

CutSheet.CutSheet cutSheet = AlumCloudPlans.Manager.GetCutSheet(elev);


I know there must be a better way because on the mid level library I've got to create an entire new task and the return is value, which is a Task, and the return that Task value.

What would ya'll recommend that I do in the mid level library?

---Lowest level

namespace CutSheet
{
public class Manager
{
public async static Task<CutSheet> GetCutSheet(IElevation elevation)
{
return new CutSheet(await new PartsProcessor.PartProcessor()
.ProcessParts(elevation));
}
}
}


---Mid level

namespace AlumCloudPlans
{
public class Manager
{
public static CutSheet.CutSheet GetCutSheet(IElevation elevation)
{
var t = Task.Factory.StartNew(async delegate
{
return await CutSheet.Manager.GetCutSheet(elevation);
});

t.Wait();
return t.Result.Result;
}

}
}


---Highest level and entry to app

namespace StorefrontSystem.Controllers
{
[Authorize]
public class CutSheetController : AlumCloudWebApiBaseController
{

public async Task<HttpResponseMessage> Get(string a, string f, int id)
{
HttpResponseMessage r = Request.CreateResponse();

IElevation elev = await ElevationManager.GetElevation(id);

CutSheet.CutSheet cutSheet = AlumCloudPlans.Manager
.GetCutSheet(elev);

var ms = new MemoryStream();
bool isPDF = f.ToLower() == "pdf";

if (isPDF)
{
using (var pdf = await cutSheet.GetCutSheetPDF(elev))
{
pdf.Save(ms, false);
}
}
else
{
using (Bitmap canvas = await cutSheet.GetDrawing())
{
canvas.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}
}

ms.Position = 0;
r.Content = new StreamContent(ms);
if (isPDF)
{
r.Content.Headers.ContentType
= new Headers.MediaTypeHeaderValue("Application/pdf");
}
else
{
r.Content.Headers.ContentType
= new Headers.MediaTypeHeaderValue("image/png");
}
return r;
}
}
}

Answer

I know it is not a good idea to have async methods in lower level libraries

Who told you that? It's a perfectly good idea to have async methods at any level, as long as the operation you're doing is naturally asynchronous. However, it's not a good idea in the lower levels to pretend something is asynchronous by using Task.Run - and this is particularly true in ASP.NET.

Assuming that your operation is naturally asynchronous, you should use async all the way up.

Mid-level:

public class Manager
{
    public static Task<CutSheet.CutSheet> GetCutSheetAsync(IElevation elevation)
    {
        return CutSheet.Manager.GetCutSheetAsync(elevation);
    }
}

Highest-level:

[Authorize]
public class CutSheetController : AlumCloudWebApiBaseController
{
    public async Task<HttpResponseMessage> Get(string a, string f, int id)
    {
        HttpResponseMessage r = Request.CreateResponse();

        IElevation elev = await ElevationManager.GetElevationAsync(id);

        CutSheet.CutSheet cutSheet = await AlumCloudPlans.Manager.GetCutSheetAsync(elev);

        ...
   }
}
Comments