首页 > 解决方案 > Inserting Nested Objects EF Core 5

问题描述

I have the following entities:

  1. Batch
  2. Samples
  3. SampleContainers
  4. SampleTests

A batch contains many samples. A sample contains many SampleContainers and many SampleTests.

I am trying to make a copy of batch and insert into database.

Attempt #1: get function in repository:

return await context.Set<TEntity>().FindAsync(id);

Controller:

var coc = await repository.Get(batchId);

coc.BatchStatusId = (int)Enums.BatchStatus.InProgress;
coc.IsTemplate = false;
coc.Id = 0;
        
var b = await repository.Add(coc);

Here only batch object was duplicated but related samples and containers were not duplicated/inserted.

Attempt #2: I changed my Get function as follows:

public async override Task<Batch> Get(int id)
{
    return await context.Set<Batch>()
            .Include(p => p.Samples)
            .FirstOrDefaultAsync(p => p.Id == id);
}

This time batch was duplicated but samples and containers and tests were all updated with new batchId/FK (I wanted them all to be duplicated).

Attempt #3: following this, I implemented as follows:

public async Task<int> DuplicateBatch([FromBody]int batchId)
{
        try
        {
            var coc = await repository.Get(batchId);

            coc.BatchStatusId = (int)Enums.BatchStatus.InProgress;
            coc.IsTemplate = false;
            coc.Id = 0;

            var samples = coc.Samples.ToList();
            repository.DetachEntity(coc);

            var b = await repository.Add(coc);
            
            var allSampleTests = await sampleTestRepo.GetAll();
            var allSampleContainers = await sampleContainersRepo.GetAll();

            var sampletests = from st in allSampleTests
                              join s in samples on st.SampleId equals s.Id
                              select st;
            var sampleContainers = from sc in allSampleContainers
                                   join s in samples on sc.SampleId equals s.Id
                                   select sc;

            sampleRepo.DetachEntities(samples);
            
            sampleTestRepo.DetachEntities(sampletests.ToList());
            sampleContainersRepo.DetachEntities(sampleContainers.ToList());

            foreach (var s in samples) 
            {
                s.BatchId = b.Id;

                var sample = await sampleRepo.Add(s);

                foreach (var st in sampletests)
                {
                    st.SampleId = sample.Id;
                    await sampleTestRepo.Add(st);
                }

                foreach(var sc in sampleContainers)
                {
                    sc.SampleId = sample.Id;                        
                    await sampleContainersRepo.Add(sc);
                }
            }                
            
            return 1;
        }
        catch (Exception ex)
        {
            return 0;
        }
 }

This time I am facing the following exception as soon as I reach Detach function:

{"The property 'Batch.Id' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal."}

标签: insert-updateef-core-5.0nested-object

解决方案


This is how I did it, most of it is self explanatory.

public async Task<int> DuplicateBatch([FromBody]int batchId)
    {
        try
        {
            //STEP 1: Fetch the entities
            var coc2 = await repository.Get(batchId);
            var samples = coc2.Samples.ToList();

            var allSampleTests = await sampleTestRepo.GetAll();
            var allSampleContainers = await sampleContainersRepo.GetAll();

            var sampletests = samples.SelectMany(st => st.SampleTests).ToList();
            var samplecontainers = samples.SelectMany(st => st.SampleContainers).ToList();

            //STEP 2: Detach
            var coc = repository.DetachEntity(coc2);
            var samplesDetached = sampleRepo.DetachEntities(samples);
            var sampleTestsDetached = sampleTestRepo.DetachEntities(sampletests);
            var sampleContianersDetached = sampleContainersRepo.DetachEntities(samplecontainers);

            //STEP 3: Update object
            coc2.BatchStatusId = (int)Enums.BatchStatus.InProgress;
            coc2.IsTemplate = false;
            


            var b = await repository.Add(coc);

            return 1;
        }
        catch (Exception ex)
        {
            return 0;
        }

    }

推荐阅读