简体   繁体   中英

Saving an EF instance which has multiple parents

When mapping back from business logic objects to EF objects, one of the main problems I have is cases where the same instance has 2 parents:

在此输入图像描述

(Objects are yellow, properties are orange)

In the business logic world, there is only one instance of the Tree object here (which appears as a child of multiple parents: Forest and Section)

When I map everything back into EF objects with AutoMapper, EF thinks there are 2 separate instances of tree (despite them having the same ID). It therefore creates a duplicate in the DB.

What is the correct way to manage this scenario so that both Forest and Section point to the same record of Tree in the DB?

Do we have to go through and manually make sure everything is attached which might be considered a duplicate?

Unfortunatelly EF needs to get same instance of a Tree object to consider him as same during saving whole Forest graph (overriding of his equality members doesn't help), which is not how Automapper maps object graphs by default.

But you can set up your Automapper configuration in the way it reuses existing instances during mapping:

var config = new MapperConfiguration(cfg =>
{
   cfg.CreateMap<Tree, TreeEF>().PreserveReferences();
});

Than if you have in your bussines model Forest and Section having a child reference to the same instance of a Tree , this reference will be preserved and no duplicates will be created.

EDIT

class Program
{
    static void Main(string[] args)
    {
        var config = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Forest, ForestEF>().PreserveReferences();
                cfg.CreateMap<Section, SectionEF>().PreserveReferences();
                cfg.CreateMap<Tree, TreeEF>().PreserveReferences();
            });

        var mapper = config.CreateMapper();

        var forest = new Forest();
        var section = new Section();
        var tree = new Tree();

        forest.Trees.Add(tree);
        forest.Sections.Add(section);
        section.Trees.Add(tree);

        var result = mapper.Map<Forest, ForestEF>(forest);

        Console.WriteLine(object.ReferenceEquals(result.Trees[0], result.Sections[0].Trees[0]));
        Console.ReadLine();
    }
}

public class Forest
{
    public IList<Tree> Trees { get; set; } = new List<Tree>();
    public IList<Section> Sections { get; set; } = new List<Section>();
}

public class Section
{
    public IList<Tree> Trees { get; set; } = new List<Tree>();
}

public class Tree
{
}

public class ForestEF
{
    public IList<TreeEF> Trees { get; set; } = new List<TreeEF>();
    public IList<SectionEF> Sections { get; set; } = new List<SectionEF>();
}

public class SectionEF
{
    public IList<TreeEF> Trees { get; set; } = new List<TreeEF>();
}

public class TreeEF
{
}

I believe that if you don't want duplicates here, both children must not only reference the ID but also the specific instance in memory so EF knows it should be the same record (navigation property). Otherwise, you have to save the parent record first and then assign the key to each child after the fact. If this isn't a GUID but an auto generated id, then you probably need to use the same reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM