Lambda Prefetch - Inheritance

Posts   
 
    
iturner
User
Posts: 32
Joined: 07-Sep-2006
# Posted on: 02-Apr-2008 16:20:31   

Hi,

I've been using the new lambda prefetch syntax but have got stuck when the prefetch path needs to incorporate inherited entities.

I've attached a zip that includes a SQL Server database backup, the lgp file, the generated entity code (adapter model) and a VS 2008 test project to illustrate the problem.

To summarise the model, I have:

  • MusicianEntity that has a 1:n relationship with
  • MusicianInstrumentEntity that has a n:1 relationship with
  • InstrumentEntity
  • DrumKitEntity that is a subclassed from InstrumentEntity and has a n:1 relationship with
  • ShellMaterialEntity

All nicely contrived, but hopefully fairly clear.

I want to retrieve all musicians, and prefetch everything else as well - including ShellMaterial IF the instrument happens to be a drumkit.

Using the standard PathEdge syntax, it would look something like this:


IPathEdge[] edges = new IPathEdge[]
{
    new PathEdge<MusicianInstrumentEntity>(MusicianEntity.PrefetchPathMusicianInstruments,
        new PathEdge<InstrumentEntity>(MusicianInstrumentEntity.PrefetchPathInstrument,
            new PathEdge<ShellMaterialEntity>(DrumKitEntity.PrefetchPathShellMaterial)))
};

With lambda syntax, I was expecting to be able to do something like:


from m in metadata.Musician.WithPath(mPath => mPath
    .Prefetch<MusicianInstrumentEntity>(m => m.MusicianInstruments).SubPath(miPath => miPath
        .Prefetch<InstrumentEntity>(mi => mi.Instrument).SubPath(iPath => iPath
            .Prefetch<ShellMaterialEntity>(i => i.ShellMaterial))));

But that's not right - because the i being passed is an InstrumentEntity not a DrumKitEntity.

So, how do I construct something like that with the lambda syntax?

Cheers Ian

Jez
User
Posts: 198
Joined: 01-May-2006
# Posted on: 02-Apr-2008 17:02:09   

Hmm. This didn't occur to me when I wrote the original code, although I think this should be possible by adding an additional overload to SubPath (haven't tested this yet though).

I think something like this might do it (on ComplexPathEdgeParser and also IPathEdgeParser)


//In IPathEdgeParser
IPathEdgeParser<TSource, TDestination> SubPath<TSubPath>(Func<IPathEdgeRootParser<TSubPath>, IPathEdgeRootParser<TSubPath>> subPath) where TSubPath : IEntityCore;

//In ComplexPathEdgeParser
public IPathEdgeParser<TSource, TDestination> SubPath<TSubPath>(Func<IPathEdgeRootParser<TSubPath>, IPathEdgeRootParser<TSubPath>> subPath) where TSubPath : IEntityCore
{
    PathEdgeRootParser<TSubPath> parser = new PathEdgeRootParser<TSubPath>(Creator, CurrentEdge.ChildEdges);
    subPath(parser);
    return this;
}

Then, you'd be able to write this:


from m in metadata.Musician.WithPath(mPath => mPath
    .Prefetch<MusicianInstrumentEntity>(m => m.MusicianInstruments).SubPath(miPath => miPath
        .Prefetch<InstrumentEntity>(mi => mi.Instrument).SubPath<DrumKitEntity>(iPath => iPath
            .Prefetch<ShellMaterialEntity>(i => i.ShellMaterial))));

Edit: Forgot the generic constraint.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-Apr-2008 18:06:10   

(Not tested)

.Prefetch<ShellMaterialEntity>(i => (i as DrumkitEntity).ShellMaterial))));

this might cause problems inside the code which tries to obtain the member name.

I'll check out Jez solution if it solves the problem simple_smile

Frans Bouma | Lead developer LLBLGen Pro
iturner
User
Posts: 32
Joined: 07-Sep-2006
# Posted on: 02-Apr-2008 18:21:43   

That did occur to me, but I figured I'd start getting NullReferenceExceptions whenever 'i' was not a DrumKitEntity. Didn't actually try it, though...

I've not looked through the source code at all, so probably best if I keep quiet whilst you two capable chaps muse upon it.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Apr-2008 13:42:08   

I think this works: from m in metadata.Musician.WithPath(mPath => mPath .Prefetch<MusicianInstrumentEntity>(m => m.MusicianInstruments).SubPath(miPath => miPath .Prefetch<DrumKitEntity>(mi => mi.Instrument).SubPath(iPath => iPath .Prefetch<ShellMaterialEntity>(i => i.ShellMaterial))));

This works too:


LinqMetaData metaData = new LinqMetaData(adapter);
var q = (from d in metaData.Department
         select d).WithPath(departmentPath => departmentPath
                .Prefetch<BoardMemberEntity>(dep => dep.Employees).SubPath(bmPath => bmPath
                    .Prefetch<FamilyCarEntity>(bm => bm.CompanyCar)));

this is similar: I specify boardmember as entity to fetch for employees. This allows me to specify company car which is a related entity of boardmember only. The query nicely attaches a type filter simple_smile

Good that you brought it up though, it's worth mentioning in the manual simple_smile

Frans Bouma | Lead developer LLBLGen Pro
iturner
User
Posts: 32
Joined: 07-Sep-2006
# Posted on: 04-Apr-2008 14:27:37   

Right, that makes sense. I knew it would be possible - just takes a while to adjust to this new syntax.

from m in metadata.Musician.WithPath(mPath => mPath .Prefetch<MusicianInstrumentEntity>(m => m.MusicianInstruments).SubPath(miPath => miPath .Prefetch<DrumKitEntity>(mi => mi.Instrument).SubPath(iPath => iPath .Prefetch<ShellMaterialEntity>(i => i.ShellMaterial))));

There is one slight tweak needed to the above to cater for those musicians who don't have a drum kit (which was part of the PathEdge example from the initial post). I've italicised the relevant bit

from m in metadata.Musician.WithPath(mPath => mPath .Prefetch<MusicianInstrumentEntity>(m => m.MusicianInstruments).SubPath(miPath => miPath .Prefetch<InstrumentEntity>(mi => mi.Instrument) .Prefetch<DrumKitEntity>(mi => mi.Instrument).SubPath(iPath => iPath .Prefetch<ShellMaterialEntity>(i => i.ShellMaterial))));

Thanks for the help. Much obliged.

Cheers Ian

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Apr-2008 17:36:48   

That will likely load the DrumKit entities twice or load the subtree into the wrong entities, am I correct?

In short: I think your initial path is different from what I defined in my previous post: I defined a path where I wanted only a subtype (and its subtypes), however you want all instruments, and for the DRUMkit you want related entities. I haven't tried your adjustment but I wouldn't be surprised if you get drumkit entities twice and with no related entities, could you test that please?

Frans Bouma | Lead developer LLBLGen Pro
iturner
User
Posts: 32
Joined: 07-Sep-2006
# Posted on: 13-Apr-2008 17:20:27   

Hi Frans,

Sorry, busy week! Finally had a chance to check it, though. And you are right, the drumkits are fetched twice from the database. Though the resulting IQueryable<MusicianEntity> does seem to be correctly assembled - i.e. the right musicians have the right types of instruments etc.

Cheers Ian

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Apr-2008 20:42:11   

I think it can be solved with Jez proposal. I'll do some tests.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 16-Apr-2008 16:21:29   

Adding Jez' fix worked like a charm simple_smile . Added in next build.

Frans Bouma | Lead developer LLBLGen Pro