IEnumerable<Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>> GetLoadPropertyTests()
        {
            // attach and load non-derived reference property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                ctx.AttachTo("People", pratik);
                var response = ctx.LoadProperty(pratik, "BestFriend");
                t.TestUtil.AssertContainsFalse(response.Query.RequestUri.OriginalString, typeof(Employee).FullName);
                Assert.IsTrue(response.Query.RequestUri.OriginalString.EndsWith("/BestFriend"));
                Person bestFriend = pratik.BestFriend;
                Assert.IsTrue(ctx.Entities.Contains(ctx.GetEntityDescriptor(bestFriend)));
                Assert.IsNotNull(ctx.Links.Single(l => l.Source == pratik && l.Target == bestFriend && l.SourceProperty == "BestFriend"));
            },
            new List<Version>() { V4 },
            null);

            // retrive entity and load non-derived reference property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                var response = ctx.LoadProperty(pratik, "BestFriend");
                Assert.IsTrue(response.Query.RequestUri.OriginalString.EndsWith(typeof(Employee).FullName + "/BestFriend"));
                Person bestFriend = pratik.BestFriend;
                Assert.IsTrue(ctx.Entities.Contains(ctx.GetEntityDescriptor(bestFriend)));
                Assert.IsNotNull(ctx.Links.Single(l => l.Source == pratik && l.Target == bestFriend && l.SourceProperty == "BestFriend"));
            },
            new List<Version>() { V4, V4 },
            null);

            // attach and load derived reference property, expect failure
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                ctx.AttachTo("People", pratik);
                var response = ctx.LoadProperty(pratik, "Manager");
                Assert.Fail("Expected exception but received none.");
            },
            new List<Version>() { V4 },
            "<?xml version=\"1.0\" encoding=\"utf-8\"?><m:error xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"><m:code /><m:message>Resource not found for the segment 'Manager'.</m:message></m:error>");

            // retrive entity and load non-derived reference property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                var response = ctx.LoadProperty(pratik, "Manager");
                Assert.IsTrue(response.Query.RequestUri.OriginalString.EndsWith(typeof(Employee).FullName + "/Manager"));
                Person manager = pratik.Manager;
                Assert.IsTrue(ctx.Entities.Contains(ctx.GetEntityDescriptor(manager)));
                Assert.IsNotNull(ctx.Links.Single(l => l.Source == pratik && l.Target == manager && l.SourceProperty == "Manager"));
            },
            new List<Version>() { V4, V4 },
            null);
        }
        IEnumerable<Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>> GetAddRelatedObjectTests()
        {
            // Add related object to retrieved object, for non-derived reference set property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                ctx.AddRelatedObject(pratik, "Friends", p100);

                ctx.SaveChanges(saveChangeOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(100, p100.ID);
                Assert.IsNotNull(pratik.Friends.Single(f => f == p100));

                Assert.AreEqual(2, ctx.Links.Where(l => l.Source == pratik).Count());
                Assert.IsNotNull(ctx.Links.Single(l => l.Target == p100));
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // Add related object to newly added object, for non-derived reference set property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                Employee p101 = new Employee { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p100);
                ctx.AddRelatedObject(p100, "Friends", p101);

                ctx.SaveChanges(saveChangeOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Friends.Single());

                Assert.AreEqual(p100, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(p101, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // Add related object to retrieved object, for derived reference set property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                ctx.AddRelatedObject(pratik, "Colleagues", p100);

                ctx.SaveChanges(saveChangeOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(100, p100.ID);
                Assert.IsNotNull(pratik.Colleagues.Single(f => f == p100));

                Assert.AreEqual(2, ctx.Links.Where(l => l.Source == pratik).Count());
                Assert.IsNotNull(ctx.Links.Single(l => l.Target == p100));
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // Add related object to newly added object, for derived reference set property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                Employee p101 = new Employee { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p100);
                ctx.AddRelatedObject(p100, "Colleagues", p101);

                ctx.SaveChanges(saveChangeOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Colleagues.Single());

                Assert.AreEqual(p100, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(p101, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4 },
            null);
        }
        IEnumerable<Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>> GetSetLinkTests()
        {
            // set link to attached objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                Employee marcelo = new Employee { ID = 6 };
                ctx.AttachTo("People", pratik);
                ctx.AttachTo("People", marcelo);
                ctx.SetLink(pratik, "BestFriend", marcelo);

                ctx.SaveChanges(saveChangeOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.BestFriend).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(person, pratik);
                Assert.AreEqual(6, marcelo.ID);
                Assert.AreEqual(marcelo, pratik.BestFriend);

                Assert.AreEqual(pratik, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(marcelo, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4 },
            null);

            // set link to retrieved objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                Employee marcelo = new Employee { ID = 6 };
                ctx.AttachTo("People", marcelo);
                ctx.SetLink(pratik, "BestFriend", marcelo);

                ctx.SaveChanges(saveChangeOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => p.BestFriend).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(6, marcelo.ID);
                Assert.AreEqual(marcelo, pratik.BestFriend);

                Assert.AreEqual(pratik, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(marcelo, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // set link to newly added objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Person p100 = new Person { ID = 100 };
                Person p101 = new Person { ID = 101 };
                ctx.AddObject("People", p100);
                ctx.AddObject("People", p101);
                ctx.SetLink(p100, "BestFriend", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.BestFriend).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.BestFriend);

                Assert.AreEqual(p100, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(p101, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4, V4 },
            null);

            // set link to existing objects, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee pratik = null;
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                foreach (Person p in query)
                {
                    pratik = (Employee)p;
                    Assert.IsTrue(ctx.GetEntityDescriptor(p).EditLink.OriginalString.EndsWith(typeof(Employee).FullName), "href for edit link did not end with type name");
                }

                PeopleManager shyam = new PeopleManager { ID = 5 };
                ctx.AttachTo("People", shyam);
                ctx.SetLink(pratik, "Manager", shyam);

                ctx.SaveChanges(saveChangesOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Manager).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(5, shyam.ID);
                Assert.AreEqual(shyam, pratik.Manager);

                Assert.AreEqual(pratik, ctx.Links.Single().Source);
                Assert.AreEqual(shyam, ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // set link to existing objects, for derived property, should fail for attach
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                PeopleManager shyam = new PeopleManager { ID = 5 };
                ctx.AttachTo("People", pratik);
                ctx.AttachTo("People", shyam);
                ctx.SetLink(pratik, "Manager", shyam);

                try
                {
                    ctx.SaveChanges(saveChangesOption);
                    Assert.Fail("Exception expected but received none.");
                }
                catch (DataServiceRequestException e)
                {
                    t.TestUtil.AssertContains(e.InnerException.Message, "Resource not found for the segment 'Manager'.");
                }
            },
            new List<Version>() { V4 },
            null);

            // set link to null, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee pratik = null;
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                foreach (Person p in query)
                {
                    pratik = (Employee)p;
                    Assert.IsTrue(ctx.GetEntityDescriptor(p).EditLink.OriginalString.EndsWith(typeof(Employee).FullName), "href for edit link did not end with type name");
                }

                ctx.SetLink(pratik, "Manager", null);
                ctx.SaveChanges(saveChangesOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Manager).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.IsNull(pratik.Manager);

                Assert.AreEqual(pratik, ctx.Links.Single().Source);
                Assert.IsNull(ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // set link to newly added objects, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                PeopleManager p101 = new PeopleManager { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p100);
                ctx.AddObject("People", p101);
                ctx.SetLink(p100, "Manager", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Manager).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Manager);

                Assert.AreEqual(p100, ctx.Links.Single().Source);
                Assert.AreEqual(p101, ctx.Links.Single().Target);
            },
            new List<Version>() { V4 /*b/c of collection properties*/, V4 /*b/c of collection properties*/, V4, V4 },
            null);

            // set link to newly added objects, for derived property with link folding
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee p100 = new Employee { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                PeopleManager p101 = new PeopleManager { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p101);
                ctx.AddObject("People", p100);
                ctx.SetLink(p100, "Manager", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Manager).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Manager);

                Assert.AreEqual(p100, ctx.Links.Single().Source);
                Assert.AreEqual(p101, ctx.Links.Single().Target);
            },
            new List<Version>() { V4 /*b/c of collection properties*/, V4 /*b/c of collection properties*/, V4 },
            null);
        }
        IEnumerable<Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>> GetDeleteLinkTests()
        {
            // delete link to attached objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                Person p1 = new Person { ID = 1 };
                ctx.AttachTo("People", pratik);
                ctx.AttachTo("People", p1);
                ctx.DeleteLink(pratik, "Friends", p1);

                ctx.SaveChanges(saveChangeOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.IsFalse(pratik.Friends.Any());
                Assert.AreEqual(0, ctx.Links.Count());
            },
            new List<Version>() { V4, V4 },
            null);

            // delete link to retrieved objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                Person p1 = new Person { ID = 1 };
                ctx.AttachTo("People", p1);
                ctx.DeleteLink(pratik, "Friends", p1);

                ctx.SaveChanges(saveChangeOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.IsFalse(pratik.Friends.Any());
                Assert.AreEqual(0, ctx.Links.Count());
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // delete link to retrieved object, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Employee marcelo = null;
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 6);
                foreach (Person p in query)
                {
                    marcelo = (Employee)p;
                    Assert.IsTrue(ctx.GetEntityDescriptor(p).EditLink.OriginalString.EndsWith(typeof(Employee).FullName), "href for edit link did not end with type name");
                }

                PeopleManager andy = new PeopleManager { ID = 2 };
                ctx.AttachTo("People", andy);
                ctx.DeleteLink(marcelo, "Colleagues", andy);

                ctx.SaveChanges(saveChangesOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 6);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(6, person.ID);
                Assert.AreEqual(marcelo, person);
                Assert.IsFalse(marcelo.Colleagues.Any());
                Assert.AreEqual(0, ctx.Links.Count());
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // Delete newly added links, for derived property with link folding
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                ctx.Format.UseJson(this.LoadModelFromString());
                PeopleManager shyam = new PeopleManager { ID = 5 };
                Employee marcelo = new Employee { ID = 6 };

                ctx.AttachTo("People", shyam);
                ctx.AttachTo("People", marcelo);

                ctx.DeleteLink(shyam, "DirectReports", marcelo);
                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as PeopleManager).DirectReports).Where(p => p.ID == 5);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(5, person.ID);
                Assert.AreEqual(shyam, person);
                Assert.AreEqual(6, marcelo.ID);
                Assert.AreEqual(1, ctx.Links.Count());
            },
            new List<Version>() { V4 , V4 },
            null);
        }
        IEnumerable<Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>> GetAddLinkTests()
        {
            // add link to attached objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                Employee pratik = new Employee { ID = 3 };
                Employee marcelo = new Employee { ID = 6 };
                ctx.AttachTo("People", pratik);
                ctx.AttachTo("People", marcelo);
                ctx.AddLink(pratik, "Friends", marcelo);

                ctx.SaveChanges(saveChangeOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(6, marcelo.ID);
                Assert.IsNotNull(pratik.Friends.Single(f => f == marcelo));

                Assert.AreEqual(2, ctx.Links.Where(l => l.Source == pratik).Count());
                Assert.IsNotNull(ctx.Links.Single(l => l.Target == marcelo));
            },
            new List<Version>() { V4, V4 },
            null);

            // add link to retrieved objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangeOption) =>
            {
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 3);
                Employee pratik = (Employee)query.Single();
                Employee marcelo = new Employee { ID = 6 };
                ctx.AttachTo("People", marcelo);
                ctx.AddLink(pratik, "Friends", marcelo);

                ctx.SaveChanges(saveChangeOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 3);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(3, person.ID);
                Assert.AreEqual(pratik, person);
                Assert.AreEqual(6, marcelo.ID);
                Assert.IsNotNull(pratik.Friends.Single(f => f == marcelo));

                Assert.AreEqual(2, ctx.Links.Where(l => l.Source == pratik).Count());
                Assert.IsNotNull(ctx.Links.Single(l => l.Target == marcelo));
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // add link to newly added objects, for non-derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                Person p100 = new Person { ID = 100 };
                Person p101 = new Person { ID = 101 };
                ctx.AddObject("People", p100);
                ctx.AddObject("People", p101);
                ctx.AddLink(p100, "Friends", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => p.Friends).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Friends.Single());

                Assert.AreEqual(p100, (Person)ctx.Links.Single().Source);
                Assert.AreEqual(p101, (Person)ctx.Links.Single().Target);
            },
            new List<Version>() { V4, V4, V4, V4 },
            null);

            // add link to existing objects, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                PeopleManager shyam = null;
                var query = ctx.CreateQuery<Person>("People").Where(p => p.ID == 5);
                foreach (Person p in query)
                {
                    shyam = (PeopleManager)p;
                    Assert.IsTrue(ctx.GetEntityDescriptor(p).EditLink.OriginalString.EndsWith(typeof(PeopleManager).FullName), "href for edit link did not end with type name");
                }

                Employee marcelo = new Employee { ID = 6 };
                ctx.AttachTo("People", marcelo);
                ctx.AddLink(shyam, "Colleagues", marcelo);

                ctx.SaveChanges(saveChangesOption);

                query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 5);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(5, person.ID);
                Assert.AreEqual(shyam, person);
                Assert.AreEqual(6, marcelo.ID);
                Assert.AreEqual(marcelo, shyam.Colleagues.Single(e => e.ID == 6));

                Assert.IsNotNull(ctx.Links.SingleOrDefault(l => l.SourceProperty == "Colleagues" && l.Source == shyam && l.Target == marcelo));
            },
            new List<Version>() { V4, V4, V4 },
            null);

            // add link to newly added objects, for derived property with link folding
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                ctx.Format.UseJson(this.LoadModelFromString());
                PeopleManager shyam = new PeopleManager { ID = 5 };
                Employee pratik = new Employee { ID = 3 };

                ctx.AttachTo("People", shyam);
                ctx.AttachTo("People", pratik);

                ctx.AddLink(shyam, "DirectReports", pratik);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as PeopleManager).DirectReports).Where(p => p.ID == 5);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(shyam.ID, person.ID);
                Assert.AreEqual(shyam, person);
                Assert.AreEqual(3, pratik.ID);
                Assert.AreEqual(pratik, shyam.DirectReports.FirstOrDefault(l => l.ID == 3));

                Assert.IsNotNull(ctx.Links.SingleOrDefault(l => l.SourceProperty == "DirectReports" && l.Source == shyam && l.Target == pratik));
            },
            new List<Version>() { V4, V4},
            null);

            // add link to newly added objects, for derived property
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                PeopleManager p100 = new PeopleManager { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                Employee p101 = new Employee { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p100);
                ctx.AddObject("People", p101);
                ctx.AddLink(p100, "Colleagues", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Colleagues.Single());

                Assert.IsNotNull(ctx.Links.SingleOrDefault(l => l.SourceProperty == "Colleagues" && l.Source == p100 && l.Target == p101));
            },
            new List<Version>() { V4 /*b/c of collection properties*/, V4 /*b/c of collection properties*/, V4, V4 },
            null);

            // add link to newly added objects, for derived property with link folding
            yield return new Tuple<Action<DataServiceContext, SaveChangesOptions>, List<Version>, string>(
            (ctx, saveChangesOption) =>
            {
                PeopleManager p100 = new PeopleManager { ID = 100 };
                p100.Skills = new List<string>();
                p100.Vacations = new List<Vacation>();

                Employee p101 = new Employee { ID = 101 };
                p101.Skills = new List<string>();
                p101.Vacations = new List<Vacation>();

                ctx.AddObject("People", p101);
                ctx.AddObject("People", p100);
                ctx.AddLink(p100, "Colleagues", p101);

                ctx.SaveChanges(saveChangesOption);

                var query = ctx.CreateQuery<Person>("People").Expand(p => (p as Employee).Colleagues).Where(p => p.ID == 100);
                ctx.MergeOption = MergeOption.OverwriteChanges;

                Person person = query.Single();
                Assert.AreEqual(100, person.ID);
                Assert.AreEqual(p100, person);
                Assert.AreEqual(101, p101.ID);
                Assert.AreEqual(p101, p100.Colleagues.Single());

                Assert.IsNotNull(ctx.Links.SingleOrDefault(l => l.SourceProperty == "Colleagues" && l.Source == p100 && l.Target == p101));
            },
            new List<Version>() { V4 /*b/c of collection properties*/, V4 /*b/c of collection properties*/, V4 },
            null);
        }