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

CosmosDb: Nested OwnsMany with OwnsOne returns NULL collection #19159

Closed
akrobet opened this issue Dec 4, 2019 · 8 comments · Fixed by #19784
Closed

CosmosDb: Nested OwnsMany with OwnsOne returns NULL collection #19159

akrobet opened this issue Dec 4, 2019 · 8 comments · Fixed by #19784
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@akrobet
Copy link

akrobet commented Dec 4, 2019

I have this issue where the root entity contains an owned collection, which owns a nested inner collection and an other entity (the model is just made up for demo purposes)

The persisted document looks like this:

{
	"Id": "9a215264-ab60-4fbd-bdbf-2161c03dce5d",
	"Discriminator": "Customer",
	"id": "Customer",
	"Addresses": [
		{
			"Id": "09a35c9b-0335-4391-8f4b-d05a2179e245",
			"Lines": [
				{
					"Value": "Line1 04-12-2019 13:08:32"
				},
				{
					"Value": "Line2 04-12-2019 13:08:32"
				}
			],
			"Title": {
				"Value": "Title 04-12-2019 13:08:32"
			}
		}
	],
	"_rid": "hYobAKSGjGcEAAAAAAAAAA==",
	"_self": "dbs/hYobAA==/colls/hYobAKSGjGc=/docs/hYobAKSGjGcEAAAAAAAAAA==/",
	"_etag": "\"250080a7-0000-0d00-0000-5de7a1c30000\"",
	"_attachments": "attachments/",
	"_ts": 1575461315
}

The data gets created properly, and is returned within the same context, but on subsequent reads, the nested inner collection is null.

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

namespace CosmosDbEFCore
{
    class Program
    {
        static void Main(string[] args)
        {
            var demo = new Demo();
            var createResults = demo.CreateData();
            var addressLinesWritten = createResults.Single().Addresses.Where(a => a.Lines != null).SelectMany(a => a.Lines).ToList();

            var readResults = demo.ReadData();
            var addressLinesRead = readResults.Single().Addresses.Where(a => a.Lines != null).SelectMany(a => a.Lines).ToList();

            Debug.WriteLine($"addressLinesWritten: {addressLinesWritten.Count()}");
            Debug.WriteLine($"addressLinesRead: {addressLinesRead.Count()}");
        }
    }

    public class Demo
    {
        public IEnumerable<Customer> ReadData()
        {
            using (var context = new DemoContext())
            {
                var customers = context.Customers.ToList();
                return customers;
            }
        }

        public IEnumerable<Customer> CreateData()
        {
            using (var context = new DemoContext())
            {
                context.Database.EnsureCreated();

                var customers = context.Customers.ToList();
                context.Customers.RemoveRange(customers);
                context.SaveChanges();
            }

            using (var context = new DemoContext())
            {
                var addresses = new List<Address>
                {
                    new Address()
                    {
                        Id = Guid.NewGuid().ToString(),
                        Lines = new List<AddressLine>
                        {
                            new AddressLine { Value = "Line1 " + DateTime.Now  },
                            new AddressLine { Value = "Line2 " + DateTime.Now  }
                        },
                        Title = new AddressTitle { Value = "Title " + DateTime.Now }
                    }
                };

                var customer = new Customer()
                {
                    Id = Guid.NewGuid().ToString(),
                    Addresses = addresses
                };

                context.Customers.Add(customer);
                context.SaveChanges();

                var results = context.Customers.ToList();

                return results;
            }
        }
    }

        public class DemoContext : DbContext
        {
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
                => optionsBuilder.UseCosmos(
                    "https://redacted.documents.azure.com:443/",
                    "REDACTED",
                    databaseName: "Demo");

            public DbSet<Customer> Customers { get; set; }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.HasDefaultContainer("Demo");
                modelBuilder.Entity<Customer>()
                    .HasPartitionKey(c => c.Id)
                    .OwnsMany(c => c.Addresses, b =>
                    {
                        b.OwnsOne(a => a.Title);
                        b.OwnsMany(a => a.Lines);
                    });
            }
        }

    public class Customer
    {
        public string Id { get; set; }
        public ICollection<Address> Addresses { get; set; }
    }
    public class Address
    {
        public string Id { get; set; }
        public AddressTitle Title { get; set; }
        public ICollection<AddressLine> Lines { get; set; }
    }
    public class AddressTitle
    {
        public string Value { get; set; } = string.Empty;
    }
    public class AddressLine
    {
        public string Value { get; set; } = string.Empty;
    }    
}
@akrobet
Copy link
Author

akrobet commented Dec 4, 2019

If I remove the public AddressTitle Title { get; set; } property from Address and mapping, I will get results back on the subsequent read, and dulicates in the same context I inserted them in.. (as described in #18948

@ajcvickers
Copy link
Member

@akrobet What versions of EF Core are you using?

@akrobet
Copy link
Author

akrobet commented Dec 5, 2019

@ajcvickers <PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="3.1.0" />

@ajcvickers
Copy link
Member

@AndriySvyryd to take a look.

@rameshmummidi30
Copy link

I am also getting same issue issue public class PlansGeneratedEventConfiguration : Microsoft.EntityFrameworkCore.IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.HasKey(entity => entity.id);
builder.Property(entity => entity.id);
builder.Property(entity => entity.GeneratePlanUniqueId);
builder.OwnsMany(entity => entity.Plans, plansBuilder =>
{
plansBuilder.Property(a => a.ArrivalTime);
plansBuilder.Property(a => a.DepartureTime);
plansBuilder.Property(a => a.DriverId);
plansBuilder.Property(a => a.TruckId);
plansBuilder.Property(a => a.Id);
plansBuilder.HasKey(a => a.Id);
plansBuilder.Property(a => a.TotalDistance);
plansBuilder.Property(a => a.TotalDuration);
plansBuilder.Property(a => a.Score);
plansBuilder.OwnsMany(a => a.Legs, planLegsBuilder =>
{
planLegsBuilder.Property(a => a.Id);
planLegsBuilder.HasKey(a => a.Id);
planLegsBuilder.Property(a => a.Sequence);
planLegsBuilder.Property(a => a.LegType).HasConversion(
v => v.ToString(),
v => (LegTypeEnum)Enum.Parse(typeof(LegTypeEnum), v));
planLegsBuilder.Property(a => a.Chassis).HasConversion(
v => v.ToString(),
v => (ChassisEnum)Enum.Parse(typeof(ChassisEnum), v));
planLegsBuilder.Property(a => a.MoveId);

					planLegsBuilder.OwnsOne(a => a.PickUpAction, sourceActionBuilder =>
					{
						//sourceActionBuilder.Property(s => s.AppointmentTime);
						sourceActionBuilder.Property(s => s.AppointmentTimeFrom);
						sourceActionBuilder.Property(s => s.AppointmentTimeTo);
						sourceActionBuilder.Property(s => s.CompletionTime);
						//sourceActionBuilder.Property(s => s.ContainerAction);
						sourceActionBuilder.Property(s => s.ContainerAction).HasConversion(
									   v => v.ToString(),
									   v => (ContainerActionEnum)Enum.Parse(typeof(ContainerActionEnum), v));
						sourceActionBuilder.OwnsOne(l => l.LocationPoint, locationGeoTimeBuilder =>
						{
							locationGeoTimeBuilder.Property(l => l.ArrivalTime);
							locationGeoTimeBuilder.OwnsOne(l => l.Location, locationBuilder =>
							{
								locationBuilder.OwnsOne(location => location.Address, addressBuilder =>
								{
									addressBuilder.Property(address => address.AddressLine);
									addressBuilder.Property(address => address.CityState);
									addressBuilder.Property(address => address.Zip);
								});
								locationBuilder.Property(location => location.Code);
								locationBuilder.Property(location => location.Name);
							});
							locationGeoTimeBuilder.OwnsOne(l => l.Geo, geoBuilder =>
							{
								geoBuilder.Property(g => g.Latitude);
								geoBuilder.Property(g => g.Longitude);
							});
						});
					});
					planLegsBuilder.OwnsOne(a => a.DropOffAction, destinationActionBuilder =>
					{
						//destinationActionBuilder.Property(s => s.AppointmentTime);
						destinationActionBuilder.Property(s => s.AppointmentTimeFrom);
						destinationActionBuilder.Property(s => s.AppointmentTimeTo);
						destinationActionBuilder.Property(s => s.CompletionTime);
						//destinationActionBuilder.Property(s => s.ContainerAction);
						destinationActionBuilder.Property(s => s.ContainerAction).HasConversion(
									   v => v.ToString(),
									   v => (ContainerActionEnum)Enum.Parse(typeof(ContainerActionEnum), v));
						destinationActionBuilder.OwnsOne(l => l.LocationPoint, locationGeoTimeBuilder =>
						{
							locationGeoTimeBuilder.Property(l => l.ArrivalTime);
							locationGeoTimeBuilder.OwnsOne(l => l.Location, locationBuilder =>
							{
								locationBuilder.OwnsOne(location => location.Address, addressBuilder =>
								{
									addressBuilder.Property(address => address.AddressLine);
									addressBuilder.Property(address => address.CityState);
									addressBuilder.Property(address => address.Zip);
								});
								locationBuilder.Property(location => location.Code);
								locationBuilder.Property(location => location.Name);
							});
							locationGeoTimeBuilder.OwnsOne(l => l.Geo, geoBuilder =>
							{
								geoBuilder.Property(g => g.Latitude);
								geoBuilder.Property(g => g.Longitude);
							});
						});
					});
					planLegsBuilder.Property(a => a.EstimatedDistance);
					planLegsBuilder.Property(a => a.EstimatedDuration);

					planLegsBuilder.Property(a => a.OrderId);
					planLegsBuilder.Property(a => a.ContainerId);
					planLegsBuilder.Property(a => a.ContainerSize);
					planLegsBuilder.Property(a => a.TurnTime);

					//planLegsBuilder.Property(a => a.LegType);



				});
		});
	}
}

@rameshmummidi30
Copy link

I am also getting same issue getting null nested object below is mapping class :

public class PlansGeneratedEventConfiguration : Microsoft.EntityFrameworkCore.IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.HasKey(entity => entity.id);
builder.Property(entity => entity.id);
builder.Property(entity => entity.GeneratePlanUniqueId);
builder.OwnsMany(entity => entity.Plans, plansBuilder =>
{
plansBuilder.Property(a => a.ArrivalTime);
plansBuilder.Property(a => a.DepartureTime);
plansBuilder.Property(a => a.DriverId);
plansBuilder.Property(a => a.TruckId);
plansBuilder.Property(a => a.Id);
plansBuilder.HasKey(a => a.Id);
plansBuilder.Property(a => a.TotalDistance);
plansBuilder.Property(a => a.TotalDuration);
plansBuilder.Property(a => a.Score);
plansBuilder.OwnsMany(a => a.Legs, planLegsBuilder =>
{
planLegsBuilder.Property(a => a.Id);
planLegsBuilder.HasKey(a => a.Id);
planLegsBuilder.Property(a => a.Sequence);
planLegsBuilder.Property(a => a.LegType).HasConversion(
v => v.ToString(),
v => (LegTypeEnum)Enum.Parse(typeof(LegTypeEnum), v));
planLegsBuilder.Property(a => a.Chassis).HasConversion(
v => v.ToString(),
v => (ChassisEnum)Enum.Parse(typeof(ChassisEnum), v));
planLegsBuilder.Property(a => a.MoveId);

				planLegsBuilder.OwnsOne(a => a.PickUpAction, sourceActionBuilder =>
				{
					//sourceActionBuilder.Property(s => s.AppointmentTime);
					sourceActionBuilder.Property(s => s.AppointmentTimeFrom);
					sourceActionBuilder.Property(s => s.AppointmentTimeTo);
					sourceActionBuilder.Property(s => s.CompletionTime);
					//sourceActionBuilder.Property(s => s.ContainerAction);
					sourceActionBuilder.Property(s => s.ContainerAction).HasConversion(
								   v => v.ToString(),
								   v => (ContainerActionEnum)Enum.Parse(typeof(ContainerActionEnum), v));
					sourceActionBuilder.OwnsOne(l => l.LocationPoint, locationGeoTimeBuilder =>
					{
						locationGeoTimeBuilder.Property(l => l.ArrivalTime);
						locationGeoTimeBuilder.OwnsOne(l => l.Location, locationBuilder =>
						{
							locationBuilder.OwnsOne(location => location.Address, addressBuilder =>
							{
								addressBuilder.Property(address => address.AddressLine);
								addressBuilder.Property(address => address.CityState);
								addressBuilder.Property(address => address.Zip);
							});
							locationBuilder.Property(location => location.Code);
							locationBuilder.Property(location => location.Name);
						});
						locationGeoTimeBuilder.OwnsOne(l => l.Geo, geoBuilder =>
						{
							geoBuilder.Property(g => g.Latitude);
							geoBuilder.Property(g => g.Longitude);
						});
					});
				});
				planLegsBuilder.OwnsOne(a => a.DropOffAction, destinationActionBuilder =>
				{
					//destinationActionBuilder.Property(s => s.AppointmentTime);
					destinationActionBuilder.Property(s => s.AppointmentTimeFrom);
					destinationActionBuilder.Property(s => s.AppointmentTimeTo);
					destinationActionBuilder.Property(s => s.CompletionTime);
					//destinationActionBuilder.Property(s => s.ContainerAction);
					destinationActionBuilder.Property(s => s.ContainerAction).HasConversion(
								   v => v.ToString(),
								   v => (ContainerActionEnum)Enum.Parse(typeof(ContainerActionEnum), v));
					destinationActionBuilder.OwnsOne(l => l.LocationPoint, locationGeoTimeBuilder =>
					{
						locationGeoTimeBuilder.Property(l => l.ArrivalTime);
						locationGeoTimeBuilder.OwnsOne(l => l.Location, locationBuilder =>
						{
							locationBuilder.OwnsOne(location => location.Address, addressBuilder =>
							{
								addressBuilder.Property(address => address.AddressLine);
								addressBuilder.Property(address => address.CityState);
								addressBuilder.Property(address => address.Zip);
							});
							locationBuilder.Property(location => location.Code);
							locationBuilder.Property(location => location.Name);
						});
						locationGeoTimeBuilder.OwnsOne(l => l.Geo, geoBuilder =>
						{
							geoBuilder.Property(g => g.Latitude);
							geoBuilder.Property(g => g.Longitude);
						});
					});
				});
				planLegsBuilder.Property(a => a.EstimatedDistance);
				planLegsBuilder.Property(a => a.EstimatedDuration);

				planLegsBuilder.Property(a => a.OrderId);
				planLegsBuilder.Property(a => a.ContainerId);
				planLegsBuilder.Property(a => a.ContainerSize);
				planLegsBuilder.Property(a => a.TurnTime);

				//planLegsBuilder.Property(a => a.LegType);



			});
	});
}

}

@JackSkylark
Copy link

I am seeing the same issue as well.

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="3.1.1" />

@nickwilsonr
Copy link

This issue is very similar to what we're seeing..
@AndriySvyryd - I created a repro of the issue we're seeing here: https://github.com/nickwilsonr/EFCosmosNullGetRepro

@ajcvickers ajcvickers modified the milestones: 3.1.x, 3.1.3 Feb 15, 2020
@AndriySvyryd AndriySvyryd removed their assignment Mar 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants