I have WCF service that return Json.
Data contract defined below
[DataContract]
public class OptionData
{
[DataMember]
public string Book { get; set; }
[DataMember]
public string Id { get; set; }
[DataMember]
public string DealId { get; set; }
[DataMember]
public string DeliveryDate { get; set; }
[DataMember]
public string ExpiryDate { get; set; }
}
And Operation Contract defined as follows
[Description("Returns List of Options by user id")]
[WebGet(UriTemplate = "{sessionId}/Application/{applicationId}?start={start}&limit={limit}&page={page}", ResponseFormat = WebMessageFormat.Json)]
public List<OptionData> GetAllTask(string sessionId, string applicationId)
I need to add dynamically new DataMember field to the OptionData class .
What is the best practice to do it ?
As long as the client receiving the json knows how to handle dynamically added class members, you can use the Expando object strategy outlined in the accepted answer to this SO question. It uses the ServiceStack library but you may be able extract the necessary code to do what you want. A simple key/value pair approach from an ExpandoObject is documented in this code snippet.
EDIT: I should point out that this approach wouldn't rely on WCF so it may not be what you need given the context of the question.
If you know you want JSON, you could always control the serialization yourself (see this post) - just return a string of JSON using an existing library.
Another option is to just us IsRequired = false if you know all the possible field names.
The final alternative is to use the same pattern WCF uses for Forward-Compatible Contracts - just attach all unknown properties to single collection object (ExtensionData). ExtensionData is just a dictionary of key/value pairs according to this post. Unfortunately - ExtensionData is not writable directly. This would be my approach to simulate what IExtensibleDataObject is doing...
[DataContract]
public class OptionData
{
[DataMember]
public string Book { get; set; }
[DataMember]
public string Id { get; set; }
[DataMember]
public string DealId { get; set; }
[DataMember]
public string DeliveryDate { get; set; }
[DataMember]
public string ExpiryDate { get; set; }
[DataMember]
public Dictionary<string, string> Metadata { get; set;}
}
I don't think this is possible.
Let's think about what your DataContract is for a moment: it's how your service defines what it knows about - either as input or output. A client has to either find this out through meta-data exchange discovery or from a static proxy class (in a dll probably) that you provide.
If you change your contract on the fly, there's no mechanism for your service to let its clients know that the contract has changed. There's just no way to change that contract on the fly.
Even if you changed the definition of the class on the fly, including the proper attributes, etc, the client would not be able to find out about it since the contract had already previously been published.
I can't imagine what kind of mechanism would be needed to communicate changes like this on the fly with a client.
The only workaround I can think of is to have a parameter that takes in a string and allows clients to pass in XML or similar which could be just about anything. That's a pretty nasty hack though...
I just ran up against this exact issue as i posted recently here:
Configuring WCF data contract for proper JSON response
My solution was to use ExpandoObject. However, I had to use Newtsoft.json to do the JSON serialization then I had to make my webservice return raw text (rather than rely on the WCF serialization). I would be happy to post my code if you like. There may be a way to do dynamic datacontracts, but I wasn't able to figure that out. But my solution does the job.
If you need to dynamically control the data structures your RESTful service is returning, I think your only option is to return something like an XDocument. Your service operation could create an XDocument of an arbitrary structure and return that. That being said, I'm not sure what would happen when WCF tries to serialize an XDocument to JSON.
Related
I should create wcf service that returns data about users, the interface and DataContract is below:
[ServiceContract]
public interface IUserInfoProvider{
[FaultContract<UserNotFound>]
public UserInfo GetUserInfo(Guid userId)}
[DataContract]
public class UserInfo
{
[DataMember] public Guid UserId { get; set; }
[DataMember] public bool? AdvertisingOptIn { get; set; }
[DataMember] public string CountryIsoCode { get; set; }
[DataMember] public DateTime DateModified { get; set; }
[DataMember] public string Locale { get; set; }
}
I have no special client for service - requests (get, post) runs from fiddler or rest plugin for browser.
Please, describe how to implement [FaultContract<>] in my case, i saw examples with [FaultContract(typeof(UserNotFound))] but never seen [FaultContract<>]
Sorry about the late answer, but I faced something similar and I would like to share what I've found:
FaultContract is possible: https://msdn.microsoft.com/en-us/library/ff650547.aspx
From MSDN:
To support the use of custom faults, WCF services use the
FaultContractAttribute to formally specify faults that can be returned
from a service operation. Types specified in a FaultContractAttribute
must be serializable as DataContract,SerializableAttribute, or
ISerializable. When a FaultException is thrown using a custom fault
defined in FaultContract, client applications can also catch these
specific faults using the FaultException generic type.
Example:
throw new FaultException<InvalidNameFault>(fault, "Invalid Name!");
I have a WCF method:
public IQueryable<AmenitySummary> GetAmenities(string searchTerm)
Now, AmenitySummary is defined thus
[DataContract]
public class AmenitySummary
{
[DataMember]
public int AmenityId { get; set; }
[DataMember]
public string Amenity { get; set; }
[DataMember]
public string LowerCaseAmenity { get; set; }
}
In my client side solution I have a project in which I call this method. The problem is that the signature in the Reference.cs file is this
public object GetAmenities(string searchTerm) {
return base.Channel.GetAmenities(searchTerm);
}
How comes? Why isn't the return type IQueryable<AmenitySummary>? What am I missing?
Not only that, but when I try to use AmenitySummary on the client side I can't do it as it's not recognised. I think this is linked.
Thanks,
S
IQueryable is not serialiable without specific references (see svcutil.exe /references).
Otherwise use a WCF DataService (OData) or return an array of AmenitySummary from the service. In the later case you can convert the array to an IQuaryable instance.
I think it's because of IQueryable. You probably need to expose it too.
See this post:
Expose IQueryable Over WCF Service
When i serialize the following:
[Serializable]
public class Error
{
public string Status { get; set; }
public string Message { get; set; }
public string ErrorReferenceCode { get; set; }
public List<FriendlyError> Errors { get; set; }
}
I get this disgusting mess:
<ErrorRootOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Printmee.Api">
<_x003C_Errors_x003E_k__BackingField>
An exception has occurred. Please contact printmee support
</_x003C_Errors_x003E_k__BackingField>
<_x003C_LookupCode_x003E_k__BackingField>988232ec-6bc9-48f3-8116-7ff7c71302dd</_x003C_LookupCode_x003E_k__BackingField>
</ErrorRootOfstring>
What gives? How can i make this pretty? JSON responses also contain the k_BackingField
By default you don't need to use neither [Serializable] nor [DataContract] to work with Web API.
Just leave your model as is, and Web API would serialize all the public properties for you.
Only if you want to have more control about what's included, you then decorate your class with [DataContract] and the properties to be included with [DataMember] (because both DCS and JSON.NET respsect these attributes).
If for some reason, you need the [Serializable] on your class (i.e. you are serializing it into a memory stream for some reason, doing deep copies etc), then you have to use both attributes in conjunction to prevent the backing field names:
[Serializable]
[DataContract]
public class Error
{
[DataMember]
public string Status { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public string ErrorReferenceCode { get; set; }
[DataMember]
public List<FriendlyError> Errors { get; set; }
}
There is a more general solution: you can configure the Json Serializer to ignore the [Serializable] attribute, so that you don't have to change the attributes in your classes.
You should make this configuration change in the application start, i.e. in Global.asax Application_Start event:
var serializerSettings =
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
var contractResolver =
(DefaultContractResolver)serializerSettings.ContractResolver;
contractResolver.IgnoreSerializableAttribute = true;
You can also make other changes to the Json serialization, like specifying formats for serializing dates, and many other things.
This will only apply to the Web API JSON serialization. The other serializations in the app (Web API XML serialization, MVC JsonResult...) won't be affected by this setting.
Try using DataContract instead of Serializable for marking your class. For more detail on why, look at this good blog post on serializing automatic properties.
The [DataContract] attributes dosn't worked for me, so it was not an option.
XmlSerializer ignores [XmlAttribute] in WebApi
The above resolution solved it for me.
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
I have such a contract:
[DataContract]
[KnownType(typeof(Person))]
public class Gadget
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public IPerson Person { get; set; }
}
It represents a gadget that belongs to a person. I just came up with this simple example, it's not important whether it makes sense or not.
So, instead of returning the Person class, I return the IPerson interface. Now the client can no longer generate a strong typed object, but will generate this:
public object Person { get; set; }
Now my question is: is it possible to let the client also generate the IPerson interface? It should have enough information, because it can only instantiate Person (only known type).
Interfaces will not be transfered by adding a service reference. These interfaces only exist in .NET, but your service is suppossed to be interoperable.
As far as your WSDL is concerned there is likely to be no way to tell Person and IPerson apart.
If you really want to use that interface you will need to move it across manually. This means editing the generated client code by hand.
I got a WCF service with a method (GetUserSoftware)to send a List to a client.
the software I have defined like this:
[DataContract]
public class Software
{
public string SoftwareID { get; set; }
public string SoftwareName { get; set; }
public string DownloadPath { get; set; }
public int PackageID { get; set; }
}
the method is going through my db to get all software availeble to the clien, and generates a list of that to send back to the client.
problem is i on the client side the list is turned into an array. and every item in that array dont contain any of my software attributs.
i have debugged my way through the server side. and seen that the list its about to send is correct. with the expected software and attributs in it.
any one know how to work around this or know what i can do ?
Did you forget [DataMemeber] attribute on your properties?
When you use DataContract attribute for a type you have to use DataMember attribute for each property or field you want to serialize and transfer between service and client. Collections are by default created as arrays. If you don't like it you can change this behavior in Add Service Reference window -> Advanced settings where you can select which collection type should be used.
First off, each of the properties that you want to serialize should have the [DataMember] attribute:
[DataContract]
public class Software
{
[DataMember]
public string SoftwareID { get; set; }
[DataMember]
public string SoftwareName { get; set; }
[DataMember]
public string DownloadPath { get; set; }
[DataMember]
public int PackageID { get; set; }
}
Second, the translation to an Array would be handled by the client, not the server.
You can mantain List instead of array on the clien when you add the Service Reference: click the "advanced" button and change the collection type to the one you want.
I was suffering with same problem and now I solved it! It was a ServiceKnownType problem. If you have a in known type loader we have to add runtime Type like;
Type aaa = Type.GetType("System.Collections.Generic.List`1[[ProjectName.BusinessObjects.Bank, ProjectName.BusinessObjects, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]]");
knownTypes.Add(aaa);
Anyone having same problem can try this. It's working in my environment!