OData Multiple models and APIs in the same site

Topics: ASP.NET Web API
Sep 26, 2012 at 2:25 AM

Hi,

I'm trying to see if it is possible to create multiple APIs for the OData interfaces. For example I want to have:

mysite/api/store and mysite/api/catalog

What prevents me from doing this are these:

- Only one ODataMediaTypeFormatter can be added to the configuration.Formatters and thus only one IEdmModel can be used

- There are some limitations with how links are generated, for example, internally there is reliance on ODataRouteNames.GetById constant, but that can be overcome i believe

Any feedback on the issues above and how to make it possible will be appreciated.

Thanks.

Sep 26, 2012 at 2:45 AM

just to get more context on your scenario before venturing an answer, could you please point out the reason for the separation ? why can't both the api's share the same edm model ?

Sep 26, 2012 at 5:33 AM
Edited Sep 26, 2012 at 8:31 AM

They are completely independent API's and can have objects with a same name. The EDM is generated automatically for each API based on the attributes specified for the classes (EntitySetAttribute, KeyAttribute and so on). If they share same EDM then there could be conflicts. 

There are also about hundred of them, so it will make it hard to find and distinguish to which system they belong.

I come from the WCF background where you can define multiple data services (catalogdataservice.svc and storedataservice.svc) with it's own api model and would like to do similar thing under web api. 

Hope this explains things.

Sep 27, 2012 at 9:09 PM

This is also good for supporting multiple versions of the API, so i can have a link api/1.0.0/store and api/2.0.0/store on the same site but using different models.

Sep 27, 2012 at 9:39 PM

The solution that we generally suggest is to use per-controller configuration. There are a couple of things that might not play well with the per-controller configuration, like, serving $metadata through ODataMetadataController. 

Sep 27, 2012 at 9:58 PM

So do i create idm model in the configuration for each controller?

Also what is your suggestion of fixing the meta data, i guess i can create custom metadatacontrollers for each system?

Thanks.

Sep 27, 2012 at 10:11 PM

1) you can create a odataformatter with the appropriate edm model per controller. you can build the models in advance and share them across the formatters and controllers.

2) you can clone the code for the metadatacontroller and modify it to pick up the right edm model depending on the routedata or the url. I know this is not an ideal thing to do but should work for now. please log an issue if you too believe that this is non-ideal.

Thanks,

RaghuRam.

Sep 28, 2012 at 12:38 AM

Thanks for the solution RaghuRam,

I will try your suggestions and see how it works out.

Oct 2, 2012 at 3:09 AM
Edited Oct 9, 2012 at 4:16 AM

How do you deal with generating links (edit, self, navigation) since the convention uses default route names for that? 

Oct 9, 2012 at 4:33 AM
Edited Oct 9, 2012 at 4:40 AM
Woland wrote:

How do you deal with generating links (edit, self, navigation) since the convention uses default route names for that? 

I see that convention "NavigationLinksGenerationConvention" is part of the static list in ODataConventionModelBuilder and has a public property PropertyNavigationRouteName which is what I'd like to set, but how do i access is it as part of the ODataConventionModelBuilder instance?

Oct 9, 2012 at 5:46 PM
Woland wrote:
Woland wrote:

How do you deal with generating links (edit, self, navigation) since the convention uses default route names for that? 

I see that convention "NavigationLinksGenerationConvention" is part of the static list in ODataConventionModelBuilder and has a public property PropertyNavigationRouteName which is what I'd like to set, but how do i access is it as part of the ODataConventionModelBuilder instance?

There is a bug tracking this issue - "enable modifying/adding conventions in the ODataConventionModelBuilder". 

Oct 10, 2012 at 3:23 PM

I second this idea about hosting multiple models in the same host.  Looking at the code there appears to be a an assumption that a single a model is registered and it lives in the HttpConfiguration.Properties.  Are you open to pull requests that change this functionality to look at a RouteData key instead?

You can then register routes with a value like EdmModelKey or even the EdmModel directly and the MetadataController and ODataMediaTypeFormatter can look in the route context first for an override to get the appropriate model.

Adam

Coordinator
Oct 10, 2012 at 9:01 PM

We are open to a pull request. Please create an issue to track this item and start a discussing thread on your proposed design. Also, if you haven't already, you will need to sign and submit a Contributors License Agreement to be able to contribute as described here:

http://aspnetwebstack.codeplex.com/wikipage?title=Contributing

Thanks for supporting this project!

Daniel Roth

Sep 20, 2013 at 5:56 AM
Just checking in - is this capability now supported?

Ideally I want to implement a public API that exposes a "public" model (i.e. not the database model), as well as an admin API that is more CRUD oriented and based upon the database model.

Of course there are good reasons why you might separate these into completely different applications, but I would like the flexibility of being able to support different EDMs on the same Web API stack.
Sep 20, 2013 at 6:19 AM
Yes, we do support hosting multiple EDM models in the same web API application. Just call MapODataRoute multiple times for each different model. If your model is dynamic, you could write code similar to what MapODataRoute does and provide the model at runtime dynamically.
Sep 20, 2013 at 8:50 PM
Edited Sep 20, 2013 at 8:51 PM
Thanks again.

I've tried it out and looks like it works very well so far.

One thing I couldn't figure out - the ODataConventionModelBuilder class allows you to define a custom namespace and container name which is great. This sets the namespace and entity container name for my entity sets. But it doesn't appear you can set the namespace for the Entity Types.

Below is a snippet the model builder code and the metadata document. As you can see, the namespace for the Entity Types is called SMDto (i.e. the internal namespace of the types exposed via OData).
modelBuilder.Namespace = "Public";
modelBuilder.ContainerName = "MyService";
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
  <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <Schema ***Namespace="SMDto"*** xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityType Name="Customer">
        ...
      </EntityType>
      ...
    </Schema>
    <Schema ***Namespace="Public"*** xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityContainer ***Name="MyService"*** m:IsDefaultEntityContainer="true">
        <EntitySet Name="Customers" EntityType="SMDto.Customer" />
        ...
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
Is there anyway to also define the namespace on the schema for the entity types?