Thursday 20 September 2018

Task.Run vs Task.FromResult

So it is possible that you have an interface with multiple implementations and one of the implementations is IO bound and the other is not. So in order to support both implementations, you end up creating an interface method that looks like following:

Task GetRowsAsync(int[] idList);

Let us say one implementation tries to get result by querying a SQL Server database and the other one tries to get result by querying a in-memory database.

So your database implementation might be like:

public Task GetRowsAsync(int[] idList)
{
   ... some logic
    await ExecuteQueryAsync(....); // await an async operation
    return x;
}

What about an in-memory implementation? Should you use a code like #1 or #2?

public Task GetRowsAsync(int[] idList)
{
    return Task.FromResult(x);
}

public Task GetRowsAsync(int[] idList)
{
    return await Task.Run(() => { return x; });
}

Well, let us perform an experiment.

        static async Task GetNumber()
        {
            return await Task.Run(() => { return 1000; });
        }

        static async Task GetNumber1()
        {
            return await Task.FromResult(1000);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started");
            while(Console.ReadLine() != "quit")
            {
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
                Console.WriteLine(GetNumber().Result);
            }
      }

Run the code by choosing GetNumber and GetNumber method. Watch the process statistics in process explorer.

GetNumber:





GetNumber1:



Learning: Task.Run does try to schedule work even though there is nothing to wait on :).

Wednesday 19 September 2018

Action Filters : Do Not Keep State

One thing to remember : Do not keep state in Action Filters of any kind. There is no exception to this rule.

Point to note is that the action filter instance is created only once and then shared across the HTTP requests.

Let us build an example.

I will create 2 action filters.

public class TestFilter : IActionFilter
    {
        private int counter = 0;

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            counter++;
            filterContext.HttpContext.Response.Headers.Add("Counter", counter.ToString());
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            
        }
    }

    public class TestFilter2 : ActionFilterAttribute
    {
        private int counter = 0;

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            counter++;
            filterContext.HttpContext.Response.Headers.Add("Counter1", counter.ToString());
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {

        }

    }

Let us apply one of the filter as Global Filter and one filter specifically to an Action.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new TestFilter());

        }

[HttpGet]
        [TestFilter2]
        public ActionResult DoSomething()
        {
            return new JsonResult() { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "Test" };
        }

Now, if we run the application, check the http headers after a few refresh.



You would notice that the counter keeps increasing with each request.

Learning: Do not write an action filter that keeps state.