4

With the move to EF6.1, our goal is to use exclusivity the Async/Await options speaking with our data sets. While porting from our previous Linq2Sql, there are many .ToList(), .FirstOrDefault(), and .Count()'s. I know we can search and fix those all, but it would be nice if we could, at compile time, prevent those functions from even being permitted into a build. Does anyone have have a creative idea on how to accomplish this? Even if they were compiler warnings that could be thrown (such as use of the Obsolete attribute).

TravisWhidden
  • 1,948
  • 1
  • 17
  • 37
  • Should be pretty straightforward with a [Roslyn](https://github.com/dotnet/roslyn)-based analyzer. – Stephen Cleary Aug 07 '15 at 21:11
  • Just want to make you aware that in 99% of the circumstances async db calls are moot and even harmful to the project: http://stackoverflow.com/a/25087273/122718 and http://stackoverflow.com/a/12796711/122718 – usr Aug 07 '15 at 21:14
  • In our environment, our API sits under IIS, where thread pool threads are quite valuable. We have run into situations (scalability) where IIS is starved of threads due to the number of requests coming in. Most calls are awaiting on DB queries to return, or disk data to read causing IIS to queue requests while waiting on threads to be released. One would say "just add more threads", but that is not the approach we wanted to take. Moving to Async tasks is something we have decided to do, and so far (time will tell), we have not had any issues with it. – TravisWhidden Aug 07 '15 at 21:19
  • 1
    OK. Maybe you are right, but probably the right answer is to set min threads to 500 and be done with that. If you have more queued than that the backend can't handle it anyway no matter what backends you are using (db/disk). But from your description it borderline makes sense to use async :) Did you consider making the hottest queries async and leaving the rest sync? Seems like a much better tradeoff. Async is poison for code quality. – usr Aug 07 '15 at 21:24
  • 1
    The point is to not have 500 threads, which each use 1 meg of memory, and create much contention on the CPU. That isn't a scalabile approach. By having some async, and some sync, you still run into the same problems. By enforcing the Async model (my original question), you encourage/force your developers to use the pattern designed best for scalability, not ease of development. Even a query which humans thing is "fast" (ie, 100ms), the blocking of that thread x 1000 users at one time, means your going to deplete your threadpool much faster. BTW, its not poison-- its elegant. – TravisWhidden Aug 07 '15 at 21:37
  • Async is a better idea then just raising the number of threads. If you still want to lessen the load on the DB server it's better to limit the possible number of simultanous connections against it. Which works fine with async as it will not sit on a thread while waiting. Anyway, the only real solution is to test and see. It depends much on server configuration and hardware what the result will be. – Mikael Eliasson Aug 08 '15 at 21:50

2 Answers2

0

You can use the .NET Compiler Platform to write a Diagnostic and Code Fix that will look for these patterns and provide warnings/errors.

You could even implement a Syntax Transformation to automatically change these constructs - though the effort might be more expensive than just doing it by hand.

Sam Axe
  • 31,472
  • 7
  • 52
  • 80
  • Yea, did consider Roslyn, that or an old school FXCop, but learning that would take a bit longer. That may be the only bet though if I want compile time warnings or errors. – TravisWhidden Aug 08 '15 at 00:44
0

Following up to this... i never found a solution that can detect this at compile time, but I was able to do this in code in the DataContext:

        public EfMyCustomContext(string connctionString)
        : base(string.Format(CONNECTION_STRING, connctionString))
    {
#if DEBUG
        this.Database.Log = LogDataBaseCall;
#endif
    }

#if DEBUG
        private void LogDataBaseCall(string s)
        {
            if (s.Contains("Executing "))
            {
                if (!s.Contains("asynchronously"))
                {
                    // This code was not executed asynchronously
                    // Please look at the stack trace, and identify what needs
                    // to be loaded. Note, an entity.SomeOtherEntityOrCollection can be loaded
                    // with the eConnect API call entity.SomeOtherEntityOrCollectionLoadAsync() before using the 
                    // object that is going to hit the sub object. This is the most common mistake
                    // and this breakpoint will help you identify all synchronous code. 
                    // eConnect does not want any synchronous code in the code base.
                    System.Diagnostics.Debugger.Break();
                }
            }
        }
#endif

Hope this helps someone else, and still would love if there was some option during compile.

TravisWhidden
  • 1,948
  • 1
  • 17
  • 37