Viewer Mail , #1
Disclaimer : These are my thoughts and opinions , although I work on the Astoria team , I am not the voice of the team.The team has its own voice and can be heard here : Project Astoria Team Blog
Hola !
I am interrupting my regularly scheduled blog post to answer some really interesting questions about one of my blog posts , i.e Working with Associations in ADO.NET Data Services. These questions are from the comments on the post and
also the public forums.
Vince says :
”Hi,
I find bad to be forced to use the Addlink to allow us to save relational object in the DB.
We have a pretty full object model but the Addlink method needs string argument. As each entity knows all navigation properties, why to not have something like entity.NavigationObject rather than string SourceProperty. At least, myObject.MyNavigationProperty.Name or ToStringName would be better.
Thanks
Nice blogs
Regards
-Vince”
e
Thank you Vince for the compliment and let me try to explain the reason we built the client this way.
For V1 , we wanted to give you POCO access to the data classes. Although we could infer the relation based on the type of the navigation property , this would break scenarios where we have more than 1 navigation properties of the same type and belonging to the same entity Set.
Consider this entity ,
public class Customer {
public List<Orders> CashPurchases{get;set;}
public List<Orders> CreditCardPurchases{get;set;}
}
If you do an AddLink with just an instance of Orders, we would have to guess which navigation property we have to add the link to .
Customer newCustomer = new Customer();
Orders cashPurchase = new Orders();
Orders creditCardPurchase = new Orders();
context.AddLink( newCustomer , " ?? ", cashPurchase);
In the above case , trying to guess the name of the association will land us in hot water as we don't know which navigation property you intended .
we cant go the other way around and try to use the instance of Orders to guess the name of the association
as we Orders may have other associations of type Customers and we dont know which one you intended.
If you do not wish to use the string to remember the association name , you can move that to a separate resource file and have all your strings localized in one place.
Consider this ..
public class StoreConstants{
public static string Customers_To_CashPurchases ="CashPurchases";
public static string Customers_To_CreditCardPurchases ="CreditCardPurchases";
}
and then in your code ,
Customer newCustomer = new Customer();
Orders cashPurchase = new Orders();
Orders creditCardPurchase = new Orders();
context.AddLink( newCustomer , StoreConstants.Customers_To_CashPurchases , cashPurchase);
context.AddLink( newCustomer , StoreConstants.Customers_To_CreditCardPurchases , creditCardPurchase);
Simon says :
”Hi Phani,
Nice clear exposition of the topic.
However, I would think that, given that we are working with a client-side proxy, the separation of object tracking and association tracking is a bit too abstract for the most common scenarios where this client class may be used, ie with out-of-the-box databinding to UI components etc.
I understand the nice separation of concerns etc, but surely if the Customer or Contact properties are set in code, the user intends them to be set.I guess it is about user intentions and usability. Could you provide an overload such as
Context.AddObject( String property,Object value, Boolean shouldTrackAssociations)
Just a thought/suggestion.
Cheers,
Simon”
Thanks Simon for the compliment and lets explore this further,
I agree that the concept of treating associations as first class resources is somewhat new to the application scenarios where the client library is being used.
At the same time , we dont want to make assumptions on how the relation exists on the server as the client does not have enough information about the server schema.
Sure , we have the CLR types reflect the associations on the server i.e Reference for 1..1 relations and Collection types for 1..N relations.
But we really dont want to make assumptions about this.
Associations are first class resources on the server too , which means that any changes to associations should happen as separate requests or as part of another request. That making the assumption of a given relation causes an additional HTTP Request , which uses bandwidth you will be paying for .
Even if we added some magic to cause
newProduct.Categories.Add(newCategory) ;
to be
Context.AddLink(newProduct,"Categories",newCategory);
then the entity classes Products and Category would have to know about the DataServiceContext ,
newProduct.Context = myDataServiceContext;
and this would just look ugly ,IMHO.
Now , that was just me explaining how things are and why we cant do this today .
Let me share my thoughts of how this should be done and see how this sits with you .
If an entity has only one navigation property of a specify type .
ex: Customers has only one Navigation property of type Address.
Regardless of whether it is a Reference property or a Collection property,
updating the CLR property on the entity should setup the link
ex:
Address newAddress = new Address(){
City=’Bellevue’,
State=’WA’
};
newCustomer.Address = newAddress;
should automatically set this relation up in this way ,
context.SetLink(newCustomer,”Address”,newAddress);
To achieve this , without letting the entities know about the context ,the entities would have to notify the context that they have navigation properties and the context should listen for changes to the navigation properties.
But when should the context hook itself up to the afore-mentioned events ?
when the source entity is modified in some way perhaps ?
ex :
context.AddObject(“Customers”,newCustomer);
//pseudo code for AddObject
public void AddObject(string entitySetName,object entity) {
ICanHazNavigationProperties navProps = entity as ICanHazNavigationProperties ;
if( navProps !=null) {
navProps.RegisterHandler(HandleNavPropChange);
}
// do something here
}
private void HandleNavPropChange(object source,string associationName,
object target,Multiplicity associationMultiplicity,NavPropAction action)
{
switch(action)
{
case ( NavPropAction.AddLink ) :
switch(associationMultiplicity) {
case Multiplicity.One :
this.SetLink(source,associationName,target);
break;
}
break;
//Other cases skipped for brevity
}
}
}
Now , if we go with this approach, the entity types themselves cannot be pure POCO objects if you want automatic
link tracking. For auto generated types using DataSvcUtil.exe or “Add Service Reference” this is no big deal.
But what about cases where one has to hand-craft the entity types by hand ?
Well, this was me just thinking out loud. If you agree/disagree , leave a comment and get featured in the next
“Viewer Mail”.
A data geek joke , just like associations are first class resources , comments are first class material for blog posts :)