An issue on generated SQL query

Posts   
1  /  2
 
    
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 28-Jul-2012 09:18:43   

Hi,

I'm facing an issue when selecting on an inner query.

var adapter = new DataAccessAdapter("server=.;initial catalog=Northwind; integrated security=true");
var md = new LinqMetaData(adapter);

 var q = from p in md.Products.Where(p => md.Orders.Any(o => o.OrderId == p.Categories.CategoryId))
                     select new
                     {
                         p.ProductId,
                         p.ProductName,
                     };

The generated sql query is :

SELECT [LPLA_1].[ProductID] AS [ProductId], [LPLA_1].[ProductName] FROM [Northwind].[dbo].[Products] [LPLA_1]  WHERE ( ( ( (  EXISTS (SELECT [LPLA_2].[ShipCountry] FROM ( [Northwind].[dbo].[Categories] [LPA_L1]  RIGHT JOIN [Northwind].[dbo].[Products] [LPA_L2]  ON  [LPA_L1].[CategoryID]=[LPA_L2].[CategoryID]) WHERE ( ( [LPLA_2].[OrderID] = [LPA_L1].[CategoryID])))))))

With the following exception:

The multi-part identifier "LPLA_2.OrderID" could not be bound.
The multi-part identifier "LPLA_2.ShipCountry" could not be bound

I know that this query seems ridiculous and it has no meaning. I just provided it to be able to reproduce it using Northwind database.

I know that it is possible to write this query as following:

var q2 = from p in md.Products
                     where md.Orders.Any(o => o.OrderId == p.Categories.CategoryId)
                     select new
                     {
                         p.ProductId,
                         p.ProductName,
                     };

But in our case we want to write it like the first form, although the generated queries are the same.

It seems that this issue only occurs when a related entity is used:


.....Where(p => md.Orders.Any(o => o.OrderId == p.Categories.CategoryId))

If I use a non related field, everything is fine:

......Where(p => md.Orders.Any(o => o.OrderId == p.CategoryId))

I tested it against the latest available build for LLBL 2.6:

  • SD.LLBLGen.Pro.ORMSupportClasses.NET20 "2.6.10.0817"

  • SD.LLBLGen.Pro.DQE.SqlServer.NET20 "2.6.10.0917"

  • SD.LLBLGen.Pro.LinqSupportClasses.NET35 "2.6.11.0518"

Thanks.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 30-Jul-2012 00:05:13   

I reproduced your issue with both latest v2.6 and latest 3.1. I though this was an old fixed issue, but looking closely into your query it looks like it's formulated in a wrong way. Shouldn't it be with a join?

var q = from p in md.Product
        join o in md.Order on p.ProductId equals o.OrderId
        select new
        {
            p.ProductId,
            p.ProductName,
        };

Is there an explicit relation between that two entities in your real scenario? How does look you real code?

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 30-Jul-2012 10:55:14   

The navigation in the Any lambda should result in a join on the entity the .Any is called on I think. Apparently this doesn't happen (it's pretty hard to do). Explicitly specifying the join instead will overcome this problem of course.

I think you meant with the second query:


var q = from p in metaData.Product
        join c in metaData.Category on p.CategoryId equals c.CategoryId 
        where metaData.Order.Any(o => o.OrderId == c.CategoryId)
        select new
        {
            p.ProductId,
            p.ProductName,
        };

which works.

This is what I meant: it specifies the join explicitly instead of implicitly. This is a workaround for these kind of queries. I must say I have a hard time understanding what you want to achieve without real code/a real model, so we need the answers to the questions David raised to fully understand what you want to accomplish (so we can suggest a workaround simple_smile ).

Will wait for reply from user before taking action on this.

Frans Bouma | Lead developer LLBLGen Pro
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 01-Aug-2012 08:37:25   

Hi,

Sorry for the delay, I'm not at the office for a few days. I'll try to post a more real sample for you as soon as I'm back.

Thanks.

arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 04-Aug-2012 14:45:11   

Hi,

It's really difficult for me to provide you with my real sample code because lots of classes and scripts are need for demonstrating it. So I'll try my best to describe my needs and will try to simulate it with Northwind sample.

We have a security feature (some sort of authorization) implemented in our framework that lets the admin to choose what information to show to the end users. For example consider the user is just allowed to view the information of warehouse documents related to some specific warehouses. All of the warehouse documents are stored in one table and each record has foreign key of the warehouse id. Consider a situation that user 'A' can only see the warehouse documents for warehouse id 10 and 20.

Simulating using Northwind database there is a business authorization constraint that a user can query only those products which their category Id is 10 or 20.

These assignments are stored in a table called BusinessSecurityInfo with the following structure:

Id  (uniqueidentifier, not null)  --> primary key
SubjectId   (int, not null) -->A unique number indicating the security rule. (not important to my problem)
OperationId (int, not null) -->A number indicating the operation used with a subject. (not important to my problem)
UserId  (uniqueidentifier, not null) -->The user id which the security rule is applied for.
Value1 (int, null)  custom values (described later)
Value2 (int, null)  (not used here, these are reserved if the 'where' condition should check for other fields also)
Value3 (int, null)  (not used here)
Value4 (int, null)  (not used here)
Value5 (int, null)  (not used here)

Data sample:

Id SubjectId OperationId UserId Value1 Value2 Value3 Value4 Value5 someid someid someid user1Id 10 null null null null someid someid someid user1Id 20 null null null null someid someid someid user2Id 30 null null null null

These records show that the user1 can only sees the products with categoryId 10 or 20, and the user2 can only sees the products with categoryId 30.

The developer can specify the security rule using some methods and classes and then the appropriated expression tree is build and applied at run time as a where clause.

Notice that my real code contains some generic classes and some base classes.The provided code is different but it fulfills the same purpose.

The query is written like this by the developer:

public IQueryable<ProductEntity> GetProducts()
{
    var productDs = GetEntityDataSource<ProductEntity>();
    var q = from p in productDs
        select new
        {
            p.ProductId,
            p.ProductName,
        };
        
    return q;
}

protected IQueryable<TEntity> GetEntityDataSource<TEntity>() //This method is implemented in the base class.
      where TEntity : class, IEntity2, new()
{
    //The DataAccessAdapter and LinqMetaData are initialized in data access class.
    
    SecurityInfo securityInfo = SecurityHelper.GetSecurityInfo(); //The GetSecurityInfo method is defined in other classes.
    if(securityInfo == null)  //If there are no rules defined simply return the datasource
        return DataAccess.EntityDataSource;  //Note that the 'EntityDataSource' property from the 'DataAccess' property returns the appropriate DataSource2<TEntity> property from LLBL generated LinqMetaData.
    
    if(!securityInfo.IsAllowed)  // if the user is not allowed at all then show him nothing --> this is checked against some other authorization mechanism(in our case it's NetSqlAzMan)
        return DataAccess.EntityDataSource(c => false);
    
    //Apply the security expression to the data source
    return DataAccess.EntityDataSource(securityInfo.SecurityExpression);
}

The SecurityExpression is built at run time at the first access. The produced expression for our example would be something like this:

Expression<Func<ProductEntity, bool>> condition = p => linqMetaData.BusinessSecurityInfo.Any(bsi => bsi.Value1 == p.Categories.CategoryId || bsi.Value1 == null);

Using the provided expression only the records which the user is allowed to query are returned. Notice that the binary expressions for checking SubjectId, OperationId and UserId are omitted for simplicity. Also the query may not be as simple as the one mentioned, for example something like this:

var productDs = GetEntityDataSource<ProductEntity>();
var suppliersDs = GetEntityDataSource<SupplierEntity>();

var q = from p in productDs
        join s in suppliersDs on p.SupplierId equals s.SupplierId
    select new
    {
        p.ProductId,
        p.ProductName,
        s.CompanyName
    };

In this case, another where condition may be applied to the supplierDs also. As you may see the real query is not what is coded by the developer. Some kind of filters may be applied to the query at run time. We have implemented this scenario and it works as expected.But the new requirement is that the developer can specify fields from parent entity. Currently the produced expression is something like :

Expression<Func<ProductEntity, bool>> condition = p => linqMetaData.BusinessSecurityInfo.Any(bsi => bsi.Value1 == p.CategoryId || bsi.Value1 == null);

And it works fine but the problem occurs when using the parent related entity :

p.Categories.CategoryId

I know that in this sample it is easy to use 'p.CategoryId' instead of 'p.Categories.CategoryId' but some times more than one level of related entities are referenced :

p.Categories.SomeOtherRelatedEntity.OtherRelatedEntityId

I guess applying the where condition like this is the easiest way.As you said if I explicitly specify the join instead, the generated query is fine but as you have surly spent lots of time building, traversing an manipulating expression trees while you were developing your linq provider, you may know that there is no easy way for me to change the linq query at run time to use joins instead of navigation property.

Now, I hope you have a good understanding of what I want to achieve. Please let me know if any part is unclear and I'll try to provide more description.

Regards.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 06-Aug-2012 01:49:02   

I think I understand the complication on explicitly create the joins in your code. I would reformulate the first Northwind scenario into this:

// 1. get the datasource            
var dataSource = md.Product.AsQueryable();

// 2. apply the fitler expression
System.Linq.Expressions.Expression<Func<ProductEntity, bool>> securityExpression = 
    p => md.Order.Any(o => o.OrderId == p.Category.CategoryId);

dataSource = dataSource.Where(securityExpression);

// 3. make the select
var results = (from p in dataSource
                    select new
                    {
                        p.ProductId,
                        p.ProductName
                    }).ToList();

.. here there are three steps process (determining the Datasource, applying a filter, fetching results) and the one causing the problem is the second, where the aliases get confused because the formulation of the implicit join.

I don't really know whether this is fixable or not, but here is a reasonable workaround: formulate the securityExpression condition as a inner query, something like

WHERE ProductId IN
(
    SELECT ProductId FROM ...
)

The workaround code would be something like:

// 1. get the datasource
var dataSource = md.Product.AsQueryable();

// 2. apply the fitler expression
var innerQuery = from p in md.Product
                    join c in md.Category on p.CategoryId equals c.CategoryId
                    where md.Order.Any(o => o.OrderId == c.CategoryId)
                    select p.ProductId;

System.Linq.Expressions.Expression<Func<ProductEntity, bool>> securityExpression =
    p => innerQuery.Contains(p.ProductId);

dataSource = dataSource.Where(securityExpression);
            
// 3. make the select 
var selectQuery = (from p in dataSource
                    select new
                    {
                        p.ProductId,
                        p.ProductName
                    }).ToList();

... with a generated sql like:

SELECT [LPLA_1].[ProductID] AS [ProductId], [LPLA_1].[ProductName] 
FROM [Northwind].[dbo].[Products] [LPLA_1]  
WHERE 
    EXISTS (    
            SELECT [LPA_L1].[ProductId] 
            FROM 
                (
                    SELECT [LPA_L2].[ProductID] AS [ProductId] 
                    FROM 
                        ([Northwind].[dbo].[Products] [LPA_L2]  
                            INNER JOIN [Northwind].[dbo].[Categories] [LPA_L3]  
                                ON  [LPA_L2].[CategoryID] = [LPA_L3].[CategoryID]) 
                    WHERE 
                        EXISTS 
                            (SELECT [LPLA_5].[ShipCountry] 
                                FROM [Northwind].[dbo].[Orders] [LPLA_5]  
                                WHERE ( ( [LPLA_5].[OrderID] = [LPA_L3].[CategoryID])))
                            
                ) [LPA_L1] 
            WHERE [LPA_L1].[ProductId] = [LPLA_1].[ProductID]
    )
David Elizondo | LLBLGen Support Team
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 06-Aug-2012 12:09:32   

Hi,

The workaround you provided is working but if the main query contains two other joins, the generated query seems incorrect.

var results = (from p in dataSource
                join od in md.OrderDetails on p.ProductId equals od.ProductId
                join s in md.Suppliers on p.SupplierId equals s.SupplierId
                select new
                {
                    p.ProductId,
                    p.ProductName
                }).ToList();

The generated SQL would be:

SELECT 
    [LPA_L1].[ProductID] AS [ProductId], [LPA_L1].[ProductName] 
FROM 
    (
        ([Northwind].[dbo].[Products] [LPA_L1]  
        INNER JOIN [Northwind].[dbo].[Order Details] [LPA_L2]  ON  [LPA_L1].[ProductID] = [LPA_L2].[ProductID]) 
        INNER JOIN [Northwind].[dbo].[Suppliers] [LPA_L3]  ON  
        ( (  EXISTS (SELECT [LPA_L5].[ProductId] FROM (SELECT [LPA_L6].[ProductID] AS [ProductId] FROM ( [Northwind].[dbo].[Products] [LPA_L6]  INNER JOIN [Northwind].[dbo].[Categories] [LPA_L7]  ON  [LPA_L6].[CategoryID] = [LPA_L7].[CategoryID]) WHERE ( (  EXISTS (SELECT [LPLA_5].[ShipCountry] FROM [Northwind].[dbo].[Orders] [LPLA_5]  WHERE ( ( [LPLA_5].[OrderID] = [LPA_L7].[CategoryID])))))) [LPA_L5] WHERE ( [LPA_L5].[ProductId] = [LPA_L1].[ProductID]))) AND [LPA_L1].[SupplierID] = [LPA_L3].[SupplierID])
    )

As you see the where condition has been moved to the join condition.

Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 07-Aug-2012 11:36:37   

The main problem we're currently facing is to determine which relations to move outside the Any() scope.

Consider this query:

var q = from p in metaData.Product.Where(p => metaData.Order.Any(o => o.Employee.EmployeeId == p.Category.CategoryId))
        select new
        {
            p.ProductId,
            p.ProductName,
        };

If we collect the relations in the filter (which is already done) and pass them all to the outside of Any(), we have a problem as the join resulting from o.Employee.EmployeeId has to stay inside the Any() scope, but the join resulting from p.Category.CategoryId does not. As 'p' isn't part of the set of parameters of the lambda passed into Any(), one can conclude the joins shouldn't be part of the Any() scope, but this is a special case which we have to look at more carefully how to solve. I see after the second pass over the expression tree, the p.Category.CategoryId is already changed to entity expression of type CategoryEntity with a correlationrelation product - category, which is correct, however the correlation relation at that spot has to be moved outside the any clause. As it's inside a filterexpression, this is special logic we have to add to the handler of filterexpression, which kind of sucks, as it's not a concern for filterexpression, but that's not uncommon for handling linq stuff.

More in general: As you can have nested any clauses, a navigation from parameter X resulting in a correlation relation should result in a move of that correlation relation to the scope where X is defined, so to the filter expression using parameter X. In the query above, this will result in the Where lambda having a relation after it's been handled, which is precisely what we want as it's then added to the join list of the query the where is defined on.

We'll look into whether this is doable. We'll first try to fix it in v3.5 and then we'll try to backport it to v2.6. Let's hope this is simple to fix (it's sometimes is wink ).

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 07-Aug-2012 14:19:55   

Well, ain't this a lovely sight wink


SELECT [LPA_L2].[ProductID] AS [ProductId],
       [LPA_L2].[ProductName]
FROM   ( [Northwind].[dbo].[Categories] [LPA_L1]
         RIGHT JOIN [Northwind].[dbo].[Products] [LPA_L2]
             ON [LPA_L1].[CategoryID] = [LPA_L2].[CategoryID])
WHERE  ((((EXISTS
           (SELECT [LPLA_2].[ShipVia]
            FROM   [Northwind].[dbo].[Orders] [LPLA_2]
            WHERE  (([LPLA_2].[OrderID] = [LPA_L1].[CategoryID]))))))) 

Will now run the full test suit and will then (if nothing breaks) port it back to v2.6.

(edit) tests all succeed. Fixed in v3.5. Will now backport the code to v2.6 and then merge the changes upwards to v3.1. It looks simple enough to add it to v2.6 I think (as we made many changes to the linq provider in v3)

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 07-Aug-2012 14:56:36   

See attached dll (v2.6).

I ported the complete last phase all/any handler from the v3.5 codebase, which additionally fixes some other issues in all/any e.g. when negative predicates were used. This could help you as well.

It might be you'll run into other issues with this (the tests all succeed though) in v2.6, as its linq provider doesn't have the multi-navigator alias fixes we added to v3.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.LinqSupportClasses.NET35.zip 88,324 07-Aug-2012 14:56.42 Approved
Frans Bouma | Lead developer LLBLGen Pro
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 08-Aug-2012 07:42:05   

Hi,

It's working fine now, I hope it won't cause any other issue.

Thanks for your support.

arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 27-Aug-2012 13:38:22   

Hi,

I'm facing another issue.Consider the following query:

var q = from p in md.Products.Where(p => md.Orders.Any(o => o.Employees.EmployeeId == p.Categories.CategoryId))
                    select new
                    {
                        p.ProductId,
                        p.ProductName,
                    };

With the the latest binaries it's working fine. As I mentioned in my previous posts we apply some where conditions at run time to the query.We apply these expressions using Linq extension methods and we can not use the query syntax. Consider the following query:

var q = from p in md.Products.Where(p => md.Orders.Any(o => o.Employees.EmployeeId == p.Categories.CategoryId))
                    select new
                    {
                        p.ProductId,
                        p.ProductName,
                    };

 q = q.Where(p => p.ProductName.Contains("P"));

So far so good and the query works as expected. The issue is raised as we use the entity instead of anonymous type:

var q = from p in md.Products.Where(p => md.Orders.Any(o => o.Employees.EmployeeId == p.Categories.CategoryId))
                    select p;

q = q.Where(p => p.ProductName.Contains("P"));

The following exception is thrown:

An exception was caught during the execution of a retrieval query: The multi-part identifier "LPA_L1.ProductId" could not be bound.
The multi-part identifier "LPA_L1.ProductName" could not be bound.
The multi-part identifier "LPA_L1.SupplierId" could not be bound.
The multi-part identifier "LPA_L1.CategoryId" could not be bound.
The multi-part identifier "LPA_L1.QuantityPerUnit" could not be bound.
The multi-part identifier "LPA_L1.UnitPrice" could not be bound.
The multi-part identifier "LPA_L1.UnitsInStock" could not be bound.
The multi-part identifier "LPA_L1.UnitsOnOrder" could not be bound.
The multi-part identifier "LPA_L1.ReorderLevel" could not be bound.
The multi-part identifier "LPA_L1.Discontinued" could not be bound.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.

The generated query is :

exec sp_executesql N'SELECT DISTINCT [LPA_L1].[ProductId], [LPA_L1].[ProductName], [LPA_L1].[SupplierId], [LPA_L1].[CategoryId], [LPA_L1].[QuantityPerUnit], [LPA_L1].[UnitPrice], [LPA_L1].[UnitsInStock], [LPA_L1].[UnitsOnOrder], [LPA_L1].[ReorderLevel], [LPA_L1].[Discontinued] FROM (SELECT [LPA_L1].[ProductId], [LPA_L1].[ProductName], [LPA_L1].[SupplierId], [LPA_L1].[CategoryId], [LPA_L1].[QuantityPerUnit], [LPA_L1].[UnitPrice], [LPA_L1].[UnitsInStock], [LPA_L1].[UnitsOnOrder], [LPA_L1].[ReorderLevel], [LPA_L1].[Discontinued] FROM ( [Northwind].[dbo].[Categories] [LPA_L2]  RIGHT JOIN [Northwind].[dbo].[Products] [LPA_L3]  ON  [LPA_L2].[CategoryID]=[LPA_L3].[CategoryID]) WHERE (  EXISTS (SELECT [LPA_L5].[ShipCountry] FROM ( [Northwind].[dbo].[Employees] [LPA_L4]  RIGHT JOIN [Northwind].[dbo].[Orders] [LPA_L5]  ON  [LPA_L4].[EmployeeID]=[LPA_L5].[EmployeeID]) WHERE ( ( [LPA_L4].[EmployeeID] = [LPA_L2].[CategoryID]))))) [LPA_L1] WHERE ( ( ( ( [LPA_L1].[ProductName] LIKE @ProductName1))))',N'@ProductName1 nvarchar(3)',@ProductName1=N'%P%'

It seems the the wrong alias is used.

Thank you in advance.

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 27-Aug-2012 21:34:04   

I'm confused is this on 2.6 or 3.x?

arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 28-Aug-2012 05:23:37   

Walaa wrote:

I'm confused is this on 2.6 or 3.x?

This is on 2.6

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 28-Aug-2012 08:09:22   

Please confirm your runtime library version. The latest one is 2.6.11.1105. With that RTL I'm getting a different exception than yours:

var adapter = new DataAccessAdapter();
var md = new LinqMetaData(adapter);
var q = from p in md.Product
            .Where(p => md.Order
                .Any(o => o.Employee.EmployeeId == p.Category.CategoryId))
        select new
        {
            p.ProductId,
            p.ProductName,
        };

q = q.Where(p => p.ProductName.Contains("P"));
var results = q.ToList();

**Exception **(stack trace attached) SD.LLBLGen.Pro.ORMSupportClasses.ORMRelationException: Relation at index 1 doesn't contain an entity already added to the FROM clause. Bad alias?

BTW, this works on v3.5.

Attachments
Filename File size Added on Approval
stacktrace.txt 12,300 28-Aug-2012 08:09.33 Approved
David Elizondo | LLBLGen Support Team
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 28-Aug-2012 11:18:43   

I'm using the following binaries:

SD.LLBLGen.Pro.DQE.SqlServer.NET20 (2.6.10.0917) SD.LLBLGen.Pro.LinqSupportClasses.NET35 (2.6.12.0807) SD.LLBLGen.Pro.ORMSupportClasses.NET20 (2.6.11.1105)

It seems you are getting another exception and I don't know why. Mine seems related to generation of a wrong alias.

Thanks.

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 28-Aug-2012 19:50:02   

Could you please attach a repro solution.

arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 29-Aug-2012 07:28:35   

A sample is attached. dll files have been removed to reduce the package size.

Attachments
Filename File size Added on Approval
LLBLNorthwind.rar 156,677 29-Aug-2012 07:33.08 Approved
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 29-Aug-2012 07:51:19   

I reproduced it now with the code I posted. I wasn't using the fixed dll Frans posted above. The exception details, etc are the same.

We will look into this.

BTW, since you are using a lot of LLBL2LINQ, it would be a good idea to upgrade to v3.5 which has a better aliasing support for Linq. You also could opt to using QuerySpec (on v3.5).

David Elizondo | LLBLGen Support Team
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 29-Aug-2012 08:05:12   

I would be happy to upgrade to the latest version, but we are at a phase in our project which is really difficult to convince the management to take the risk of any possible upgrading issue. The upgrading costs would be also important for them wink

Wish you luck.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 29-Aug-2012 10:54:36   

As this issue is with v2.6, it's not said it can be fixed. As it works in v3.5, it's likely it's caused by an issue which is fixed by a refactor in the linq provider and additional code we added in v3. I'll look into it, but I can't promise this is fixable.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 29-Aug-2012 12:03:10   

v2.6 query:


SELECT DISTINCT [LPA_L1].[ProductId],
                [LPA_L1].[ProductName],
                [LPA_L1].[SupplierId],
                [LPA_L1].[CategoryId],
                [LPA_L1].[QuantityPerUnit],
                [LPA_L1].[UnitPrice],
                [LPA_L1].[UnitsInStock],
                [LPA_L1].[UnitsOnOrder],
                [LPA_L1].[ReorderLevel],
                [LPA_L1].[Discontinued]
FROM   (SELECT [LPA_L1].[ProductId],
               [LPA_L1].[ProductName],
               [LPA_L1].[SupplierId],
               [LPA_L1].[CategoryId],
               [LPA_L1].[QuantityPerUnit],
               [LPA_L1].[UnitPrice],
               [LPA_L1].[UnitsInStock],
               [LPA_L1].[UnitsOnOrder],
               [LPA_L1].[ReorderLevel],
               [LPA_L1].[Discontinued]
        FROM   ( [Northwind].[dbo].[Categories] [LPA_L2]
                 RIGHT JOIN [Northwind].[dbo].[Products] [LPA_L3]
                     ON [LPA_L2].[CategoryID] = [LPA_L3].[CategoryID])
        WHERE  (EXISTS
                (SELECT [LPLA_2].[ShipCountry]
                 FROM   [Northwind].[dbo].[Orders] [LPLA_2]
                 WHERE  (([LPLA_2].[OrderID] = [LPA_L2].[CategoryID]))))) [LPA_L1]
WHERE  (((([LPA_L1].[ProductName] LIKE @ProductName1)))) 

v3.5 query:


SELECT DISTINCT [LPA_L2].[CategoryID] AS [CategoryId],
                [LPA_L2].[Discontinued],
                [LPA_L2].[ProductID]  AS [ProductId],
                [LPA_L2].[ProductName],
                [LPA_L2].[QuantityPerUnit],
                [LPA_L2].[ReorderLevel],
                [LPA_L2].[SupplierID] AS [SupplierId],
                [LPA_L2].[UnitPrice],
                [LPA_L2].[UnitsInStock],
                [LPA_L2].[UnitsOnOrder]
FROM   ( [Northwind].[dbo].[Categories] [LPA_L1]
         RIGHT JOIN [Northwind].[dbo].[Products] [LPA_L2]
             ON [LPA_L1].[CategoryID] = [LPA_L2].[CategoryID])
WHERE  ((((EXISTS
           (SELECT [LPLA_2].[ShipVia]
            FROM   [Northwind].[dbo].[Orders] [LPLA_2]
            WHERE  (([LPLA_2].[OrderID] = [LPA_L1].[CategoryID]))))
      AND ([LPA_L2].[ProductName] LIKE @p1)))) 

As you can see, the main difference is that in v3.5 the additional where clause is merged properly, while it's not merged in v2.6. I think (but have to check) that this is caused by the additional code we added to v3's linq provider regarding query merges, i.e.: it's now more clever what it should do. (as linq is a chain of wrapping sequence operators, each one results in a query around the former's result, which then have to be merged properly into a single SQL query).

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39887
Joined: 17-Aug-2003
# Posted on: 29-Aug-2012 13:10:51   

After debugging v3.5 and v2.6 side-by-side, I found that in v2.6 it set a flag to require a projection, while in v3.5 it didn't, resulting in a merge in v3.5 and not in v2.6. It was just 1 line of code, removing it made it work. All tests pass, but with linq you'll never know. It's a problematic issue, although it makes sense the line of code shouldn't be there.

I've attached the new linq provider fix. I'd also grab the latest orm support classes dll fix which closes a connection leak in Count() queries on linq, found here: http://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID=119882&ThreadID=20219

We'll release a new full build of v2.6 once you have confirmed this dll fixes the issue.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.LinqSupportClasses.NET35.zip 89,263 29-Aug-2012 13:11.06 Approved
Frans Bouma | Lead developer LLBLGen Pro
arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 29-Aug-2012 13:20:48   

Ok, I'll check this out. Thanks

arash
User
Posts: 54
Joined: 16-Dec-2008
# Posted on: 29-Aug-2012 13:51:40   

I tested it with patches you provided and it's working fine.simple_smile

Thank you very much for your support.

1  /  2