Rants
I am proud to announce that in partnership, with DevWorx of México, the Silverlight Tour will now be available in Spanish for the Latin American and Spanish markets. The first three classes are happening on:
- September 29th - October 1st, 2008 - Mexico City, México
- September 29th - October 1st, 2008 - Guadalajara, México
- October 8th - 10th, 2008 - Monterrey, México
I gladly welcome DevWorx to the family of Silverlight Tour partners. If you are want to learn Silverlight 2 and are looking for a class in Spanish, they can definitely help you out!
UPDATE: I know the title is wrong and it should be IUpdateable but I didn't want to break any links for any RSS feeds that already had it.
If you haven't read Part 1 or Part 2 first, you should start there.
In this final part of the IUpdateable implementation, we will discuss the rest of the methods which includes ClearChanges, ResolveResource, ReplaceResource, SetReference, AddReferenceToCollection, RemoveReferenceFromCollection and SaveChanges.
ClearChanges is a simple method that simple undo's any changes that have accumulated. In my implementation, I simply clear the UpdateCache that we discussed in Part 2 as well as clear the NHibernate Session to undo any changed that were previously saved (but not persisted to the database).
ResolveResource is a funny little method. In the implementation documentation for the IUpdateable interface, they specify that in most cases you can return a token or anything you want that uniquely identifies an item instead of an actual object reference. For example, when you implement CreateResource the return value can be the actual resource or can be some token that you know how to resolve to the resource. This method is the one that will turn that token into an object reference if you've used that functionality. In our case we're actually returning object references so this becomes a non-issue and we just return the object that is passed in.
Next up is ReplaceResource. Not surprising, this method is used to complete replace a resource with another copy (who may have the correct values already set). The first parameter is a query who must return a single object. For my implementation, I first retrieve the single instance from the query and then walk through the properties to set the new values. Simple really.
The next couple of methods have to do with relationships between objects. The SetReference method takes three parameters: the target resource, the property name and the property value. The property value is an object who needs to be assigned to a property of the target. The property name to be assigned in the 2nd parameter. The implementation is pretty simple: I used the existing SetValue implementation to set the correct property with the correct value.
The AddReferenceToCollection and RemoveReferenceFromCollection are related methods. One adds an object to a collection and one removes it from the collection. Both of these methods take the same signature that SetReference uses (target, property name, property value). The difference here is instead of setting the reference, you need to add or remove it from a property (which is the collection). For my AddReferenceToCollection implementation, I use reflection to find an "Add" method and invoke it with the new reference. For the RemoveReferenceFromCollection, I do the same but I am looking for a "Remove" method to invoke. Using reflection in this way does not require that the collections follow any specific pattern but do require an Add and Remove method. I chose this implementation because I was copying ADO.NET Data Services' implementation that they use for the Entity Framework. I could have expected IList since that is the standard way that NHibernate usually implements collections, but I decided to use this implementation first and if I had to refactor it I would.
The last method to implement is SaveChanges. This method is pretty straightforward as all pending changes that the IUpdateable interface has applied should be applied during this call. IUpdateable expects that SaveChanges will be atomic (e.g. pass/fail, no partial updates). To enforce that requirement, I used the NHibernate Session object's BeginTransaction method to start a transaction so that I could rollback any changes that failed. Because I had kept a cache of updates so I could clear them if necessary, I walked through them once the transaction had began and applied them to the Session object. Finally I flushed the session to force the persistence to happen and ended the transaction. I did this all in a try...catch block so I could rollback the changes if any exceptions were thrown. SaveChanges does not have a return value so if it fails you are expected to return a DataServiceException. So in my catch block, after rolling back the transaction, I throw a DataServiceException and pass along the caught exception as the inner exception.
Overall the implementation was pretty simple. I hope that these articles will help anyone who is trying to implement this for their own data that they want to use in ADO.NET Data Services.
Microsoft has released a new version of their DeepZoom Composer tool that adds a number of features including panoramic stiching and integration with PhotoZoom! Go Grab it!
Only another week before my Boston Silverlight Tour stop. We will be in Boston for the Silverlight Tour on August 11-13th, 2008.
If you are in the market for three days of intense Silverlight development instruction, take a gander at the site. You can view the full Outline for the course here:
http://www.silverlight-tour.com/outline.aspx
I usually prefer to avoid just link posts, but since I have gotten this question a lot lately in my class and at user group talks, I thought i'd share. A couple of months ago Scott Hanselman convinced Microsoft to allow him to release something called RockScroll:

This tool is an addin for Visual Studio to be a scrollbar replacement for most code windows (though it doesn't work for XML files for some reason). It does some nifty things for me:
- Gives me a visual layout of the file instead of hoping that I am scrolling to the right place. Just click on the preview and it moves there.
- Bookmarks and breakpoints are marked as red and blue dots on the preview so I can find that breakpoint I set really quickly.
- Edited lines are highlighted so I can find the last place I edited.
As Scott says in his blog, its strictly "It works on my machine" but i've been running with it since it's release with zero problems and no slow-downs. Its now a must on all my development machines!
You can download it from Scott's blog:
http://www.hanselman.com/blog/IntroducingRockScroll.aspx
NOTE: This blog post is an adjunct topic to the IUpdateable series of posts that I have here and here.
As part of my work on the ADO.NET Data Services support for NHibernate.LINQ I decided that we should support IExpandProvider as well.
For the uninitiated, IExpandProvider is another interface that ADO.NET Data Services support to allow the expansion feature of Data Services to work. Essentially, expansion is about eager loading of related entities as part of a Data Service Query. For example, if you had a Data Services query that said:
http://www.silverlightdata.com/simple/HibProducts.svc/Products(1)
This URI would go retrieve the Product, but if you wanted to retrieve the Category (a related entity) as part of the single call, you could annotate the URI with a query string parameter called $expand to indicate the paths to eager load:
http://www.silverlightdata.com/simple/HibProducts.svc/Products(1)?$expand=Category
In fact, the expansion support allows you to specify multiple paths (separated by commas) to eager load:
http://www.silverlightdata.com/simple/HibProducts.svc/Products(1)?$expand=Category,Supplier
The way it works inside the Data Service is that checks to see if the context object specified in the Data Service support IExpandProvider (the built-in Entity Framework provider does this automatically). If it supports the interface, it calls the ApplyExpansions method. At that point, the provider is supposed to take the IQueryable interface and change the query to support the expansions. Remember, the methodology of Data Services is to take the URI syntax and convert the query into an IQueryable instance. At the end of the process it executes this query so the IExpandProvider happens before the database is ever involved. All it needs to do is to expand the expression tree to add the expressions necessary to expand the nodes.
So how did I go about it? My first step was to add expansion support to the LINQ interface. Before I got started, eager loading was the responsibility of the user of the LINQ query. There was no way to make eager loading happen in the expression tree. I modeled the support after the way that Entity Framework supports eager loading and added a new expression (called Expand in the NHibernate.LINQ provider) that instructs the underlying provider to eager load a certain path. Interestingly the ICriteria support in NHibernate already had support for eager loading so it was more plumbing to the NHibernate functionality that any real work. The expand works during the data source of the LINQ query:
var query = from timesheet in session.Linq<Timesheet>().Expand("Entries")
where timesheet.Entries.Count > 0
select timesheet;
By adding the Expand method call, it adds a hint to the query to eager load the Entries on the Timesheet object. Now that expansion is supported in the LINQ provider, I can wire up the IExpandProvider to simply add that expression for every path.
To support it, I simple walk through each of the paths that are passed to the ApplyExpansions method, but its not quite that simple. The method signature is:
IEnumerable ApplyExpansions(IQueryable queryable,
ICollection<ExpandSegmentCollection> expandPaths);
The first property is the query that you are going to modify for the expansions. The second parameter is a list of ExpansionSegmentCollections. Yup, a collection of a collection. I am unclear why this is so, but nevertheless, I simply go through each collection of collections (I've never been passed more than one, but perhaps MS can explain why). The ExpandSegmentCollection is a collection of ExpandSegment objects. This object supports two pieces of information: Name and Filter. Name is the path that is requested in the URI syntax; and Filter which it is unclear the exact use case. In my implementation, I simply threw an error if I found a filter since I couldn't find any code path that caused them so I didn't know how to modify the query to support them. My guess is that its there for future use.
So there you go, its added. This new interface in coordination with the IUpdateable makes any NHibernate object supported in not only LINQ but also ADO.NET Data Services. What do you think?
I've been reading Neal's blog for a while so when this book was launched I wanted to get a copy. I expected a more esoteric book from Neal, but this book was exceptionally applicable. His advice on the nature of being productive including links to specific tools impressed me a lot. I also admired his even handedness in his treatment of operating systems. Its hard to find someone that will talk about Microsoft and Apple technologies without religiosity. When it was good in Windows, he mentioned it. When it was good in OSX, he mentions it.
Maybe its because I am an old guy, but I thought his commentary about how much tools are making us soft (my term, not his) was nice to hear. Having used a simple text editor to do most of my coding the first decade of development, ideas like Intellisense and background complication make me a little crazy.
On the writing side, his prose is well thought out and exceptionally readable. You can get through the book pretty quickly but I found my self post-it noting a bunch of pages to revisit. The book is chocked full of well thought out specific advice for developers to help them be more productive.
I wholeheartedly recommend this book to every developer, no matter your technology (Windows, OSX, .NET, Java, RoR, etc.)
Next Monday night (July 28th, 2008), i'll be giving the short Q&A session at the Atlanta .NET Users Group. The topic? NHibernate's LINQ and ADO.NET Data Services support. If you're interested in using NHibernate but don't want to give up your LINQ skills, stop by for a listen!
In case you read my blog and didn't know that we were coming to Boston, what are you waiting for? We will be in Boston for the Silverlight Tour on August 11-13th, 2008. If you're interested in signing up, we are not full yet!
Now that my ADO.NET Data Services support has been merged into the trunk of NHibernate.LINQ, I do have some caveats about using NHibernate.LINQ with ADO.NET Data Services. ADO.NET Data Services is a beta 1 product so there are some bugs and issues that you will either need to avoid or work around.
The biggest issue is around entity identity. ADO.NET Data Services must know how identity is established for objects in order to support the Data Service. It does this in a two step process:
- First it looks for attributes that describe the 'primary key'.
- Failing that, it looks for properties on the entity called ID, or ending with "ID".
The second approach is where I expect most of NHibernate projects to fall into since you really don't want to pollute your objects with technology specific information (the attributes). This approach works well except that there is a bug in the Beta 1 version of ADO.NET Data Services. If the properties are specified in a base class and the keys are specified ending in "ID" (instead of just being called "ID"), then the search for the identifiers fails and Data Services fails to want to serve these objects. For example:
public class AbstractCategory
{
public virtual int CategoryID { get; set; }
public virtual string CategoryName { get; set; }
public virtual string Description { get; set; }
public virtual byte[] Picture { get; set; }
public virtual IList Products { get; set; }
}
public class Category : AbstractCategory
{
}
}
If this is your scenario, I might suggest waiting for later build of ADO.NET Data Services to be released as this is definitely a bug not expected behavior and I have gotten word from Microsoft that it is fixed in the RTM (which isn't available yet).
The next issue is that for collections, ADO.NET Data Services must understand the types that belong in a collection. In this case our above example will not work either in that having the Products in a Category as a simple IList can't tell ADO.NET Data Services what types of objects to deal with. If we change this to an IList<Products>, it works fine. If we have to change our entities to work with ADO.NET Data Services, this is what our new Category might look like instead:
public class Category
{
public virtual int CategoryID { get; set; }
public virtual string CategoryName { get; set; }
public virtual string Description { get; set; }
public virtual byte[] Picture { get; set; }
public virtual IList<Product> Products { get; set; }
}
With these changes, ADO.NET Data Services work fine.
If you are new to ADO.NET Data Services, this blog entry may help with some debugging issues in using it:
http://wildermuth.com/2008/06/07/Debugging_ADO_NET_Data_Services_with_Fiddler2
Lastly, I want to follow up on a note that Ayende mentioned on his announcement of my examples. In his blog post, he said:
From a technological perspective, I think this is awesome. However, there are architectural issues with exposing your model in such a fashion. Specifically, with regards to availability and scalability on the operations side, and schema versioning and adaptability on the development side.
I think he's right in that there is a schema version issue here that needs to be addressed but that the availability and scalability problems are ones that would be in the underlying data model itself. Since ADO.NET Data Services are just a convenience around WCF's REST Service Model, we can scale out or up depending on our needs (as well as caching).
What I think is important is to understand the reason behind ADO.NET Data Services. It is not a model to replace typical Web Service or Message Bus architectures. Its not all that fast or efficient. Its purpose is to allow the creation of a simple model to allow communication across the firewall. What I mean is that it is meant for the AJAX and RIA developers. Its a way of communicating data to clients that run on the Internet.
Its important to understand that data you expose with ADO.NET Data Services is not magically more secure...in fact, since its meant for client-side consumption of data, you should not allow data to be exposed by ADO.NET Data Services that is sensitive. Remember, that consuming data in the client is not secure in itself. If you wouldn't feel safe consuming data in client-side JavaScript, don't expose it via ADO.NET Data Services.