private Address CloneAddress(Address source)
 {
     return new Address
            {
                Street = source.Street,
                City = source.City,
                State = source.State,
                ZipCode = source.ZipCode,
                SiteInfo = CloneSiteInfo(source.SiteInfo)
            };
 }
        private void TestComplexCurentValue(DbEntityEntry<Building> entityEntry,
                                            DbComplexPropertyEntry<Building, Address> propertyEntry,
                                            Func<Address> getter)
        {
            var initialState = entityEntry.State;

            // Get these nested entries at the beginning and check that they remain in sync
            var cityEntry = propertyEntry.Property(a => a.City);
            var environmentEntry = propertyEntry.ComplexProperty(a => a.SiteInfo).Property(i => i.Environment);

            Assert.Equal(initialState == EntityState.Modified, propertyEntry.IsModified);
            Assert.Equal("Redmond", propertyEntry.CurrentValue.City);
            Assert.Equal("Clean", propertyEntry.CurrentValue.SiteInfo.Environment);
            Assert.Equal("Redmond", cityEntry.CurrentValue);
            Assert.Equal("Clean", environmentEntry.CurrentValue);

            // Getting complex object should return actual object
            Assert.Same(getter(), propertyEntry.CurrentValue);

            // Set to same value; prop should not get marked as modified
            propertyEntry.CurrentValue = getter();
            Assert.Equal(initialState == EntityState.Modified, propertyEntry.IsModified);
            Assert.Equal(initialState, entityEntry.State);

            // Set to new object with the same values; should get marked as modified
            var sameAddress = CloneAddress(getter());
            propertyEntry.CurrentValue = sameAddress;
            Assert.Equal("Redmond", propertyEntry.CurrentValue.City);
            Assert.Equal("Clean", propertyEntry.CurrentValue.SiteInfo.Environment);
            Assert.Equal("Redmond", cityEntry.CurrentValue);
            Assert.Equal("Clean", environmentEntry.CurrentValue);
            CheckPropertyIsModified(entityEntry, (DbComplexPropertyEntry)propertyEntry, initialState);
            Assert.Same(sameAddress, getter());

            // Reset state
            if (initialState == EntityState.Unchanged)
            {
                entityEntry.State = EntityState.Unchanged;
            }

            // Set to new value; prop marked as modified
            var newAddress = new Address
                             {
                                 Street = "300 Main St",
                                 City = "Ames",
                                 State = "IA",
                                 ZipCode = "50010",
                                 SiteInfo = new SiteInfo { Zone = 2, Environment = "Contaminated" }
                             };

            propertyEntry.CurrentValue = newAddress;
            Assert.Equal("Ames", propertyEntry.CurrentValue.City);
            Assert.Equal("Contaminated", propertyEntry.CurrentValue.SiteInfo.Environment);
            Assert.Equal("Ames", cityEntry.CurrentValue);
            Assert.Equal("Contaminated", environmentEntry.CurrentValue);
            CheckPropertyIsModified(entityEntry, (DbComplexPropertyEntry)propertyEntry, initialState);
            Assert.Same(newAddress, getter());

            // New value reflected in record
            if (initialState != EntityState.Deleted && initialState != EntityState.Detached)
            {
                var addressValues = (DbPropertyValues)entityEntry.CurrentValues["Address"];
                var siteValues = (DbPropertyValues)addressValues["SiteInfo"];

                Assert.Equal("Ames", addressValues["City"]);
                Assert.Equal("Contaminated", siteValues["Environment"]);

                // Change record; new value reflected in entry
                addressValues["City"] = "Cedar Falls";
                siteValues["Environment"] = "Peachy";

                Assert.Equal("Cedar Falls", propertyEntry.CurrentValue.City);
                Assert.Equal("Peachy", propertyEntry.CurrentValue.SiteInfo.Environment);
            }

            // Set to null
            Assert.Throws<InvalidOperationException>(() => propertyEntry.CurrentValue = null).ValidateMessage(
                "DbPropertyValues_ComplexObjectCannotBeNull", "Address", "Building");

            // Set to new value that has a nested null complex object
            // Should always throw, but currently only throws if the entity is Added/Modified/Unchanged
            if (initialState != EntityState.Detached && initialState != EntityState.Deleted)
            {
                var addressWithNull = new Address
                                      {
                                          Street = "300 Main St",
                                          City = "Ames",
                                          State = "IA",
                                          ZipCode = "50010",
                                          SiteInfo = null
                                      };
                Assert.Throws<InvalidOperationException>(() => propertyEntry.CurrentValue = addressWithNull).
                    ValidateMessage("DbPropertyValues_ComplexObjectCannotBeNull", "SiteInfo", "Address");
            }
        }
        private void TestComplexOriginalValue(DbEntityEntry<Building> entityEntry,
                                              DbPropertyEntry<Building, Address> propertyEntry)
        {
            var initialState = entityEntry.State;

            if (initialState == EntityState.Added)
            {
                Assert.Throws<InvalidOperationException>(() => { var _ = propertyEntry.OriginalValue; }).ValidateMessage
                    ("DbPropertyValues_CannotGetValuesForState", "OriginalValues", "Added");
                Assert.Throws<InvalidOperationException>(() => propertyEntry.OriginalValue = new Address()).
                    ValidateMessage("DbPropertyValues_CannotGetValuesForState", "OriginalValues", "Added");
                return;
            }

            if (initialState == EntityState.Detached)
            {
                Assert.Throws<InvalidOperationException>(() => { var _ = propertyEntry.OriginalValue; }).ValidateMessage
                    ("DbPropertyEntry_NotSupportedForDetached", "OriginalValue", propertyEntry.Name,
                     entityEntry.Entity.GetType().Name);
                Assert.Throws<InvalidOperationException>(() => propertyEntry.OriginalValue = new Address()).
                    ValidateMessage("DbPropertyEntry_NotSupportedForDetached", "OriginalValue", propertyEntry.Name,
                                    entityEntry.Entity.GetType().Name);
                return;
            }

            Assert.Equal(initialState == EntityState.Modified, propertyEntry.IsModified);
            Assert.Equal("Redmond", propertyEntry.OriginalValue.City);
            Assert.Equal("Clean", propertyEntry.OriginalValue.SiteInfo.Environment);

            // Set to new object with the same values; should not get marked as modified because no values changing
            var sameAddress = CloneAddress(propertyEntry.OriginalValue);
            propertyEntry.OriginalValue = sameAddress;
            Assert.Equal("Redmond", propertyEntry.OriginalValue.City);
            Assert.Equal("Clean", propertyEntry.OriginalValue.SiteInfo.Environment);
            Assert.Equal(initialState == EntityState.Modified, propertyEntry.IsModified);
            Assert.Equal(initialState, entityEntry.State);

            // Set to new value; prop marked as modified
            var newAddress = new Address
                             {
                                 Street = "300 Main St",
                                 City = "Ames",
                                 State = "IA",
                                 ZipCode = "50010",
                                 SiteInfo = new SiteInfo { Zone = 2, Environment = "Contaminated" }
                             };

            propertyEntry.OriginalValue = newAddress;
            Assert.Equal("Ames", propertyEntry.OriginalValue.City);
            Assert.Equal("Contaminated", propertyEntry.OriginalValue.SiteInfo.Environment);
            CheckPropertyIsModified(entityEntry, propertyEntry, initialState);

            // New value reflected in record
            if (initialState != EntityState.Added && initialState != EntityState.Detached)
            {
                var addressValues = (DbPropertyValues)entityEntry.OriginalValues["Address"];
                var siteValues = (DbPropertyValues)addressValues["SiteInfo"];

                Assert.Equal("Ames", addressValues["City"]);
                Assert.Equal("Contaminated", siteValues["Environment"]);

                // Change record; new value reflected in entry
                addressValues["City"] = "Cedar Falls";
                siteValues["Environment"] = "Peachy";

                Assert.Equal("Cedar Falls", propertyEntry.OriginalValue.City);
                Assert.Equal("Peachy", propertyEntry.OriginalValue.SiteInfo.Environment);
            }

            // Set to null
            Assert.Throws<InvalidOperationException>(() => propertyEntry.OriginalValue = null).ValidateMessage(
                "DbPropertyValues_ComplexObjectCannotBeNull", "Address", "Building");

            // Set to new value that has a nested null complex object
            // Should always throw, but Originally only throws if the entity is Added/Modified/Unchanged
            if (initialState != EntityState.Detached && initialState != EntityState.Deleted)
            {
                var addressWithNull = new Address
                                      {
                                          Street = "300 Main St",
                                          City = "Ames",
                                          State = "IA",
                                          ZipCode = "50010",
                                          SiteInfo = null
                                      };
                Assert.Throws<InvalidOperationException>(() => propertyEntry.OriginalValue = addressWithNull).
                    ValidateMessage("DbPropertyValues_ComplexObjectCannotBeNull", "SiteInfo", "Address");
            }
        }