Working with Associations in ADO.NET Data Services

I’ve seen that users are having some question about how to deal with certain types of relations between entities and how to deal with them in the client library.I will outline the a couple of common scenarios of using the client library among a few relationship types. I'll be using the AdventureWorks Database for examples of relations.

1..1 Associations

This is the case of entities associated with each other 1..1 . An example from Adventureworks is the relation between Individual and Contact,Customer Entities.

An instance of the Individual entity type should have associated Contact and Customer entities, this is another way of saying “ A row in the Individual table should have links to a row in the Contact and Customer table”.
In case of 1..1 relations , the left( child ) ends of the association are expressed as properties on the right ( parent ) end of the relation.This is shown in the metadata of the service as :

 <EntityType Name="Individual">
<Key>
  <PropertyRef Name="CustomerID" /> 
</Key>
<!—Primitive & Complex Properties --> 
  <Property Name="CustomerID" Type="Edm.Int32" Nullable="false" /> 
  <Property Name="Demographics" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" /> 
  <Property Name="ModifiedDate" Type="Edm.DateTime" Nullable="false" /> 
<!-- Navigation Properties , Signifying related entities of an association -->
  <NavigationProperty Name="Contact" 
    Relationship="AdventureWorks.FK_Individual_Contact_ContactID" 
   FromRole="Individual" ToRole="Contact" /> 
  <NavigationProperty Name="Customer"    Relationship="AdventureWorks.FK_Individual_Customer_CustomerID" 
   FromRole="Individual" ToRole="Customer" /> 
</EntityType>

This leads the client utility( DataSvcUtil.exe ) to generate a type Individual which has instances of types Customer and Contact as Properties.

The generated class on the client looks like this :

 public class Individual {
//Other properties removed for brevity 
 public Contact Contact {
            get {
                return this._Contact;
            }
            set {
                this._Contact = value;
            }
        }
public Customer Customer {
            get {
                return this._Customer;
            }
            set {
                this._Customer = value;
            }
        }
}

Trivia : Properties of a type P in an entity type E which signify that the type E has a 1..1 relation with the type P of the property are called as Reference Properties.

Now that we have set the stage, lets look at how one would use the client library to interact with the related types.

You have new instances of Individual , Contact and Customer and you want to persist the new entities and their relation to the store.

 Individual individual = new Individual();
//Initialize all its necessary properties
Customer newCustomer = new Customer();
//Initialize all its necessary properties
Contact contact = new Contact();
//Initialize all its necessary properties

//Add the newly created entities to be tracked by the client context

TestContext.AddObject("Individual", individual);
TestContext.AddObject("Customer", newCustomer);  
TestContext.AddObject("Contact", contact);

Now what ?

Since the entities are created and added to the store can’t I just set the Customer and Contact Property to the Individual object and that will save the relation ?

 individual.Customer = newCustomer;
individual.Contact = contact;
TestContext.SaveChanges();

Shouldn’t this be enough ?

Nope , that’s not enough to save the relation to the store.

Why ?

Remember that the client context only gives you POCO access to entities in the store and any new entities that you create.The Client context does not track any relations unless you explicitly ask it to do so !

Neat , now how do I ask it to do that ?

Beta 1 Code :
You use the AddLink method defined on the context.

 TestContext.AddLink(individual, "Contact", contact);
TestContext.AddLink(individual, "Customer", newCustomer);

The signature and the intent of the AddLink method is lucid , it binds 2 entities into a relation , its kinda like a priest at a wedding,

“I now bind you into the holy bond of 1..1 relations. You may now change your facebook status“

okay , jokes aside , this is what it looks like ..

AddLink ( Parentinstance ,”ChildPropertyName”,ChildInstance)

RTM Code :

You use the SetLink method defined on the context.

 TestContext.SetLink(individual, "Contact", contact);
TestContext.SetLink(individual, "Customer", newCustomer);

The signature and the intent of the SetLink method is lucid , it binds 2 entities into a relation , its kinda like a priest at a wedding,

This is what it looks like ..

SetLink ( Parentinstance ,”ChildPropertyName”,ChildInstance)

Deleting the parent entity

Deleting the parent entity means that the relations with the child entities are also removed.

Think of this as the argument of “How do I delete an entity that has 1..1 links with other   entities?”

What happens if I delete the parent entity without deleting the links ?

It depends on your store . If your database is configured to do a cascade delete on deletion of the parent entity ,you might get away with deleting the entity without removing the links

The right way to delete the entity is to remove all the links it has with the child entities and then delete the entity itself.

The code would look like this..

 Individual existingIndividual = TestContext.Individual.Expand("Customer,Contact").Take<Individual>(1).First<Individual>();

TestContext.DetachLink(existingIndividual, "Contact", existingIndividual.Contact);
TestContext.DetachLink(existingIndividual, "Customer", existingIndividual.Customer);
TestContext.DeleteObject(existingIndividual);
TestContext.SaveChanges();

Wait !! what’s with the expand there ?
Well, if you need to delete the relation , the way to identify the relation or the link is to have both the right and the left end of the relations to be materialized. without the expand , the Contact and the Customer property are null , and we don’t know which relation to delete.In case of 1..1 relations it might be easy to predict based on the relation name ,

but wouldn’t be easy in case of 1..n relations.

Well, that’s it from me , In the next part , we shall discuss entities with 1..N relations and how to work with them using the astoria context.

If you have any questions  , leave a comment . If you have any issues with code and need help , please post your query on the astoria forums as I can’t promise that I will be able to reply to emails sent to me directly .The whole team is very active on the forums and the more eyes on a problem the better.

I will update the code used in  this sample and upload it later.