0

I have this situation: I have a MasterPlanComponent object that holds multiple objectives. These objectives hold multiple targets.

Upon creating a masterPlanComponent (with 2 objectives) we create 4 targets for both of those objectives.

This is what that's supposed to look like:

  • 1 component > 2 objectives > 8 targets (4 for one, 4 for the other, targets are blank and just need to be created in the database)

The problem with the below code is that only one set of 4 targets are created, and only for the second objective.

public async Task<MasterPlanComponent> CreateMasterPlanComponent(int masterPlanId, CreateMasterPlanComponentCommand command) 
{

    var masterPlan  = await _context.MasterPlans
        .Include(mp => mp.Components)
        .Include(mp => mp.Objectives)
        .SingleOrDefaultAsync(m => m.MasterPlanId == masterPlanId);

        var targets = new  List<ObjectiveNetTarget>();
    //creating new targets and setting two of their fields
    for (int i = 0 ; i < 4 ;  i++)
    {
        targets.Add(new ObjectiveNetTarget
        {
            CustomerMarketSegment = command.Scope.CustomerMarketSegment,
            OrganizationUnit = new OrganizationUnitRef
            {
                Name = "Sales",
                Code = "1251"

            }
        });

    }
    var masterPlanComponent = Mapper.Map<CreateMasterPlanComponentCommand, MasterPlanComponent>(command);
    foreach (var objective in masterPlanComponent.Objectives)
    {
        objective.TargetValues = new List<ObjectiveNetTarget>(targets);
    }

    masterPlanComponent.Status = MasterPlanComponentStatuses.New;
    masterPlan.Components.Add(masterPlanComponent);
    masterPlan.Objectives = masterPlan.Objectives.Concat(masterPlanComponent.Objectives).ToList();
    //masterPlanComponent.Objectives targets, I can see that both of them have 4 net targets as it should be
    await _context.SaveChangesAsync();

    _logger.LogInformation("New master plan component created.");
    _logger.LogInformation("Master plan component id: " + masterPlanComponent.ComponentId.ToString());
    //after I try to save the context however, only one of them has it. 
    return masterPlanComponent;
}

this code results in only 4 targets being written in the database each of them pointing to only one (the last one) of the objectives

wonder
  • 75
  • 8

1 Answers1

1

This sounds like it is caused because you are creating the targets ahead of time, and then passing them to each objective. When you pass the targets you created to the first objective, EF starts tracking them and marks them as to-be-inserted. When you pass the same csharp objects by ref to the 2nd objective, they are already marked as to-be-inserted and are simply updated to be referencing the 2nd objective rather than the first.

Try creating new target objects in csharp for each objective. EF will only insert 1 row : 1 csharp object reference.

  • Do i not do that with this line foreach (var objective in masterPlanComponent.Objectives) { objective.TargetValues = new List(targets); } ? What would be a non ugly way to do this, considering I have targets that are essentialy the same, with the only difference being which objective they belong to ? – wonder Mar 12 '20 at 13:46
  • 1
    @wonder Correct, you're not doing that with the new list. The reference to the list is new, but the contents reference the same 4 target objects. You could use a local function to create your 4 targets (meaning local just to this method, if this is the only place that it is used). [Read about them here.](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions) This local function could return a new collection of new targets. Then on each objective you would `objective.TargetValues = CreateTargets();` – Joshua Studt Mar 12 '20 at 14:24
  • 1
    You can check out [this answer](https://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c) which explains why your new list doesn't suffice, and might give you an idea of how to proceed. – Joshua Studt Mar 12 '20 at 14:38