locked
Two-way linking RRS feed

  • Question

  • I need to download a certain set of data and know links in all directions.

    I start at a single type and expand 3 related types. (A -> B -> C and A -> D) This gives be a link to those items, but one-way only. Is there an easy and bandwidth friendly way to get the links in the other direction?

    I could expand the query to go backwards (A -> B -> C -> B -> A), but this causes a lot of redundant data to get passed over the web.

    I could also manually loop through all the links and attach them in the other direction.

    Is there an easier way? Will this be improved in 1.5 and/or 2?

    Thanks!

    Wednesday, September 2, 2009 3:47 PM

Answers

  • In EF scenarios the associations are bi-directional, and this seems easy to do, but in the case of the reflection provider, all bi-directional relationships are represented as two one-way associations.  The client tries to use very little to no metadata while it is running and therefore doesn't know how to do the fixup that you desire.  As a solution, you can add code, through partial classes, to the generated client classes and do the fix up automatically.   I have an example of this below using Northwind where the Order -> Order_Details -> Product scenario is playing the role of the A -> B -> C types you listed in your question.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using CSharpClient.NorthwindService;
    using System.Data.Services.Client;
    using System.Xml.Linq;
    using System.ComponentModel;
    
    namespace CSharpClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                XNamespace dsNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
                NorthwindEntities context = new NorthwindEntities(new Uri("http://localbox:21726/Northwind.svc"));
    
                Order o = ((DataServiceQuery<Order>)context.Orders.Where(or => or.OrderID == 10248)).Expand("Order_Details/Product").First();
                foreach (var od in o.Order_Details)
                {
                    Console.WriteLine("\tod.Order == o: {0}", od.Order == o);
                    if (od.Product != null)
                    {
                        Console.WriteLine("\t\tod.Product.Order_Details.Contains(od): {0}", od.Product.Order_Details.Contains(od));
                    }
                }
            }
        }
    }
    // prints the results below with fixup in place
    //
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    
    
    namespace CSharpClient.NorthwindService
    {
    
        public partial class Order
        {
            public Order()
            {
                this.Order_Details.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FixUpOrderDetails);
            }
    
            void FixUpOrderDetails(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                foreach (var orderDetail in this.Order_Details.Where(od => od.Order == null))
                {
                    orderDetail.Order = this;
                }
            }
        }
    
        public partial class Order_Detail
        {
            public Order_Detail()
            {
                this.PropertyChanged += new PropertyChangedEventHandler(FixUpProduct);
            }
    
            void FixUpProduct(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "Product")
                {
                    if (!this.Product.Order_Details.Contains(this))
                    {
                        this.Product.Order_Details.Add(this);
                    }
                }
            }
        }
    
    }
    
    Thursday, April 8, 2010 10:16 PM

All replies

  • In EF scenarios the associations are bi-directional, and this seems easy to do, but in the case of the reflection provider, all bi-directional relationships are represented as two one-way associations.  The client tries to use very little to no metadata while it is running and therefore doesn't know how to do the fixup that you desire.  As a solution, you can add code, through partial classes, to the generated client classes and do the fix up automatically.   I have an example of this below using Northwind where the Order -> Order_Details -> Product scenario is playing the role of the A -> B -> C types you listed in your question.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using CSharpClient.NorthwindService;
    using System.Data.Services.Client;
    using System.Xml.Linq;
    using System.ComponentModel;
    
    namespace CSharpClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                XNamespace dsNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
                NorthwindEntities context = new NorthwindEntities(new Uri("http://localbox:21726/Northwind.svc"));
    
                Order o = ((DataServiceQuery<Order>)context.Orders.Where(or => or.OrderID == 10248)).Expand("Order_Details/Product").First();
                foreach (var od in o.Order_Details)
                {
                    Console.WriteLine("\tod.Order == o: {0}", od.Order == o);
                    if (od.Product != null)
                    {
                        Console.WriteLine("\t\tod.Product.Order_Details.Contains(od): {0}", od.Product.Order_Details.Contains(od));
                    }
                }
            }
        }
    }
    // prints the results below with fixup in place
    //
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    //od.Order == o: True
    //        od.Product.Order_Details.Contains(od): True
    
    
    namespace CSharpClient.NorthwindService
    {
    
        public partial class Order
        {
            public Order()
            {
                this.Order_Details.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FixUpOrderDetails);
            }
    
            void FixUpOrderDetails(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                foreach (var orderDetail in this.Order_Details.Where(od => od.Order == null))
                {
                    orderDetail.Order = this;
                }
            }
        }
    
        public partial class Order_Detail
        {
            public Order_Detail()
            {
                this.PropertyChanged += new PropertyChangedEventHandler(FixUpProduct);
            }
    
            void FixUpProduct(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "Product")
                {
                    if (!this.Product.Order_Details.Contains(this))
                    {
                        this.Product.Order_Details.Add(this);
                    }
                }
            }
        }
    
    }
    
    Thursday, April 8, 2010 10:16 PM
  • Like Jeff said, if you use EF provider, you only need to add the relationship from one side, and the provider will do fix-up logic to make it bi-directional.

    If you are implementing your own custom IUpdatable with reflection service provider/your own IDSP, then you can implement this fix-up logic on the server side inside the IUpdatable interface. So on the client side you also only set the link from one side, this will reduce the data on the wire.

    There's an OData SDK that shows you how to do this:

    http://www.odata.org/developers/odata-sdk

    take a look at "Sample Services", it's custom provider has an IUpdatable implementation that supports bi-directional navigation properties.

     

    Regards,

    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Thursday, April 8, 2010 10:28 PM
    Answerer