- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Yet another LINQ error
I am writing a linq query and once again incorrect aliases are being generated. I have updated to the 17th july runtimes. Adapter templates.
Here is the trace:
Initial expression to process: value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2
1[Shivam.SCP.DAL.EntityClasses.TicketEntity]).Where(t => (t.IsActive = True)).WithPath(value(SD.LLBLGen.Pro.LinqSupportClasses.IPathEdge[])).GroupJoin(value(SD.LLBLGen.Pro.LinqSupportClasses.DataSource2
1[Shivam.SCP.DAL.EntityClasses.UserEntity]), t => t.AssignedToId, u => Convert(u.UserId), (t, tg) => new <>f__AnonymousType282(t = t, tg = tg)).SelectMany(<>h__TransparentIdentifier7 => <>h__TransparentIdentifier7.tg.DefaultIfEmpty(), (<>h__TransparentIdentifier7, g) => new <>f__AnonymousType2a
2(<>h__TransparentIdentifier7 = <>h__TransparentIdentifier7, g = g)).OrderByDescending(<>h__TransparentIdentifier8 => <>h__TransparentIdentifier8.g.FirstName).ThenByDescending(<>h__TransparentIdentifier8 => <>h__TransparentIdentifier8.g.Surname).Select(<>h__TransparentIdentifier8 => <>h__TransparentIdentifier8.<>h__TransparentIdentifier7.t).Count() Method Enter: CreatePagingSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSubQuery Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSubQuery Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Generated Sql query: Query: SELECT [LPA_L2].[TicketId], [LPA_L2].[ProjectId], [LPA_L2].[AssignedToId], [LPA_L2].[CreatedById], [LPA_L2].[PriorityId], [LPA_L2].[StatusId], [LPA_L2].[CreatedOn], [LPA_L2].[TicketNo], [LPA_L2].[Subject], [LPA_L2].[IsActive] FROM [ClientPortal].[dbo].[Ticket] [LPLA_1] WHERE ( ( ( [LPA_L2].[IsActive] = @IsActive1))) Parameter: @IsActive1 : Boolean. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: True.Method Exit: CreateSelectDQ Method Exit: CreateSubQuery Generated Sql query: Query: SELECT [LPA_L2].[TicketId], [LPA_L2].[ProjectId], [LPA_L2].[AssignedToId], [LPA_L2].[CreatedById], [LPA_L2].[PriorityId], [LPA_L2].[StatusId], [LPA_L2].[CreatedOn], [LPA_L2].[TicketNo], [LPA_L2].[Subject], [LPA_L2].[IsActive] FROM ( (SELECT [LPA_L2].[TicketId], [LPA_L2].[ProjectId], [LPA_L2].[AssignedToId], [LPA_L2].[CreatedById], [LPA_L2].[PriorityId], [LPA_L2].[StatusId], [LPA_L2].[CreatedOn], [LPA_L2].[TicketNo], [LPA_L2].[Subject], [LPA_L2].[IsActive] FROM [ClientPortal].[dbo].[Ticket] [LPLA_1] WHERE ( ( ( [LPA_L2].[IsActive] = @IsActive1)))) [LPA_L2] LEFT JOIN [ClientPortal].[dbo].[User] [LPA_L3] ON [LPA_L2].[AssignedToId] = [LPA_L3].[UserId]) Parameter: @IsActive1 : Boolean. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: True.
Method Exit: CreateSelectDQ Method Exit: CreateSubQuery Generated Sql query: Query: SELECT DISTINCT TOP 1 COUNT(*) AS [LPAV_] FROM (SELECT [LPA_L2].[TicketId], [LPA_L2].[ProjectId], [LPA_L2].[AssignedToId], [LPA_L2].[CreatedById], [LPA_L2].[PriorityId], [LPA_L2].[StatusId], [LPA_L2].[CreatedOn], [LPA_L2].[TicketNo], [LPA_L2].[Subject], [LPA_L2].[IsActive] FROM ( (SELECT [LPA_L2].[TicketId], [LPA_L2].[ProjectId], [LPA_L2].[AssignedToId], [LPA_L2].[CreatedById], [LPA_L2].[PriorityId], [LPA_L2].[StatusId], [LPA_L2].[CreatedOn], [LPA_L2].[TicketNo], [LPA_L2].[Subject], [LPA_L2].[IsActive] FROM [ClientPortal].[dbo].[Ticket] [LPLA_1] WHERE ( ( ( [LPA_L2].[IsActive] = @IsActive1)))) [LPA_L2] LEFT JOIN [ClientPortal].[dbo].[User] [LPA_L3] ON [LPA_L2].[AssignedToId] = [LPA_L3].[UserId])) [LPA_L1] Parameter: @IsActive1 : Boolean. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: True.
Method Exit: CreateSelectDQ Method Exit: CreatePagingSelectDQ: no paging. A first chance exception of type 'SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException' occurred in SD.LLBLGen.Pro.ORMSupportClasses.NET20.DLL
Here is the exception message:
An exception was caught during the execution of a retrieval query: The multi-part identifier "LPA_L2.IsActive" could not be bound. The multi-part identifier "LPA_L2.TicketId" could not be bound. The multi-part identifier "LPA_L2.ProjectId" could not be bound. The multi-part identifier "LPA_L2.AssignedToId" could not be bound. The multi-part identifier "LPA_L2.CreatedById" could not be bound. The multi-part identifier "LPA_L2.PriorityId" could not be bound. The multi-part identifier "LPA_L2.StatusId" could not be bound. The multi-part identifier "LPA_L2.CreatedOn" could not be bound. The multi-part identifier "LPA_L2.TicketNo" could not be bound. The multi-part identifier "LPA_L2.Subject" could not be bound. The multi-part identifier "LPA_L2.IsActive" could not be bound.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.
Here is the stack trace
at SD.LLBLGen.Pro.ORMSupportClasses.RetrievalQuery.Execute(CommandBehavior behavior) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchDataReader(IRetrievalQuery queryToExecute, CommandBehavior readerBehavior) at Shivam.SCP.DAL.DataAccessAdapter.FetchDataReader(IRetrievalQuery queryToExecute, CommandBehavior readerBehavior) in D:\Projects\Shivam Client Portal\Main\Source\SCP v1.0\Shivam.SCP.DAL\DataAccessAdapter.cs:line 292 at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List
1 valueProjectors, IGeneralDataProjector projector, IRetrievalQuery queryToExecute, Dictionary
2 typeConvertersToRun) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.Count[TSource](IQueryable
1 source) at Shivam.SCP.BL.Util.Ticket.Find(Int32 userId, Int32 projectId, Int32 statusId, PageRequestInfo info) in D:\Projects\Shivam Client Portal\Main\Source\SCP v1.0\Shivam.SCP.BL\Util\Ticket.cs:line 79 at Shivam.SCP.WebUI.Controllers.TicketController.GetTicketList() in D:\Projects\Shivam Client Portal\Main\Source\SCP v1.0\Shivam.SCP.WebUI.v2.0\Controllers\TicketController.cs:line 96
Here is the code that was executed (ex is thrown on the call to Count()): I have removed code that was not execute due to the arguments provided.
In the switch statement the "assignedto" switch is executed. Error occurs regardless of the sort direction.
public static IQueryable<TicketEntity> Find(int userId, int projectId, int statusId, PageRequestInfo info)
{
using (TransactionManager.DataAccessAdapter)
{
var ticketList = TransactionManager.Meta.Ticket.Where(t => t.IsActive == true);
ticketList = ticketList.WithPath(path => path.Prefetch<PriorityEntity>(p => p.PriorityUsingPriorityId));
IOrderedQueryable<TicketEntity> finalTicket;
switch (info.SortBy.ToLower())
{
case "subject":
finalTicket = info.SortDirection == SortOperator.Ascending ?
ticketList.OrderBy(p => p.Subject) :
ticketList.OrderByDescending(p => p.Subject);
break;
case "assignedto":
finalTicket = info.SortDirection == SortOperator.Ascending ?
(IOrderedQueryable<TicketEntity>)from t in ticketList join u in TransactionManager.Meta.User on t.AssignedToId equals u.UserId into tg from g in tg.DefaultIfEmpty() orderby g.FirstName, g.Surname select t :
(IOrderedQueryable<TicketEntity>)from t in ticketList join u in TransactionManager.Meta.User on t.AssignedToId equals u.UserId into tg from g in tg.DefaultIfEmpty() orderby g.FirstName descending, g.Surname descending select t;
break;
default: //name
finalTicket = info.SortDirection == SortOperator.Ascending ?
ticketList.OrderBy(p => p.CreatedOn) :
ticketList.OrderByDescending(p => p.CreatedOn);
break;
}
if (info.PageIndex == 0)
info.TotalItems = finalTicket.Count();//ex is thrown here
return finalTicket.TakePage(info.PageIndex + 1, info.PageSize);
}
}
There is no entity inheritence in this scenario.
The Tables: I've commented the irrevelent relationships.
Ticket table:
CREATE TABLE [dbo].[Ticket](
[TicketId] [int] IDENTITY(1,1) NOT NULL,
[ProjectId] [int] NOT NULL,
[AssignedToId] [int] NULL,
[CreatedById] [int] NOT NULL,
[PriorityId] [int] NOT NULL,
[StatusId] [int] NOT NULL,
[CreatedOn] [dbo].[Date] NOT NULL,
[TicketNo] [nvarchar](32) NULL,
[Subject] [nvarchar](256) NOT NULL,
[IsActive] [dbo].[Flag] NOT NULL,
CONSTRAINT [PK_Ticket] PRIMARY KEY CLUSTERED
(
[TicketId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
--ALTER TABLE [dbo].[Ticket] WITH CHECK ADD CONSTRAINT [FK_Ticket_Priority] FOREIGN KEY([PriorityId])
--REFERENCES [dbo].[Priority] ([PriorityId])
GO
--ALTER TABLE [dbo].[Ticket] CHECK CONSTRAINT [FK_Ticket_Priority]
GO
--ALTER TABLE [dbo].[Ticket] WITH CHECK ADD CONSTRAINT [FK_Ticket_Project] FOREIGN KEY([ProjectId])
--REFERENCES [dbo].[Project] ([ProjectId])
GO
--ALTER TABLE [dbo].[Ticket] CHECK CONSTRAINT [FK_Ticket_Project]
GO
--ALTER TABLE [dbo].[Ticket] WITH CHECK ADD CONSTRAINT [FK_Ticket_Status] FOREIGN KEY([StatusId])
--REFERENCES [dbo].[Status] ([StatusId])
GO
--ALTER TABLE [dbo].[Ticket] CHECK CONSTRAINT [FK_Ticket_Status]
GO
ALTER TABLE [dbo].[Ticket] WITH CHECK ADD CONSTRAINT [FK_Ticket_User] FOREIGN KEY([AssignedToId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[Ticket] CHECK CONSTRAINT [FK_Ticket_User]
GO
--ALTER TABLE [dbo].[Ticket] WITH CHECK ADD CONSTRAINT [FK_Ticket_User1] FOREIGN KEY([CreatedById])
--REFERENCES [dbo].[User] ([UserId])
GO
--ALTER TABLE [dbo].[Ticket] CHECK CONSTRAINT [FK_Ticket_User1]
User table:
CREATE TABLE [dbo].[User](
[UserId] [int] IDENTITY(1,1) NOT NULL,
[Username] [dbo].[Name] NOT NULL,
[Password] [nvarchar](64) NOT NULL,
[FirstName] [dbo].[Name] NOT NULL,
[Surname] [dbo].[Name] NOT NULL,
[Email] [dbo].[Email] NOT NULL,
[IsActive] [dbo].[Flag] NOT NULL CONSTRAINT [DF_User_IsActive] DEFAULT ((0)),
[LastLogin] [dbo].[Date] NULL,
[UpdatedOn] [dbo].[Date] NOT NULL,
[UpdatedBy] [dbo].[CurrentUser] NOT NULL,
[CreatedOn] [dbo].[Date] NOT NULL,
[CreatedBy] [dbo].[CurrentUser] NOT NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_User] UNIQUE NONCLUSTERED
(
[Username] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
...the UDDTs i suppose u'll need
CREATE TYPE [dbo].[CurrentUser] FROM [nvarchar](64) NOT NULL
GO
CREATE TYPE [dbo].[Date] FROM [smalldatetime] NOT NULL
GO
USE [ClientPortal]
GO
CREATE TYPE [dbo].[Email] FROM [nvarchar](128) NOT NULL
GO
CREATE TYPE [dbo].[Flag] FROM [bit] NOT NULL
GO
CREATE TYPE [dbo].[Name] FROM [nvarchar](128) NOT NULL
defaults
CREATE DEFAULT [dbo].[CurrentDate] AS GetDate()
GO
CREATE DEFAULT [dbo].[CurrentUser] AS User_name()
Will look into it. Please next time, cut the query to a query which illustrates the problem. I've now wade through all the code to find what's really going on. Also as you specify a prefetch path, check whether this is influencing the problem. If not, remove it so we don't have to worry about that. debugging expression trees which are very big is almost impossible, so the smaller the query the better. Also, you use defaultifempty() which has no meaning, yet this might be the cause (so we know where to look). If you remove it, does it influence the error? That's information we really want to know up front as it can save us a lot of time.
I thought that was how a left join was done in LINQ... wasn't working off an LLBL specific sample.
Ok leave it for now then, tomorrow morning I will try and drop the withpath and see what happens and play with removing DefaultIfEmpty and get back to you.
Today, I learn't the value of tracing
When I use:
LinqMetaData metaData = new LinqMetaData(adapter);
var q1 = metaData.Order.Where(o => o.EmployeeId == 2);
var q = from o in q1
join c in metaData.Customer on o.CustomerId equals c.CustomerId into oc
from x in oc.DefaultIfEmpty()
orderby x.CompanyName ascending
select o;
int count = q.Count();
I get:
SELECT DISTINCT TOP 1 COUNT(*) AS [LPAV_]
FROM
(
SELECT [LPA_L2].[OrderID] AS [OrderId], [LPA_L2].[CustomerID] AS [CustomerId], [LPA_L2].[EmployeeID] AS [EmployeeId],
[LPA_L2].[OrderDate], [LPA_L2].[RequiredDate], [LPA_L2].[ShippedDate], [LPA_L2].[ShipVia], [LPA_L2].[Freight],
[LPA_L2].[ShipName], [LPA_L2].[ShipAddress], [LPA_L2].[ShipCity], [LPA_L2].[ShipRegion],
[LPA_L2].[ShipPostalCode], [LPA_L2].[ShipCountry]
FROM ([Northwind].[dbo].[Orders] [LPA_L2] LEFT JOIN [Northwind].[dbo].[Customers] [LPA_L3]
ON [LPA_L2].[CustomerID] = [LPA_L3].[CustomerID])
WHERE ( ( ( ( [LPA_L2].[EmployeeID] = @EmployeeId1))))
) [LPA_L1]
Which is what I would have expected: the provider has a special visitor to remove the where clause in the join to avoid a derived table (as it otherwise doesn't know which side to refer to). Your sql query has that where clause still inside the join within a derived table.
However when I add the prefetch path node as you did, I can reproduce it:
LinqMetaData metaData = new LinqMetaData(adapter);
var q1 = metaData.Order.Where(o => o.EmployeeId == 2);
q1 = q1.WithPath(path => path.Prefetch<OrderDetailEntity>(p => p.OrderDetails));
var q = from o in q1
join c in metaData.Customer on o.CustomerId equals c.CustomerId into oc
from x in oc.DefaultIfEmpty()
orderby x.CompanyName ascending
select o;
int count = q.Count();
produces:
SELECT DISTINCT TOP 1 COUNT(*) AS [LPAV_] FROM
(
SELECT [LPA_L2].[OrderId], [LPA_L2].[CustomerId], [LPA_L2].[EmployeeId], [LPA_L2].[OrderDate],
[LPA_L2].[RequiredDate], [LPA_L2].[ShippedDate], [LPA_L2].[ShipVia], [LPA_L2].[Freight],
[LPA_L2].[ShipName], [LPA_L2].[ShipAddress], [LPA_L2].[ShipCity], [LPA_L2].[ShipRegion],
[LPA_L2].[ShipPostalCode], [LPA_L2].[ShipCountry]
FROM ((
SELECT [LPA_L2].[OrderId], [LPA_L2].[CustomerId], [LPA_L2].[EmployeeId], [LPA_L2].[OrderDate],
[LPA_L2].[RequiredDate], [LPA_L2].[ShippedDate], [LPA_L2].[ShipVia], [LPA_L2].[Freight],
[LPA_L2].[ShipName], [LPA_L2].[ShipAddress], [LPA_L2].[ShipCity], [LPA_L2].[ShipRegion],
[LPA_L2].[ShipPostalCode], [LPA_L2].[ShipCountry]
FROM [Northwind].[dbo].[Orders] [LPLA_1]
WHERE ((([LPA_L2].[EmployeeId] = @EmployeeId1)))
) [LPA_L2] LEFT JOIN [Northwind].[dbo].[Customers] [LPA_L3]
ON [LPA_L2].[CustomerId] = [LPA_L3].[CustomerID]
)
) [LPA_L1]
And my guess is that the visitor to remove the where won't properly do its job as the prefetch path is embedded in the middle of the join.
When I move the prefetch path to a place OUTSIDE the join (where it should be, it's something you should add at the last minute to the very OUTSIDE of the query) ->
LinqMetaData metaData = new LinqMetaData(adapter);
var q1 = metaData.Order.Where(o => o.EmployeeId == 2);
var q = from o in q1
join c in metaData.Customer on o.CustomerId equals c.CustomerId into oc
from x in oc.DefaultIfEmpty()
orderby x.CompanyName ascending
select o;
q = q.WithPath(path => path.Prefetch<OrderDetailEntity>(p => p.OrderDetails));
int count = q.Count();
it indeed produces proper SQL as the where clause is moved outside the join side properly:
SELECT DISTINCT TOP 1 COUNT(*) AS [LPAV_]
FROM
(
SELECT [LPA_L2].[OrderID] AS [OrderId], [LPA_L2].[CustomerID] AS [CustomerId], [LPA_L2].[EmployeeID] AS [EmployeeId],
[LPA_L2].[OrderDate], [LPA_L2].[RequiredDate], [LPA_L2].[ShippedDate], [LPA_L2].[ShipVia], [LPA_L2].[Freight],
[LPA_L2].[ShipName], [LPA_L2].[ShipAddress], [LPA_L2].[ShipCity], [LPA_L2].[ShipRegion],
[LPA_L2].[ShipPostalCode], [LPA_L2].[ShipCountry]
FROM ([Northwind].[dbo].[Orders] [LPA_L2] LEFT JOIN [Northwind].[dbo].[Customers] [LPA_L3]
ON [LPA_L2].[CustomerID] = [LPA_L3].[CustomerID])
WHERE ( ( ( ( ( [LPA_L2].[EmployeeID] = @EmployeeId1)))))
) [LPA_L1]
Could you try for me please to move the Withpath to the bottom of the query? (as you're fetching entities anyway, it's the right place to put the statement, not at the beginning)
Hi Frans,
moving the withpath got rid if the exception however my result set is not being ordered.
If I call:
finalTicket.TakePage(info.PageIndex + 1, info.PageSize).ToList();
like that I get the expected SQL. However... if I return finalTicket and then from it run:
(from t in ticket
select new
{
t.TicketNo,
t.Subject,
createdDate = t.CreatedOn.ToShortDateString(),
assignTo = "Fred",//t.UserUsingAssignedToId.FirstName + " " + t.UserUsingAssignedToId.Surname,
t.StatusId,
t.TicketId,
Status = t.StatusUsingStatusId.Name
}).ToList()
Things go a bit awry. I've noticed that referencing related entities automatically creates joins, that's awesome! But the ORDER BY goes out the window...
I changed my switch to:
case "assignedto":
finalTicket = info.SortDirection == SortOperator.Ascending ?
ticketList.OrderBy(p => p.UserUsingAssignedToId.FirstName).OrderBy(p => p.UserUsingAssignedToId.Surname) :
ticketList.OrderByDescending(p => p.UserUsingAssignedToId.FirstName).OrderByDescending(p => p.UserUsingAssignedToId.Surname);
break;
and it's working! I guess LLBL figures out what type of join to do based on whether AssignedToId is null or not (i still get unassigned tickets in the resultset). This is awesome, I had no idea LINQ had these sort of smarts inbuilt, I can definately cut down on the amount of prefetching I am doing from now on.
This means I can do where clauses on joined tables (i'm assuming) too. OMG that was one of my main complaints about linq because I thought u had to put the where clause in the original LINQ statement. But now I can just go ticket.Where(p.StatusUsingStatusId.Name =="Closed") and llbl will do the rest (again, assuming). Sweet!
worldspawn wrote:
I changed my switch to:
case "assignedto": finalTicket = info.SortDirection == SortOperator.Ascending ? ticketList.OrderBy(p => p.UserUsingAssignedToId.FirstName).OrderBy(p => p.UserUsingAssignedToId.Surname) : ticketList.OrderByDescending(p => p.UserUsingAssignedToId.FirstName).OrderByDescending(p => p.UserUsingAssignedToId.Surname); break;
and it's working! I guess LLBL figures out what type of join to do based on whether AssignedToId is null or not (i still get unassigned tickets in the resultset). This is awesome, I had no idea LINQ had these sort of smarts inbuilt, I can definately cut down on the amount of prefetching I am doing from now on.
![]()
Yes, if the FK field is nullable, it automatically creates a left join
This means I can do where clauses on joined tables (i'm assuming) too. OMG that was one of my main complaints about linq because I thought u had to put the where clause in the original LINQ statement. But now I can just go ticket.Where(p.StatusUsingStatusId.Name =="Closed") and llbl will do the rest (again, assuming). Sweet!
Glad it's working. Everything is now OK or are there still things left to look into?
Glad it's working. Everything is now OK or are there still things left to look into?
No that's it. All is well, thanks