Discriminator filter missing when Entity Instance in projection

Posts   
 
    
TomDog
User
Posts: 620
Joined: 25-Oct-2005
# Posted on: 12-Nov-2012 10:29:08   

When projecting a query on a entity in TargetPerEntityHierarchy the Discriminator filter is not included when the query is projected with Entity Instance onto another type.

e.g. Adventure works DB with SalesOrderHistory as a subtype of TransactionHistory (Discriminator column TransactionType of S). Code examples in LINQPad.

from t in SalesOrderHistory
select new {t}

produces this SQL with no Discriminator filter

SELECT [LPLA_1].[ActualCost]           AS [F4_0],
       [LPLA_1].[ModifiedDate]       AS [F4_1],
       [LPLA_1].[ProductID]         AS [F4_2],
       [LPLA_1].[Quantity]           AS [F4_3],
       [LPLA_1].[ReferenceOrderID]   AS [F4_4],
       [LPLA_1].[ReferenceOrderLineID] AS [F4_5],
       [LPLA_1].[TransactionDate]     AS [F4_6],
       [LPLA_1].[TransactionID]     AS [F4_7],
       [LPLA_1].[TransactionType]     AS [F4_8]
FROM   [AdventureWorks].[Production].[TransactionHistory] [LPLA_1]

while

SalesOrderHistory

produces

SELECT [AdventureWorks].[Production].[TransactionHistory].[ActualCost]         AS [F4_0],
       [AdventureWorks].[Production].[TransactionHistory].[ModifiedDate]         AS [F4_1],
       [AdventureWorks].[Production].[TransactionHistory].[ProductID]           AS [F4_2],
       [AdventureWorks].[Production].[TransactionHistory].[Quantity]             AS [F4_3],
       [AdventureWorks].[Production].[TransactionHistory].[ReferenceOrderID]     AS [F4_4],
       [AdventureWorks].[Production].[TransactionHistory].[ReferenceOrderLineID] AS [F4_5],
       [AdventureWorks].[Production].[TransactionHistory].[TransactionDate]   AS [F4_6],
       [AdventureWorks].[Production].[TransactionHistory].[TransactionID]       AS [F4_7],
       [AdventureWorks].[Production].[TransactionHistory].[TransactionType]   AS [F4_8]
FROM   [AdventureWorks].[Production].[TransactionHistory]
WHERE  (([AdventureWorks].[Production].[TransactionHistory].[TransactionType] = 'S' /* @p1 */
      OR [AdventureWorks].[Production].[TransactionHistory].[TransactionType] IS NULL))

with correct Discriminator filter.

Happens with both Template groups. SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll 3.5.12.1026 SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll 3.5.12.1004 Project file: AW.llblgenproj-http://rapiddevbookcode.codeplex.com/SourceControl/changeset/view/100384#1935429

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39738
Joined: 17-Aug-2003
# Posted on: 12-Nov-2012 12:13:42   

We'll look into it, but it might be this isn't doable as the entity projection is done at a point where there's no knowledge whether a type filter has been added or not.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39738
Joined: 17-Aug-2003
# Posted on: 13-Nov-2012 11:04:48   

Reproduced: crashes the engine as the factory used might not be able to produce the entities requested. Looking into it. Same occurs with TPE inheritance, it just doesn't add a typefilter.

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 620
Joined: 25-Oct-2005
# Posted on: 13-Nov-2012 11:26:24   

Count after projection also missing type filter

(from t in SalesOrderHistory
select new {t.TransactionID}).Count()
SELECT TOP(@p2) COUNT(*) AS [LPAV_]
FROM   (SELECT [LPLA_1].[TransactionID]
        FROM   [AdventureWorks].[Production].[TransactionHistory] [LPLA_1]) [LPA_L1] 
Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39738
Joined: 17-Aug-2003
# Posted on: 13-Nov-2012 11:43:30   

ok, here's the reason this doesn't auto-add a type filter. It doesn't check whether the fields are from 1 single entity in a custom projection, it simply checks whether the fields are from subtypes and it then makes sure the subtypes are reachable in the SQL. It does add type filters when fetching a normal entity query because it then knows it's for that entity type.

The reason this isn't done automatically is that when 1 or more fields in the projection are from another entity, a type filter makes the returned set also wrong. Say you have a projection with 2 fields from Clerk and 3 from Manager. These two types are siblings, both are subtypes of Employee. If I add auto-type filters, the query will never result in a row, as the data can't be in both tables at the same time, as the types are siblings. So the type filters are not added automatically, but are explicit.

The query you supplied, might look like an entity fetch, but that's artificial: for the runtime it's a custom projection, the same as when you'd add 3 fields from the subtype to a new anonymous type.

There is room for some improvement here: it could verify whether all fields in the projection are from a single entity type. If so, and that type is a subtype, an entity filter should be added. The question now is: will that break applications or not. For some it might, for others it might not. I can't add this to the current codebase, even though it might look like a simple adjustment.

We'll postpone this addition to v4 and only add it if we have studied the impact enough. When doing projections on inheritance types, a type filter therefore is recommended. (Cast<T> will do, so:

from t in SalesOrderHistory.Cast<SalesOrderHistoryEntity>()
select new {t}

fixes it. Again, this looks redundant, but for the runtime it has little choice at the moment than to not append the type filter.

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 620
Joined: 25-Oct-2005
# Posted on: 14-Nov-2012 11:35:53   

Otis wrote:

There is room for some improvement here: it could verify whether all fields in the projection are from a single entity type. If so, and that type is a subtype, an entity filter should be added. The question now is: will that break applications or not. For some it might, for others it might not. I can't add this to the current codebase, even though it might look like a simple adjustment.

We'll postpone this addition to v4 and only add it if we have studied the impact enough. When doing projections on inheritance types, a type filter therefore is recommended. .

How about a switch to turn type filters on projections on or off?

but failing that I could do something like this I guess

  partial class LinqMetaData
  {
    public IQueryable<PurchaseOrderHistoryEntity> PurchaseOrderHistoryTs
    {
      get { return PurchaseOrderHistory.Cast<PurchaseOrderHistoryEntity>(); }
    }

    public IQueryable<SalesOrderHistoryEntity> SalesOrderHistoryTs
    {
      get { return SalesOrderHistory.Cast<SalesOrderHistoryEntity>(); }
    }

    public IQueryable<WorkOrderHistoryEntity> WorkOrderHistoryTs
    {
      get { return WorkOrderHistory.Cast<WorkOrderHistoryEntity>(); }
    }
  }

and always use the Typesafe(Ts) properties rather than the real ones.

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39738
Joined: 17-Aug-2003
# Posted on: 15-Nov-2012 10:27:20   

TomDog wrote:

Otis wrote:

There is room for some improvement here: it could verify whether all fields in the projection are from a single entity type. If so, and that type is a subtype, an entity filter should be added. The question now is: will that break applications or not. For some it might, for others it might not. I can't add this to the current codebase, even though it might look like a simple adjustment.

We'll postpone this addition to v4 and only add it if we have studied the impact enough. When doing projections on inheritance types, a type filter therefore is recommended. .

How about a switch to turn type filters on projections on or off?

That will be obsolete in v4 and will stick around forever.

but failing that I could do something like this I guess

  partial class LinqMetaData
  {
    public IQueryable<PurchaseOrderHistoryEntity> PurchaseOrderHistoryTs
    {
      get { return PurchaseOrderHistory.Cast<PurchaseOrderHistoryEntity>(); }
    }

    public IQueryable<SalesOrderHistoryEntity> SalesOrderHistoryTs
    {
      get { return SalesOrderHistory.Cast<SalesOrderHistoryEntity>(); }
    }

    public IQueryable<WorkOrderHistoryEntity> WorkOrderHistoryTs
    {
      get { return WorkOrderHistory.Cast<WorkOrderHistoryEntity>(); }
    }
  }

and always use the Typesafe(Ts) properties rather than the real ones.

You could do that, in the cases where you need the projection to anonymous types.

Frans Bouma | Lead developer LLBLGen Pro