public void Inherit_Run_CUD_Update_Derived()
        {
            // Inheritance is City <-- CityWithEditHistory <-- CityWithInfo
            CityDomainContext citiesContext    = new CityDomainContext(TestURIs.Cities);
            DateTime          priorLastUpdated = DateTime.Now;

            // Load all cities, not just derived ones
            LoadOperation   lo                 = citiesContext.Load(citiesContext.GetCitiesQuery());
            SubmitOperation so                 = null;
            CityWithInfo    cityWithInfo       = null;
            string          originalName       = null;
            string          originalStateName  = null;
            string          originalCountyName = null;

            // wait for Load to complete
            EnqueueConditional(() => lo.IsComplete);

            EnqueueCallback(delegate
            {
                cityWithInfo = citiesContext.Cities.OfType <CityWithInfo>().FirstOrDefault();
                Assert.IsNotNull(cityWithInfo, "expected to find at least one CityWithInfo entity");
                Assert.IsFalse(cityWithInfo.EditHistory.Contains("update"), "Did not expect edit history to be set yet.");

                originalName       = cityWithInfo.Name;
                originalStateName  = cityWithInfo.StateName;
                originalCountyName = cityWithInfo.CountyName;

                cityWithInfo.Info = "inserted new info";

                so = citiesContext.SubmitChanges();
            });
            // wait for submit to complete
            EnqueueConditional(() => so.IsComplete);
            EnqueueCallback(delegate
            {
                if (so.Error != null)
                {
                    Assert.Fail("Unexpected error on submit: " + so.Error.Message);
                }

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                CityWithInfo updatedCity = citiesContext.Cities.OfType <CityWithInfo>().SingleOrDefault <CityWithInfo>
                                               (c => (c.Name == originalName &&
                                                      c.StateName == originalStateName &&
                                                      c.CountyName == originalCountyName));
                Assert.IsNotNull(updatedCity, "Did not find modified City after the submit");
                Assert.IsTrue(updatedCity.EditHistory.Contains("update"), "EditHistory was" + updatedCity.EditHistory);
                Assert.AreEqual("inserted new info", updatedCity.Info, "Updated Info did not get applied");
            });

            EnqueueTestComplete();
        }
        public void Inherit_Run_CUD_Insert_Derived()
        {
            // Inheritance is City <-- CityWithEditHistory <-- CityWithInfo
            CityDomainContext citiesContext    = new CityDomainContext(TestURIs.Cities);
            DateTime          priorLastUpdated = DateTime.Now;

            // Load all cities, not just derived ones
            LoadOperation   lo = citiesContext.Load(citiesContext.GetCitiesQuery());
            SubmitOperation so = null;

            // wait for Load to complete
            EnqueueConditional(() => lo.IsComplete);

            EnqueueCallback(delegate
            {
                CityWithInfo newCity = new CityWithInfo()
                {
                    Name = "CocoaVille", StateName = "WA", CountyName = "King", Info = "stuff"
                };
                citiesContext.Cities.Add(newCity);

                so = citiesContext.SubmitChanges();
            });
            // wait for submit to complete
            EnqueueConditional(() => so.IsComplete);
            EnqueueCallback(delegate
            {
                if (so.Error != null)
                {
                    Assert.Fail("Unexpected error on submit: " + so.Error.Message);
                }

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                CityWithInfo newCity = citiesContext.Cities.OfType <CityWithInfo>().SingleOrDefault <CityWithInfo>(c => (c.Name == "CocoaVille"));
                Assert.IsNotNull(newCity, "Did not find modified City after the submit");
                Assert.IsTrue(newCity.EditHistory.Contains("insert"), "EditHistory was" + newCity.EditHistory);
            });

            EnqueueTestComplete();
        }
        public void Inherit_Run_CUD_Delete_Derived()
        {
            // Inheritance is City <-- CityWithEditHistory <-- CityWithInfo
            CityDomainContext citiesContext    = new CityDomainContext(TestURIs.Cities);
            DateTime          priorLastUpdated = DateTime.Now;
            LoadOperation     lo                 = null;
            SubmitOperation   so                 = null;
            CityWithInfo      cityWithInfo       = null;
            string            originalName       = null;
            string            originalStateName  = null;
            string            originalCountyName = null;

            // Invoke service operation to clear out all static data
            // (we rely on static data for deleted cities so that it
            //  survives across queries)
            InvokeOperation invoke = citiesContext.ResetData(null, null);

            EnqueueConditional(delegate
            {
                return(invoke.IsComplete);
            });
            EnqueueCallback(delegate
            {
                if (invoke.Error != null)
                {
                    Assert.Fail("Failed on invoke of ResetData: " + invoke.Error.Message);
                }
            });

            EnqueueCallback(delegate
            {
                // Load all cities, not just derived ones
                lo = citiesContext.Load(citiesContext.GetCitiesQuery());
            });


            // wait for Load to complete
            EnqueueConditional(() => lo.IsComplete);

            EnqueueCallback(delegate
            {
                cityWithInfo = citiesContext.Cities.OfType <CityWithInfo>().FirstOrDefault();
                Assert.IsNotNull(cityWithInfo, "expected to find at least one CityWithInfo entity");
                Assert.IsFalse(cityWithInfo.EditHistory.Contains("update"), "Did not expect edit history to be set yet.");

                originalName       = cityWithInfo.Name;
                originalStateName  = cityWithInfo.StateName;
                originalCountyName = cityWithInfo.CountyName;

                // Delete it.  Note that the delete CUD method in the CityDomainService
                // moves the deleted city over into DeletedCities so it can still be found
                citiesContext.Cities.Remove(cityWithInfo);

                so = citiesContext.SubmitChanges();
            });
            // wait for submit to complete
            EnqueueConditional(() => so.IsComplete);
            EnqueueCallback(delegate
            {
                if (so.Error != null)
                {
                    Assert.Fail("Unexpected error on submit: " + so.Error.Message);
                }

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                CityWithInfo deletedCity = citiesContext.Cities.OfType <CityWithInfo>().SingleOrDefault <CityWithInfo>
                                               (c => (c.Name == originalName &&
                                                      c.StateName == originalStateName &&
                                                      c.CountyName == originalCountyName));
                Assert.IsNull(deletedCity, "Did not expect to find deleted City after the submit");

                // Load the deleted cities (it was tombstoned)
                citiesContext.Cities.Clear();
                lo = citiesContext.Load(citiesContext.GetDeletedCitiesQuery());
            });

            // Wait for deleted city query to complete
            EnqueueConditional(() => lo.IsComplete);

            EnqueueCallback(delegate
            {
                if (lo.Error != null)
                {
                    Assert.Fail("Unexpected error on load of deleted queries: " + lo.Error.Message);
                }

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                CityWithInfo deletedCity = citiesContext.Cities.OfType <CityWithInfo>().SingleOrDefault <CityWithInfo>
                                               (c => (c.Name == originalName &&
                                                      c.StateName == originalStateName &&
                                                      c.CountyName == originalCountyName));
                Assert.IsNotNull(deletedCity, "Expected to find deleted City in the tombstone list");
                Assert.IsTrue(deletedCity.EditHistory.Contains("delete"), "Expected edit history to show delete but it only shows: " + deletedCity.EditHistory);
            });

            EnqueueTestComplete();
        }
        public void Inherit_Run_Call_Derived_Custom_Method_On_Abstract_Base_()
        {
            // Inheritance is City <-- CityWithEditHistory <-- CityWithInfo
            // This test invokes a custom method declared on CityWithEditHistory via a CityWithInfo instance
            EntityChangeSet   changeset;
            List <string>     propChanged      = new List <string>();
            CityDomainContext citiesProvider   = new CityDomainContext(TestURIs.Cities);
            CityWithInfo      cityWithInfo     = null;
            DateTime          priorLastUpdated = DateTime.Now;

            LoadOperation   lo = citiesProvider.Load(citiesProvider.GetCitiesWithInfoQuery());
            SubmitOperation so = null;

            // wait for Load to complete, then invoke some domain methods
            EnqueueConditional(() => lo.IsComplete);
            EnqueueCallback(delegate
            {
                cityWithInfo = citiesProvider.Cities.FirstOrDefault() as CityWithInfo;
                Assert.IsNotNull(cityWithInfo, "Cities[0] should have been CityWithInfo but was " + citiesProvider.Cities.First().GetType().Name);
                changeset = citiesProvider.EntityContainer.GetChanges();
                Assert.IsTrue(changeset.IsEmpty);
                cityWithInfo.PropertyChanged += delegate(object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                    // We filter to only those properties we see in the City hierarchy
                    BindingFlags flags  = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
                    bool isCityProperty = (typeof(CityWithInfo).GetProperty(e.PropertyName, flags) != null ||
                                           typeof(CityWithEditHistory).GetProperty(e.PropertyName, flags) != null ||
                                           typeof(City).GetProperty(e.PropertyName, flags) != null);
                    if (isCityProperty)
                    {
                        propChanged.Add(e.PropertyName);
                    }
                };

                priorLastUpdated = cityWithInfo.LastUpdated;
                Assert.IsTrue(cityWithInfo.CanTouchHistory);
                cityWithInfo.TouchHistory("xxx");
            });

            // wait for prop changed for domain method guards
            EnqueueConditional(() => propChanged.Count > 0);

            // Inject small delay so DateTime.Now executed on server is later than value in cithWithInfo
            EnqueueDelay(50);

            // Test validation that we will, in fact, get a different time stamp
            EnqueueCallback(delegate
            {
                Assert.AreNotEqual(priorLastUpdated, DateTime.Now, "Expected difference in times after small delay");
            });

            EnqueueCallback(delegate
            {
                Assert.IsTrue(propChanged.Contains("CanTouchHistory"));
                propChanged.Clear();

                changeset = citiesProvider.EntityContainer.GetChanges();
                Assert.AreEqual(1, changeset.ModifiedEntities.Count);

                so = citiesProvider.SubmitChanges();

                Assert.AreEqual(1, so.ChangeSet.ModifiedEntities.Count);
                Assert.AreEqual(0, so.ChangeSet.AddedEntities.Count);
                Assert.AreEqual(0, so.ChangeSet.RemovedEntities.Count);
            });
            // wait for submit to complete, then verify invokedEntities in changeset
            EnqueueConditional(() => so.IsComplete);
            EnqueueCallback(delegate
            {
                Assert.IsNull(so.Error);
                Assert.AreEqual(1, so.ChangeSet.ModifiedEntities.Count);

                // verify we got the property change notification for the city entity as a result of autosync
                Assert.AreEqual(13, propChanged.Count, "Received different property notifications than expected:\r\n" + string.Join(",", propChanged.ToArray()));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "EditHistory"));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "LastUpdated"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAssignCityZone"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAssignCityZoneIfAuthorized"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAutoAssignCityZone"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanTouchHistory"));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "IsTouchHistoryInvoked"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanSetCityInfo"));

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                CityWithInfo newCityWithInfo = citiesProvider.Cities.OfType <CityWithInfo>().SingleOrDefault <CityWithInfo>(c => (c.EditHistory.Contains("touch")));
                Assert.IsNotNull(newCityWithInfo, "Did not find modified CityWithInfo after the submit");
                Assert.AreNotEqual(newCityWithInfo.LastUpdated, priorLastUpdated, "Expected lastUpdated to be modified by submit");
                Assert.IsTrue(newCityWithInfo.EditHistory.Contains("touch=xxx"), "EditHistory was" + newCityWithInfo.EditHistory);
            });

            EnqueueTestComplete();
        }
        public void Inherit_Run_Call_Base_Custom_Method_On_Derived_Entity()
        {
            EntityChangeSet   changeset;
            List <string>     propChanged    = new List <string>();
            CityDomainContext citiesProvider = new CityDomainContext(TestURIs.Cities);

            LoadOperation   lo = citiesProvider.Load(citiesProvider.GetCitiesQuery());
            SubmitOperation so = null;

            CityWithInfo cityWithInfo = null;

            // wait for Load to complete, then invoke some domain methods
            EnqueueConditional(() => lo.IsComplete);
            EnqueueCallback(delegate
            {
                changeset = citiesProvider.EntityContainer.GetChanges();
                Assert.IsTrue(changeset.IsEmpty);

                cityWithInfo = citiesProvider.Cities.OfType <CityWithInfo>().FirstOrDefault();
                Assert.IsNotNull(cityWithInfo, "Expected to find a CityWithInfo type in entity list");

                cityWithInfo.PropertyChanged += delegate(object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                    // We filter to only those properties we see in the City hierarchy
                    BindingFlags flags  = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
                    bool isCityProperty = (typeof(CityWithInfo).GetProperty(e.PropertyName, flags) != null ||
                                           typeof(CityWithEditHistory).GetProperty(e.PropertyName, flags) != null ||
                                           typeof(City).GetProperty(e.PropertyName, flags) != null);
                    if (isCityProperty)
                    {
                        propChanged.Add(e.PropertyName);
                    }
                };

                Assert.IsTrue(cityWithInfo.CanAssignCityZone);
                cityWithInfo.AssignCityZone("Zone15");
            });

            // wait for prop changed for domain method guards
            EnqueueConditional(() => propChanged.Count > 0);
            EnqueueCallback(delegate
            {
                Assert.IsTrue(propChanged.Contains("CanAssignCityZone"));
                propChanged.Clear();

                changeset = citiesProvider.EntityContainer.GetChanges();
                Assert.AreEqual(1, changeset.ModifiedEntities.Count);
                so = citiesProvider.SubmitChanges();

                Assert.AreEqual(1, so.ChangeSet.ModifiedEntities.Count);
                Assert.AreEqual(0, so.ChangeSet.AddedEntities.Count);
                Assert.AreEqual(0, so.ChangeSet.RemovedEntities.Count);
            });
            // wait for submit to complete, then verify invokedEntities in changeset
            EnqueueConditional(() => so.IsComplete);
            EnqueueCallback(delegate
            {
                if (so.Error != null)
                {
                    Assert.Fail(so.Error.Message);
                }
                Assert.AreEqual(1, so.ChangeSet.ModifiedEntities.Count);
                // verify we got the property change notification for the city entity as a result of autosync
                Assert.AreEqual(14, propChanged.Count, "Received different property notifications than expected:\r\n" + string.Join(",", propChanged.ToArray()));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "EditHistory"));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "ZoneName"));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "ZoneID"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAssignCityZone"));
                Assert.AreEqual(1, propChanged.Count(prop => prop == "IsAssignCityZoneInvoked"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAutoAssignCityZone"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanAssignCityZoneIfAuthorized"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanSetCityInfo"));
                Assert.AreEqual(2, propChanged.Count(prop => prop == "CanTouchHistory"));

                // verify entities are auto-synced back to the client as a result of the domain method execution on server
                Assert.AreEqual(15, citiesProvider.Cities.Single <City>(c => (c.ZoneName == "Zone15")).ZoneID);

                // verify unchanged entities
                Assert.AreEqual(0, citiesProvider.Cities.First(c => (c.ZoneName == null)).ZoneID);
            });

            EnqueueTestComplete();
        }