1

Short Question: How do I call a getter explicitly without using the result and prevent compiler optimization from removing the call.

Longer Explanation of what I want to do

I'm using entity framework with web api to build a simple rest api. I'm using lazy-loading with proxies to realize one-to-many realations.

Now on a DELETE-Request I want to delete the entity including all child entities (this works fine). Then I want to return the deleted entity including children. This will fail because lazy-loading the children during serialization after DELETE obviously doesn't work.

    [HttpDelete("{id}")]
    public RolloutPlan Delete(int id)
    {
        var parent = _context.ParentEntities.Find(id);

        _context.ParentEntities.Remove(parent);
        _context.SaveChanges();

        return parent; // lazy-loading children here will fail
    }

So what I would like to do is to explicitly call the getter for the children before calling DELETE to load them beforehand:

    [HttpDelete("{id}")]
    public RolloutPlan Delete(int id)
    {
        var parent = _context.ParentEntities.Find(id);
        var children = parent.Children; // lazy-load children before DELETE.


        _context.ParentEntities.Remove(parent);
        _context.SaveChanges();

        return parent;
    }

This will fail however because the compiler will remove the variable children as it is unused. If I do something with the variable children though it works fine:

    [HttpDelete("{id}")]
    public RolloutPlan Delete(int id)
    {
        var parent = _context.ParentEntities.Find(id);
        var children = parent.Children; // lazy-load children before DELETE.

        // prevent the compiler from removing the call to parent.Children
        _logger.LogInformation("Children.Count:" + children.Count);

        _context.ParentEntities.Remove(parent);
        _context.SaveChanges();

        return parent; // lazy-loading children here will fail
    }

Edit: Lazy-Loading by adding an assignment does work (my mistake)

So what would be the best way to go about this issue? I guess there is a smart way to explizitly load the relation in entity framework that I'm currently unaware of and that would be the best solution for my issue. But I'm also really curious to know how solve this issue in general (calling getters explicilty).

Entites:

public class ParentEntity
{
    public int? Id { get; set; }
    public virtual ICollection<ChildEntity> Children { get; set; }
}

public class ChildEntity
{
    public int Id { get; set; }
}
Gellweiler
  • 751
  • 1
  • 12
  • 25
  • Do you not just use .Include() as per ^^^ so that it eager instead of lazy loads – Milney Jan 14 '20 at 08:44
  • @Milney Perhaps. I have no experience with EF so I could only comment on the compiler optimisation side of things. – ProgrammingLlama Jan 14 '20 at 08:46
  • @John yeah that anwsers the compiler optimisation part of it. Thanks. – Gellweiler Jan 14 '20 at 08:50
  • @Milney Will try that out. Thanks for the hint. – Gellweiler Jan 14 '20 at 08:50
  • 1
    *"This will fail however because the compiler will remove the variable children as it is unused."* Optmizations may remove variable, inline the calls etc., but are not allowed to *remove* calls. You can use C# discard (`_ = parent.Children;`), but `var children = parent.Children;` would also work. – Ivan Stoev Jan 14 '20 at 08:54
  • @Ivan Stoev Yeah your right it works with `var children = parent.Children` or `_ = parent.Children`. My mistake my build wasn't clean. – Gellweiler Jan 14 '20 at 09:02

2 Answers2

2

Do Eager Loading using Include

[HttpDelete("{id}")]
[MethodImpl(MethodImplOptions.NoOptimization)]
public RolloutPlan Delete(int id)
{
    var parent = _context.ParentEntities.Include(x => x.Children ).FirstOrDefault(id)

    // lazy-load steps
    // compiler optimization needs to be turned off for this method
    // to prevent unused variable from being removed.
    var children = parent.Children;

    _context.ParentEntities.Remove(rolloutPlan);
    _context.SaveChanges();

    return rolloutPlan;
}
divyang4481
  • 1,584
  • 16
  • 32
2

Explicit loading with .Find() and .Include doesn't seem to work in EF Core 3. But this worked (see also: https://stackoverflow.com/a/7348694/1245992):

    [HttpDelete("{id}")]
    public async Task<RolloutPlan> Delete(int id)
    {
        // Explicitly include children here,
        // because lazy-loading them on serialization will fail.
        var parent = await _context
            .ParentEntities
            .Include(p => p.Children)
            .SingleAsync(p => p.Id == id);

        _context.ParentEntities.Remove(parent);
        _context.SaveChanges();

        return parent;
    }
Gellweiler
  • 751
  • 1
  • 12
  • 25