Reusing Query parts

Posts   
 
    
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 14-Mar-2015 17:36:23   

If have a large query like this (based on PrivateClientFileEntity)...

        static PrivateClientFileEntity FetchPrivateClientFile(int privateClientFileID)
        {
            using (var logger = ProcessLogger.CreateWithAutoCompleteHack("ContextHelper.FetchPrivateClientFile", privateClientFileID))
            {
                using (var adapter = logger.CreateDataAccessAdapter())
                {
                    var qf = new QueryFactory();
                    var q = qf.PrivateClientFile
                        .Where(PrivateClientFileFields.ID == privateClientFileID)




                        // *** I want to reuse from here down ***


                        .WithPath(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true))
                        .WithPath(PrivateClientFileEntity.PrefetchPathFileAnswers
                            .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion))
                        .WithPath(PrivateClientFileEntity.PrefetchPathAdviser
                            .WithSubPath(AdviserEntity.PrefetchPathPerson
                                .WithSubPath(PersonEntity.PrefetchPathPrimaryAddress)))
                        // Bring back the file's Clients
                        .WithPath(PrivateClientFileEntity.PrefetchPathClients
                            .WithOrdering(ClientFields.DisplayOrder.Ascending())
                            // For each client
                            .WithSubPath(ClientEntity.PrefetchPathQuestionnaireResponses
                                .WithOrdering(QuestionnaireResponseFields.CreatedDate.Descending())
                                .WithSubPath(QuestionnaireResponseEntity.PrefetchPathResponseAnswers
                                    .WithSubPath(QuestionnaireResponseAnswerEntity.PrefetchPathResponseAnswerDetails)))


                            .WithSubPath(ClientEntity.PrefetchPathATRCategories)

                            // Bring back their home address
                            .WithSubPath(ClientEntity.PrefetchPathPrimaryAddress)

                            // And any additional addresses
                            .WithSubPath(ClientEntity.PrefetchPathAdditionalAddresses
                                .WithSubPath(AdditionalAddressEntity.PrefetchPathAddress)
                            )

                            // And their assets/asset alterations.ownerships
                            .WithSubPath(LegalBodyEntity.PrefetchPathOwnedAssets
                                .WithSubPath(AssetEntity.PrefetchPathIncome)
                                .WithSubPath(AssetEntity.PrefetchPathOutgoing)
                                .WithSubPath(AssetEntity.PrefetchPathValuations)
                                .WithSubPath(AssetEntity.PrefetchPathProvider)
                                .WithSubPath(PolicyEntity.PrefetchPathPolicyAlterations)
                                .WithSubPath(PolicyEntity.PrefetchPathPolicyType)
                                .WithSubPath(PolicyEntity.PrefetchPathComponents
                                    .WithSubPath(ComponentFundsEntity.PrefetchPathClientFunds
                                        .WithSubPath(ClientFundEntity.PrefetchPathValuations)))

                                .WithSubPath(AssetEntity.PrefetchPathOwnerships
                                    .WithSubPath(AssetToOwnerEntity.PrefetchPathLegalBody))
                            )

                            // Incomes
                            .WithSubPath(LegalBodyEntity.PrefetchPathIncomeOwnerships
                                .WithSubPath(IncomeToOwnerEntity.PrefetchPathIncome
                                    .WithSubPath(IncomeEntity.PrefetchPathIncomeType)
                                    .WithSubPath(IncomeEntity.PrefetchPathAsset)
                                    .WithSubPath(IncomeEntity.PrefetchPathOwnerships)
                                )
                            )

                            // Outgoings
                            .WithSubPath(LegalBodyEntity.PrefetchPathOutgoingOwnerships
                                .WithSubPath(OutgoingToOwnerEntity.PrefetchPathOutgoing
                                    .WithSubPath(OutgoingEntity.PrefetchPathOutgoingType)
                                    .WithSubPath(OutgoingEntity.PrefetchPathAsset)
                                    .WithSubPath(OutgoingEntity.PrefetchPathLiability)
                                    .WithSubPath(OutgoingEntity.PrefetchPathOwnerships)
                                )
                            ) 
                            // Liabilities
                            .WithSubPath(LegalBodyEntity.PrefetchPathOwnedLiabilities
                                .WithSubPath(LiabilityEntity.PrefetchPathValuations)
                                .WithSubPath(LiabilityEntity.PrefetchPathOutgoing)
                                .WithSubPath(LiabilityEntity.PrefetchPathProvider)
                                .WithSubPath(NonMortgageLiabilityEntity.PrefetchPathLiabilityType)
                                .WithSubPath(LiabilityEntity.PrefetchPathOwnerships
                                    .WithSubPath(LiabilityToOwnerEntity.PrefetchPathLegalBody)))

                            // Trusts
                            .WithSubPath(LegalBodyEntity.PrefetchPathLegalBodyToTrustees
                                .WithSubPath(TrusteeEntity.PrefetchPathTrust
                                    .WithSubPath(TrustEntity.PrefetchPathBeneficiaries)
                                    .WithSubPath(TrustEntity.PrefetchPathOwnedAssets
                                        .WithSubPath(AssetEntity.PrefetchPathIncome)
                                        .WithSubPath(AssetEntity.PrefetchPathOutgoing)
                                        .WithSubPath(AssetEntity.PrefetchPathValuations)
                                        .WithSubPath(AssetEntity.PrefetchPathSubAssets)
                                        .WithSubPath(PolicyEntity.PrefetchPathPolicyAlterations)
                                        .WithSubPath(PolicyEntity.PrefetchPathPolicyType)
                                        .WithSubPath(PolicyEntity.PrefetchPathComponents
                                            .WithSubPath(ComponentFundsEntity.PrefetchPathClientFunds
                                                .WithSubPath(ClientFundEntity.PrefetchPathProviderFund
                                                    .WithSubPath(ProviderFundEntity.PrefetchPathValuations))
                                                .WithSubPath(ClientFundEntity.PrefetchPathValuations)))
                                        .WithSubPath(AssetEntity.PrefetchPathOwnerships
                                            .WithSubPath(AssetToOwnerEntity.PrefetchPathLegalBody))
                                    )))


                            .WithSubPath(LegalBodyEntity.PrefetchPathLegalBodyToBeneficiaries
                                .WithSubPath(BeneficiaryEntity.PrefetchPathTrust
                                    .WithSubPath(TrustEntity.PrefetchPathBeneficiaries)
                                    .WithSubPath(TrustEntity.PrefetchPathOwnedAssets
                                        .WithSubPath(AssetEntity.PrefetchPathIncome)
                                        .WithSubPath(AssetEntity.PrefetchPathOutgoing)
                                        .WithSubPath(AssetEntity.PrefetchPathValuations)
                                        .WithSubPath(AssetEntity.PrefetchPathSubAssets)
                                        .WithSubPath(PolicyEntity.PrefetchPathPolicyAlterations)
                                        .WithSubPath(PolicyEntity.PrefetchPathPolicyType)
                                        .WithSubPath(PolicyEntity.PrefetchPathComponents
                                            .WithSubPath(ComponentFundsEntity.PrefetchPathClientFunds
                                                .WithSubPath(ClientFundEntity.PrefetchPathProviderFund
                                                    .WithSubPath(ProviderFundEntity.PrefetchPathValuations))
                                                .WithSubPath(ClientFundEntity.PrefetchPathValuations)))
                                        .WithSubPath(AssetEntity.PrefetchPathOwnerships
                                            .WithSubPath(AssetToOwnerEntity.PrefetchPathLegalBody))
                                    )))
                                )
                            )

                            // And client association
                            .WithSubPath(LegalBodyEntity.PrefetchPathAssociations
                                .WithSubPath(AssociationEntity.PrefetchPathTarget
                                )
                            )

                            .WithSubPath(LegalBodyEntity.PrefetchPathTags)

                            .WithSubPath(LegalBodyEntity.PrefetchPathAssetOwnerships
                                .WithSubPath(AssetToOwnerEntity.PrefetchPathAsset
                                    .WithSubPath(NonPolicyAssetEntity.PrefetchPathNonPolicyAssetType)))
                        )
                        .WithPath(PrivateClientFileEntity.PrefetchPathDependents
                            .WithSubPath(DependentEntity.PrefetchPathPerson));

                    var result = adapter.FetchFirst(q);

                    return result;
                }
            }
        }

...and I now want to reuse the Prefetch bits in another query like this...

                var qf = new QueryFactory();

                var query = qf.SuitabilityReport
                    .Where(SuitabilityReportFields.ID == suitabilityReportID)
                    .WithPath(SuitabilityReportEntity.PrefetchPathClientFile

                *** What can I add here?? ***

...how can I split the original query into a second method so it can be reused in multiple queries?

I really don't want to copy/paste - just keep one version in one central place.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 15-Mar-2015 10:13:19   

Why not create functions/methods which produce the prefetch paths for you? This way you can re-use them without re-using objects which might contain state from a previous query?

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 16-Mar-2015 08:59:42   

That's basically what I meant to say. But what would the signature for a function/method that only adds prefetch paths look like?

In the two example I gave above, there is a different starting point for the PrivateClientFileEntity.

The first is

var qf = new QueryFactory(); var q = qf.PrivateClientFile .Where(PrivateClientFileFields.ID == privateClientFileID)

but the second is

var qf = new QueryFactory();
var query = qf.SuitabilityReport
             .Where(SuitabilityReportFields.ID == suitabilityReportID)
             .WithPath(SuitabilityReportEntity.PrefetchPathClientFile

So the first is a 'direct' additional of WithPath but the second already has a WithPath to get the PrivateClientFileEntity.

Would something like this work....

        public static EntityQuery<T> AddPrivateClientFullPrefetchPaths<T>(EntityQuery<T> source) where T: IEntityCore
        {
            return source
                 .WithPath(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true))
            ..... etc.....

.. or is the order in the hierarchy from the EntityQuery<T> relevant?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 16-Mar-2015 14:20:25   

.WithPath adds the element(s) specified to the existing prefetch path already there (as a new node). So if the path specified has to be part of the existing path already there, then it goes wrong. If it can be added as a separate node from the root, then your method is OK.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 16-Mar-2015 15:09:48   

I can't make it work. disappointed disappointed disappointed Its either a big list of Paths or a big list of SubPaths. One method won't work for the two scenarios.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 16-Mar-2015 18:30:19   

What do you need to make it work? (prefetch path the elements are added to is currently internal to EntityQuery<T> )

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 16-Mar-2015 18:57:02   

Not sure to be honest. Personally, I think it is a reasonable scenario for a feature enhancement but you might not. simple_smile

I also think I might be out of my depth trying to suggest how to make it work - I don't know the internals well enough.

Internal methods can be got at with Reflection but since it is in a generic class, that makes it more difficult. Even if it became public, I'm not sure that would help.

How far are you willing to go? Just help me hack it as a one-off or add a feature enhancement that can do it?

Off the top of my head, (and assuming for now you might be willing to consider a new feature), I'm thinking along the lines of a WithPathSet() method and two overloads. I have a vague notion of how this might work from the callers point of view and could flesh it out if you think it might be a good new feature.

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 16-Mar-2015 21:05:22   

Could you please specify why you can't make it work? What function/signature did you use?

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 17-Mar-2015 08:30:48   

One scenario requires .WithPath and the other .WithSubPath. I can only choose one or the other in a common method which defeats the purpose somewhat.

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 17-Mar-2015 10:23:22   

OK, I've had a bit of a think about how it could work

1) Add a new class called "PrefetchPathSet" - Stores the IPrefetchPathElementCore elements in the correct hierarchy but doesn't do anything else with them (there are 5 top-level elements in the top message sample) - Maybe is generic? - PrefetchPathSet<TEntity> to indicate the top level Entity type for which the prefetch paths are for - or that might hinder rather than help

2) PrefetchPathSet has .WithPath() chaining method using exactly the same parameters as EntityQuery<T> does and returns itself - Allows exact cut & paste from an existing structure (like in the top message) for convenience - Will keep the indenting looking nice (in Resharper, when you type the semi-colon, it formats the whole query)

3) The user-created Common Method would then have a signature like public static PrefetchPathSet CreatePrivateClientFilePrefetchPathSet() - Body would look like this:- return new PrefetchPathSet() .WithPath(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true)) .WithPath(PrivateClientFileEntity.PrefetchPathFileAnswers .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion)) .WithPath(PrivateClientFileEntity.PrefetchPathAdviser .WithSubPath(AdviserEntity.PrefetchPathPerson .WithSubPath(PersonEntity.PrefetchPathPrimaryAddress)))

              etc.

4) New method on EntityQuery<T> with sig like "public EntityQuery<T> WithPathSet(PrefetchPathSet prefetchPathSet) - Takes the multiple top level Elements in the prefetchPathSet and applies them as though they were applied to the Query with .WithPath(...) - Returns EntityQuery<T> for chaining

5) Another new method overload on EntityQuery<T> with a sig like "public EntityQuery<T> WithPathSet(IPrefetchPathElement2 element, PrefetchPathSet prefetchPathSet) - Applies the element parameter as a .WithPath as normal - Then takes the multiple top level Elements in the prefetchPathSet and applies them on element as though they were .WithSubPath(...) calls - Returns EntityQuery<T> for chaining

I believe that all the other constructs available on IPrefetchPathElementCore - WithOrdering() etc. - will work as normal and, if any require a QueryFactory, well that can be passed into the Common Method as an additional parameter.

So, in summary, one way of defining a set of related prefetch paths from a common point and then two ways of inserting them in queries.

What do you think?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 17-Mar-2015 14:10:31   

What's the difference between a prefetchpathset and a PrefetchPath2 object, as that already is a set of nodes?

The main reason I asked my previous question was to know whether making the prefetchpath property public or not would help, but I now see that that is not really going to help as you need the node to append the path to as a subpath.

So path P produced by your method is sometimes used as the path (so not as a subpath of some node) and sometimes as the subpath of node N, already in the path. Currently you can't look up that path as the prefetch path property is internal.

So why not create another method which either adds the parentnode + P as subpath to the query or P directly as the path to the query? That would solve your problem without much effort from anyone I think.

The main problem with extending the api with all kinds of prefetch path related classes is that it already is very complicated with the various apis. I am not very fond of adding yet another way to defined prefetch paths if it isn't absolutely necessary.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 17-Mar-2015 16:56:10   

Otis wrote:

The main problem with extending the api with all kinds of prefetch path related classes is that it already is very complicated with the various apis. I am not very fond of adding yet another way to defined prefetch paths if it isn't absolutely necessary.

I know. smile smile That's why I described the issue/solution in great detail. I can't say its absolutely necessary but it would seem to provide a 'final' solution if it was possible.

So why not create another method which either adds the parentnode + P as subpath to the query or P directly as the path to the query? That would solve your problem without much effort from anyone I think. 

True but only for the scenario as described here - maintaining two large but virtually identical methods. But if a PrefetchPathSet-type solution was in place then I can create mutiple PrefetchPathSet creating methods. I would also be able to have parameters optionally including paths or not like I currently do with some filters. I would be able to pass PrefetchPathSet instances around in the app. Much flexibility without any (further) API change.

Also the parentNode in the "(parentNode + P)" isn't fixed - there are other places where I have an EntityQuery which prefetches a PrivateClientFile (and P). It is only the P that is common.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 18-Mar-2015 09:27:28   

simmotech wrote:

Otis wrote:

The main problem with extending the api with all kinds of prefetch path related classes is that it already is very complicated with the various apis. I am not very fond of adding yet another way to defined prefetch paths if it isn't absolutely necessary.

I know. smile smile That's why I described the issue/solution in great detail. I can't say its absolutely necessary but it would seem to provide a 'final' solution if it was possible.

No, it has the same problem: you can't append an element to an existing prefetchpathset as a subpath of a given node, which is what your problem is. If I have a path P and it has to be a subpath of a node N which is already in a prefetch path (or prefetchpathset, it's the same object, as a prefetchpath2 object is already a set like you say) I still have to specify N and look it up in the existing prefetch path, or prefetchpathset.

That's the problem here: you have a path, and you either want it to add to a prefetch path as the nodeset to use, or as a subpath of a node N, but N is already in the prefetch path.

q.WithPath(p) just does q.PrefetchPath2Object.Add(p), like you'd do in the early days with PrefetchPath2 objects.

So why not create another method which either adds the parentnode + P as subpath to the query or P directly as the path to the query? That would solve your problem without much effort from anyone I think. 

True but only for the scenario as described here - maintaining two large but virtually identical methods. But if a PrefetchPathSet-type solution was in place then I can create mutiple PrefetchPathSet creating methods. I would also be able to have parameters optionally including paths or not like I currently do with some filters. I would be able to pass PrefetchPathSet instances around in the app. Much flexibility without any (further) API change. Also the parentNode in the "(parentNode + P)" isn't fixed - there are other places where I have an EntityQuery which prefetches a PrivateClientFile (and P). It is only the P that is common.

I was thinking about: (syntax might be off in details, not tested)

public EntityQuery<T> AddPath<T>(this EntityQuery<T> q, PrefetchPathElement2 parentNode, PrefetchPath2 path)
{
    if(parentNode==null)
    {
        return q.WithPath(p);
    }
    else
    {
        return q.WithPath(parentNode.WithSubPath(path));
    }
}

Which you then call as:

var qf = new QueryFactory();
var query = qf.SuitabilityReport
    .Where(SuitabilityReportFields.ID == suitabilityReportID)
    .AddPath(SuitabilityReportEntity.PrefetchPathClientFile, CreateBigPrefetchPath());

and

var qf = new QueryFactory();
var q = qf.PrivateClientFile
            .Where(PrivateClientFileFields.ID == privateClientFileID)
            .AddPath(null, CreateBigPrefetchPath());
Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 18-Mar-2015 11:43:40   

Just need to clarify what you envisaged CreateBigPrefetchPath() looking like:

        static PrefetchPath2 CreateBigPrefetchPath()
        {
            var p = new PrefetchPath2(EntityType.PrivateClientFileEntity);

            p.Add(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true);

            p.Add(PrivateClientFileEntity.PrefetchPathFileAnswers
                .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion));

            // etc....

            return p;
        }

or maybe

        static PrefetchPath2 CreateBigPrefetchPath()
        {
            var p = new PrefetchPath2(EntityType.PrivateClientFileEntity)
                        {
                            ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true),
                            PrivateClientFileEntity.PrefetchPathFileAnswers
                                .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion)

                            // etc....
                        };

            return p;
        }

So I can have a PrefetchPath2 with multiple top-level items. Will your AddPath() method still work or will it only work if PrefetchPath2 has had one Add() call? (I think I am confused because although WithPath() accepts an IPrefetchPathElementCore and that indicates to me a single item, a PrefetchPath2 can have multiple items)

Assuming it does work with multiple calls to Add() then I'm very happy with your solution! smile Although I would suggest:- - Have two overloads of AddPath() - one with parentNode and one without. Its as easy to write two methods without if statements as write a single method with one. Also no need for null from the caller side. - I still think WthPathSet() is a better name because AddPath implies singular but as long as there is such a method available I am happy.

  • Completely Optional for the framework but I can write it externally so not a problem:
  • Have a PrefetchPathSet object with an private PrefetchPath2 object.
  • Have WithPath() chaining methods that return itself. They simply call Add() on the wrapped PrefetchPath2 object. This allows me to cut and paste the same constructs to/from EntityQuery<T> without change.
  • Add an implicit conversion operator so a PrefetchPathSet can be used as the parameter in your new AddPath() method.
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 18-Mar-2015 17:49:49   

simmotech wrote:

Just need to clarify what you envisaged CreateBigPrefetchPath() looking like:

        static PrefetchPath2 CreateBigPrefetchPath()
        {
            var p = new PrefetchPath2(EntityType.PrivateClientFileEntity);

            p.Add(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true);

            p.Add(PrivateClientFileEntity.PrefetchPathFileAnswers
                .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion));

            // etc....

            return p;
        }

or maybe

        static PrefetchPath2 CreateBigPrefetchPath()
        {
            var p = new PrefetchPath2(EntityType.PrivateClientFileEntity)
                        {
                            ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true),
                            PrivateClientFileEntity.PrefetchPathFileAnswers
                                .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion)

                            // etc....
                        };

            return p;
        }

So I can have a PrefetchPath2 with multiple top-level items. Will your AddPath() method still work or will it only work if PrefetchPath2 has had one Add() call? (I think I am confused because although WithPath() accepts an IPrefetchPathElementCore and that indicates to me a single item, a PrefetchPath2 can have multiple items)

WithPath accepts multiple elements. I wrote the AddPath method just as an illustration, so it could return a path, WithPath indeed accepts IPrefetchPathElementCore elements, more than one though (you can specify as much as you like).

This means this: if you want to fetch Order and both its related Customer and Employee entities, you need: order->Customer and order->Employee.

so you create a PrefetchPath2 object and add two elements: (don't mind the names of the elements, it's an illustration)

var orderPath = new PrefetchPath2(EntityType.OrderEntity);
orderPath.Add(OrderEntity.PrefetchPathCustomer);
orderPath.Add(OrderEntity.PrefetchPathEmployee);

.WithPath() usage here is this:

qf.Order
    .WithPath(OrderEntity.PrefetchPathCustomer, OrderEntity.PrefetchPathEmployee);

Which will call Add for each element specified in WithPath on the prefetchpath2 object inside the EntityQuery<T>.

The above is equal to:

qf.Order
    .WithPath(OrderEntity.PrefetchPathCustomer)
    .WithPath(OrderEntity.PrefetchPathEmployee);

as WithPath simply performs an Add on the prefetch path to add another node at the first level (so at the first level away from the root, which is the entity the query is for).

The AddPath method was a method you write btw, I won't add it, as it's an easy helper for your situation. simple_smile I used it to illustrate what I meant what you can do to easily (if I understand it right) fix the problem of not wanting to copy/paste the large path.

PrefetchPath2 is a collectionbase element, so you can enumerate it.

So you can create the path using the method and then do (example):

foreach(IPrefetchPathElement2 element in CreateBigPrefetchPath())
{
    query.WithPath(element);
}

which will add the elements at the root level. SubPath elements are inside the nodes, so they're not at that level.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 18-Mar-2015 18:30:25   

Right. I can see how that works for scenario 1 (and have just confirmed it working smile smile )

Scenario 2 needs a bit more thinking about because if I am looping then I can't use the parent IPrefetchPathElement2 more than once.

Will have a go tomorrow morning.

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 19-Mar-2015 08:57:24   

Thanks very much for solving this, Frans. All seems to be working well. smile smile smile

For anyone else requiring this, here are the extension methods I added:-

        public static EntityQuery<T> WithPathSet<T>(this EntityQuery<T> query, PrefetchPath2 prefetchPathSet) where T: IEntityCore
        {
            foreach (IPrefetchPathElementCore rootElement in prefetchPathSet)
            {
                query.WithPath(rootElement);
            }

            return query;
        }

        public static EntityQuery<T> WithPathSet<T>(this EntityQuery<T> query, IPrefetchPathElement2 parentNode, PrefetchPath2 prefetchPathSet) where T: IEntityCore
        {
            foreach (IPrefetchPathElementCore rootElement in prefetchPathSet)
            {
                parentNode.WithSubPath(rootElement);
            }

            query.WithPath(parentNode);

            return query;
        }

Here is the optional PrefetchPathSet class to allow cut/paste from existing code/chaining:-

    public class PrefetchPathSet
    {
        readonly PrefetchPath2 prefetchPath;

        public PrefetchPathSet(Enum rootEntityType)
        {
            prefetchPath = new PrefetchPath2(rootEntityType);
        }

        public PrefetchPathSet WithPath(IPrefetchPathElement2 element)
        {
            prefetchPath.Add(element);

            return this;
        }

        public static implicit operator PrefetchPath2(PrefetchPathSet prefetchPathSet)
        {
            return prefetchPathSet.prefetchPath;
        }
    }

Here is a cut-down snippet from the path creation method:-

            return new PrefetchPathSet(EntityType.PrivateClientFileEntity)
                        .WithPath(ClientFileEntity.PrefetchPathFactFindNotes.WithFilter(FactFindNoteFields.Current == true))
                        .WithPath(PrivateClientFileEntity.PrefetchPathFileAnswers
                            .WithSubPath(FileAnswerEntity.PrefetchPathFileQuestion));

A snippet when using the PathSet directly on the query:-

        var q = qf.PrivateClientFile
            .Where(PrivateClientFileFields.ID == privateClientFileID)
            .WithPathSet(CreatePrivateClientFilePrefetchPathSet());

And finally, a snippet when using the PathSet further down:-

            var result = qf.SuitabilityReport
                .Where(SuitabilityReportFields.ID == Task.SuitabilityReportID)
                .WithPathSet(SuitabilityReportEntity.PrefetchPathClientFile, CreatePrivateClientFilePrefetchPathSet());

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 19-Mar-2015 10:03:21   

Finally, one addition to PrefetchPathSet to allow nesting:-

        public PrefetchPathSet WithPathSet(IPrefetchPathElement2 element, PrefetchPath2 prefetchPathSet)
        {
            foreach (IPrefetchPathElementCore rootElement in prefetchPathSet)
            {
                element.WithSubPath(rootElement);
            }

            prefetchPath.Add(element);

            return this;
        }

Sample use:-

        public static PrefetchPathSet ForSuitabilityReport()
        {
            return new PrefetchPathSet(EntityType.SuitabilityReportEntity)
                .WithPathSet(SuitabilityReportEntity.PrefetchPathClientFile, ForPrivateClientFile())
                .WithPath(SuitabilityReportEntity.PrefetchPathProposal);
        }

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39888
Joined: 17-Aug-2003
# Posted on: 19-Mar-2015 12:47:43   

Thanks for sharing! simple_smile

Frans Bouma | Lead developer LLBLGen Pro