Linq query weirdness

Posts   
 
    
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 12-Jun-2009 15:06:51   

I'm am running adapter. Latest 12th may build.

i have loop looping thru a string array. The array is {"a", "3400"}


foreach (var search in searchCriteria)
                    {
                        clientList = clientList.Where(p => p.Firstname.Contains(search));
                    }

the sql output is:


exec sp_executesql N'SELECT [LPA_L1].[ClientId] FROM (SELECT DISTINCT [LPA_L3].[ClientId], [LPA_L4].[Firstname], [LPA_L4].[Surname], [LPA_L4].[Email], [LPA_L3].[Name], [LPA_L3].[Email] AS [ClientEmail], [LPA_L3].[PostalPostcode] AS [Postcode], [LPA_L3].[PostalSuburb] AS [Suburb], [LPA_L5].[Name] AS [State] FROM (( [LinkFire].[dbo].[Client] [LPA_L3]  LEFT JOIN [LinkFire].[dbo].[Contact] [LPA_L4]  ON  [LPA_L3].[ClientId] = [LPA_L4].[ClientId]) INNER JOIN [LinkFire].[dbo].[State] [LPA_L5]  ON  [LPA_L3].[PostalStateId] = [LPA_L5].[StateId])) [LPA_L1] WHERE ( ( ( ( ( ( [LPA_L1].[Firstname] LIKE @Firstname1)) AND ( [LPA_L1].[Firstname] LIKE @Firstname2)))))',N'@Firstname1 nvarchar(6),@Firstname2 nvarchar(6)',@Firstname1=N'%3400%',@Firstname2=N'%3400%'

Note the weirdness. @Firstname1 is 3400.. @firstname2 is 3400 as well. @FirstName one should be '%a%'. LLBL is getting confused when i query the same field twice (or more than once) and always uses the last value ('a' was first, '3400' was second)

Oh the execute line is this:


var ids = (from c in clientList select c.ClientId).ToArray();

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 12-Jun-2009 15:48:15   

Could you create a real repro query so we can try to reproduce it? Your post doesn't contain enough information to reproduce it

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 13-Jun-2009 05:56:24   

Ok here is the whole query.


public static IQueryable<ClientEntity> Find(string searchName, int pageIndex)
        {
            using (DataAccessAdapter adapter = new DataAccessAdapter())
            {
                LinqMetaData meta = new LinqMetaData(adapter);
                var clientList = (from o in meta.Client
                                   join c in meta.Contact on o.ClientId equals c.ClientId into oc
                                   from c in oc.DefaultIfEmpty()
                                   join s in meta.State on o.PostalStateId equals s.StateId
                                   select new
                                   {
                                       ClientId = o.ClientId,
                                       Firstname = c.Firstname,
                                       Surname = c.Surname,
                                       Email = c.Email,
                                       Name = o.Name,
                                       ClientEmail = o.Email,
                                       Postcode = o.PostalPostcode,
                                       Suburb = o.PostalSuburb,
                                       State = s.Name
                                   });

                if (!string.IsNullOrEmpty(searchName))
                {
                    var searchCriteria = searchName.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (var search in searchCriteria)
                    {
                        clientList = clientList.Where(p => p.Firstname.Contains(search));
                    }
                }
                var ids = (from c in clientList select c.ClientId).ToArray();
                var finalClients = (from c in TransactionManager.Meta.Client
                                    where ids.Contains(c.ClientId)
                                    select c).WithPath(opath => opath.Prefetch<StateEntity>(p => p.StateUsingPhysicalStateId)
                             .Prefetch<StateEntity>(p => p.StateUsingPostalStateId)).OrderBy(p => p.Name);

                return finalClients.TakePage(pageIndex + 1, 10);
            }
        }

I've attached the db.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 15-Jun-2009 14:52:37   

Will look into it.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 15-Jun-2009 18:23:52   

I could reproduce it.


var customers = from o in metaData.Order
                join c in metaData.Customer on o.CustomerId equals c.CustomerId into oc
                from x in oc.DefaultIfEmpty()
                select new { CustomerId = x.CustomerId, CompanyName = x.CompanyName, Country = x.Country };

string searchTerms = "U A";
var searchCriteria = searchTerms.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

foreach(var search in searchCriteria)
{
    customers = customers.Where(p => p.Country.Contains(search));
}
var ids = (from c in customers select c.CustomerId).ToArray();

(two like statements with "%A%"

And resharper gave the answer simple_smile below 'search' in customers.Where(p => p.Country.Contains(search)); it displayed a warning: "Access to modified closure". Together with the result that only the latest value is used gave the answer: 'search' in the foreach loop is 'U' the first time, and the where clause is added to the query. However it's not ran yet. So the next iteration the same variable 'search' is set to 'A'. THEN the query is ran, but the expression tree simply contains a reference to the local variable 'search'.

To fix this do:

foreach (var search in searchCriteria)
{
    string termToSearchOn = search;
    clientList = clientList.Where(p => p.Firstname.Contains(termToSearchOn));
}

It's subtle, but essential to do this. With this statement, you specify a reference in the expression tree to the local variable termToSearchOn which was created at that specific iteration. Ain't deferred execution lovely? sunglasses

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 17-Jun-2009 11:53:02   

Thanks Frans, that's one weird, obscure error. smile