I have an entity defined as follows (code generated by the VS2010 POCO generator):
public partial class TransactionList
{
public int tlRecordId { get; set; }
public Nullable<int> tlTracer { get; set; }
public Nullable<int> tlRecordType { get; set; }
public Nullable<int> tlPayType { get; set; }
public Nullable<int> pdmNumber { get; set; }
public string pdmName { get; set; }
public string zoneName { get; set; }
public string groupName { get; set; }
public Nullable<int> serviceCarNumber { get; set; }
public Nullable<int> moneyCarNumber { get; set; }
public Nullable<System.DateTime> tlPayDateTime { get; set; }
public Nullable<System.DateTime> tlExpDateTime { get; set; }
public Nullable<int> senderPdmNumber { get; set; }
public Nullable<int> tlAmount { get; set; }
public Nullable<int> tlTicketNo { get; set; }
}
and there is a second partial class for this class (manually written) which contains the Key attribute.
[DataServiceKey("tlRecordId")]
public partial class TransactionList
{ }
So there are no complex types/primitives collections defined within this class. If I expose this class using WCF Data Services, I get the following exception:
The server encountered an error processing the request. The exception message is 'The property 'TransactionList' on type 'DomainObjects.EntityFrameworkModel.Gac' is not a valid property. Properties whose types are collection of primitives or complex types are not supported.'. See server logs for more details. The exception stack trace is:
at System.Data.Services.Providers.ReflectionServiceProvider.BuildTypeProperties(ResourceType parentResourceType, IDictionary
2 knownTypes, IDictionary
2 childTypes, Queue1 unvisitedTypes, IEnumerable
1 entitySets) at System.Data.Services.Providers.ReflectionServiceProvider.PopulateMetadataForTypes(IDictionary2 knownTypes, IDictionary
2 childTypes, Queue1 unvisitedTypes, IEnumerable
1 entitySets) at System.Data.Services.Providers.ReflectionServiceProvider.PopulateMetadata(IDictionary2 knownTypes, IDictionary
2 childTypes, IDictionary2 entitySets) at System.Data.Services.Providers.BaseServiceProvider.PopulateMetadata() at System.Data.Services.DataService
1.CreateProvider() at System.Data.Services.DataService1.EnsureProviderAndConfigForRequest() at System.Data.Services.DataService
1.HandleRequest() at System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody) at SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
The class Gac contains the property TransactionList defined as follows:
public IQueryable<TransactionList> TransactionList
{
get { return _myContext.TransactionList.AsNoTracking(); }
}
Why do I get this exception? I do not see any helpful information in the WCF server log. One useful information for you can be that this entity is representing a database view. I already expose a similar entity which contains the same types of properties int, DateTime, string) and it is working.
public class DummyService : DataService<CustomContext>
{
protected override CustomContext CreateDataSource()
{
//I need a single endpoint exposing data from more databases. Here I pass
//the data needed for the creation of connection strings
var dataSource = new CustomContext(new int []{1,2,3});
return dataSource;
}
}
///This class represents my single endpoint exposing data from various databases.
///Every database has the same DB schema.
public class CustomContext: IDisposable
{
private static IList<Gac> _gacList;
//Here I construct the list of accessible data sources - databases. This list
// can vary
public CustomContext(IEnumerable<AreaCodes> areaCodes)
{
//This is the list of various databases exposed via OData
_gacList = new List<Gac>();
foreach (AreaCodes ac in areaCodes)
{
//When calling the constructor of Gac, the DbContext gets created.
_gacList.Add(new Gac(ac.Id));
}
}
//the entity which will be exposed
public IQueryable<Gac> Gac
{
get { return _gacList != null ? _gacList.AsQueryable() : null; }
}
}
///This class represents a single data source - a database.
//All databases are exposed over single endpoint
[DataServiceKey("GacId")]
public class Gac: IDisposable
{
private MyContext _myContext;
private int _gacId;
public Gac(int areaCode)
{
//Here I finally create my context
_myContext= new MyContext("...Connection String Generated based on areaCode");
_myContext.Configuration.ProxyCreationEnabled = false;
_myContext.Configuration.LazyLoadingEnabled = false;
_gacId = areaCode;
}
//This property uniquely identifies a GAC object. It is an entity key.
public int GacId
{
get { return _gacId; }
}
//Expose an entity from the EF layer
public IQueryable<TransactionList> TransactionList
{
get { return _myContext.TransactionList.AsNoTracking(); }
}
//...Other about 25 IQueryable properties
}
//generated Entity Framework Conceptual Layer
public partial class MyContext: DbContext
{
//This entity represents my database view which I want to access to
public DbSet<TransactionList> TransactionList { get; set; }
//...Other about 25 generic DbSet properties
}
Do you expose a property of type IQueryable on your Context class? If not, then that's the problem. In order for a class to be recognized as an entity it must be exposed in an entity set (have a property on the Context class) and it must have a key (either heuristics or DataServiceKey attribute).
Also note, that it seems you're defining a reflection based service over an EF based data store. This will have problems with more complex queries. WCF DS generates somewhat different expression trees for reflection provider versus EF provider (necessary due to the space of valid expressions supported by these). The trees generated for reflection provider are not always compatible with EF (and the other way round).
If you could share more of your Context class, maybe there's a way to do this without forcing the usage of reflection provider. I understand that you expose several DBs over the service, but how do you run which DB to run the incomming request against? If it can be determined by looking at the request alone, and each request can run only on a single DB, then I think just a bit of logic inside the CreateDataSource should be enough.