I'm trying to expose a model to be available for OData
services. The approach I'm currently taking is along the lines of:
1) Defining a class in the model to expose IQueryable
collections such as:
public class MyEntities
{
public IQueryable<Customer> Customers
{
get
{
return DataManager.GetCustomers().AsQueryable<Customer>();
}
}
public IQueryable<User> Users
{
get
{
return DataManager.GetUsers().AsQueryable<User>();
}
}
}
2) Set up a WCF DataService
with the queryable collection class such as:
public class MyDataService : DataService<MyEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
config.SetEntitySetAccessRule("Users", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
I'm running into 3 issues and/or limitations with this approach:
1) I'm unable to add any derived class collections to the IQueryable
lists.
2) I must apply the IgnoreProperties
attribute to hide any members that are derived from a base type.
3) I'm unable to prevent unwanted entities from being accessed by the OData
service and causing errors. For example, I only want BLL layer objects to be exposed, but it seems like the model is being reflected far beyond the members of the classes I added to the queryable list, and picking up all the DAL classes, causing errors being undefined and also having the same name as the BLL classes. There are no links to DAL classes from BLL class members. At the very least, I would like to have these classes ignored altogether.
Any pointers on how to address any of these issues would be greatly appreciated. Should I be doing a different approach on this? For example, should I implement IQueryable
directly in my model collections?
Thanks.
The reflection provider is not designed to handle rich data models with a fair amount of inheritance and other dependences. I ended up building a custom provider that could handle queries, updates, inheritance, and relationships based on Alex James' excellent blog post on Creating a Data Service Provider.
An example implementation with 3 CLR classes:
ResidentialCustomer
,Customer
, andUser
is provided below.ResidentialCustomer
extendsCustomer
,Customer
has a list ofUser
s, andUser
has a reference back toCustomer
.An interface for
DataContext
classes such as:A class to implement
IDataServiceMetadataProvider
such as:A class to implement
IDataServiceQueryProvider
such as:A class to implement
IDataServiceUpdateProvider
such as:A class to implement
IServiceProvider
such as:The
DataContext
class holds the CLR collections and wires up the service operations such as:Then, create your data service using the custom data service class and your data context, such as:
Lots of wiring up, but pretty straightforward once you've got the hang of it.
The reflection provider which you're using is designed to walk all public types/properties. So the #3 and probably even #2 (which I don't fully understand what's the problem) are by design because of that.
#1 is also by design but for a different reason - the reflection provider can only expose one entity set for each type hierarchy. It doesn't support so called "MEST" (Multiple Entity Sets per Type), because it would not know which one to pick. It needs a 1 to 1 mapping between entity types and entity sets.
The reflection provider is meant for simple services which are "Easy" to setup. It's definitely not designed for customizations.
If you want greater control, then you need custom provider, which can be either implemented directly (if it's based on existing CLR classes it's not that hard), or through some library, like the one suggested in the comments above.