views:

101

answers:

2

I have a view, AdvertView in my database, this view is a simple join between some tables (advert, customer, properties). Then I have a simple linq query to fetch all adverts for a customer:

public IEnumerable<AdvertView> GetAdvertForCustomerID(int customerID)
{
    var advertList = 
        from advert in _dbContext.AdvertViews
        where advert.Customer_ID.Equals(customerID)
        select advert;
    return advertList;
}

I then wish to map this to modelItems for my MVC application:

public List<AdvertModelItem> GetAdvertsByCustomer(int customerId)
{
    List<AdvertModelItem> lstAdverts = new List<AdvertModelItem>();
    List<AdvertView> adViews = _dataHandler.GetAdvertForCustomerID(customerId).ToList();
    foreach(AdvertView adView in adViews)
    {
        lstAdverts.Add(_advertMapper.MapToModelClass(adView));
    }
    return lstAdverts;
}

I was expecting to have some performance issues with the SQL, but the problem seems to be with the .ToList() function. I'm using ANTS performance profiler and it reports that the total runtime of the function is 1.400ms, and 850 of those is with the ToList(). So my question is, why does the tolist function take such a long time here?

+3  A: 

GetAdvertForCustomerID doesn't return results, it returns a query.

ToList enumerates the query. The results are hydrated at that point. The database trip is happening within the ToList call.

You should get the generated sql (by using the SqlProfiler or the DataContext.Log property), and take it to query analyzer to examine the execution plan and IO (SET STATISTICS IO ON).

David B
David is very right here. IMO you should implicitly return IQueryables. Either return a `List<T>` or `T[]` or use `IQueryable<T>` as return type of the method definition. That makes it very explicit what will be happening.
Steven
I don't have any beef with the return type of the method. A basic linq fact is that queries are executed when enumerated. ToList enumerates its source. This knowledge is as essential in the linq world as knowing that lines of code are executed in order.
David B
Typo in my previous comment. It should read "you should NOT implicitly return IQueryables".
Steven
However, users of your method might not expect that iterating the collection twice will result in two calls to the database.
Steven
If I was given an IQueryable and had no knowledge of what generated it, I would certainly treat that as an expensive iterate-once-only source.
David B
I should probably have specified this better but the performance issue is NOT with the SQL. SqlProfiler reports 139 IO, execution time of 0. There is also just one single SQL server call from the iteration. What's also realy strange is that we have similar calls other places (but against tables and not views) that perform very good.
devzero
Sql Profiler doesn't measure the network - doesn't measure the cost of the roundtrip. Is this a large amount of data? It also doesn't measure the cost of new'ing up those instances. Half the method's time is spent in the ToList (new'ing up AdvertViews), and half outside the ToList (new'ing up AdvertModelItems)...
David B
The roundtrip time should be small, another function we use that fetches a single value from the DB clocks inn at 17.243ms using ANTS. The data amount should also be small. The result set is 47 rows, 23 columns. The biggest column contains less than 50 chars. The time is not split in 2, advertViews tolist: 800ms, advertModelItems : 13,633ms
devzero
+1  A: 

.ToList will execute the query, so that includes the entire roundtrip to the database, getting the data, and materializing the entity objects for every record returned... ...you're most likely spending most of that time db-side, so take a closer look at the execution plan for your view.

KristoferA - Huagati.com
DB side execution time (according to SqlServer profiler = 0, with 139 IO ops.
devzero