3
votes

Currently we are evaluating whether we should use WCF data services as DAL for our application and I am doing a POC to see if WCF data service craters to all our scenarios. If I am having a data model as below

DataModel

and I want to Get All products > $100 ordered by a person with Id 1 how would I do it using Linq to OData as there are many restrictions on Linq operators when using with OData.

dbContext.LineItems.Where(li => li.Order.PersonId == 1 && li.Product.Cost > 
120).Select( li => new { Product = li.Product})

The above code generates very obscure T-SQL on Sql server, ideally I would want to drill down from top to bottom (from Order to Products if wring LINQ Query or direct T-SQL) joining tables but using OData it always seems to be going from bottom to top which I don't feel very comfortable and in some cases generates very bad sql when we have to query more then two table in a singe query.

In our application we are expecting scenarios like this where we have to query(write conditions) up to 4 tables in a singe query and select a single entity. For these kind of complex queries is OData correct at all?

Thanks for reading a long post.

3
Doing complex queries over WCF is a bad idea and probably means your domain model is too fine-grained. Whether OData can do complex queries is irrelevant if you have a bad model. You should expose only the entities needed to fulfill a scenario, not the detailed database schemaPanagiotis Kanavos
@PanagiotisKanavos I am sure our database is very normalized(I assume this normal database design) with complex relations between tables, I am sure we will end up in scenarios where we have primary key for Table A and we have to get data from Table D by going from Table A - Table B - Table C - Table D where the relationships between the tables can be 1-1, 1-* or Many - many , so I am debating whether OData(WCF data service) is a good option in such scenarios, as WCF dataservice i being proposed as data access layer(only way to access DB and can't use EF directly) by our architect.Sri Harsha Velicheti

3 Answers

3
votes

In some cases you will find that you can't produce the Odata query in LinQ, it's not that the query isn't possible, it's just that the Linq provider can't do it. At the start of our project we swiftly found ourselves writing the Odata query first and then trying to convert it to LinQ; it didn't take long for us to resort to simply using Odata for the queries.

This post might offer you some other useful information: Disadvantages of OData? particularly the linked post on using views.

1
votes

WCF Data Services gives you access to a DataServiceContext instance. If you have generated your references this will be typed for you. However, the query operators available to you are limited and I expect you will experience various degrees of friction trying to use it compared to just using Entity Framework and Linq to Entities.

I expect this because although I am using a slightly different data model to yours, it is a similar hierarchical model and I started the query lower down as you did:

var query = context.DataSources.
Where(x => x.ReportLayouts[0].ReportLayoutID == 1045 &&  x.InstanceName == "hello").
Select(li => new { InstanceName = li.InstanceName });

The result on a ToList()

An unhandled exception of type 'System.NotSupportedException' occurred in Microsoft.Data.Services.Client.dll

Additional information: The expression (([10007].ReportLayouts.get_Item(0).ReportLayoutID == 1045) And ([10007].InstanceName == "hello")) is not supported.

In order to get that data I then tried this as a test:

var query2 = context.DataSources.Select(x => x.ReportLayouts[0].ReportLayoutID == 1045);
var result2 = query2.ToList();

'System.NotSupportedException' Additional information: The method 'Select' is not supported.

I then started with my top level entity, equivalent to your Person and ran this:

var query3 = context.Reports.Where(x =>x.ReportLayoutID == 34 &&  x.DatabaseInstance.ServerName == "hello");
var result3 = query3.ToList();

This works fine. I am now struggling to get from this top level entity to the lower level entity via navigation properties. Using the DataServiceContext class directly I can more easily compose a DataServiceQuery that translates directly to the URL formats supported by ODATA with AddQueryOption:

query = query.AddQueryOption("$expand", expandPropertyName);

This allows you to load navigation properties explicitly by generating a url similar to this:

ReportService.svc/DataSources?expand=dataobjects

The generated service reference also provides access to the DataServiceContext.LoadProperty method. Once loaded you can query it but this involves round trips.

Personally I would not use WCF Data Services as your DAL as this is what Entity Framework is built for. If you need to use a service to expose an EF model to external clients that is a different matter, but your DAL should still be Entity Framework. WCF Data Services would be appropriate for an application doing very simple CRUD (no business logic). I would not trust it for complex queries run internally as a DAL. The web methods it supports are also very crude only accepting primitives such as string and int as parameters. I would investigate WCF if a service is required, which I suspect it is not in your case as you mentioned using it as a DAL.

0
votes

If you want to have a great control over OData communication I'd recommend to use a library that better matches OData protocol. You can have a look at my article "12 reasons to consume OData feeds using Simple.OData.Client" where I present some challenges using WCF Data Services (especially when multiple entities involved) and why Simple.OData.Client can be a better fit. Here's the link:

http://www.codeproject.com/Articles/686240/12-reasons-to-consume-OData-feeds-using-Simple-ODa