0

I have this EF query:

var records = mydata.Where(a => some condition).ToList();

Records is a list of Record objects. Record is a database table that has a one to many relationship called "relation".

The EF object represents that member variable as Collection<Relation>.

After the where above, I see that "relation" member variable contains a collection of 18 entities.

I need to duplicate those Record objects, so that I am detaching them first, this way:

var detached = this.DetachEntities(records, db, "RecordId");

Where this.DetachEntities is defined this way:

    private T DetachEntity<T>(T entity, Repositories.GestionActivosEntities db, string keyName) where T : class
    {
        db.Entry(entity).State = EntityState.Detached;

        if (entity.GetType().GetProperty(keyName) != null)
        {
            entity.GetType().GetProperty(keyName).SetValue(entity, 0);
        }

        return entity;
    }

    private List<T> DetachEntities<T>(List<T> entities, Repositories.GestionActivosEntities db, string keyName) where T : class
    {
        foreach (var entity in entities)
        {
            this.DetachEntity(entity, db, keyName);
        }

        return entities;
    }

After I detached the entities, I am doing:

db.Record.AddRange(detached);

The problem, is that, after I detached the list, the relation is not copied also, resulting that "relation" member variable contains no elements.

How can I keep the relation elements after detaching?

EDIT:

This shows the debugging session:

2 instructions 2 show the problem

The breakpoint is placed in the line where the query is done. The cursor shows the line after the query was executed.

This screenshot shows the child records ValorCampo and Workflow

This screenshot shows the child records ValorCampo and Workflow

This screenshot shows the entity after detaching it. Note the child records are not present in the detached object

This screenshot shows the entity after detaching it. Note the child records are not present in the detached object

As I have told, the problem only is when detaching. Child records are not kept. Database rows remain untouched.

jstuardo
  • 3,901
  • 14
  • 61
  • 136
  • What do you mean by "the relation is not copied also" and what "relation" member variable contains no elements? Please [edit] your question to include a [mcve], which can be compiled and tested by others. – Progman Dec 18 '19 at 22:38
  • I can edit the question tomorrow but I am not sure if that will help. As I told you, this is a one to many relationship. "Record" s the parent entity, and "Relation" is the collection of child items. The original Record contains 18 child elements. After detaching, the Records contains 0 child elements. – jstuardo Dec 18 '19 at 23:49
  • The MCVE will help as it shows how the data is added, how you work with context objects and how you check for the number of entities. Detaching an object does not remove the row in the database. So the MCVE should clearly show that the number of child elements is not as expected. – Progman Dec 19 '19 at 00:05
  • @Progman As you should know, relationships are loaded automatically by entity framework.By doing "mydata.Where(a => some condition).ToList()", all relationships records are loaded automatically by the framework. I do nothing to load them.By the way, I think it is because that by default "lazy loading" is used. How can I force certain relationship to be loaded at the first query? But only one relationship. I am not interested in the others. Furthermore, maybe you did not understand the problem. Records are not removed in the DB. Problem is that detached object does not keep child records. – jstuardo Dec 19 '19 at 10:20
  • Relationships of entities are not loaded automatically, only on request. Otherwise you would load the whole database when you load only one entity. You can use `Include()` to load the related entities in your query without loading entities/relationships you don't want. "Lazy loading" might not be the issue here, but it's hard to tell. Please provide a MCVE which clearly shows the error in counting the entities. – Progman Dec 19 '19 at 10:27
  • @Progman I edit the question. I have added screenshots of debugging session. – jstuardo Dec 19 '19 at 11:18
  • Keep in mind that `Detach()` only detach the given object, not the related entities, see https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.detach?view=netframework-4.8. Also, the children lists are empty because there are no entities "pointing" to the detached entities, they are still referencing to the original entities (which aren't in the current context anymore) based on the foreign keys they have. It wouldn't make sense to have them in these children list of the detached objects. The foreign key can have only one Id value. – Progman Dec 19 '19 at 11:26
  • @Progman yes, I know that, but I was not actually looking for an explanation of why this is happening. I am looking for a solution. Microsoft documents only tells what is known, but not how to solve it. The title of the question is about "Duplicating Entities". To me, it would be very easy to do a For Each through the entities and then creating new objects, using db.Entity.Add instruction and finally db.SaveChanges, but I think this could be terrible slow. That's is why I thought about detaching so that I would not need to assign the properties "by hand" using a for each loop. – jstuardo Dec 19 '19 at 11:42
  • You might want to look at https://stackoverflow.com/questions/24828287/entity-framework-duplicate-object-and-all-child-properties/24828907#24828907 to load the entities with `AsNoTracking()` (in combination with `Include()`). However you might need to reset the primary key values so EF doesn't try to insert a new row with an existing primary key for the "parent" entity as well for all "child" entities. – Progman Dec 19 '19 at 11:50

1 Answers1

0

I had the same problem, unfortunately navigation properties are lost after detaching an Item or when entity state is changed to detached.

what you can do is clone the entity one way to do this is : Context.Entry(your_entity).CurrentValues.ToObject();

however this will not clone the navigation properties either

if you fully want to clone an object among with navigation properties the easiest way for me to achieve it was using automapper library for c#

Below is a sample usage:

var config = new MapperConfiguration(cfg => cfg.CreateMap<originalObject, T>());
var mapper = new Mapper(config);
// or
var mapper = config.CreateMapper();

T  clonedObject = mapper.Map<T>(originalObject);

after you clone and detach the original object, you can add

db.Record.AddRange(clonedObject );

and below is a generic extension to do it

public static object Map<T>(this T source)
    {
        var fullName = source.GetType().FullName;
        var sourceType = source.GetType();

        var baseType = ObjectContext.GetObjectType(source.GetType());

        var config = new MapperConfiguration(cfg =>
            cfg.CreateMap(sourceType, baseType));




        var mapper = config.CreateMapper();

        var entity = mapper.Map(source, sourceType, baseType);

        return entity;
}

where you can call it like

var clonedObject = originalObject.Map();

Hope this helps!

Asım Gündüz
  • 1,257
  • 3
  • 17
  • 42