locked
Custom Metadata Provider RRS feed

  • Question

  • I am writing my own CustomDataProvider based on a LINQ-to-SQL model. The LINQ-to-SQL exposes entities such as Customer and Order. I will be using the LINQ-to-SQL model solely for READs, all UPDATES will be channelled through proxy classes (I have an existing API to handle creates, updates and deletes on these entities). 

    This requires me to create custom Metadata, Query and Update Providers. 

    In terms of the Metadata I am using reflection against the model to determine the resources (e.g. Customer and Order) and their properties (e.g. Name, Order Number, etc). The code is as follows:

     

     

      private void AddResource<T>(string name, CustomMetadataProvider metadata, bool isOpenType)
      {
       var resourceType = new ResourceType
       (
        typeof(T),
        ResourceTypeKind.EntityType,
        null,
        "",
        name,
        false
       ) { IsOpenType = isOpenType };
    
       // use reflection to get all properties
       typeof(T)
        .GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Where(pi => pi.DeclaringType == typeof(T))
        .Select(pi => new ResourceProperty(
          pi.Name,
          (Attribute.GetCustomAttributes(pi).OfType<System.Data.Linq.Mapping.ColumnAttribute>().Where(ea => ea.IsPrimaryKey).Count() == 1)
          ? ResourcePropertyKind.Primitive | ResourcePropertyKind.Key
          : ResourcePropertyKind.Primitive,
          ResourceType.GetPrimitiveResourceType(pi.PropertyType)
         )
        )
        .ToList()
        .ForEach(prop => resourceType.AddProperty(prop));
    
       var resourceSet = new ResourceSet(string.Format("{0}s", name), resourceType);
    
    
       metadata.AddResourceSet(resourceSet);
       metadata.AddResourceType(resourceType);
      }
    

     

    This all works fine until I want to create ResourceAssociationSets, e.g. a Customer has many Orders, an Order is for a single Customer. In the model they are represented as EntitySets.

    My question is, rather than having to use Reflection can I get the Metadata direct from the Model. For out-of-the-box implementations (i.e. no custom providers) the framework is obviously doing something today to extract that information...can I hook into this mechanism?

    If not, can anyone see how I can build the metadata in a generic fashion (in my example I refer to just 2 entities, in fact I have 500) rather than having to hand-code each resource with their appropriate relationships?

    Thanks


    Wednesday, April 20, 2011 8:02 AM

Answers

  • There's no way to hook into the reflection provider - unfortunately. So you will have to write it on your own. But modeling the associations is not that hard, what we do is basically:

    - First we determine all the entity sets, by walking all the IQueryable properties on the context class (simplified, we also verify that the T in the IQueryable<T> is an entity type).

    - Then when we find a property on an entity class which is of a class type, we check if we've seen an entity set with that type. If so, we treat this property as a navigation property and define a one-way association between this entity and the target entity set. This time it's 1 to 1.

    - If we find a property on an entity class of type IEnumerable<T> where T is recognized as a type of some entity set. We also define this property as navigation property with the association, but this time it's a 1 to many.

    - Bunch of other stuff for primitive and complex types, but that is reasonably straightforward.

    Thanks,


    Vitek Karas [MSFT]
    Wednesday, April 20, 2011 4:07 PM
    Moderator

All replies

  • Hi,

    If you only need to implement the update behavior on your own, and you want the stock read behavior, you can do that. Just use the model as a reflection provider and implement the IUpdatable on your context. The reflection provider doesn't have a default IUpdatable anyway, so there's nothing to "override".

    Thanks,


    Vitek Karas [MSFT]
    Wednesday, April 20, 2011 10:39 AM
    Moderator
  • One more question...I would like to support OpenType and OpenProperties for my resources. Is this still possible using a ReflectionProvider? I have managed it using a custom IDataServiceQueryProvider but to do so I also Had a custom IDataServiceMetadataProvider and IDataServiceUpdateProvider. 

    I've noticed if I want to include my own IDataServiceQueryProvider I need to implement an IDataServiceMetadataProvider.

     

    Thanks


    Wednesday, April 20, 2011 1:54 PM
  • If you want to support open types, you will have to implement IDataServiceMetadataProvider. Also IDataServiceMetadataProvider and IDataServiceQueryProvider goes hand in hand - either you need to implement both or neither.

     

    Thanks

    Pratik


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Wednesday, April 20, 2011 3:44 PM
    Moderator
  • Thanks Pratik,

     

    So back to my original question:

    "...rather than having to use Reflection can I get the Metadata direct from the Model. For out-of-the-box implementations (i.e. no custom providers) the framework is obviously doing something today to extract that information...can I hook into this mechanism?

    If not, can anyone see how I can build the metadata in a generic fashion (in my example I refer to just 2 entities, in fact I have 500) rather than having to hand-code each resource with their appropriate relationships?"

    Thanks

    Wednesday, April 20, 2011 3:51 PM
  • There's no way to hook into the reflection provider - unfortunately. So you will have to write it on your own. But modeling the associations is not that hard, what we do is basically:

    - First we determine all the entity sets, by walking all the IQueryable properties on the context class (simplified, we also verify that the T in the IQueryable<T> is an entity type).

    - Then when we find a property on an entity class which is of a class type, we check if we've seen an entity set with that type. If so, we treat this property as a navigation property and define a one-way association between this entity and the target entity set. This time it's 1 to 1.

    - If we find a property on an entity class of type IEnumerable<T> where T is recognized as a type of some entity set. We also define this property as navigation property with the association, but this time it's a 1 to many.

    - Bunch of other stuff for primitive and complex types, but that is reasonably straightforward.

    Thanks,


    Vitek Karas [MSFT]
    Wednesday, April 20, 2011 4:07 PM
    Moderator
  • Vitek,

    When you put it like that it sounds easy...NOT. Reflection is not my bag, but I'll give it a go!

    Thanks for all your help. If you have any code lying about under your desk or in a drawer that may make life easier for me I would appreciate it. If not don't worry :)

     

    Wednesday, April 20, 2011 4:16 PM
  • Vitek does has some code lying around about Providers and its published at:

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

    At the bottom of the page there is a link for OData Provider Toolkit. Perhaps you already have found this. If not its a great resource for how to implement all of these server custom providers.

    Thanks,

    Chris


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Thursday, April 21, 2011 3:31 PM
    Moderator
  • It was code specifically to help with the reflection that I was after...don't worry sorted it now. Thanks
    Thursday, April 21, 2011 4:06 PM