Using Automapper on object tree with more than one circular reference between related objects gives inconsistent mapped output

I’ve run into some issues while using Automapper on an object tree where there are multiple circular references present, but I can’t say if the behavior is intended/expected when using Automapper, if I’m doing something wrong, or if it is indeed a bug. I don’t think what I am doing is particularly novel, so it would surprise me if the observed behavior is unexpected, but in that case I would like to know why it happens and how it can be avoided.

I’m working on an API in ASP.NET Core 8 and am using Automapper (13.0.1) to map from domain objects to a set of DTOs. I’ll isolate the problem I face by focusing on a representative set of objects rather than showing the whole model.

Say I have three objects, Book, Author and Publisher. A book can be authored by more than one author, so the Book has a List<Author> Authors property. Conversely, each author can have authored mulitple books, so the Author object has a List<Book> Books property. There exists a many-to-many relationship between these two objects (although I have only defined one author for each book). Each book is probably to be issued only by one publisher, so the Book object also defines a property called Publisher (of type Publisher), while the Publisher object has a List<Book> Books property. So there is a one-to-many relationship between Book and Publisher.

Using EF core, I can retrieve all the books from the data store, List<Book> books = DbContext.Books.ToList() and by default the Authors property will be an empty list and Publisher property will be null.

If I ask for either the authors or the publisher to be included, e.g. List<Book> books = DbContext.Books.Include(x=>x.Publisher).ToList() I will have one circular reference through Books.First().Publisher.Books, and a similar circular reference if I ask for the authors instead. Using MaxDepth(2) on the mapping profile between the Book entity and DTO takes care of that.

If I include both the authors and the publisher in the query, I have two circular references: Books.First().Publisher.Books and Books.First().Authors.First().Books. When I use Automapper on this object, I get unexpected behaviour (at least for me):

[
    {
        "id": 1,
        "bookName": "Book A",
        "publisherId": 1,
        "publisher": {
            "publisherName": "Publishing house Ltd",
            "books": [
                {
                    "bookName": "Book B",
                    "publisherId": 1,
                    "authors": [],
                    "id": 2
                },
                {
                    "bookName": "Book C",
                    "publisherId": 1,
                    "authors": [],
                    "id": 3
                }
            ],
            "id": 1
        },
        "authors": [
            {
                "firstName": "John",
                "lastName": "Doe",
                "books": [],
                "id": 1
            }
        ]
    },
    {
        "id": 2,
        "bookName": "Book B",
        "publisherId": 1,
        "publisher": {
            "name": "Publishing house Ltd",
            "books": [
                {
                    "bookName": "Book A",
                    "publisherId": 1,
                    "authors": [
                        {
                            "firstName": "John",
                            "lastName": "Doe",
                            "books": [],
                            "id": 1
                        }
                    ],
                    "id": 1
                },
                {
                    "bookName": "Book C",
                    "publisherId": 1,
                    "authors": [],
                    "id": 3
                }
            ],
            "id": 1
        },
        "authors": []
    },
    {
        "id": 3,
        "bookName": "Book C",
        "publisherId": 1,
        "publisher": {
            "name": "Publishing house Ltd",
            "books": [
                {
                    "bookName": "Book A",
                    "publisherId": 1,
                    "authors": [
                        {
                            "firstName": "John",
                            "lastName": "Doe",
                            "books": [],
                            "id": 1
                        }
                    ],
                    "id": 1
                },
                {
                    "bookName": "Book B",
                    "publisherId": 1,
                    "authors": [],
                    "id": 2
                }
            ],
            "id": 1
        },
        "authors": []
    }
]

In the list of mapped Book DTOs, the first book (call it Book A) will have the Publisher property populated, and that property’s Books property will be populated as well, with all books except the first book (Book A). Each book inside this property (Book B… ++) will have an empty array in their Authors property. Book A will also have the Authors property populated.

The second book, Book B, will have no Authors property mapped, just the Publisher property. However, unlike for Book A, where inside the Publisher property each Book (except Book A) were mapped in a similar fashion (e.g. each Book’s Authors property was an empty array), now some Books will have their Authors property mapped, while others have empty arrays in this property.

In addition to the different shape of the mapped DTO objects (some have the Authors property populated, while others do not), this also means that the information about who the actual author of Book B and C are is lost. Book B, in my example, was authored by a Author with Id=2 and Book C by an Author with Id=3. All three books were published by the same Publisher (with Id=1).

One solution that seems to work is to perform two database calls on and ask for one attached property at a time. In that case, the properties (either the Publisher or the Authors) will be correctly mapped on the first level of the resulting tree (the Book DTO) for each book in a list of Book DTOs. I can then copy the Authors object of one set of Book DTOs onto the other set of books (where the Publisher were already correctly mapped).

This, however, feels a bit wrong.

Please let me know if additional information is needed, or if this can be settled by the information I have provided so far.

Thanks!

EXPECTED BEHAVIOR: I tried including more than one attached property in the same database query. When the result is mapped to a DTO with Automapper, the resulting list of objects are not of the same shape. Some have attached properties on the top level, while other has it deeper down the object tree.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật