Efcore: Cosmos DB: NullReferenceException on SaveChanges when saving modified owned collection

Created on 16 Oct 2018  路  11Comments  路  Source: dotnet/efcore

Found this using nightly builds of Microsoft.EntityFrameworkCore.Cosmos.

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=Microsoft.EntityFrameworkCore.Cosmos
  StackTrace:
   at Microsoft.EntityFrameworkCore.Cosmos.Update.Internal.DocumentSource.UpdateDocument(JObject document, IUpdateEntry entry)
   at Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal.CosmosDatabase.Save(IUpdateEntry entry)
   at Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal.CosmosDatabase.SaveChanges(IReadOnlyList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Demos.Program.Main(String[] args) in C:\Users\divega\source\repos\CosmosDBNullRef\CosmosDBNullRef\Program.cs:line 64

Including the repro below. The error does not occur if I only query for the post and then modify the collection and save.

using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace Demos
{
    public class Program
    {
        public static void Main(string[] args)
        {

            using (var context = new BloggingContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                context.Blogs.AddRange(
                    new Blog
                    {
                        BlogId = 1,
                        Name = "ADO.NET",
                        Url = "http://blogs.msdn.com/adonet",
                        Posts = new List<Post>
                        {
                            new Post
                            {
                                PostId = 1,
                                Title = "Welcome to this blog!",
                                Tags = new List<Tag>
                                {
                                    new Tag
                                    {
                                        Name = "Meta"
                                    }
                                }

                            },
                            new Post
                            {
                                PostId = 2,
                                Title = "Getting Started with ADO.NET",
                                Tags = new List<Tag>
                                {
                                    new Tag
                                    {
                                        Name = "Entity Framework Core"
                                    },
                                    new Tag
                                    {
                                        Name = "ADO.NET"
                                    }
                                }
                            }
                        }
                    });

                context.SaveChanges();
            }

            using (var context = new BloggingContext())
            {
                var blog = context.Blogs.Include(b=>b.Posts).Single(b => b.Name == "ADO.NET");
                // adding element to owned collection
                blog.Posts[1].Tags.Add(new Tag { Name = ".NET Core" }); 
                // Exception is thrown here:
                context.SaveChanges();
            }
        }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseCosmos(
                    "https://localhost:8081",
                    "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
                    "EFCoreDemo");
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>().OwnsMany(p => p.Tags).HasKey(t => t.Name);
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public List<Tag> Tags { get; set; }
    }

    public class Tag
    {
        public string Name { get; set; }
    }
}

closed-fixed type-bug

All 11 comments

@divega I can fix the exception, but the Tag wouldn't be persistent anyway. The underlying issue is that we don't mark it as Added when detecting changes because the PK is already set. I don't remember exactly what was the reasoning for this behavior and whether we were already planning to change it. @ajcvickers thoughts?

@AndriySvyryd Is this a behavior specifically for owned types? Normally, DetectChanges always marks things as Added, regardless of key value. But I vaguely remember we tried to make some scenarios for owned entities work "better" by special casing what we do in DetectChanges. Is this something we should revisit?

I'm talking about https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore/ChangeTracking/Internal/EntityGraphAttacher.cs#L73
It's not owned-specific, though note that the Async versions got out-of-sync (pun intended) and does check ownership.

@ajcvickers Nevermind, that wasn't the real root issue. The existing owned entity wasn't tracked after querying due to https://github.com/aspnet/EntityFrameworkCore/issues/13579 and that confused the model differ as it was still part of the relationship snapshot.

@AndriySvyryd whats the progress on this. I am having the same issue with preview 4

14455 has been merged, does that mean this should be fixed now? Because I am still running into this issue. Is there a workaround I could use meanwhile?

@TomasHubelbauer That comment indicates that #14455 was required before this could be fixed, but not that this is fixed itself.

@TomasHubelbauer It's also worth noting that if you're using Cosmos in the 3.0 previews, then there is still a lot of work left to do to get the query overhaul integrated with Cosmos. This won't happen for preview6, so if you are doing this you're probably better off staying on preview5 for now.

Fixed in 7d4b425069486936369c923a1fe4a83240eb84e2

Currently Include doesn't work, so the repro wouldn't work as is, but the tracking issue is fixed.

Was this page helpful?
0 / 5 - 0 ratings