Showing posts with label odata. Show all posts
Showing posts with label odata. Show all posts

Friday, June 29, 2018

Asp.net WebApi OData V4 problems with nested $expands

Leave a Comment

I have a OData V4 over Asp.net WebApi (OWIN).

Everything works great, except when I try to query a 4-level $expand.

My query looks like:

http://domain/entity1($expand=entity2($expand=entity3($expand=entity4))) 

I don't get any error, but the last expand isn't projected in my response.

More info:

  1. I've set the MaxExpandDepth to 10.
  2. All my Entities are EntitySets.
  3. I'm using the ODataConventionModelBuilder.
  4. I've opened an SQL-profiler and could see that the query (and the result) is correct. It's some filter that occurs after the query is executed.
  5. I've searched the web and didn't find anything suitable.
  6. I've tried different entity 4 level $expands and they didn't work as well.

Edit:

I've overridden the OnActionExecuted:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {     base.OnActionExecuted(actionExecutedContext);      var objectContent = actionExecutedContext.Response.Content as ObjectContent;     var val = objectContent.Value;      var t = Type.GetType("System.Web.OData.Query.Expressions.SelectExpandWrapperConverter, System.Web.OData");     var jc = Activator.CreateInstance(t) as JsonConverter;     var jss = new JsonSerializerSettings();     jss.Converters.Add(jc);      var ser = JsonConvert.SerializeObject(val, jss);  } 

The serialized value contains entity4.

I still have no idea what component removes entity4 in the pipe.

Edit #2:

I've create an adapter over DefaultODataSerializerProvider and over all the other ODataEdmTypeSerializer's. I see that during the process the $expand for entity4 exists and when the ODataResourceSerializer.CreateNavigationLink method is called on that navigationProperty (entity4) then it returns null.

I've jumped into the source code and I could see that the SerializerContext.Items doesn't include the entity4 inside it's items and the SerializerContext.NavigationSource is null.

To be specific with versions, I'm using System.Web.OData, Version=6.1.0.10907.

0 Answers

Read More

Tuesday, October 24, 2017

Using OData in .NET Core Web API for MongoDB

2 comments

OData is now supported in .NET Core and 7.2.0 was released. But can it be used with MongoDB? I have searched, but I could not find anything that says one way or the other.

EDIT:

I've found a nuget package https://www.nuget.org/packages/microsoft.aspnetcore.odata and in ConfigureServices I've added this:

And this seems to work for me:

public void ConfigureServices(IServiceCollection services) {     ...     services.AddOData();     services.AddSingleton<IODataModelManger, ODataModelManager>(DefineEdmModel);     ... }  private ODataModelManager DefineEdmModel(IServiceProvider services) {     var modelManager = new ODataModelManager();      var builder = new ODataConventionModelBuilder();     builder.EntitySet<TestDTO>(nameof(TestDTO));     builder.EntityType<TestDTO>().HasKey(ai => ai.Id); // the call to HasKey is mandatory     modelManager.AddModel(nameof(Something), builder.GetEdmModel());      return modelManager; } 

Controller

[HttpGet("all")] public async Task<IQueryable<TestDTO>> Get() {     // plug your entities source (database or whatever)     var test = await TestService.GetTest();      var modelManager = (IODataModelManger)HttpContext.RequestServices.GetService(typeof(IODataModelManger));     var model = modelManager.GetModel(nameof(Something));     var queryContext = new ODataQueryContext(model, typeof(TestDTO), null);     var queryOptions = new ODataQueryOptions(queryContext, HttpContext.Request, Provider);      return queryOptions         .ApplyTo(test, new ODataQuerySettings         {             HandleNullPropagation = HandleNullPropagationOption.True         }, null)         .Cast<TestDTO>(); } 

Service

public async Task<IQueryable<TestDTO>> GetTest() {     return await GenericRepository.TestAll(); } 

Repositories

public async Task<IQueryable<TEntity>> TestAll() {     var res = new GetManyResult<TEntity>();     try     {         DateTime startTime = DateTime.Now;         var collection = GetCollection<TEntity>().AsQueryable();         var entities = collection.ToArray<TEntity>().AsQueryable();         return entities } 

But is this the best way to do it?

I mean, shouldn't the collection contain only the elements that meet the filters, beeing more optimised?

If yes, how do I achieve this?

1 Answers

Answers 1

I think theres only currently one connected service available in the visual studio market place for MongoDB. Link Here.

ODBC Driver for MongoDB provides high-performance and feature-rich connectivity solution for ODBC-based applications to access MongoDB databases from Windows, MacOS, Linux. Full support for standard ODBC API functions, MongoDB data types and SQL queries implemented in our driver makes interaction of your database applications with MongoDB fast, easy and extremely handy.

Looks like it would handle all of the things you'd expect it to when connecting to MongoDB.

However it's worth noting that, that is only a trail and I've been unable to find any 'open source' versions

Read More

Monday, September 25, 2017

Is ODATA using Microsoft Web API really REST architecture?

Leave a Comment

The more I research about Microsoft framework on ODATA I tend to believe that it is not suited for enterprise application. The framework expects all the database to be directly exposed as ViewModel, even for simple operations like Pagination & sorting.

We would be forced to use stasteful mechanism to persist page numbers rendered to the JavaScript client.

Or am I not understanding Microsoft implmentation of OData correctly?

EDIT-1:

Is ODATA V4 a Stateful Architecture? As promoted by Microsoft patterns team. I do not see any easy path of Migration from Asp.Net Web API (REST) to OData (Sounds STATEFUL) Architecture.

EDIT-2: Paging, sorting & grouping is part of incoming request from the client.

1 Answers

Answers 1

In short the MS Odata server side implementation is not statefull and it can be considered a REST architecture.

We would be forced to use stasteful mechanism to persist page numbers rendered to the JavaScript client

You provide paging information in the request. For example, if you wanted 10 items of page 2 you would take the top 10 and skip 10.

odata-url/?$count=true&$top=10&$skip=10 

As you can see the client/caller specifies the paging, there is no need for the server to track state of the client.

Also adding $count=true will return the total number of records based on the passed in filter included in the result set (in the above example there is no filter). This will allow the client to calculate the number of pages there are.


The framework expects all the database to be directly exposed as ViewModel...

Also not true. You can return an IQueryable<T> where T is your type. T does not have to be an EF model. For example,returning the following from a DbContext is acceptable.

public IQueryable<SomeEntity> Get() {     return dbContext.SomeEntities         .Where(x => optionalPreFiltereExpression)         .Select(x => new SomeDTO(){             Prop1 = x.Prop1,             Collection1 = x.CollectionOfInterest,             // etc         }); } 

To further illustrate that point you could also return a hard coded list of objects, although this might not be very likely in production.

public IQueryable<SomeEntity> Get() {     return new List<SomeDTO>(){         new SomeDTO(){             Prop1 = 5,             Prop2 = "Hi there"             // etc},         new SomeDTO(){             Prop1 = 6,             Prop2 = "Goodbye"             // etc}         }).AsQueryable(); } 

There are many resources on all the options for OData. I am not going to include everything here, otherwise I might as well just be creating a 2nd set of documentation.

Read More

Tuesday, September 12, 2017

Ignore SSL Errors when generating OData Client

Leave a Comment

I am trying to generate client code for an OData v4 service using Visual Studio. My service is running on a server that doesn't have a DNS name, and is using a self-signed certificate for HTTPS. My service is not avaiable over HTTP.

When I run the OData Client Code Generator T4 template, I get the SSL Error at the bottom of this post. When I attempt to add the service via the 'Add Connected Service' context menu, I get an error saying it could not connect.

I believe VS is not connecting to my service because the SSL Certificate doesn't match the domain name of the URL, since the server is being accessed via IP Address instead of by domain name. If this were a client program, I could write my own certificate validation routine, overriding the existing policy. Since this code is running in Visual Studio, however, I do not have access to the runtime (I think).

Surely I am not the first person to come up against this. How do you generate an OData Client when your service is only available via IP Address?

Detailed error:

Severity    Code    Description Project File    Line    Suppression State Error       Running transformation: System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.    at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)    at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)    at System.Net.TlsStream.CallProcessAuthentication(Object state)    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)    at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)    at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)    at System.Net.ConnectStream.WriteHeaders(Boolean async)    --- End of inner exception stack trace ---    at Microsoft.VisualStudio.TextTemplatingCB56359812AF85B717EE86D198C095A18F392F5ED334B1B5D270CD3394D2871D814F7AE052FF43EC12182D14B978B90A2434B91A696421B885CA34564683123A.GeneratedTextTransformation.CodeGenerationContext.GetEdmxStreamFromUri(Uri metadataUri) in :line 972    at Microsoft.VisualStudio.TextTemplatingCB56359812AF85B717EE86D198C095A18F392F5ED334B1B5D270CD3394D2871D814F7AE052FF43EC12182D14B978B90A2434B91A696421B885CA34564683123A.GeneratedTextTransformation.CodeGenerationContext.GetEdmxStringFromMetadataPath(Uri metadataUri) in :line 935    at Microsoft.VisualStudio.TextTemplatingCB56359812AF85B717EE86D198C095A18F392F5ED334B1B5D270CD3394D2871D814F7AE052FF43EC12182D14B978B90A2434B91A696421B885CA34564683123A.GeneratedTextTransformation.CodeGenerationContext..ctor(Uri metadataUri, String namespacePrefix) in :line 547    at Microsoft.VisualStudio.TextTemplatingCB56359812AF85B717EE86D198C095A18F392F5ED334B1B5D270CD3394D2871D814F7AE052FF43EC12182D14B978B90A2434B91A696421B885CA34564683123A.GeneratedTextTransformation.TransformText() in :line 67         972  

My OData Service URI (from the T4 template):

public const string MetadataDocumentUri = "https://10.144.128.44/mgmt/odata/$metadata";

0 Answers

Read More

Wednesday, August 30, 2017

ODATA - how to generate odata service from Edmx file

Leave a Comment

There is some Odata lib which I can use that from edmx file it generate an odata service? By providing only edmx file it create the service that can answer the metadata calls...

I've found this library

https://github.com/htammen/n-odata-server

But it requires json not edmx/metadata.xml file...

I see the Olingo lib but I didn't find any functionality that can do it ...

https://olingo.apache.org

Any direction if it possible?

I prefer to use some nodejs lib if there is some combination that could work, but its not mandatory

I've also find this lib https://github.com/jaystack/jaysvcutil

1 Answers

Answers 1

If you are happy to use .Net, you could try RESTier. Follow the instructions here: http://odata.github.io/RESTier/, except don't generate a new EF data model class. Instead add your edmx model into the project.

Then go to the section 'Configure the OData Endpoint', and rather than entering:

        await config.MapRestierRoute<EntityFrameworkApi<AdventureWorksLT>>(             "AdventureWorksLT",             "api/AdventureWorksLT",             new RestierBatchHandler(GlobalConfiguration.DefaultServer)); 

use your data model class (the class that inherits DbContext) rather than AdventureWorksLT in EntityFrameworkApi<AdventureWorksLT> , and change the route name and prefix to something more suitable.

Read More

Thursday, August 3, 2017

Why is Entity Framework generating the following nested SQL for Azure Mobile Services Table Controllers

Leave a Comment

I'm trying to get to the bottom of an entity Framework issue when using it with a TableController

I've created the following setup.

  1. The basic TodoItem example provided with a new Mobile Web Api which leverages EntityFramework, TableController & the default EntityDomainManager

    public class TodoItemController : TableController<TodoItem> {     protected override void Initialize(HttpControllerContext controllerContext)     {         base.Initialize(controllerContext);         context = new MobileServiceContext();         context.Database.Log += LogToDebug;         DomainManager = new EntityDomainManager<TodoItem>(context, Request);     }      public IQueryable<TodoItem> GetAllTodoItems()     {         var q = Query();         return q;     } 
  2. A vanilla Web API 2 controller.

    public class TodoItemsWebController : ApiController {      private MobileServiceContext db = new MobileServiceContext();     public TodoItemsWebController()     {         db.Database.Log += LogToDebug;     }      public IQueryable<TodoItem> GetTodoItems()     {         return db.TodoItems;     } 

I've gone through the tablecontroller code with a fine tooth comb, digging into the Query method, which is just proxying the call via the DomainManager to add in the Where(_ => !_.IsDeleted) modfiication to the IQueryable

Yet the two queries produce VERY different SQL.

For the regular Web API Controller you get the following SQL.

SELECT      [Extent1].[Id] AS [Id],      [Extent1].[Version] AS [Version],      [Extent1].[CreatedAt] AS [CreatedAt],      [Extent1].[UpdatedAt] AS [UpdatedAt],      [Extent1].[Deleted] AS [Deleted],      [Extent1].[Text] AS [Text],      [Extent1].[Complete] AS [Complete]     FROM [dbo].[TodoItems] AS [Extent1] 

But for the TableController, you get the following chunk of SQL which has a *Magic* Guid in the middle of it, and results in a Nested SQL statement. The performance of this goes to complete garbage when you start dealing with any of the ODATAv3 queries like $top, $skip, $filter and $expand.

SELECT TOP (51)      [Project1].[C1] AS [C1],      [Project1].[C2] AS [C2],      [Project1].[C3] AS [C3],      [Project1].[Complete] AS [Complete],      [Project1].[C4] AS [C4],      [Project1].[Text] AS [Text],      [Project1].[C5] AS [C5],      [Project1].[Deleted] AS [Deleted],      [Project1].[C6] AS [C6],      [Project1].[UpdatedAt] AS [UpdatedAt],      [Project1].[C7] AS [C7],      [Project1].[CreatedAt] AS [CreatedAt],      [Project1].[C8] AS [C8],      [Project1].[Version] AS [Version],      [Project1].[C9] AS [C9],      [Project1].[Id] AS [Id]     FROM ( SELECT          [Extent1].[Id] AS [Id],          [Extent1].[Version] AS [Version],          [Extent1].[CreatedAt] AS [CreatedAt],          [Extent1].[UpdatedAt] AS [UpdatedAt],          [Extent1].[Deleted] AS [Deleted],          [Extent1].[Text] AS [Text],          [Extent1].[Complete] AS [Complete],          1 AS [C1],          N'804f84c6-7576-488a-af10-d7a6402da3bb' AS [C2],          N'Complete' AS [C3],          N'Text' AS [C4],          N'Deleted' AS [C5],          N'UpdatedAt' AS [C6],          N'CreatedAt' AS [C7],          N'Version' AS [C8],          N'Id' AS [C9]         FROM [dbo].[TodoItems] AS [Extent1]     )  AS [Project1]     ORDER BY [Project1].[Id] ASC 

You can see the results of both queries here. https://pastebin.com/tSACq6eg

So my questions are:

  • Why is the TableController generating the SQL in this way?

  • What is the *magic* guid in the middle of the query? (it will stay the same until I stop and restart the app so I don't know if it's session, client or DB context specific)

  • Where exactly in the pipeline is the TableController making these Modifications to the IQueryable? I assume it's done through some middleware step, or an on executed attribute later in the request after the Query() method is called, but I cannot for the life of me find it.

2 Answers

Answers 1

According to your description, I did some research and found that Azure Mobile Server SDK uses the following code line under TableControllerConfigProvider.cs for adding additional query related filters for the same actions with the QueryableAttribute for enabling a controller action to support OData query parameters.

controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider()); 

Note: the additional filters would be executed after your action has been executed and return the IQueryable.

You could check EnableQueryAttribute.cs and found that OnActionExecuted would call the ExecuteQuery method and eventually call ODataQueryOptions.ApplyTo for applying OData query options ($filter, $orderby, $top, $skip, and $inlinecount, etc.) to the given IQueryable.

Per my understanding, the Nested SQL statement is generated by OData component. After invoked ODataQueryOptions.ApplyTo, your IQueryable has been modified and the related sql statement has been modified too. I did some test in my regular Web API Controller as follows, you could refer to it:

Request:

Get http://localhost:58971/api/todoitem?$top=2&$select=Text,Id,Version 

Before applying OData query options:

enter image description here

After applied OData query options:

enter image description here

Answers 2

Your one of the tables being synced between the backend and client because if that your are getting 2nd sql.

Read more here : https://documentation.devexpress.com/wpf/17927/Common-Concepts/Scaffolding-Wizard/Tutorials/Building-Outlook-Inspired-and-Hybrid-UI-Applications/Lesson-3-Customize-Layout-of-the-Collection-Views

Read More

Friday, September 23, 2016

Determine an OData server's version

Leave a Comment

Given a known OData endpoint, what's the best way to determine the version of the OData service? The client in this scenario can support any version (1-4) but I need to know how to format the request.

For example, the OData-Version returns "4.0" for a V4 service, but a V3 service wouldn't even have that header.

In addition, querying the service root URL could be quite expensive for a service with a large number of entities. For example, a basic Dynamics 2016 Online service with no custom objects returns 2.7KB of data, when all I really want is the version header.

So what is the lightest-weight solution for getting a reliable version number? It's ok if the solution is "check this or, if missing, then check that". What's the "this" and "that"?


I found one question (How to find OData version from metadata) which seemed to get me partly there, but there are some issues with the answer.

First, it's focused on finding min/max version numbers where I would really prefer the max.

Second, it requires querying metadata, but that's a potentially massive load. /$metadata on Dynamics CRM 2016 Online results in a 3.7MB response (that takes 30 seconds to download on my current connection). I thought about requesting a dummy entity, like /dummy__entity and then examining the headers, but that seems a bit iffy to me because it would unnecessarily trigger error logging on the server and I'm not sure that an error response is always likely to have the headers I'm looking for.

1 Answers

Answers 1

OData 1.0/2.0/3.0

According to MS-ODATA 1.7 Versioning and Capability Negotiation:

The OData protocol that is defined in this document enables limited capability negotiation using the DataServiceVersion (section 2.2.5.3) and MaxDataServiceVersion (section 2.2.5.7) version request headers and the DataServiceVersion (section 2.2.5.3) response header.

When it says "limited", it means limited:

In a response from the server to the client, the DataServiceVersion (section 2.2.5.3) header is specified. The value states the version of the protocol that the server used in the request to generate the response and that is used by the client to determine if it can correctly interpret the response (that is, the value is not larger than the value of the MaxDataServiceVersion (section 2.2.5.7) header sent in the associated request). The value of the header is the lowest version of the protocol the server can use to fulfill the request.

So, basically, a conformant service capable of handling OData versions 1.0 to 3.0 would return "1.0" for features defined by OData 1.0, "2.0" for features defined by OData 2.0 and not present in OData 1.0, etc.

OData 4.0

According to OData Version 4.0 Part 1: Protocol, Section 8.1.5 Header OData-Version

OData services MUST include the OData-Version header on a response to specify the version of the protocol used to generate the response. The client MUST interpret the response according to the rules defined in the specified version of the protocol.

According to What's New in OData Version 4.0, Section 2.1.1 Improved: Protocol Versioning

Services now respond with the maximum protocol version supported by the server and indicated acceptable by the client.

Also "downgrade" to versions prior to 4.0 is not covered, and service publishers are advised to use new service root URLs for 4.0 services.

So for future versions of OData, from 4.0 onward, it seems that I can reliably get the maximum version I was seeking.

Conclusion

There is no apparent way to get the maximum version of OData supported by a pre-4.0 service. The DataServiceVersion response header will contain the lowest possible version number based on the URL features present, the version of OData that the service supports and the client-requested version.

However, beginning with OData 4.0, the OData-Version response header will always contain the maximum version based on what the service supports and what the client requested.

Unfortunately, in every instance I've tried so far, passing a "DataServiceVersion" header to an OData 4.0 service results in a 500 Internal Server Error response (with no OData-Version header). So it would seem that sending both OData-Version and DataServiceVersion headers isn't guaranteed to work.

Best bet seems to be sending OData-Version and then looking for a DataServiceVersion response header (which will likely be "1.0" even for a service that supports 3.0). If that header is present in the response, then send a second request with a DataServiceVersion header of "3.0". If you get a 4xx response, then try "2.0" etc.

Read More

Friday, April 29, 2016

How publish changes to OData feed from Excel

Leave a Comment

I've created an IUpdateable ODATA feed using WCF which I can connect to using Excel 2010 Power Query. Excel is able to retrieve data but I cannot find a 'publish' button or anyway to tell Excel to push updates back to the server.

Does Excel / Power Query support ODATA CRUD functions out of the box?

Is there another tool I could use to update data without writting code?

1 Answers

Answers 1

Power Query supports reading from data sources, but our goal today is not Create/Update/Delete.

I agree with you--it would be awesome to publish data from Excel without having to write any code!

If you think this is something Power Query should have, I'd go to https://ideas.powerbi.com and vote up the feature (you might be the first with the idea).

Read More

Friday, March 11, 2016

Error when using $select and $expand with Odata v4.0 on Web API 2.2

Leave a Comment

I have a web api 2.2 that is configured for OData v4. I want to return a user by ID and include only the user group Id's that the user is a member of. When I do this

http://localhost/User?$filter=id eq 312&$select=*,userGroups/id&$expand=userGroups 

I get this error

The query specified in the URI is not valid. Found a path with multiple navigation properties in a select clause. Please reword your query such that each level of select or expand only contains either TypeSegments or Properties.

Found a path with multiple navigation properties in a select clause. Please reword your query such that each level of select or expand only contains either TypeSegments or Properties.

The query will execute if I remove ",userGruops/id"

1 Answers

Answers 1

You should write your query like this:

http://localhost/User?$filter=id eq 312&$select=*&$expand=userGroups($select=id) 

By the way, you can also remove the $select=* segment as all non-navigation properties are by default included in the response.

Read More