redarrowlabs / Argo

:squirrel: c# object => json.api relational mapping
MIT License
7 stars 3 forks source link

Specify load strategy per query #82

Open zeitlerc opened 6 years ago

zeitlerc commented 6 years ago

Eager loading of relationships is a performance improvement where the relationship is accessed in the same session. However, not all queries on the same resource will require the same relationship access, which means those queries actually take a performance hit instead. The Load Strategy is defined on the model itself, which means the implementer has to guess at how the relationships will be used in general and choose the better of two evils.

It would be better to override the Load Strategy per query, so that the implementer can tune performance on a query-by-query basis. If the implementer knows the relationship data will be used, it can be eagerly loaded for that query. If the Load Strategy on the model is eager and that relationship data will not be used, then the implementer could turn off eager loading in that instance. This is consistent with other ORMs and feasible using JsonApi.

Below is one possible look. Otherwise CreateQuery could have additional parameters, or the implementation could be "opt-in only" where the only option is to specify which relationships will be loaded.

// Method PeopleToVisit
var puppies = session
    .CreateQuery<Puppy>()
    .LoadStrategy(x => x.Owners, LoadStrategy.Eager)
    .LoadStrategy(x => x.Leash, LoadStrategy.Lazy)
    .Where(x => x.Breed = BreedType.GoldenRetriever);
return puppies
    .Select(x => new VisitResponse {
        Puppy = x,
        OwnerNames = x.Owners.Select(x => x.Name).ToArray();
    });

// Method PuppiesToWalk
var puppies = session
    .CreateQuery<Puppy>()
    .LoadStrategy(x => x.Leash, LoadStrategy.Eager)
    .LoadStrategy(x => x.Owners, LoadStrategy.Lazy)
    .Where(x => x.LastWalkTime <= DateTime.Now.AddHours(-1));
return puppies
    .Select(x => new WalkResponse {
        PuppyName = x.Name,
        Leash = x.Leash
    });

// Opt-in possible look
var puppies = session
    .CreateQuery<Puppy>()
    .EagerLoad(x => x.Owners)
    .EagerLoad("leash");

// CreateQuery possible look
var puppies = session
    .CreateQuery<Puppy>(eagerLoad: new [] { "owners" });
engenb commented 6 years ago

Ah yes. This could allow models to be more situationally versatile. I think I like the second opt in approach better. Lazy should be implicitly assumed as the default, as it is the default for the api spec as well.

we'll need to validate the expression is actually a HasMany. Ideally this would be done via compilation, but I think it'll have to be at runtime