2
votes

I have a WCF Data Service layer that is exposing POCO entities generated by the POCO T4 template. These POCO entities are created in their own project (i.e. Company.ProjectName.Entities) because I'd like to share them wherever possible.

I have a set of interfaces in another project (Company.ProjectName.Clients) that reference these POCO types by adding an assembly reference to the Company.ProjectName.Entities.dll. One of the implementation of these interfaces is a .NET client that I want to consumes the service using the WCF Data Service Client Library.

I've used the Add Service Reference to add service reference. This generated the DataServiceContext client class and the POCO entities that are used by the service. However, these POCO types gemerated by the Add Service Reference utility now have a different namespace (i.e. Company.ProjectName.Clients.Implementation.WcfDsReference).

What that means is that the POCO types defined in the interfaces cannot be used by the types generated by the utility without have to cast or map.

i.e. Suppose I have:

 1. POCO Entity:     Company.ProjectName.Entities.Account
 2. Interface:       interface IRepository<Company.ProjectName.Entities.Account>{....}
 3. Implementation:  ServiceClientRepository : IRepository<Company.ProjectName.Entities.Account>
 4. WcfDsReference:  Company.ProjectName.Clients.Implementation.WcfDsReference
          &          Company.ProjectName.Clients.Implementation.WcfDsReference.Account


   Let's say I want to create a DataServiceQuery query on the Account, I won't be able to do this:

   var client = new WcfDsReference(baseUrl);
   var accounts = client.CreateQuery<Company.ProjectName.Entities.Account>(...)

      OR:         client.AddToAccounts(Company.ProjectName.Entities.Account)

   , because the CreateQuery<T>() expects T to be of type            &       Company.ProjectName.Clients.Implementation.WcfDsReference.Account

What I currently have to do is to pass the correct entity to the CreateQuery method and have to map the results back to the type the interface understands. (Possible with a mapper but doesn't seems like a good solution.)

So the question is, is there a way to get the Add Service Reference utility to generate methods that use the POCO types that are in the Company.ProjectName.Entities namespace?

One solution I am thinking of is to not use the utility to generate the DataServiceContext and other types, but to create my own.

The other solution is to update the IRepository<T> interface to use the POCO types generated by the utility. But this sounds a little bit hacky.

Is there any better solution that anyone has come up with or if there's any suggestion?

1
+1 I would really like to know this too.julealgon
possible duplicate of Service reference complex typesJon B
In your client project where you do an add service reference, add the Company.ProjectName.Entities.dll as a reference. Now launch the 'Add service reference' utility and try generating the proxy. By default 'Add service reference' will reuse the types defined in the referenced assemblies, resulting in your client proxy code not redefining the POCO types, but use them from Company.ProjectName.Entities.dll directly.Praburaj

1 Answers

1
votes

Ok, a few hours after starting the bounty I found out why it wasn't working as expected on my end.

It turns out that the sharing process is quite easy. All that needs to be done is mark the model classes with the [DataServiceKey] attribute. This article explains the process quite well, in the 'Exposing another Data Model' section

With that in mind, what I was trying to do is the following:

  1. Placing the model on a separate class library project C, sharing it with both webapplication projects A and B
  2. Create the data service on project A
  3. Add the service reference on project B
  4. Delete the generated model proxies out of the service reference, and update it to use my model classes in project C
  5. Add the DataServiceKey attribute to the models, specifying the correct keys

When I tried this it did not work, giving me the following error:

There is a type mismatch between the client and the service. Type {MyType} is not an entity type, but the type in the response payload represents an entity type. Please ensure that types defined on the client match the data model of the service, or update the service reference on the client.

This problem was caused by a version mismatch between project C (which was using the stock implementations on the System.Data.OData assemblies) and the client project B that was calling the service (using the Microsoft.Data.OData assemblies in the packages). By matching the version on both ends, it worked the first time.

After all this, one problem remained though: The service reference procedure is still not detecting the models to be shared, meaning proxies are being created as usual. This led me to opt out of the automatic service integration mechanic, instead forcing me to go forward with a simple class of my own to serve as the client to the Wcf Data service. Basically, it's a heavily trimmed version of the normally autogenerated class:

using System;
using System.Data.Services.Client;
using System.Data.Services.Common;
using Model;

public class DataServiceClient : DataServiceContext
{
    private readonly Lazy<DataServiceQuery<Unit>> m_units;

    public DataServiceClient(Uri _uri)
        : base(_uri, DataServiceProtocolVersion.V3)
    {
        m_units = new Lazy<DataServiceQuery<Unit>>(() => CreateQuery<Unit>("Units"));
    }

    public DataServiceQuery<Unit> Units
    {
        get { return m_units.Value; }
    }
}

This is simple enough because I'm only using the service in readonly mode. I would still like to use the service reference feature though, potentially avoiding future maintenance problems, as evidenced by the hardcoded EntitySet name in this simple case. At the moment, I'm using this implementation and have deleted the service reference altogether.

I would really like to see this fully integrated with the service reference approach if anyone can share a workaround to it, but this custom method is acceptable for our current needs.