I'm trying to update an object with another object asynchronously. I'm trying to get the CustomerId value from Statues and then use it to call a specific customer and pass those values into PreviousStatuses. Then update the StatusToAdd with PreviousStatuses. If I pass Statues to StatusToAdd the values update. However, it's the wrong customer id. That's why I'm using PreviousStatuses.
This is the error I get:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at DSPDRewrite.Pages.Popups.AddStatusComponent.UpdateValues(String id)
at DSPDRewrite.Pages.Popups.AddStatusComponent.OnInitializedAsync()
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)
[Parameter]
public int CustomerId { get; set; }
[CascadingParameter]
public Task<AuthenticationState> AuthState { get; set; }
public Status Statuses;
public Status PreviousStatuses;
//IList<Status> PreviousStatuses;
public Dspd1056Status StatusToAdd = new Dspd1056Status();
public Customer customer;
public int AccountStatusId = 0;
protected override async Task OnInitializedAsync()
{
Statuses = await dataService.Get1056StatusById(CustomerId);
//int id = Int32.Parse(Statuses.CustomerId);
// Statuses = await dataService.Get1056StatusById(id);
Console.WriteLine(Statuses.CustomerId);
await UpdateValues(Statuses.CustomerId);
}
async Task UpdateValues(string id)
{
PreviousStatuses = await dataService.Get1056StatusById(Int32.Parse((id)));
StatusToAdd.AccountCurrent = PreviousStatuses.AccountCurrent;
StatusToAdd.StartDate = PreviousStatuses.StartDate;
StatusToAdd.EndDate = PreviousStatuses.EndDate;
StatusToAdd.Units = PreviousStatuses.Units;
StatusToAdd.Ppc = PreviousStatuses.Ppc;
StatusToAdd.EndStatus = PreviousStatuses.EndStatus;
StatusToAdd.ContinuallyFunded = PreviousStatuses.ContinuallyFunded;
StatusToAdd.AnnualUnits = PreviousStatuses.AnnualUnits;
StatusToAdd.Elg = PreviousStatuses.Elg;
StatusToAdd.ReceiptDate = PreviousStatuses.ReceiptDate;
StatusToAdd.RahTripsFunded = PreviousStatuses.RahTripsFunded;
StatusToAdd.Rate = PreviousStatuses.Rate;
StatusToAdd.AccountTotal = PreviousStatuses.AccountTotal;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (CustomerId != 0)
{
customer = await dataService.GetCustomerById((int)CustomerId);
StateHasChanged();
}
}
}
A few things to consider:
OnInitializedAsync
Can your .Get1056StatusById() method return null (e.g. if CustomerId is not found)? If so that appears to be the cause of the error: your code calls
await UpdateValues(Statuses.CustomerId);
That suggests Statuses is null. I would add a null check to this code to handle that case. Not sure why the Console.WriteLine didn't throw the null reference first, I assume the sample code didn't have it when the error was thrown.
A few other points: fields and methods in Components should normally be private or protected. If you are going to expose outside the Component they should be properties. Methods are rarely called outside a component. My suggested changes would be:
[Parameter] public int CustomerId { get; set; }
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
Status Statuses;
Status PreviousStatuses;
Dspd1056Status StatusToAdd = new Dspd1056Status();
Customer customer;
int AccountStatusId = 0;
protected override async Task OnInitializedAsync()
{
Statuses = await dataService.Get1056StatusById(CustomerId);
if(Statuses != null)
{
Console.WriteLine(Statuses.CustomerId);
await UpdateValues(Statuses.CustomerId);
}
}
async Task UpdateValues(string id)
{
PreviousStatuses = await dataService.Get1056StatusById(Int32.Parse((id)));
// rest...
Your component HTML should obviously check for a null Statuses before it tries to render:
#if(Statuses != null)
{
<p>Customer is #Statuses.CustomerId</p>
}
This is such a frequently used pattern in Blazor apps I usually use a <NotNull> component:
https://gist.github.com/conficient/eab57ade2587104d71ac6f26ddfd4865
Related
I have created a class, ObjectBuilder - I am injecting my dbContext into the class constructor and setting it to a read only attribute in the constructor method.
The class has a few different methods that use the dbContext, for example 'CreateorUpdateObject.
In my API controller I am instantiating the ObjectBuilder class. I am then trying to call the 'CreateorUpdateObject' method three times in a row. The first time everything works as intended. The second time, I get an ObjectDisposedException. I understand what this means. However I can't work out how to resolve this issue, seemingly executing a single method will always dispose the context - therefore none of the subsequent methods will work. I am not using any using clauses or disposing of the context myself. Can someone explain to me how to prevent dbContext from dispsoing so I can chain methods.
All the best!
Here is a snippet of the ObjectBuilder Class
class ObjectBuilder
{
readonly GuidObjectBuilder _guidObjectBuilder;
readonly projectviewerContext _db;
bool UpdatedGuidsArray { get; set; }
int UpdatedObjectCount { get; set; }
int CreatedObjectCount { get; set; }
ProjectObjects ActiveObject { get; set; }
public ObjectBuilder(GuidObjectBuilder guidObjectBuilder, projectviewerContext db)
{
this._guidObjectBuilder = guidObjectBuilder;
this._db = db;
this.UpdatedGuidsArray = false;
this.UpdatedObjectCount = 0;
this.CreatedObjectCount = 0;
}
public async Task<dynamic> CreateOrUpdateObject(string projectName, string tag, string pidClass, string guId, string fileName)
{
//SEND ONE ENTRY RETURNS A PROJECT_OBJECT MODEL
if (this._db == null) throw new ArgumentNullException("The object builder class has not been set a database. Set the object with a database");
try
{
//Check for an initial entry
List<ProjectObjects> matchingProjectObjects = (from data in this._db.ProjectObjects.ToList()
where data.ProjectName == projectName &&
data.Tag == tag
select data).ToList();
if (matchingProjectObjects.Any())
{
this.ActiveObject = this.UpdateProjectObject(matchingProjectObjects[0], pidClass, guId, fileName);
this._db.Update(this.ActiveObject);
this.UpdatedObjectCount++;
}
else
{
this.ActiveObject = this.CreateProjectObject(projectName, tag, pidClass, guId, fileName);
this._db.Add(this.ActiveObject);
this.CreatedObjectCount++;
}
await this._db.SaveChangesAsync();
if (this.UpdatedGuidsArray)
{
//Add new guid
await this._guidObjectBuilder.CreateOrUpdateUponModelOpen(this.ActiveObject.Id, guId, fileName);
}
this.UpdatedGuidsArray = false;
return this.ActiveObject;
}
catch (System.Exception error)
{
return error;
}
}
}
Here is a snippet of my test endpoint:
[HttpPost]
[Route("api/objectCreation/test1")]
public async Task<dynamic> TestOne()
{
try
{
projectviewerContext db = new projectviewerContext();
ObjectBuilder objectBuilder = new ObjectBuilder(new GuidObjectBuilder(db), db);
await objectBuilder.CreateOrUpdateObject("Project 1", "VV00011", "Gate Valve", "XXCCBBVVNNDDFFHH", "3D_Model.nwd");
await objectBuilder.CreateOrUpdateObject("Project 1", "VV00012", "Gate Valve", "XXFFGGHHIILLMMNN", "3D_Model.nwd");
await objectBuilder.CreateOrUpdateObject("Project 1", "VV00014", "Gate Valve", "123456789", "PID_drawing.nwd");
return "bang";
}
catch (System.Exception error)
{
return error.Message;
}
}
Why does the Status of my Task return "WaitingForActivasion" instead of "Running" ?
If I remove Task.Run I get stuck in the while loop, so I assume its not running asynchronous.
public class StateManagerTest
{
[Fact]
public void Start_TaskStatus()
{
StateManager manager = new StateManager();
manager.Start();
Assert.True(manager.Status == System.Threading.Tasks.TaskStatus.Running.ToString());
}
}
public class StateManager
{
private CancellationTokenSource cts = new();
private Task updateTask;
public HashSet<StateItem> StateItems { get; private set; }
public Provider Provider { get; private set; }
public List<OutputService> OutputServices { get; private set; }
public string Status
{
get => updateTask.Status.ToString();
}
public StateManager()
{
StateItems = new();
OutputServices = new();
Provider = new();
}
public void Stop()
{
cts.Cancel();
}
public void Start()
{
updateTask = Task.Run(() => Update(cts.Token))
.ContinueWith(t => Debug.WriteLine(t.Exception.Message), TaskContinuationOptions.OnlyOnFaulted);
}
private async Task Update(CancellationToken token)
{
while (true)
{
// get changes from outputs
Dictionary<StateItem, object> changes = new Dictionary<StateItem, object>();
foreach (var service in OutputServices)
{
var outputChanges = await service.GetChanges();
foreach (var change in outputChanges)
changes.TryAdd(change.Key, change.Value);
}
// write changes to provider source
await Provider.PushChanges(changes);
// update state
await Provider.UpdateStateItems();
// update all services
foreach (var service in OutputServices)
await service.UpdateSource();
if (token.IsCancellationRequested)
return;
}
}
}
As others have noted, WaitingForActivation is the correct state for a Promise Task that is not yet completed. In general, I recommend not using Task.Status or ContinueWith; they are relics from a time before async/await existed.
How to get status of long running task
I believe you would want progress reporting, which is done yourself. The T in IProgress<T> can be a string if you want a simple text update, or a double if you want a percentage update, or a custom struct if you want a more complex update.
I'am trying to run my Controller Action in async way.
How do I use async Task ? Or How to run in async way
// Db context
public class DeptContext : DbContext
{
public LagerContext(DbContextOptions<LagerContext> options)
: base(options)
{
Database.Migrate();
}
public DbSet<Department> Departments { get; set; }
public DbSet<Product> Products { get; set; }
}
// This is my Interface IDepRepository
Task<Department> GetDepartmentWithOrWithoutProducts(int deptId, bool includeProducts);
// And my Repository class DepRepository
public class DepRepository : IDepRepository
{
private DeptContext db;
public DepRepository(DeptContext context)
{
db = context;
}
// I'am geting Department name with products or Without products
public async Task<Department> GetDepartmentWithOrWithoutProducts(int deptId, bool includeProducts)
{
if(includeProductss)
{
return await db.Departments.Include(c => c.Products).Where(s => s.deptId == deptId).SingleAsync();
}
return await db.Departments.Where(s => s.deptId == deptId).SingleAsync();
}
}
So How should I do now in my Controller to do it as async way: I tried as following but I don't know if it's right to do like this following:
I'm not getting any error but I don't if it's right way ...
using System.Threading.Tasks;
using System.Net;
using Microsoft.Data.Entity;
using Microsoft.EntityFrameworkCore;
[Route("api/departments")]
public class DepartmentsController : Controller
{
private IDeptRepository _deptInfoRepository;
public DepartmentsController(IDeptRepository deptInfoRepository)
{
_deptInfoRepository = deptInfoRepository;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetDepatment(int id, bool includeProducts = false)
{
var dept = _deptInfoRepository.GetDepartmentWithOrWithoutProducts(id, includeComputers);
if(dept == null)
{
return BadRequest();
}
if(includeProducts)
{
var depResult = new DepartmentDto() { deptId = dept.deptId, deptName = dept.deptName };
foreach(var department in dept.Products)
{
depResult.Products.Add(new ProductDto() { productId = department.productId, deptId = department.deptId, ProductName = department.ProductName });
}
return Ok(depResult);
}
var departmentWithoutProductResult = new DepartmentsWithoutProductsDto() { DeptId = dept.deptId, DeptName = dept.DeptName};
return Ok(departmentWithoutProductResult);
}
How do I do to get my controller in async way.. I don't know where to put those await and ToListAsync(). Thank you in advance!
The interface should be renamed to better show the intent.
public interface IDepRepository {
Task<Department> GetDepartmentWithOrWithoutProductsAsync(int deptId, bool includeProducts);
//...
}
Which would update the implementation accordingly. Since the method is not actually using anything after the async call then there not really any reason to tag the method as async. Just return the Task.
public Task<Department> GetDepartmentWithOrWithoutProductsAsync(int deptId, bool includeProducts) {
if(includeProductss) {
return db.Departments.Include(c => c.Products).Where(s => s.deptId == deptId).SingleAsync();
}
return db.Departments.Where(s => s.deptId == deptId).SingleAsync();
}
The controller action however needs to await the task and then continue after the task has completed so therefore that method will be tagged with async.
[HttpGet("{id}")]
public async Task<IActionResult> GetDepatment(int id, bool includeProducts = false) {
var dept = await _deptInfoRepository.GetDepartmentWithOrWithoutProductsAsync(id, includeComputers);
if (dept == null) {
return BadRequest();
}
if (includeProducts) {
var depResult = new DepartmentDto() { deptId = dept.deptId, deptName = dept.deptName };
foreach (var department in dept.Products) {
depResult.Products.Add(new ProductDto() {
productId = department.productId,
deptId = department.deptId,
ProductName = department.ProductName
});
}
return Ok(depResult);
}
var departmentWithoutProductResult = new DepartmentsWithoutProductsDto() { DeptId = dept.deptId, DeptName = dept.DeptName};
return Ok(departmentWithoutProductResult);
}
What I can't tell from your code is the datatype of what GetDepartments returns. My guess is that you are using EF Core and GetDepartments returns a DbSet or a LINQ query against a DbSet. If that is the case, then after the line where your depEntities variable is set, that variable points to a deferred object (an expression tree that has not been evaulated yet). Or in other words, the actual query has not been sent to the database yet. When you loop over the depEntities (with your foreach loop), you are causing the actual potentially long-running work to occur (database access). That's what you want to await on. So, yes, you could make an async version of GetDepartments or you could also probably change your code to be:
var depEntities = await _depRepository.GetDepartments().ToListAsync();
The call to ToListAsync will enumerate the deferred object and perform the database access. Your return statement would just return results. Behind the scenes, the method actually returns on your await statement and resumes after the work you're awaiting on completes.
One last note.. any database exceptions will occur at the point where the deferred object is enumerated.
You should not do any await on already-prepared results list. It's already contain required data - what you want to wait to?
You should make new async version of your GetDepartments() method and await while obtaining data from repository:
var depEntities = await _depRepository.GetDepartmentsAsync();
I have an application which currently runs Tasks on a time interval, however I would like more control over that, to be able to stop a running task and restart it by clicking a UI.
There are 6 tasks at the moment, but I would want to keep things generic, to be able to easily more when required. I was hoping to be able to create a wrapper to control them, which I can pass a method into as a parameter.
As such I created an object, which I create as many of as there are tasks, I can get status updates from it as well as manage it
I want to:
- Start a method/Task
- Stop a method/Task
- Restart a method/Task
- Get feedback from it's log/updates/progress/errors that I record to updates List
Is this a good way to do this, is there a better way to achieve what I'm after?
public class ManagedTask
{
public ManagedTask()
{
CreateNewToken();
}
public int Id { get; set; }
public string DescriptiveName { get; set; }
public Action<CancellationToken> TheVoidToRun { private get; set; }
private CancellationTokenSource CTokenSource { get; set; }
private CancellationToken CToken { get; set; }
private Task TheRunningThing { get; set; }
public void StartIt()
{
if (TheRunningThing == null || TheTaskStatus() == TaskStatus.Canceled || TheTaskStatus() == TaskStatus.RanToCompletion)
{
CreateNewToken();
}
// Start up the Task
AddUpdate($"Starting Task at {DateTime.Now}");
TheRunningThing = Task.Run(() => TheVoidToRun?.Invoke(CToken), CToken);
AddUpdate($"Started Task at {DateTime.Now}");
}
public void EndIt()
{
AddUpdate($"Cancelling Task at {DateTime.Now}");
CTokenSource.Cancel();
// Do - If in progress try to stop (Cancellation Token)
// Do - Stop future repeats
}
private void CreateNewToken()
{
CTokenSource = new CancellationTokenSource();
CTokenSource.Token.ThrowIfCancellationRequested();
CToken = CTokenSource.Token;
}
public TaskStatus TheTaskStatus() => TheRunningThing.Status;
internal List<string> Updates { get; set; }
private void AddUpdate(string updates)
{
// Do stuff
}
}
So I have various methods which I'd like to pass into this such like:
public class AvailableTasks
{
public async void DoStuffThatIsCancelable(CancellationToken token)
{
DoTheLongStuffOnRepeat(token);
}
public async void DoAnotherThingThatIsCancelable(CancellationToken token)
{
DoTheLongStuffOnRepeat(token);
}
private async void DoTheLongStuffOnRepeat(CancellationToken token)
{
// Do stuff
for (int i = 0; i < 20; i++)
{
while (!token.IsCancellationRequested)
{
try
{
await Task.Delay(500, token);
}
catch (TaskCanceledException ex)
{
Console.WriteLine("Task was cancelled");
continue;
}
Console.WriteLine($"Task Loop at {(i + 1) * 500}");
}
}
}
}
Here is how I was thinking of calling it.
private static readonly List<ManagedTask> _managedTasks = new List<ManagedTask>();
public static void SetupManagedTasks()
{
var at = new AvailableTasks();
var mt1 = new ManagedTask
{
Id = 1,
DescriptiveName = "The cancelable task",
TheVoidToRun = at.DoStuffThatIsCancelable,
};
_managedTasks.Add(mt1);
var mt2 = new ManagedTask
{
Id = 2,
DescriptiveName = "Another cancelable task",
TheVoidToRun = at.DoAnotherThingThatIsCancelable,
};
_managedTasks.Add(mt2);
mt1.StartIt();
mt2.StartIt();
Console.WriteLine($"{mt1.DescriptiveName} status: {mt1.TheTaskStatus()}");
Console.WriteLine($"{mt2.DescriptiveName} status: {mt2.TheTaskStatus()}");
}
public static void CancelTask(int id)
{
var mt = _managedTasks.FirstOrDefault(t => t.Id == id);
if (mt != null)
{
mt.EndIt();
Console.WriteLine($"{mt.DescriptiveName} status: {mt.TheTaskStatus()}");
}
}
public static void GetTaskStatus(int id)
{
var mt = _managedTasks.FirstOrDefault(t => t.Id == id);
if (mt != null)
{
Console.WriteLine($"{mt.DescriptiveName} status: {mt.TheTaskStatus()}");
}
}
However even with all the above, I suffer from the Status only ever showing RanToCompletion.
How can I structure the above to achieve what I want?
Thanks,
David
I suffer from the Status only ever showing RanToCompletion.
This is because your methods are using async void. They should be async Task. As I describe in my async best practices article, you should avoid async void.
Other notes...
Start a method/Task
Restart a method/Task
You can start (or restart) a task on the thread pool by using Task.Run. However, if you have naturally asynchronous tasks, then you can represent them as Func<Task> and just invoke the Func<Task> to start them.
Stop a method/Task
The only appropriate way to do this is with a CancellationToken, which it looks like you're using correctly.
Get feedback from it's log/updates/progress/errors that I record to updates List
I recommend using IProgress<T> for any kind of progress updates.
I have a simple Web API method that looks like this:
public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
var taskId = await TaskManager.CreateTask(taskType);
TaskManager.Run(taskId);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content =
new StringContent($"Task {taskType.GetDescription()} was started.")
};
}
TaskManager.Run is decalared like this:
public async Task Run(int id)
I was expecting it to return "Task was started" message immediately after TaskManager.Run(taskId) But the request continues to run synchronously.
But if to replace the call TaskManager.Run(taskId) with:
Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(100)));
Then it runs asynchronously.
So I believe this is something to do with the resources shared by TaskManager and main thread. Can a shared resource lock the execution?
I'm using Castle Windsor. One WindsorContainer container is declared in Web API project.
TaskManager utilizes BaseTaskRunner class inside of it. One more WindsorContainer is declared in BaseTaskRunner.
Web API's container uses LifeStyle.PerWebRequest for all components. BaseTaskRunner's container uses LifeStyle.Singleton (not sure if it's correct LifeStyle). Could the call be locked for example by DdContext or other classes declared in both of the containers?
UPD:
I don't want to wait the TaskManager.Run to complete. But what happens is that return statement still waits for the TaskManager.Run to complete (even without await statement on TaskManager.Run).
In other words it does not matter how I call the TaskManager.Run:
TaskManager.Run(taskId);
or
await TaskManager.Run(taskId);
It waits for TaskManager.Run to complete in both cases.
Here is the code of TaskManager:
public class TaskManager : ITaskManager
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
public async Task<int> CreateTask(TaskType type, byte[] data = null, object config = null)
{
var task = new BackgroundTask
{
Type = type,
Status = BackgroundTaskStatus.New,
Config = config?.SerializeToXml(),
Created = DateTime.Now,
Data = data
};
TaskRepository.Add(task);
TaskRepository.SaveChanges();
return task.Id;
}
public async Task Run(int id, bool removeOnComplete = true)
{
var task = TaskRepository.GetById(id);
Run(task, removeOnComplete);
}
public async Task Run(TaskType type, bool removeOnComplete = true)
{
var tasksToRun = TaskRepository.Get(t => t.Type == type);
tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
}
public async Task Run(BackgroundTask task, bool removeOnComplete = true)
{
switch (task.Type)
{
case TaskType.SpreadsheetImport:
new SpreadsheetImportTaskRunner().Run(task);
break;
}
}
}
And some other classes:
public class SpreadsheetImportTaskRunner : BaseTaskRunner
{
public IForecastSpreadsheetManager SpreadsheetManager { get; set; }
protected override void Execute()
{
SpreadsheetManager.ImportActuals(Task.Data);
}
protected override void Initialize()
{
base.Initialize();
SpreadsheetManager = _container.Resolve<IForecastSpreadsheetManager>();
}
}
BaseTaskRunner:
public class BaseTaskRunner
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
protected IWindsorContainer _container = new WindsorContainer();
protected BackgroundTask Task { get; set; }
public async Task Run(BackgroundTask task)
{
Initialize();
Task = task;
try
{
Execute();
}
catch (Exception ex)
{
SetError(ex.ToString());
}
}
protected virtual void Execute()
{
}
protected virtual void Initialize()
{
_container.Install(new TaskRunnerComponentsInstaller());
TaskRepository = _container.Resolve<IRepository<BackgroundTask>>();
}
}
I still believe this is something to do with the WindsorContainer and common classes which are resolved in several different threads.
The issue is that you're not using await on the Task being returned from the invocation of the TaskManager.Run function. Consider the below:
public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
var taskId = await TaskManager.CreateTask(taskType);
await TaskManager.Run(taskId);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content =
new StringContent($"Task {taskType.GetDescription()} was started.")
};
}
Now it will work asynchronously as you'd expect. The await sets a continuation marker in the async state-machine, instructing it to return to this portion of the method upon completion of the asynchronous operation defined in the TaskManager.Run.
UPDATE
You are missing lots of await statements, and there are times where you need to not mark methods as async. It appears as though there are some mis-understandings as it pertains to these keywords. Here is what your TaskManager class should look like.
public class TaskManager : ITaskManager
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
public async Task<int> CreateTask(TaskType type,
byte[] data = null,
object config = null)
{
var task = new BackgroundTask
{
Type = type,
Status = BackgroundTaskStatus.New,
Config = config?.SerializeToXml(),
Created = DateTime.Now,
Data = data
};
TaskRepository.Add(task);
TaskRepository.SaveChanges();
return task.Id;
}
public ask Run(int id, bool removeOnComplete = true)
{
var task = TaskRepository.GetById(id);
return Run(task, removeOnComplete);
}
public Task Run(TaskType type, bool removeOnComplete = true)
{
var tasksToRun = TaskRepository.Get(t => t.Type == type);
return tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
}
public Task Run(BackgroundTask task, bool removeOnComplete = true)
{
switch (task.Type)
{
case TaskType.SpreadsheetImport:
return new SpreadsheetImportTaskRunner().Run(task);
break;
}
}
}
}
Ideally, if the method is marked as a return type of Task and the method doesn't need to unwind any tasks within its execution it can simply return the Task functionality for its implementation. For example, notice how dramatically my TaskManager class differs from yours -- I'm only marking methods as async that need to actually await. These two keywords should be married, if a method uses async there should be an await. But only use await if the method needs to unwind and use the asynchronous operation.