I have converted my web service to wcf service which has some datacontracts. As a best practice it is mentioned and advisable that the DataContracts should inherit from IExtensibleDataObject. I get the point that in case of addition or removal of datamembers, IExtensibleDataObject is helpful. But i am not able to get how will the clients access removed datamembers. Here is my code:
[ServiceContract(Namespace = "http://mycompany.com/2010/08/")]
public class MyWebService {
[OperationContract]
public Employee Add(Employee emp)
{
// Some Processing
}
}
[DataContract(Name = "Employee", Namespace = "http://mycompany.com/2010/08/")]
public class Employee : IExtensibleDataObject {
[DataMember] public string FirstName;
[DataMember] public string LastName;
public ExtensionDataObject ExtensionData { get; set; }
}
Now in my next version of web service I made some changes to DataContract as
[DataContract(Name = "Employee", Namespace = "http://mycompany.com/2010/09/")]
public class Employee : IExtensibleDataObject {
[DataMember] public string FirstName;
[DataMember] public string LastName;
[DataMember(IsRequired = true)] public string MiddleName;
public ExtensionDataObject ExtensionData { get; set; }
}
However my client that is accessing my older version of web service is now getting error for not supplying the MiddleName field. I am still confused for the usage of IExtensionDataObject.
that is incorrect usage of IExtensibleDataObject. You have modified data contract on the server side an you have marked new field as required so it means you have broken versioning and nothing helps you.
IExtensibleDataObject is for other purpose. Let assume that you have modified your client so that data contract on the client contains MiddleName. Now you set the MiddleName and use Add service operation. What value of MiddleName will be in returned Employee object? If you don't use IExtensibleDataObject the value will be null, if you use IExtensibleDataObject the value will be same as you set to input parameter.
When using DataContractSerializer WCF throws away all non understood parameters. IExtensibleDataObject avoid this by storing all those parameters in special collection and sending them back to client.
If you want to use contract versioning forget about required fields. That is the first thing which will break it.
I'm afraid that's not the correct usage of IExtensibleDataObject, the IExtensibleDataObject interface is designed to support version round-tripping, have a read of this MSDN article on forward compatibility:
http://msdn.microsoft.com/en-us/library/ms731083.aspx
And here's another article on best practices on Data Contract versioning in general:
http://msdn.microsoft.com/en-us/library/ms733832.aspx
Make ExtensionData Property "Virtual" as shown below:
public virtual ExtensionDataObject ExtensionData
{
get { return theData; }
set { theData = value; }
}
Related
I am writing a proxy to wrap a WCF service with ASP.net Core and this is my first time using ASP.Net core.
I am using an auto-generated WCF service contract, and the issue is my WCF service changes frequently, so I must update/refresh the WCF service, and whenever I update my contract, I lose my minor tweaks.
I'm only trying to:
Hide some properties - via changing public string Property to internal string Property. I've tried [IgnoreDataMember] and [JsonIgnore] but those don't seem to work
Make some properties required - via RequiredAttribute
Default some property values - via DefaultValueAttribute
I've tried two approaches so far but they're not working fully.
This represents the automatically generated WCF Contract where I want to require & default MyProperty1 and hide MyProperty2:
// This is the automatically generated WCF Contract
public partial class MyClass
{
private string myPropertyField1;
private string myPropertyField2;
public string MyProperty1
{
get { return this.myPropertyField1; }
set { this.myPropertyField1 = value; }
}
public string MyProperty2
{
get { return this.myPropertyField2; }
set { this.myPropertyField2 = value; }
}
}
Method 1:
ModelMetadataType to override the contract metdata. This partially works, but not for all attributes for some reason? It feels like a bug.
[ModelMetadataType(typeof(MyClassMetadata))]
public partial class MyClass { }
public partial class MyClassMetadata
{
[Required] // This does not work
[DefaultValue("SomeValue")] // This DOES work?
public string MyProperty1 { get; set; } // I want this required & defaulted
[IgnoreDataMember] // This does not work to hide
[JsonIgnore] // This does not work to hide
public string MyProperty2 { get; set; } // I want this hidden from Swagger view
}
Which works to default values, but it doesn't appear to make it required or hidden?
Method 2:
I tried creating derived class, and then using the derived class instead of MyClass for the controller api arguments, but then when I try to call it I receive an error that says something like Type MyClassDerived was not expected. Use XmlInclude to specify unexpected types...
public partial class MyClassDerived : MyClass
{
[Required] // This puts "*" next to it in Swagger
[DefaultValue("SomeValue")] // This defaults the value in Swagger
public new string MyProperty1
{
get { return base.MyProperty1; }
set { base.MyProperty1 = value; }
}
internal new string MyProperty2 // This works to hide from Swagger
{
get { return base.MyProperty2; }
set { base.MyProperty2 = value; }
}
}
How can I hide/default/require contract properties without directly modifying an auto-generated WCF contract service class?
I am working on a project and trying to integrate SignalR into it. I have a class with a data contract which allows me to use an underscore separated format on my client, and standard Pascal Case on the server, something like this:
[DataContract]
public class Foo {
[DataMember(Name = "first_name")]
FirstName { get;set; }
[DataMember(Name = "last_name")]
LastName { get;set; }
[DataMember(Name = "phone")]
Phone { get;set; }
}
This works fine when passing data through a fetch command to the Razor Page OnGet and OnPost methods, but does not work when using SignalR.
When sending data to server via SignalR, first_name and last_name are null, while phone gets sent correctly.
How can I make SignalR respect the DataMember Name when serializing/deserializing?
I was able to resolve this issue by using the Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson package and adding the following in Startup.cs
services.AddSignalR()
.AddNewtonsoftJsonProtocol();
I suppose currently you are sending the data as an object, somehow like this:
Foo foo = new Foo();
_hub.Clients.All.InvokeAsync("ReceiveMessage", foo);
What I suggest is that you create the JSON by hand on the server side and send that, because you have better controll over the property names that way.
Add the JsonProperty attribute to the Foo class:
[DataContract]
public class Foo {
[DataMember(Name = "first_name")]
[JsonProperty("first_name")]
FirstName { get;set; }
[DataMember(Name = "last_name")]
[JsonProperty("last_name")]
LastName { get;set; }
[DataMember(Name = "phone")]
[JsonProperty("phone")]
Phone { get;set; }
}
And when you send the message, send the JSON string instead of letting SinglaR parse your object:
Foo foo = new Foo();
string json = JsonConvert.SerializeObject(foo);
_hub.Clients.All.InvokeAsync("ReceiveMessage", json);
The fields are not being serialized because their serialization attributes ([DataMember]) are not compatible with SignalR's default serialization engine (System.Text.Json).
One possible solution is to switch the serialization engine to Newtonsoft's JSON implementation, as suggested in #Aleksandr Albert's answer. Another solution is to use System.Text.Json.Serialization.JsonIncludeAttribute instead, e.g.
public class Foo {
[JsonPropertyName("first_name")]
[JsonInclude]
FirstName { get;set; }
[JsonPropertyName("last_name")]
[JsonInclude]
LastName { get;set; }
[JsonPropertyName("phone")]
[JsonInclude]
Phone { get;set; }
}
Each solution is (in)compatible with one of the serialization engines.
I have a WCF service which returns ExtensionDataObject during runtime as attached snapshot:
Im struck with fetching value for these objects. Could anyone please help here:
Have tried with below code using reflection, which throws Parameter count missing exception
List<System.Runtime.Serialization.ExtensionDataObject> extData = temp.Select(x => x.ExtensionData).ToList();
var GetCountry = extData.GetType().GetProperties();
string Country = string.Empty;
foreach (var property in GetCountry)
{
string name = property.Name;
object value = property.GetValue(extData, null);
if (name == "Country")
Country = value.ToString();
}
The Extensiondataobject field is generated to control the data contract incompatibility between the server and the client, so it will return a field named extensiondataobject. In other words, your client data contract implements the IExtensionDataObject interface.
[DataContract(Namespace="abcd")]
public class Product: IExtensibleDataObject
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
public ExtensionDataObject ExtensionData { get ; set ; }
}
If we capture this request through Fiddle, you can even see all the data directly.
In a word, you only need to add the Country property to the Data class of X object. It will be deserialized automatically. This class should be your client-side data contract class, instead of the server-side data class.
Finally, it seems that the value of these fields is null. We should ensure that the server and client data contracts have the same namespace. It cannot be the default value(http://tempuri.org). As I defined above, this namespace attribute should be consistent with the server-side value.
Feel free to let me know if there is anything I can help with.
I have a WCF service that implements a data contract. I then have a client that consumes that service with it's own implementation of the data contract.
If the data contracts don't match exactly, it doesn't generate any sort of error, nor does it return any data.
public class RecipeClient : ClientBase<IRecipeService>, IRecipeService
{
public RecipeEntity[] GetAllRecipes()
{
var recipe = Channel.GetAllRecipes();
return recipe;
}
}
In the above example, after the call is made, recipe contains an empty array of RecipeEntity.
I would expect it to not return any data, but why doesn't it generate an error?
It is for backward compatibility. If you add in datacontract of existing service some not required properties, all existing clients will work without errors.
As if was mentioned it's for backward compatibility, but you can mark some properties as required. And if there is no such property in a message, an exception will be thrown:
[DataContract]
public class Recipe
{
[DataMember]
public string Name { get; set; }
[DataMember(IsRequired = true)]
public string Rank { get; set; }
}
I have a WCf service with Contracts shown below.
[MessageContract]
public class ServiceRequest
{
[MessageBodyMember]
public int RequestId { get; set; }
[MessageBodyMember]
public OrderDetails OrderDetails { get; set; }
}
[DataContract]
public class OrderDetails
{
[IsLogRequired]
public int OrderId { get; set; }
[IsLogRequired]
public int Quantity { get; set; }
public string CustomerName { get; set; }
}
[IsLogRequired] is custom Attribute.
We need to get all properties in the request which have "[IsLogRequired]" attribute when the request is received. We want to do it as generic solution so that it can be plugged into all services.
We thought of using "MessageInspector" to do this implementing "IDispatchMessageInspector".
How do i get the actual request object from "System.ServiceModel.Channels.Message" parameter of IDispatchMessageInspector.AfterReceiveRequest() method?
Please correct me if i am using a wrong interface or wrong method. Any other solution to this?
I am assuming that "[IsLogRequired] is custom property." means a custom attribute...
Simple answer is that there is no solution to transfer custom attributes that are decorating the data contract as you described it.
Data contracts should be pure and not encumbered by business logic. The know how about the what should be done with various fields belongs to a service implementation.
Possible approach could look like this:
public class OrderService : IOrderService
{
private void ProcessOrder(Order order)
{
var ra = new AuditMetadataResourceAccess();
MethodInfo[] fieldsToLog = ra.GetLoggingFields(typeof(OrderDetal));
if (fieldsToLog.Any())
{
var logger = new LogingEngine();
logger.Log(fieldsToLog, order.OrderDetails);
}
}
}
You could move this implementation inside message inspector or operation invoker. Carlos Figueira has extensive description of each WCF extensibility point.
"How do i get the actual request object from "System.ServiceModel.Channels.Message" parameter of IDispatchMessageInspector.AfterReceiveRequest() method?"
I am assuming you are referring to Web request. WebOperationContext.Current but you need to have ASP.NET Compatibility Mode turned on.