Rants Tagged with “ADO.NET Data Services”

1    (Total Pages: 1/Total Results: 9)

ADO.NET Data Services for Multiple Databases?

Silverlight Logo

This all started with an innocent question by Bob Archer on Twitter. Bob wondered whether he could use ADO.NET Data Services in an application that was being touted as "Software as a Service" (SaaS). His concern was the apparent hard wiring of the Data Source in the DataService definition. This design might assume that you had to connect to a single database for all requests.

In his case, the Entity Framework metadata was the same but the connection string might choose a different physical server and database name. I thought that it shouldn't be too difficult. My first attempt to help him was to suggest interceptors for every entity type and manually change the database string based on some data in the request. This felt hacky so I asked Pablo Castro (at Microsoft) for some advice on the problem. He of course had the solution.

The solution was to override the protected CreateDataSource method on the Service. This method is called during each request to create the Data Source. This happens in the pipeline after the request information has been parsed so you can use request parameters to decide what Data Source to create.  You still need to return a Data Source that is the same type as the generic parameter but this means you can determine how to create the object. For example:

public class ProductService : DataService<ProductEntities>
{
  // ...

  protected override ProductEntities CreateDataSource()
  {
    HttpRequest req = HttpContext.Current.Request;

    if (req.IsAuthenticated)
    {
      return new ProductEntities("Authenticated DB Connection String");
    }
    else
    {
      return new ProductEntities("Anonymous DB Connection String");
    }
  }

}

This sample shows that you would need to return a ProductEntities data source, but you can determine how the creation of the data source happens. In the case of how ADO.NET Data Services works, this is called on each and every request separately and not cached so you can safely create a data source based on your current request as shown above.  Thanks Pablo, you made me look good again!

Updated My Silverlight 2/Astoria MSDN Article

Silverlight Logo

I just found out that my changes to the article and code for Silverlight 2 RTW are now live. The article makes the small change that it no longer requires you to use EdmGem to build your proxy (using the Service Reference instead).  I've also updated the code example to be compatible with .NET 3.5 SP1 and Silverlight 2

In case you're not familiar with this MSDN Magazine article, it covers how to implement data access using ADO.NET Data Services and Silverlight 2 including reading and writing data.  Let me know if you have questions about it.

New Silverlight 2 ADO.NET Data Service Example

Silverlight Logo

I've finally had a chance to update my Silverlight 2-ADO.NET Data Services example. In this new sample I show how to create a Line-of-Business application (an XBox Game editor) using ADO.NET Data Services against both an Entity Framework model and NHibernate. Unlike earlier examples, this one includes implementation against the ADO.NET Data Service Silverlight 2 library to support saving of changed entities. In addition, I show some techniques for paging, retrieving simple types over an ADO.NET Data Service and full styling of the application. I hope to add support for Forms Authentication in the coming weeks.

Feel free to post replies with questions about the sample.

SQL Data Services is Cool...but It Won't Work in Silverlight 2

Silverlight Logo

If you are thinking about using SQL Data Services (the data part of the Azure stack) in your Silverlight 2 project, think again. As you might know, ADO.NET Data Services (Astoria) will not work cross-domain regardless of a security policy file (because of some limitations in the two networking stacks that Silverlight 2 uses). Its a problem but in most use-cases ADO.NET Data Services (Astoria) is used on the same domain so no biggie...but...

The Azure SQL Data Service uses Astoria to expose their data to the client...that means that with the ADO.NET Client Library that you can't access SQL Data Services. The reality is that since SQL Data Services requires basic authentication, it would not be terribly secure to call it in any case but this seals the deal.

The only good news is that since Astoria is just REST, you might be able to do the work to call it manually using WebClient or HttpWebRequest but its not an easy fix.

Too bad too, it would have been a great mix...SL2 + SDS.

VS2008 + Silverlight 2 Beta 2 - Update

Silverlight Logo

After some discussion, Mike Flasko (Program Manager, ADO.NET Data Services) has clarified the relationship between the Silverlight 2 Beta 2 Data Services Library and the SP1 of VS2008/.NET 3.5.  Specifically he says:

While the Silverlight Beta 2 data services library may work in some scenarios against a .NET Framework 3.5 SP1 RTM server, you should NOT count on the two being fully compatible with each other.

For me, I am going to wait to roll up to SP1 RTM until Silverlight 2 RTM's.  Caveat emptor. Follow the above link to read his full discussion.

Implementing IExpandProvider for NHibernate.LINQ

Silverlight Logo

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?

NHibernate.LINQ with ADO.NET Data Services

Silverlight Logo

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 ServicesADO.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.

Implementing IUpdatable (Part 2)

Silverlight Logo

If you haven't read Part 1 yet, you can read it here.

After spending time creating my own caches of reflection data I found the NHibernate type information to be more complete and faster. Go figure.  At this point I am using the SessionFactory's GetClassMetadata and GetCollectionMetadata to return IClassMetadata and ICollectionMetadata interfaces. So far this has given me every piece of runtime information I need and means that I don't need to do any nasty (and potentially fragile) walking through the property interface of the context object. Whew...

So to refactor my GetResource and CreateResource calls. GetResource is easy.  GetResource passes in the query and the full type name (though the full type name may be missing in some cases). The query it passes it must resolve to a single result.  That means I can simply execute the the query and if it returns more or less than one result, return an error.  Once I have the result I can just check it against the full type name (if it was passed in) and return the value.

CreateResource is a bit more complicated. The IClassMetadata allows me to call their Instantiate method to create a new instance of the class but the new instance is not initialized with any data.  Most notably, the missing detail is a key. In the case of many entities, this is fine (e.g. if zero ID is a valid "new" entity).  But in others the key needs to be specified. (Customer in the Northwind database is an example of this.) The problem is that we have to keep a reference to the new objects so that when ADO.NET Data Services asks us to set properties, get properties and save, we have to have the object in the Session.  But we can't add it to the Session with an invalid key.  What to do?  My solution for now is a local cache of objects that are not ready to be saved (only really happens with newly created objects). When we are ready to save, we'll just add it to the Session then (when its either valid or we'll throw an exception because its invalid).

Now a quick mention of GetProperty and SetProperty. These are both pretty easy as the IClassMetadata includes a similar method to set and get properties. The only wrinkle is that if you try and set a key using the GetProperty/SetProperty, the keys are not in the list of properties.  Because of this I just checked to see if the property is part of the key and set the key instead, if its not part of the key we can just set or get the property.  Now that we have the basic part of the interface complete  its down to the hard parts.   But that's for the next part!

 

Implementing IUpdatable (Part 1)

Silverlight Logo

I have been diving pretty deep into ADO.NET Data Services (see an upcoming article about ADO.NET Data Services and Silverlight 2 coming soon). I've been looking at the story around non-Entity Framework models through a Data Service and thought that NHibernate through a Data Service would be a great example.

So I tried to get it to work with the NHibernate LINQ project. The example model that the project uses is a simple Northwind model. I thought I'd just take that model and expose it via ADO.NET Data Services. I crufted up a simple Data Context object for ADO.NET Data Model but it didn't work. ADO.NET Data Services was complaining about the fact that the end-points (e.g. IQueryable<Customer>) was pointing at an Entity. This was a bug...a but in ADO.NET Data Services.  I hacked together a fix to get around it (and reporting it).  If you're interest, the problem is that if the key of the entity is in a base class and *not* named "ID", it fails to find it.  Again, this is a bug not a feature of ADO.NET Data Services.

Now that it was working, now what? I wanted to be able to make changes to the model but the NHibernate context doesn't support the updating interface: IUpdatable.  (Though I sure wish they'd rename it IUpdateProvider to match the IExpandProvider interface). If you're not familiar with this requirement, note that only the Entity Framework currently support the IUpdatable interface (and is in fact implemented inside of ADO.NET Data Services not directly in Entity Framework).  This means that DataSets and LINQ to SQL do not support it either. 

That's where I went to the Rhino Google Group (the discussion point for this project) and offered to implement the IUpdatable for the NHibernate provider. I thought it would be a fun exercise to understand the guts of the ADO.NET Data Service stack.

My first chore was to attempt to find a solution to decouple System.Data.Services.dll from the rest of the LINQ provider. My reasoning for this is that ADO.NET Data Services requires .NET 3.5 SP1 and I didn't want to tie the changes to that version of the Framework. Interestingly this is the same tactic that the ADO.NET Data Services team took.  This is why the Entity Framework does not implement the IUpdatable interface themselves, but the ADO.NET Data Service does it for them.  For the rest of the world, it expects a IUpdatable implementation directly on the context object that is used as the starting point for the service. After running around a number of ideas and running it by the Rhino people, we've decided to table that problem and get the interface working.  That will be a fight we fight later with a number of proposed solutions.

So now that architecture is out of the way, its time to start actually implementing the interface.  For the first pass, I am going to implement IUpdatable directly on NHibernateContext and refactor it later for less tight coupling. While I am at it, I also want to add support for IExpandProvider so that we can do expansions through NHibernate.

After adding the interface to the context object, I looked through the interface (its about a dozen calls that need to be implemented) to determine where to start. I am starting with the simple calls (GetResource, CreateResource, DeleteResource).  These calls are used by the service to do basic object manipulation.

For GetResource, I am handed a query and possibly a type name. GetResource is supposed to invoke the query to get the one and only one result (implementors should throw an error if the result returns more than one result) and then check the type name against the result.  In some cases you won't get the type name, but in most cases you will need to verify that the type of the result is the same as the type name. This is used to make sure that derived types are correctly retrieved from the data provider.

In implementing CreateResource, I noticed that I needed to be able to look up type information. Type information is important as I need information about the containers (e.g. IQueryable endpoints) on the context object.  This information is not readily available as its the classes that derive from NHibernate that add these end-points. I could get the type information via NHibernate's type information or using Reflection (neither are particularly cheap). For the first iteration I choose Reflection since I know that better, but I wanted to mitigate the cost with some caching. To that end, I've created some small in-memory caches of the reflection information that hopefully will help with the performance of these lookups.  This type information will help act like a factory to create new instances of the object. Unlike other ORM's, NHibernate doesn't use factories but uses simple object creation so we can just use the reflection information to create the instance of the new object and attach it to the NHibernate ISession.

Lastly, the simplest of the three methods to implement is the DeleteResource as it passes in a query (again, should always only return a single result) and mark it deleted in the session.  The change shouldn't be persisted (there is a SaveChanges call on the IQueryable interface that facilities the actual persistence to the data store).

Overall a fun couple of days.  Now on to the rest of the interface.  I'll keep you posted with my experience dealing with them!