Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use a shared datatype as DataContract in WCF

Tags:

c#

wcf

From what I can see I have to create a "special" datatype for each kind of data I want to transfer over WCF, so if I have a shared class like

public class District
{
    public long Id { get; set; }
    public string Name { get; set; }
}

and I want to send a District object using WCF, I have to create a datacontract and therefore I have to create a new WCF class

[DataContract]
public class WCFDistrict
{
    [DataMember]
    public long Id { get; set; }

    [DataMember]
    public string Name { get; set; }
}

And then when I use it in a WCF service in my implementation I have to parse the data from one object to the other

public WCFDistrict GetDistrict(long id)
{
    var district = _districtRepository.GetDistrict(id);
    return new WCFDistrict {Id = district.Id, Name = district.Name};
}

Is there someway to just reuse the shared class as a DataContract, without having those attributes on it? Or should I create a interface on the classes they can share so I can just cast it between them? Or something third?

like image 859
Mech0z Avatar asked Jan 06 '13 18:01

Mech0z


People also ask

Is DataContract mandatory in WCF?

No, the DataContractAttribute is not required - WCF will infer serialization rules.

What is DataContract in WCF?

A data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged. That is, to communicate, the client and the service do not have to share the same types, only the same data contracts.

What is DataContract attribute?

[DataContract] attribute specifies the data, which is to serialize (in short conversion of structured data into some format like Binary, XML etc.) and deserialize(opposite of serialization) in order to exchange between the client and the Service.

Which WCF attribute specifies that the data type can be used by client application?

The KnownTypeAttribute class allows you to specify, in advance, the types that should be included for consideration during deserialization. Normally, when passing parameters and return values between a client and a service, both endpoints share all of the data contracts of the data to be transmitted.


2 Answers

First, you are not strictly required to provide a DataContract; WCF will serialize Plain Old Class Objects (POCO) correctly as long as you are on .Net 3.5 SP1 or later.

Second, you can share the same physical class file in projects on both the server and client side; we have projects that have hundreds of classes (and code) that are directly shared this way and it saves a tremendous amount of time and effort in development and testing.

There are a couple of steps required to get this up and running (doing this from memory so I may need adjust the answer):

1) In the client side, if you are using VB, create a project in the same default namespace as the classes that you want to use in the client side (for C#, this isn't important since the namespace is embedded in the classes).

2) Add the class files to the project as links so that you have one physical copy of the class.

3) Add a dataContractSerializer to your WCF configuration if you don't have one already.

4) In the client-side, right-click on the service and choose Configure Service Reference... In the resulting dialog, ensure that Reuse types in all referenced assemblies is checked and that the Reuse types in all referenced assemblies option is chosen.

5) The trickiest part to getting this working is for collections.

a) Decorate the collection with the CollectionDataContract attribute.

b) Add an entry for this collection to the reference.svcmap's CollectionMappings table. To find the reference.svcmap, show all files in the project and then expand the service. To edit it, just double-click on the file. You will add an entry in here for each specific collection that you are serializing and you need to differentiate between those items that have a List<> base and those that have a Dictionary<> base. If you don't take this step, WCF will automatically serialize these classes to the underlying generic signature and you will lose the use of your classes.

The entries in this table look something like this:

<CollectionMappings>
  <CollectionMapping TypeName="System.Collections.Generic.Dictionary`2" Category="Dictionary" />
  <CollectionMapping TypeName="System.Collections.Generic.List`1" Category="List" />
  <CollectionMapping TypeName="System.Collections.Specialized.StringCollection" Category="List" />
  <CollectionMapping TypeName="My.Namespace.MyDictionaryCollection" Category="Dictionary" />

When you add these entries and save the file, the WCF client-side generator will rebuild the reference.cs or reference.vb file depending on what language you are using. You can tell if you don't have the references configured correctly by looking at the generated code: if that code contains class definitions, then the WCF code generator was not able to map into your copied classes for some reason.

One final note: sometimes the WCF code generator completely fails to generate the code and this is always due to a problem in the service (usually a class isn't unique enough or a type was not able to be serialized for one reason or another).

In order to debug this type of problem, the easiest thing to do is add WCF diagnostic logging, which will generate a file that can be opened by a special tool (forget the name of it) that allows you to drill into the error messages and discover exactly what went wrong. This has saved us untold hours of work. To configure this logging, add the following to your web.config anywhere in the <configuration> section:

  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="traceListener"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData="c:\log\WcfTrace.svclog"  />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

Once you have added this and saved web.config, attempt to update the service reference in the client, then double-click on the log file you specified and the tool will open.

like image 134
competent_tech Avatar answered Nov 08 '22 03:11

competent_tech


You're gonna need those attributes. To make you're life easier you can use a tool like AutoMapper to do the wiring up of the data transfer objects as the manual setting of all the properties can be tedious.

It is good practice anyway to keep your model classes separate from your data contracts so that changes to your model does not have to directly impact your data contracts.

like image 32
Quinton Bernhardt Avatar answered Nov 08 '22 03:11

Quinton Bernhardt