3

I have this linq query:

 public static Banner getSideBarBanner(){
    DataClassesDataContext db = new DataClassesDataContext();
    var bannerSiderBar = (from b in db.Banners
                  where b.Position.Equals(EBannersPosition.siderbar.ToString())
                  && b.Visible == true
                  select b).FirstOrDefault();
    return bannerSiderBar;
}

well, I use dotTrace to profile the application and I see that the query execution takes a lot of time (over 2s)

enter image description here

I am simply wondering, why so much time especially when my Banner table has about 30 records!!!

Thanks in advance for your opionions...

UPDATE: Banner's table schema:

enter image description here

UPDATE 2: If I use simple SQL connection instead of linq, the query execution takes 700ms which is a huge improvement...

 public static Banner getSideBarBanner()
{
    Banner bannerFound = new Banner();
    SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Library_prodConnectionString"].ConnectionString);
    try
    {
        myConnection.Open();
        SqlCommand myCommand = new SqlCommand("SELECT path, link FROM Banner b WHERE b.Position = @position AND b.Visible = 1 ", myConnection);
        myCommand.Parameters.Add(new SqlParameter("@position", EBannersPosition.siderbar.ToString()));
        SqlDataReader myReader = myCommand.ExecuteReader();
        while (myReader.Read())
        {
            if (myReader["path"] != null)
                bannerFound.Path = myReader["path"].ToString();
            if (myReader["link"] != null)
                bannerFound.Link = myReader["link"].ToString();
        }
        myConnection.Close();
    }
    catch (Exception e)
    {
        CreateLogFiles Err = new CreateLogFiles();
        Err.ErrorLog(HttpContext.Current.Server.MapPath("~/Site/Logs/ErrorLog"), e.ToString());
    }
    return bannerFound;
}

This tells me that translation of the linq query to sql has a very poor performance...What do you think?

Cristian Boariu
  • 9,603
  • 14
  • 91
  • 162

5 Answers5

2

You should consider grabbing the trial for http://l2sprof.com/ (if you're using LINQ to SQL) or http://efprof.com/ (if you're using Entity Framework) and using that to figure out what SQL your query is generating.

They're both free for 30 days, which I hope would be plenty of days to figure out this problem. ;)

Another possibility, pointed out by Robert in the comments, is to set the Log property on your DataContext, which will output the generated SQL to wherever you want.

You also could just use the SQL Server Profiler, which probably will display much more than you need, but hey, it would probably still get the job done.

adamjford
  • 7,478
  • 6
  • 29
  • 41
  • 2
    While you can certainly obtain one of these tools to get the generated SQL, [it's not necessary to do so.](http://msdn.microsoft.com/en-us/library/bb386961.aspx) – Robert Harvey Mar 28 '11 at 19:53
  • @Robert: Hey, that's probably easier than using the SQL Server Profiler. I'll add that to my answer. Thanks! – adamjford Mar 28 '11 at 19:58
1

Remember that LINQ is delayed in execution until you enumerate through the results. In this case, when you call FirstOrDefault, that's where it is actually running the database query, which might explain the delay.

It's not that FirstOrDefault is taking 2s, it's that the entire query is.

With that in mind, if you want people to narrow down further, you'll need to post your schema, data, etc.

Brook
  • 5,949
  • 3
  • 31
  • 45
  • While it's true that delayed execution skews the profiling results, your answer doesn't really shed any light on the reasons for the query's poor performance. – Robert Harvey Mar 28 '11 at 19:48
  • 2
    @Robert: That's true I guess, but I wanted to point out that `FirstOrDefault` is likely not the culprit in and of itself, but rather the query in general. – Brook Mar 28 '11 at 19:50
1

First off, the time spent calling FirstOrDefault() is the time spent digesting the Linq expression tree into SQL, sending that SQL to the DB, retrieving the result in a result set, and mapping that result set to an object. That can take a while.

Second, I would profile the database. Do a trace and figure out the exact SQL statement sent to the DB for this call. If that statement does not include a representation of the FirstOrDefault limitation, such as SELECT TOP 1 ..., you're pulling ALL the records out of the table just to discard all but one of them. Linq2SQL should be smarter than that in this case; if not, look into upgrading to MSEF or NHibernate (a big job for just one query, I grant you, but if this statement isn't producing an efficient query then any query like it won't be efficient either).

KeithS
  • 70,210
  • 21
  • 112
  • 164
  • The query is a simple one, and there's supposedly only 30 records in the table that it's pulling from, so it's hard to imagine any way this query is taking 2 seconds to execute. Clearly, there's some critical information the OP hasn't told us.. – Robert Harvey Mar 28 '11 at 19:55
  • I agree, but from what he *has* said, this is the answer; either the query isn't being constructed efficiently, or he's not throwing enough hardware at his data layer to efficiently retrieve the results. – KeithS Mar 28 '11 at 20:07
1

Add an index to Position, and try this:

 public static Banner getSideBarBanner()
 {
    DataClassesDataContext db = new DataClassesDataContext();

    string thisPosition = EBannersPosition.siderbar.ToString();

    var bannerSiderBar 
        = db.Banners.FirstOrDefault<Banner>
             (x => x.Position == thisPosition && x.Visible);

    return bannerSiderBar;
}

Basically the ideas here are to:

  1. Put the FirstOrDefault up front and make it strongly-typed, and
  2. remove the multiple executions of EBannersPosition.siderbar.ToString(),
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • @Robert Harvey: Thanks, I've added index to Position and replaced my method with yours and...still the amount of time it's almost the same (2.167 for Execution)...Just weird... – Cristian Boariu Mar 28 '11 at 20:49
  • @Cristian - Try removing pieces of the query one at a time to see if they make a noticeable difference. I would start by removing `FirstOrDefault` by calling `ToList()` before `FirstOrDefault`. If that doesn't make a difference, try removing conditions from the `Where` clause one at a time. – Chris Shouts Mar 28 '11 at 20:56
  • @Chris: `ToList()` will just execute that part of the query immediately, but it might be useful to return the complete list only and see how long it takes. If it's still taking 2 seconds to return 30 records, well... – Robert Harvey Mar 28 '11 at 21:02
  • @Robert - Yes, my thought was that most of the time seems to be taken up by L2S actually creating the query from the expression tree, so perhaps it would actually be faster to craft a 'dumber' L2S query and let the application code find the appropriate record. – Chris Shouts Mar 28 '11 at 21:10
  • @Chris - Done your suggestion - ToList takes 1.883 ms 1 call from which BuildQuery takes 791ms – Cristian Boariu Mar 28 '11 at 21:12
  • @Robert - I've updated my question using SqlCommand instead of Linq and it takes 700ms instead of 2199 which tells me that linq translation to sql has performance issues... – Cristian Boariu Mar 28 '11 at 22:02
  • 1
    700 ms is still an eternity for such a simple query. How healthy is that machine you're running this stuff on? – Robert Harvey Mar 28 '11 at 22:08
1

What you are witnessing, is, in my opinion, a problem with dotTrace. It reports exaggerated times for anything that is related to Linq-To-Sql. (See my comments on Best .NET memory and performance profiler?) It is not the only profiling product out there that has this problem.

I have experienced that myself and only very late in the process tried to verify the times of dotTrace with the System.Diagnostics.StopWatch. Of course, a profiler cannot report as accurate timings as StopWatch. But they were off by a large margin (some factors) and completely misrepresent the time taken by your code (for the L2S part).

It is somewhat evidenced by the fact that the total execution time exceeds the actual SQL Server work by a few factors.

Keep in mind though, that Linq-To-Sql (L2S) itself incurs some overhead, which can be significant at times. The object creation by L2S for each data row is not as simple as calling a constructor on an object and populating its properties. Not only because the Model classes are more than simple objects but also because it does a lot of verification of schema and datatypes and whatnot.

And of course, the compilation of the queries themselves can take quite some time.

So, in summary, try to get timings by using StopWatch. It would be good if you could share some results if you can verify my claims about dotTrace.

UPDATE1: One thing you could try, is to take the timings not on the first run, but on the second run of the code. Just to make sure you don't hit any one-time-costs. Also, with Linq, you always have the option to use compiled queries. Just search around for that a bit. In my experience you'll get the same inaccurate results though.

One note on compiled queries - don't use them, if not absolutely necessary. They have some major disadvantages, if all, that you are looking for, is a simple ORM. One is, you lose identity tracking. Also, you cannot use them for WHERE expr IN (setexpr)-type queries (list.Contains(...). Another one, of course, is readability. And finally, if you are going to use them, you might want to look at Nexterday's autocompilation for L2S (http://linqautocompiler.codeplex.com/)

Community
  • 1
  • 1
skarmats
  • 1,907
  • 15
  • 18
  • This is my feeling too...When I start the site with the profiler dotTrace ON it takes about 16s to load. Without profiling it takes about 5-6s. – Cristian Boariu Mar 29 '11 at 17:43
  • This, in itself, is expected behavior for profilers though. The code will run longer because, after all there is a lot going on while profiling. – skarmats Mar 29 '11 at 17:47
  • Profilers take that overhead into account though and try to produce accurate times. The problem is, that for L2S the times are not correct in my experience. So the percentages are wrong as well, leading to a misleading picture – skarmats Mar 29 '11 at 17:48
  • You could try the most recent nightly of the next version of dotTrace: http://confluence.jetbrains.net/display/NetProf/dotTrace+4.5+Nightly+Builds I just did a quick test and the results seem to be good - I am not sure though as I don't have any really meaningful code at hand. – skarmats Mar 29 '11 at 18:01