Filter on additional field of intermediate entity when prefetching on a many-to-many relationship

Posts   
 
    
Posts: 35
Joined: 19-Mar-2007
# Posted on: 15-Dec-2010 14:43:03   

I have a many to many situation where three entities are interchained (see table creation scripts below). I have created m:n-relations for the Card entity and the CardHolder entity in the llbl-designer. When I try to fetch a collection of AccessLevel entities with a prefetch of the Card collection (via CardHolderCardAccessLevel) I believe that I need to specify an additional filter for the CardHolderId to get the desired result (as I only want the access levels for a specific card holder). I can't get to work. I've tried to tapp in on the aliases of the FilterRelations och the prefetch object used and a bunch of other variants. Is it possible to get this three-fold scenario working in LLBL?

Here's a variant of the code:



        private static PrefetchPath2 GetNormalAccessLevelPrefetch(IPredicateExpression cardHolderFilter)
        {
            var cardHolderCardAccessLevelOnCardHolderRelations = new RelationCollection();
            cardHolderCardAccessLevelOnCardHolderRelations.Add(CardHolderCardAccessLevelEntity.Relations.CardHolderEntityUsingCardHolderId);

            var prefetch = new PrefetchPath2(EntityType.AccessLevelEntity);
            prefetch.Add(AccessLevelEntity.PrefetchPathCardHolderCardAccessLevels, 0, cardHolderFilter, cardHolderCardAccessLevelOnCardHolderRelations);
            var rel = new RelationCollection();
            foreach (IRelation item in AccessLevelEntity.PrefetchPathCardCollectionViaCardHolderCardAccessLevel.FilterRelations)
            {
                rel.Add(item);
            }
            rel.Add(CardHolderCardAccessLevelEntity.Relations.CardHolderEntityUsingCardHolderId);
            prefetch.Add(AccessLevelEntity.PrefetchPathCardCollectionViaCardHolderCardAccessLevel, 0, new PredicateExpression(CardHolderCardAccessLevelFields.CardHolderId == 3), rel);
            return prefetch;
        }


... and here's the exception thrown:


Server Error in '/' Application.
--------------------------------------------------------------------------------

Relation at index 2 doesn't contain an entity already added to the FROM clause. Bad alias? 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.ServiceModel.FaultException`1[[System.ServiceModel.ExceptionDetail, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]: Relation at index 2 doesn't contain an entity already added to the FROM clause. Bad alias?

Source Error: 


Line 14:         public CardHolderAccessLevelData GetCardHolderAccessLevelDataByCorporateId(string corporateId)
Line 15:         {
Line 16:             return Channel.GetCardHolderAccessLevelDataByCorporateId(corporateId);
Line 17:         }
Line 18: 


Source File: C:\Projects\IdCardAccessControl\Contracts\Proxies\AccessControlServiceClient.cs    Line: 16 

Stack Trace: 


[FaultException`1: Relation at index 2 doesn't contain an entity already added to the FROM clause. Bad alias?]
   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +9464367
   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +345
   IdCardAccessControl.Contracts.IAccessControl.GetCardHolderAccessLevelDataByCorporateId(String corporateId) +0
   IdCardAccessControl.Contracts.Proxies.AccessControlServiceClient.GetCardHolderAccessLevelDataByCorporateId(String corporateId) in C:\Projects\IdCardAccessControl\Contracts\Proxies\AccessControlServiceClient.cs:16
   IdCardManagement.BizLogic.AccessControlLogic.GetAccessControlProductsForPerson(String corporateId) in C:\Projects\IdCardMgmt\BizLogic\AccessControlLogic.cs:35
   IdCardManagement.Presenter.Controls.AccessControlControlPresenter.Select() in C:\Projects\IdCardMgmt\Presenter\Controls\AccessControlControlPresenter.cs:42
   IdCardManagement.Presenter.Controls.AccessControlControlPresenter.OnCorporateIdChangedView(Object sender, EventArgs e) in C:\Projects\IdCardMgmt\Presenter\Controls\AccessControlControlPresenter.cs:35
   IdCardManagement.Web.Controls.AccessControlControl.HandleCorporateIdSetEvent() in C:\Projects\IdCardMgmt\Web\Controls\AccessControlControl.ascx.cs:49
   IdCardManagement.Web.Controls.AccessControlControl.set_CorporateId(String value) in C:\Projects\IdCardMgmt\Web\Controls\AccessControlControl.ascx.cs:30
   IdCardManagement.Web.Authenticated.Profile.OnPersonLoadedCompletePersonControl(Object sender, EventArgs e) in C:\Projects\IdCardMgmt\Web\Authenticated\Profile.aspx.cs:33
   IdCardManagement.Web.Controls.PersonControl.HandlePersonLoadedCompleteEvent() in C:\Projects\IdCardMgmt\Web\Controls\PersonControl.ascx.cs:157
   IdCardManagement.Web.Controls.PersonControl.LoadPerson() in C:\Projects\IdCardMgmt\Web\Controls\PersonControl.ascx.cs:140
   IdCardManagement.Web.Controls.PersonControl.Page_Load(Object sender, EventArgs e) in C:\Projects\IdCardMgmt\Web\Controls\PersonControl.ascx.cs:120
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
   System.Web.UI.Control.OnLoad(EventArgs e) +91
   System.Web.UI.Control.LoadRecursive() +74
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Control.LoadRecursive() +146
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2207




--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1 



CREATE TABLE [dbo].[AccessLevel](
    [AccessLevelId] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
 CONSTRAINT [PK_AccessLevel] PRIMARY KEY CLUSTERED 
(
    [AccessLevelId] ASC
)) ON [PRIMARY]



CREATE TABLE [dbo].[Card](
    [CardId] [bigint] IDENTITY(1,1) NOT NULL,
    [CardNumber] [nvarchar](20) NULL,
    [CardStatus] [int] NULL,
 CONSTRAINT [PK_Card] PRIMARY KEY CLUSTERED 
(
    [CardId] ASC
)) ON [PRIMARY]




CREATE TABLE [dbo].[CardHolder](
    [CardHolderId] [bigint] IDENTITY(1,1) NOT NULL,
    [CorporateId] [nvarchar](50) NULL,
    [EmployeeNumber] [nvarchar](50) NULL,
    [Surname] [nvarchar](50) NULL,
    [GivenName] [nvarchar](50) NULL,
    [Comment] [nvarchar](250) NULL,
    [Picture] [varbinary](max) NULL,
 CONSTRAINT [PK_CardHolder] PRIMARY KEY CLUSTERED 
(
    [CardHolderId] ASC
)
) ON [PRIMARY]


CREATE TABLE [dbo].[CardHolderCardAccessLevel](
    [AccessLevelId] [bigint] NOT NULL,
    [CardHolderId] [bigint] NOT NULL,
    [CardId] [bigint] NOT NULL,
    [Comment] [nvarchar](500) NULL,
    [ValidFrom] [datetime] NULL,
    [ValidTo] [datetime] NULL,
 CONSTRAINT [PK_CardHolderCardAccessLevel] PRIMARY KEY CLUSTERED 
(
    [AccessLevelId] ASC,
    [CardHolderId] ASC,
    [CardId] ASC
)
) ON [PRIMARY]


ALTER TABLE [dbo].[CardHolderCardAccessLevel]  WITH CHECK ADD  CONSTRAINT [FK_CardHolderCardAccessLevel_AccessLevel] FOREIGN KEY([AccessLevelId])
REFERENCES [dbo].[AccessLevel] ([AccessLevelId])
GO

ALTER TABLE [dbo].[CardHolderCardAccessLevel]  WITH CHECK ADD  CONSTRAINT [FK_CardHolderCardAccessLevel_Card] FOREIGN KEY([CardId])
REFERENCES [dbo].[Card] ([CardId])
GO


ALTER TABLE [dbo].[CardHolderCardAccessLevel]  WITH CHECK ADD  CONSTRAINT [FK_CardHolderCardAccessLevel_CardHolder] FOREIGN KEY([CardHolderId])
REFERENCES [dbo].[CardHolder] ([CardHolderId])
GO


I'm using ORM support classes version 3.0.10.111, Adapter, .NET 4, SQL Server 2008.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 15-Dec-2010 15:30:21   

I'm not sure what all this code is doing.

You said you are fetching AccessCards..fine. Then you want to prefetch Cards along.. so you should use the following code:

            var prefetch = new PrefetchPath2(EntityType.AccessLevelEntity);
            prefetch.Add(AccessLevelEntity.PrefetchPathCardCollectionViaCardHolderCardAccessLevel);

Then you need to filter those cards by CardHolderId then you need to modify the above code to the following:

            var rel = new RelationCollection();
            rel.Add(CardEntity.Relations.CardHolderCardAccessLevelEntity...);

            var prefetch = new PrefetchPath2(EntityType.AccessLevelEntity);
            prefetch.Add(AccessLevelEntity.PrefetchPathCardCollectionViaCardHolderCardAccessLevel, 0, new PredicateExpression(CardHolderCardAccessLevelFields.CardHolderId == 3), rel);

This should be it, if I'm not missing anything.

Posts: 35
Joined: 19-Mar-2007
# Posted on: 16-Dec-2010 05:49:03   

That worked just fine!

I feel kind of embarrassed. I almost had it but messed it up for the reason of trying to re-use an already existing relation collection and predicate expression. Darn!

Well, many thanks for putting me in the right direction!