I'm trying to write a WCF Web service that will return my data as JSON so I can call it from some client script.
I know I need to decorate any classes I want to return from the Web Methods in a [DataContract] attribute and then any Members in a [DataMember]. That in mind I want to return entity types so I went to the Entity ObjectContext classes.
However when I look at the .edmx file I can see that the classes have been decorated like so...
[EdmEntityTypeAttribute(NamespaceName="PteDotNetModel", Name="AssocFile")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class AssocFile : EntityObject
When I then try and add [DataContract] I get an error saying I can't have duplicated attributes. I'm confused whilst they are similar they are clearly different no?
The second part of my question is how I return entity types over a WCF service?
The two attributes are the same; the trailing "Attribute" can be excluded. From MSDN:
By convention, all attribute names end with the word "Attribute" to distinguish them from other items in the .NET Framework. However, you do not need to specify the attribute suffix when using attributes in code.
Related
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?
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:
Placing the model on a separate class library project C, sharing it with both webapplication projects A and B
Create the data service on project A
Add the service reference on project B
Delete the generated model proxies out of the service reference, and update it to use my model classes in project C
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.
We have a 3rd party dll wich contains (among other things) our entities.
The entites are all marked with the [Serializeable] attribute.
We now need to creat a new WCF services which will expose some of this entities.
The problem is, since the entites are not declared with the DataContract and DataMember attributes, the property names are appended with __BackingField!
I know using the DataContarct\Member attributes will solve this issue, but given that I cannot modify the 3rd party dll with the entities, is there a different workaround?
Types decorated with the [Serializable] attribute have their fields serialized, not properties (that's the [Serializable] "contract"). If the 3rd party types use automatic properties (as shown below), the compiler will create a field with the k_BackingField suffix, and this is what will be serialized.
If you cannot change the types in the 3rd party library, one alternative would be to use that same library on the client. When creating the proxy for the service (using either svcutil or Add Service Reference), you can reference the 3rd party library, and the generated client won't create new types for the contracts, instead reusing the types from the library. This way you won't have to deal with types with public _BackingField property names.
Automatic properties:
[Serializable]
public class MyType
{
public string MyProp { get; set; }
}
The compiler will turn it into something similar to
[Serializable]
public class MyType
{
private string <MyProp>k_BackingField;
public string MyProp
{
[CompilerGenerated]
get { return this.<MyProp>k_BackingField; }
[CompilerGenerated]
set { this.<MyProp>k_BackingField = value; }
}
}
You can use the XmlSerializerFormatAttribute to use XmlSerializer instead of DataContractSerializer in the service implementation.
It will perform slower but it should sovle your problem.
I am assuming you want to expose these third party types from a service.
One solution which you may consider is to maintain a separate library which mirrors the types in the third party library.
This has the following benefits:
Ownership - You own the types you are exposing therefore you control the serialization/deserialization across your service boundary.
You are insulated from sudden changes to the other party's types and can change your interfaces in a controlled fashion.
From a SOA perspective if you are exposing another party's types on your service the other party should supply the types in a contractural format like XSD. I think your design calls for some fairly unreasonable hoop-jumping on your part.
It may be more work up front but it is kind of a one-off exercise.
Hope this helps some.
I have a server side service called ConstructionManager, one of its operation is GetAll() which return a list of constructions. Construction is a data contract, and there are several types which inherit from Construction (Buildings, Apartments etc..)
When I send the list of apartments all is good, all properties is on their place, but when i receive that list at client side, and see what it is in the received object at run time, in Non Public Members i saw all the properties that are specific to type that inherits from Construction, like Rooms, Floor, but in Result View it shows all properties have the value "0", and not the value with which they were sent.
On data contract Construction, at the top of class, are KnownType attributes to inherited classes.
It maybe helpful to know, I use Web Service Software Factory.
Sorry for my bad English.
If you see that properties are populated in an object in your service just before it is sent over the wire to the client (i.e. just before serialization), and then see that the received object is missing the values in those properties just after it is received by the client, it means that they were lost in the serialization process.
There are 2 things you need to remember about serialization:
You need to make sure the classes you send over the wire are marked with the [DataContract] attribute, and that all properties within that are marked with the [DataMember] attribute. If a property is not a .NET type, then the class that defines (and the properties within it) it also needs to be marked up with these attributes.
Class Inheritance is lost in serialization. If you create an object of type "Building", and your WCF service method returns a type of "Construction", then the message sent to the client might not serialize correctly (eg. could it only be serializing the properties defined in the base type "Construction"?). I suggest you test this out by making your service return the inherited type rather than the base type, and see if it fixes the problem.
I think that the 2nd point is the most likely cause of your issues. If you provide your code I can help you in more detail
I am very confused about the DataContract attribute in WCF. As per my knowledge it is used for serializating user defined type like classes. I wrote one class which is exposed at client side like this.
[DataContract]
public class Contact
{
[DataMember]
public int Roll { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Address { get; set; }
[DataMember]
public int Age { get; set; }
}
It is working properly but when I remove DataContract and DataMember it also works properly. I can't understand why it is working properly. Can any one tell me what is the actual use of DataContract?
My service contract looks like this
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
Contact XmlData(string id);
}
Since a lot of programmers were overwhelmed with the [DataContract] and [DataMember] attributes, with .NET 3.5 SP1, Microsoft made the data contract serializer handle all classes - even without any of those attributes - much like the old XML serializer.
So as of .NET 3.5 SP1, you don't have to add data contract or data member attributes anymore - if you don't then the data contract serializer will serialize all public properties on your class, just like the XML serializer would.
HOWEVER: by not adding those attributes, you lose a lot of useful capabilities:
without [DataContract], you cannot define an XML namespace for your data to live in
without [DataMember], you cannot serialize non-public properties or fields
without [DataMember], you cannot define an order of serialization (Order=) and the DCS will serialize all properties alphabetically
without [DataMember], you cannot define a different name for your property (Name=)
without [DataMember], you cannot define things like IsRequired= or other useful attributes
without [DataMember], you cannot leave out certain public properties - all public properties will be serialized by the DCS
So for a "quick'n'dirty" solution, leaving away the [DataContract] and [DataMember] attributes will work - but it's still a good idea to have them on your data classes - just to be more explicit about what you're doing, and to give yourself access to all those additional features that you don't get without them...
In terms of WCF, we can communicate with the server and client through messages. For transferring messages, and from a security prospective, we need to make a data/message in a serialized format.
For serializing data we use [datacontract] and [datamember] attributes.
In your case if you are using datacontract WCF uses DataContractSerializer else WCF uses XmlSerializer which is the default serialization technique.
Let me explain in detail:
basically WCF supports 3 types of serialization:
XmlSerializer
DataContractSerializer
NetDataContractSerializer
XmlSerializer :- Default order is Same as class
DataContractSerializer/NetDataContractSerializer :- Default order is Alphabetical
XmlSerializer :- XML Schema is Extensive
DataContractSerializer/NetDataContractSerializer :- XML Schema is Constrained
XmlSerializer :- Versioning support not possible
DataContractSerializer/NetDataContractSerializer :- Versioning support is possible
XmlSerializer :- Compatibility with ASMX
DataContractSerializer/NetDataContractSerializer :- Compatibility with .NET Remoting
XmlSerializer :- Attribute not required in XmlSerializer
DataContractSerializer/NetDataContractSerializer :- Attribute required in this serializing
so what you use depends on your requirements...
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. A data contract precisely defines, for each parameter or return type, what data is serialized (turned into XML) to be exchanged.
Windows Communication Foundation (WCF) uses a serialization engine called the Data Contract Serializer by default to serialize and deserialize data (convert it to and from XML). All .NET Framework primitive types, such as integers and strings, as well as certain types treated as primitives, such as DateTime and XmlElement, can be serialized with no other preparation and are considered as having default data contracts. Many .NET Framework types also have existing data contracts.
You can find the full article here.
A data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged.
Data contract can be explicit or implicit. Simple type such as int, string etc has an implicit data contract. User defined object are explicit or Complex type, for which you have to define a Data contract using [DataContract] and [DataMember] attribute.
A data contract can be defined as follows:
It describes the external format of data passed to and from service operations
It defines the structure and types of data exchanged in service messages
It maps a CLR type to an XML Schema
It defines how data types are serialized and deserialized. Through serialization, you convert an object into a sequence of bytes that can be transmitted over a network. Through deserialization, you reassemble an object from a sequence of bytes that you receive from a calling application.
It is a versioning system that allows you to manage changes to structured data
We need to include System.Runtime.Serialization reference to the project. This assembly holds the DataContract and DataMember attribute.
Data contract: It specifies that your entity class is ready for Serialization process.
Data members: It specifies that the particular field is part of the data contract and it can be serialized.
Also when you call from http request it will work properly but when your try to call from net.tcp that time you get all this kind stuff
DataMember attribute is not mandatory to add to serialize data. When DataMember attribute is not added, old XMLSerializer serializes the data. Adding a DataMember provides useful properties like order, name, isrequired which cannot be used otherwise.
The data is to be transferred and processed within service and they store the values, so in the WCF terminology they are called “Data Contract”.
Where each member of the Class; i.e., The data contract is called “Data Member” and they are also to be decorated with the Attributes.
Source
I have a WCF web service that implements a parameter List<ICustomObject>. In the generated client code, the parameter is List<Object>?!? How can I make it accept the List<ICustomObject>, as an interface, or do I have to use a concrete class? I have the concrete class marked as [KnowType] and [Serializable]
You should use concrete class, because you should set DataMember and DataContract for it to serilize it, and with interface you can't do this.
WCF works with anything that is expressible in XML schema - interfaces per se are not.
You need to define your lists to be lists of concrete types - otherwise the client will not be able to know what to do with the list and will fall back to a List<Object>.