Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defining which wins if they are different, navigational links or foreign key values? #21105

Open
JonPSmith opened this issue Jun 2, 2020 · 0 comments

Comments

@JonPSmith
Copy link

@JonPSmith JonPSmith commented Jun 2, 2020

Summary

I am trying to get a confirmation/denial of the statement "a set navigational properties overrides the foreign key in a detached entity class" because I use this feature to copy entity classes with specific relationships.

Detailed reasoning

While updating the book Entity Framework Core in Action I wrote a unit test (see below) to find out what happens if a navigational property is set to one entity instance, and a foreign key was is set to link to a different entity instance.

[Fact]
public void TestWhatHappensIfRelationshipLinkAndForeignKeyAreBothSetOk()
{
    //SETUP
    var sqlOptions = SqliteInMemory.CreateOptions<EfCoreContext>();
    using (var context = new EfCoreContext(sqlOptions))
    {
        context.Database.EnsureCreated();

        //ATTEMPT
        var book1 = new Book
        {
            Title = "Test1",
            Reviews = new List<Review>()
        };
        var book2 = new Book
        {
            Title = "Test2",
        };
        context.Add(book2);
        context.SaveChanges();

        //This review has its FK set to book2, but its added to book1
        var review = new Review { BookId = book2.BookId, NumStars = 1 };
        book1.Reviews.Add(review);
        context.Add(book1);
        context.SaveChanges();

        //VERIFY
        var books = context.Books.Include(x => x.Reviews).OrderBy(x => x.Title).ToList();
        //This shows that adding the review to book1's Reviews navigational collection wins over the FK being set.
        books.Select(x => x.Reviews.Count).ShouldEqual(new[] { 1, 0 });
    }
}

(If you need the entity classes I can provide them, but the code is just to explain what I am looking at)

Having this information I copied an entity class with its relationships by

  1. Reading in the entity class with its relationships that I wanted to copy using AsNoTracking
  2. Resetting the primary keys to default (but not the foreign keys)
  3. Add(topEntity) and called SaveChanges

Here is the unit test that copies an Order, with its LineItems, but doesn't copy the Book entity.

[Fact]
public void TestCopyOrderOk()
{
    //SETUP
    var sqlOptions = SqliteInMemory.CreateOptions<EfCoreContext>();
    using (var context = new EfCoreContext(sqlOptions))
    {
        context.Database.EnsureCreated();

        var books = context.SeedDatabaseFourBooks();                
        var order = new Order                                       
        {
            CustomerId = Guid.Empty,                                
            LineItems = new List<LineItem>
            {
                new LineItem                                        
                {                                                   
                    LineNum = 1, ChosenBook = books[0], NumBooks = 1
                },                                                  
                new LineItem                                        
                {                                                   
                    LineNum = 2, ChosenBook = books[1], NumBooks = 2
                },                                                  
            }
        };
        context.Add(order);                                         
        context.SaveChanges();                                      
    }
    using (var context = new EfCoreContext(sqlOptions))
    {
        //ATTEMPT
        var order = context.Orders                   
            .AsNoTracking()                          
            .Include(x => x.LineItems)               
            // no include for the Book entity because I don't want to copy the book entity.                                        
            .Single();                               

        order.OrderId = default;                     
        order.LineItems.First().LineItemId = default;
        order.LineItems.Last().LineItemId = default; 
        context.Add(order);                          
        context.SaveChanges();    

        //VERIFY part left out
}                  

NOTE: This only works because the foreign key OrderId is overridden by the navigational property LineItems in the Order entity, but the BookId foreign key works, because the navigational property ChosenBook is null.

My requested solution

It would be useful if the statement that "a set navigational properties overrides the foreign key in a detached entity class" was confirmed. If that statement isn't true then knowing that is also useful.

@JonPSmith JonPSmith changed the title Question: defining which wins if they are different, navigational links or foreign key values? Defining which wins if they are different, navigational links or foreign key values? Jun 2, 2020
@ajcvickers ajcvickers added this to the Backlog milestone Jun 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.