IQueryable Expression<Func<.. with nested children in different function or class

Posts   
 
    
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 01-Apr-2019 16:17:38   

Hi Frans,

I'm using your Derived Models templates which work fine. So my question is more help in general, maybe you would, I hope.

In some cases I have some extra metadata which are needed in the outcome of the generated code. I can now regenerate the code which you generate for my special cases. But I have this one thing I can't fix. I have this lambda created by LLBLGen:

        private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.ProjectEntity, VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot>> CreateProjectionFunc()
        {
            return p__0 => new VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot()
            {
                ProjectId = p__0.ProjectId,
                ProjectStatusId = p__0.RechtProject.ProjectStatusId,
                RechtLicentieContracten = p__0.MBRechtLicentieContracten.Select(p__1 => new VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRootTypes.RechtLicentieContract()
                {
                    ContractLicentieTypeId = p__1.ContractLicentieTypeId,
                    ContractVernieuwen = p__1.ContractVernieuwen,
                    LandId = p__1.LandId,
                    LicentieContractSoortId = p__1.LicentieContractSoortId,
                    RechtLicentieContractId = p__1.RechtLicentieContractId,

                }).ToList(),

    // __LLBLGENPRO_USER_CODE_REGION_START ProjectionRegion_RechtenProjectSearchRoot 
    // __LLBLGENPRO_USER_CODE_REGION_END 
            };
        }

I have a problem with this, because I cannot recreate it that way.

                RechtLicentieContracten = p__0.MBRechtLicentieContracten.Select(p__1 => new VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRootTypes.RechtLicentieContract()
                {
                    ContractLicentieTypeId = p__1.ContractLicentieTypeId,
                    ContractVernieuwen = p__1.ContractVernieuwen,
                    LandId = p__1.LandId,
                    LicentieContractSoortId = p__1.LicentieContractSoortId,
                    RechtLicentieContractId = p__1.RechtLicentieContractId,

                }).ToList(),

I'm only able to create one level. I can create all classes seperately, but not in a hierarchy. Given this, I can create this:

        private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.ProjectEntity, VA.Pac.RootDto.RechtenProjectSearchRoot.RechtenProjectSearchRoot>> CreateRechtenProjectSearchRootProjectionFunc()
        {
            return alias => new VA.Pac.RootDto.RechtenProjectSearchRoot.RechtenProjectSearchRoot()
            {
                    ProjectId = alias.ProjectId,
                    ProjectStatusId = alias.RechtProject.ProjectStatusId,
                        RechtLicentieContracten = alias.MBRechtLicentieContracten.Select(x => x.ProjectToRechtLicentieContract()).ToList(),
                };
        }

and in a different (or the same (partial) class, i can decide), the call to the select:

    public static partial class RechtLicentieContractLinqProjection
    {
        private static readonly System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity, VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract>> RechtLicentieContractProjectorExpression = CreateRechtLicentieContractProjectionFunc();
        private static readonly Func<VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity, VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract> CompiledRechtLicentieContractProjector = CreateRechtLicentieContractProjectionFunc().Compile();

        public static IQueryable<VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract> ProjectToRechtLicentieContract(this IQueryable<VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity> baseQuery)
        {
            return baseQuery.Select(RechtLicentieContractProjectorExpression);
        }

        public static VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract ProjectToRechtLicentieContract(this VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity entity)
        {
            return CompiledRechtLicentieContractProjector(entity);
        }

        private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity, VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract>> CreateRechtLicentieContractProjectionFunc()
        {
            return alias => new VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract()
            {
                    ContractLicentieTypeId = alias.ContractLicentieTypeId,
                    ContractVernieuwen = alias.ContractVernieuwen,
                    LandId = alias.LandId,
                    LicentieContractSoortId = alias.LicentieContractSoortId,
                    RechtLicentieContractId = alias.RechtLicentieContractId,
              };
        }
    }

But this way all underlying properties of the entity "RechtLicentieContract" are retrieved, although most are not used in the Derived Model. This must have something to do with the Expression<Func<. But I can't get my head around it. What would be the way to go? Would you have a code sample?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 01-Apr-2019 21:41:05   

I'm not sure I understand the case. Did you modify your own template to generate the Derived model or something similar? If that's the case, please share the modified template or the code specific to the problem.

Also, which version of LLBLGen Designer are you using?

Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 01-Apr-2019 22:22:54   

No, it's a more general question to Frans for using my own generator to create Linq Projection for specific cases. No bug or 'problem'. Has nothing to do with specific version.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 02-Apr-2019 10:26:29   

Not sure why you think all properties are fetched? The Expression<Func<T>> lambda's are part of a linq expression tree and thus also the projection? So it will query all fields in a table perhaps but the final projection will only project the ones in the final select.

I also don't fully understand what the exact problem is you can't solve...

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 02-Apr-2019 10:41:27   

Your code, with the inline projection on children, creates an Expression<Func<T>> with only the selected fields and so only selects these fields from the Db.

In my code the projection of children is handed over to another projection function and there somehow the limited projection is lost (although the projection works itself) but then all fields are fetched from the Db. Thats not same.

Maybe I better ask this on stackoverflow, but I know you excel in this matter wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 02-Apr-2019 11:44:37   

If the projection method returns an System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.ProjectEntity, VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot>>

then the projection is inlined in the main select and it should be the select on the linq query (and thus only read those fields).

What I'm a bit lost at is that you do have 2 methods in the last code block: one returns the expression<func>, the other a dto object. The latter will cause what you're seeing as it materializes the dto.

The former will return an expression<func<>> which doesn't do anything, the expression has to be evaluated by a linq provider.

Doesn't the first do what you want? Perhaps I don't understand the problem in full...

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 02-Apr-2019 13:50:28   

Sorry for my messy explanation then.

I can't figure out how to hook up CreateRechtLicentieContractProjectionFunc() in the first expression


     private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.ProjectEntity, VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot>> CreateProjectionFunc()
        {
            return p__0 => new VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot()
            {
                ProjectId = p__0.ProjectId,
                ProjectStatusId = p__0.RechtProject.ProjectStatusId,
                RechtLicentieContracten = p__0.MBRechtLicentieContracten.Select(p__1 => 
                
                xxxx CreateRechtLicentieContractProjectionFunc xxxx somehow? 

                ).ToList(),
            };
        }

        private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.MBRechtLicentieContractEntity, VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract>> CreateRechtLicentieContractProjectionFunc()
        {
            return alias => new VA.Pac.RootDto.RechtenProjectSearchRoot.RechtLicentieContract()
            {
                    ContractLicentieTypeId = alias.ContractLicentieTypeId,
                    ContractVernieuwen = alias.ContractVernieuwen,
                    LandId = alias.LandId,
                    LicentieContractSoortId = alias.LicentieContractSoortId,
                    RechtLicentieContractId = alias.RechtLicentieContractId,
             };
        }

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 02-Apr-2019 15:33:38   

The .Select() method expects a lambda, and the method provides that. Wouldn't it work to just do:

private static System.Linq.Expressions.Expression<Func<VA.Pac.DomainData.EntityClasses.ProjectEntity, VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot>> CreateProjectionFunc()
{
    return p__0 => new VA.Pac.DomainData.Aggregates.DtoClasses.RechtenProjectSearchRoot()
    {
        ProjectId = p__0.ProjectId,
        ProjectStatusId = p__0.RechtProject.ProjectStatusId,
        RechtLicentieContracten = p__0.MBRechtLicentieContracten.Select(CreateRechtLicentieContractProjectionFunc()).ToList(),
    };
}

?

That would satisfy the compiler and it should work at runtime (altho with linq you'll never know wink )

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 03-Apr-2019 20:46:20   

I tried, but it says: the type arguments cannot be inferred from the usage.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 04-Apr-2019 11:02:33   

That's weird, as the method returns a typed lambda.... sorry then I'm out of ideas... If you have a tiny repro for me, I could have a look (in the form of generated code, e.g. on northwind). The method passed to select() returns what select needs... no idea why that wouldn't compile (but all I have is the quoted code so perhaps something else is off...)

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 04-Apr-2019 11:56:49   

Strange yeah. But I have given up. I've created a new function that will parse the needed hierarchies so the same code you produce for the persistence of Derived models I can now also produce. It was less work then trying to figure out the use of Expression<func<>> in a lambda. Now I can finally implement my own properties smile Thanks for the effort Frans! Have a nice day