Naming Generic DataContracts in WCF - wcf

I am using a Generic Class as a Response Data Contract. All is good and this is streamlining the design of my WCF service significantly.
Each request is given a standard response object with the following signature:
Status (Enum)
Message (String)
Result (T)
Below is the Response Class:
[DataContract]
public class Response<T>
{
public Response() {}
public Response(T result)
{
this.result = result;
if (result != null)
{
this.status = Status.StatusEnum.Success;
}
else
{
this.status = Status.StatusEnum.Warning;
}
}
public Response(T result, Status.StatusEnum status)
{
this.status = status;
this.message = message;
}
public Response(T result, Status.StatusEnum status, string message)
{
this.status = status;
this.message = message;
this.result = result;
}
[DataMember]
public Status.StatusEnum status { get; set; }
[DataMember]
public string message { get; set; }
[DataMember]
public T result { get; set; }
}
And this works brillantly. Only problem I have is that the WCF Client is given a really crappy name for this object "ResponseOfAccountnT9LOUZL"
Is there a way to get around this issue?
Should I be using this class as just a Abstract class which is inherited?
I'd rather not have multiple classes cluttering my code.

Ok found the Answer
You can specify the Serialised version using the following syntax:
[DataContract(Name = "MyClassOf{0}{1}")]
class MyClass { }
So if I had a Class called Response which takes a Generic T parameter
I would use
[DataContract(Name = "ResponseOfType{0}")]
class Response { }

[DataContract(Name = "ReturnObjectOfType{0}")]
public class ReturnObject<T>
{....
//Iservice
[OperationContract]
ReturnObject<AdresKisiBilgi> BeldeAdresKisiBilgiSorgula(string tcKimlikNo);
//Service
public ReturnObject<HbysBusiness.MernisGuvenService.AdresKisiBilgi> BeldeAdresKisiBilgiSorgula(string tcKimlikNo)
{
return new MernisBiz().BeldeAdresKisiBilgiSorgula(tcKimlikNo);
}
client:
public ReturnObjectOfAdresKisiBilgi BeldeAdresKisiBilgiSorgula(string tcKimlikNo)
{....
Thank you Harry

Related

How to use Patch method in minimal api application?

I am trying to use patch method in my minimal api application this is my code :
Car class
public class Car
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "producent")]
public Producent Producent { get; set; }
[JsonProperty(PropertyName = "age")]
public int Age { get; set; }
[JsonProperty(PropertyName = "yearCreated")]
public int YearCreated { get; set; }
[JsonProperty(PropertyName = "engine")]
public Engine Engine { get; set; }
public Car()
{
Id= Guid.NewGuid().ToString();
YearCreated = DateTime.Now.Year - Age;
}
}
ICarService:
public interface ICarService
{
Task<IEnumerable<Car>> GetAll();
Task<Car> GetById(string id,string partitionKey);
Task Create(Car car);
Task<bool> Update(Car car);
Task<Car> UpdatePatchAsync(string id, string partitionKey,List<PatchOperation> patchOperations);
Task<bool> Delete(string id,string partitionKey);
}
patch method in service
public async Task<Car> UpdatePatchAsync(string id, string partitionKey, List<PatchOperation> patchOperations)
{
var result = await _container.PatchItemAsync<Car>(id, new PartitionKey(partitionKey),
patchOperations:patchOperations );
return result;
}
my requests:
[HttpPatch]
public static async Task<IResult> Patch(ICarService service,string id,string partitionKey,
[FromBody]List<PatchOperation> operations)
{
var updatedCar = await service.UpdatePatchAsync(id,partitionKey,operations);
if (updatedCar == null)
{
return Results.NotFound();
}
return Results.Ok(updatedCar);
}
app.MapMethods("/cars/patch/{id}/{partitionKey}", new string[] { "PATCH" }, CarRequests.Patch);
I use cosmosDb database and when i code like this:
{
"op": "replace",
"path": "age",
"value": 22
}
i get the error
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.Azure.Cosmos.PatchOperation'. Path: $[0] | LineNumber: 3 | BytePositionInLine: 3.
---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.Azure.Cosmos.PatchOperation'.
The problem is you are using [FromBody]List<PatchOperation> operations.
This is making ASP.NET attempt to deserialize a JSON raw string into PatchOperation, which when we look at our source code, has no parameterless constructor: https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos/src/Patch/PatchOperation.cs, it is an abstract class.
The way it is meant to be used is through PatchOperation.XXXX, like PatchOperation.Add, you cannot use it to deserialize into it. You could have your own MyPatchOperation<Car> that has the exposed properties for deserialization, and then simply have a method in that class that based on which properties are populated, calls PatchOperation<Car>.Add (or whatever operation).
Not the full code but:
public class MyPatchOperation
{
[JsonProperty(PropertyName = "op")]
public string Operation { get; set; }
[JsonProperty(PropertyName = "path")]
public string Path { get; set; }
[JsonProperty(PropertyName = "value")]
public string Value { get; set; }
public PatchOperation<Car> ToPatchOperation()
{
switch (this.Operation)
{
case "replace":
return PatchOperation<Car>.Replace(this.Path, this.Value);
/* other operations */
}
}
}

Serializing generic c# model to xml in .net core 3.0

I am using .net core 3.0 to create Web APIs. I want to serialize a generic model to XML. But by default, it serializes the generic model to JSON only.
When I tried to serialize a simple model to XML, it gets serialized to XML properly.
Depending upon the headers passed, I want to serialize the model. Here is what I have done till now:
In startup.cs file:
services.AddControllers(options =>{
options.RespectBrowserAcceptHeader = true; // false by default
}).
AddXmlSerializerFormatters().
Below is the example of code:
My Generic Model:
public class ApiResponseModel<T>
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }
public ApiResponseModel(int errorCode, string errorMessage, T data)
{
this.ErrorCode = errorCode;
this.ErrorMessage = errorMessage;
this.Data = data;
}
}
Action to return data:
[HttpGet]
public async Task<IActionResult> Get()
{
IEnumerable<RoleDTO> roleList = null;
roleList = await _repository.RoleRepository.GetAllRolesAsync();
var data = new ApiResponseModel<IEnumerable<RoleDTO>>(200, "Success", roleList);
return Ok(data);
}
This return JSON response though Accept header is specified as application/xml.
But if I simply return roleList it gives the response in XML format.
[HttpGet]
public async Task<IActionResult> Get()
{
IEnumerable<RoleDTO> roleList = null;
roleList = await _repository.RoleRepository.GetAllRolesAsync();
return Ok(roleList);
}
How can I get the XML response for the generic models also?
There're two reasons :
Your ApiResponseModel<T> has no parameterless constructor.
You're using XmlSerializer by AddXmlSerializerFormatters()(instead of XML DataContractSerializer), which will not take care of IEnumerable<T> serialization by default.
To fix this issue, add a parameterless constructor:
public class ApiResponseModel<T>
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }
public ApiResponseModel() { } // add a parameterless constructor
public ApiResponseModel(int errorCode, string errorMessage, T data)
{
this.ErrorCode = errorCode;
this.ErrorMessage = errorMessage;
this.Data = data;
}
}
And consider adding a XML DataContractSerializer formatters to take care of IEnumerable<T> serialization:
services.AddControllersWithViews(opts =>{
opts.RespectBrowserAcceptHeader = true;
})
.AddXmlDataContractSerializerFormatters()
.AddXmlSerializerFormatters()

Can not implement discriminated union and serialize it

Hello i am trying to create a hierarchy of classes using a discriminated unionand it seems i can't serialize them.I keep getting this error :
Newtonsoft.Json.JsonSerializationException: 'Self referencing loop detected for property 'AsRun' with type 'MsgPattern.Message+Run'. Path ''.'
Base class
[Serializable]
public abstract partial class Message {
public enum Type {
WALK = 0,
RUN = 1
}
protected abstract Type Discriminator { get; }
public Type Kind => this.Discriminator;
internal static Message Create(string data) {
var message = JsonConvert.DeserializeObject<Message>(data);
switch (message.Kind) {
case Type.RUN:message= message.AsRun;break;
case Type.WALK:message= message.AsWalk;break;
}
return message;
}
[JsonIgnore]
public bool IsWalk => this.Kind==Type.Walk;
[JsonIgnore]
public bool IsRun => this.Kind==Type.Run;
[JsonIgnore]
public Message.Walk AsWalk => this as Message.Walk;
[JsonIgnore]
public Message.Run AsRun => this as Message.Run;
}
Dervived
partial class Message {
public class Run : Message {
protected override Type Discriminator => Type.RUN;
public string Location { get; set; }
public int Speed { get; set; }
}
}
partial class Message {
public class Walk : Message {
protected override Type Discriminator => Type.WALK;
public int Gait { get; set; }
public bool IsJogging { get; set; }
}
}
Usage
class Program {
static void Main(string[] args) {
Message.Run run = new Message.Run { Location = "asa", Speed = 33 };
string data = JsonConvert.SerializeObject(run);
Message msg=Message.Create(data);
}
}
I will get these type of messages via json and i want to be able to do actions based on their type. I do not understand why i can't serialize them .
P.S I know it's a self-referencing loop but I need those As[something] and Is[Something] fields.

How to resolve an error with WCF data contract -Message = "There was an error while trying to serialize parameter

I have two data contracts of same contents in two different namespaces. i have copied one datacontract to another and
passed to a particular method. but it is giving the below error and throwing exception. it is not going into that method.
Please let me know any ideas /suggestions on how to resolve this.Appreciate your help:
Exception errror:
{"Type 'System.Collections.Generic.List`1[[GlobalWcfServiceLib.TopicDetailsInfo, GlobalWcfContracts, Version=1.2.2.0, Culture=neutral,
PublicKeyToken=17c64733a9775004]]' with data contract name 'ArrayOfTopicDetailsInfo:http://CName.GlobalService/11.1/2010/11'
is not expected. Consider using a DataContractResolver or add any types not known statically to the list of
known types - for example, by using the KnownTypeAttribute attribute or by
adding them to the list of known types passed to DataContractSerializer."}
Message = "There was an error while trying to serialize parameter
http://CName.SharedServices.DBServiceLib:subscriptionDataContract.
The InnerException message was 'Type 'System.Collections.Generic.List`1
[[GlobalWcfServiceLib.TopicDetailsInfo,
GlobalWcfContra...
//////
Here is my scenario : i am copying the data from dc to new data contract as below. after copying , when i am executing the createsubscriptions method i am getting the above mentioned error. i have given the details of data contract and error attached to this question. please refer to that as well.
Method1(SubscriptionDataContracts dc)
{
SubscriptionDataContract subscriptionDataContract = new SubscriptionDataContract();
List<SubscriptionTopicInfo> topicsInfo = dc.TopicList;
List<SubscriptionTopic> newTopicsList = new List<SubscriptionTopic>();
subscriptionDataContract.ExtensionData = dc.ExtensionData;
subscriptionDataContract.UserID = dc.UserID;
for (int i = 0; i < topicsInfo.Count; i++)
{
SubscriptionTopic topic = new SubscriptionTopic();
topic.DBHandle = topicsInfo[i].DBHandle;
topic.Topic = topicsInfo[i].Topic;
topic.Target = topicsInfo[i].Target;
newTopicsList.Add(topic);
}
subscriptionDataContract.TopicList = newTopicsList;
CreateSubscriptions(subscriptionDataContract); //getting the above mentioned error in another dll after going into this method
}
////////////////////////////////
//My data contract
[DataContract(Name = "TopicDetailsInfo", Namespace = "http://CName.GlobalService")]
[Serializable]
public class TopicDetailsInfo
{
protected object topic;
protected object baseObjectType;
[DataMember]
public object BaseObjectType
{
get
{
return baseObjectType;
}
set
{
baseObjectType = value;
}
}
[DataMember]
public object TopicID
{
get
{
return topic;
}
set
{
topic = value;
}
}
static public TopicDetailsInfo CreateTopic<T, mT>(IComparable<T> objectType, IComparable<mT> objectID)
{
var topicDetails = new TopicDetailsInfo();
topicDetails.BaseObjectType = objectType;
topicDetails.TopicID = objectID;
return topicDetails;
}
}
[DataContract(Name = "SubscriptionTopicInfo", Namespace = "http://CName.GlobalService")]
[KnownType(typeof(List<TopicDetailsInfo>))]
[Serializable]
public class SubscriptionTopicInfo
{
private object topic;
private object target;
private object creator;
[DataMember]
public object Topic
{
get
{
return topic;
}
set
{
topic = value;
}
}
[DataMember]
public object Target
{
get
{
return target;
}
set
{
target = value;
}
}
[DataMember]
public object DBHandle
{
get
{
return creator;
}
set
{
creator = value;
}
}
static public SubscriptionTopicInfo CreateSubscriptions<T, mT, nT>(IList<TopicDetailsInfo> topic, IComparable<mT> target, IComparable<nT> handle)
{
var subscriptionTopic = new SubscriptionTopicInfo();
subscriptionTopic.Target = target;
subscriptionTopic.Topic = topic;
subscriptionTopic.DBHandle = handle;
return subscriptionTopic;
}
}
[DataContract(Name = "SubscriptionData", Namespace = "http://CName.GlobalService")]
[KnownType(typeof(List<SubscriptionTopicInfo>))]
[Serializable]
public class SubscriptionDataContracts : IExtensibleDataObject
{
private ExtensionDataObject extensionDataObjectValue;
[DataMember]
public string UserID
{
get;
set;
}
[DataMember]
public string ProjectID
{
get;
set;
}
[DataMember]
public string FromDiscipline
{
get;
set;
}
[DataMember]
public string ModuleID
{
get;
set;
}
[DataMember]
public string SessionID
{
get;
set;
}
[DataMember]
public List<SubscriptionTopicInfo> TopicList
{
get;
set;
}
public ExtensionDataObject ExtensionData
{
get
{
return extensionDataObjectValue;
}
set
{
extensionDataObjectValue = value;
}
}
}

VS-generated clients of my service throw Deserialization exceptions

Our WCF service has just one method:
[ServiceContract(Name = "Service", Namespace = "http://myservice/")]
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
public interface IService {
Response Execute(Request request);
}
public class Service : IService {
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider) {
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request) {
return new MyResponse { Result = MyEnumHere.FirstValue };
}
}
Both the Request and Response class includes a ParameterCollection member.
[Serializable]
[CollectionDataContract(Name = "ParameterCollection", Namespace = "http://myservice/")]
[KnownType("GetKnownTypes")]
public class ParameterCollection : Dictionary<string, object> {
private static IEnumerable<Type> GetKnownTypes()
{
return KnownTypesResolver.GetKnownTypes();
}
}
Subclasses of Request and Response store their values into the ParameterCollection value bag.
I am using the KnownTypesResolver class to provide type information across all Service objects.
public static class KnownTypesResolver {
public static IEnumerable<Type> GetKnownTypes()
{
var asm = typeof(IService).Assembly;
return asm
.GetAllDerivedTypesOf<Response>() // an extension method
.Concat(new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(MyClassHere),
typeof(MyClassListHere),
});
}
}
If I'm not mistaken, everything should have proper type information for proxy class generation tools to produce well-defined classes client-side.
However, whenever one of the Response subclasses (i.e. MyResponse) contains an enum value such as MyEnumHere, WCF starts complaining that the deserializer has no knowledge of the MyEnumHere value. It should have. I provided a KnownTypeAttribute for this very reason.
The client-side proxy class does have a MyEnumHere enum in the Reference.cs file; the problem is that the ParameterCollection class has no KnownTypeAttributes generated for it.
I resorted to hand-editing and including the following lines in the generated Reference.cs file:
//>
[KnownTypeAttribute(typeof(MyEnumHere))]
[KnownTypeAttribute(typeof(MyEnumHere?))]
[KnownTypeAttribute(typeof(MyClassHere))]
[KnownTypeAttribute(typeof(MyClassListHere))]
//<
public class ParameterCollection : Dictionary<string, object> { /* ... */ }
Hand-editing generated files is horrible. But this makes the clients work. What am I doing wrong? How can I define my Service objects so that the VS-proxy classes that are generated are correct from the get-go?
Thanks for your time.
WCF does not work well with Dictionary because it is not interoperable. You may use Array, List or custom collection to make sure that your data is properly serialized.
Code below uses List<ParamCollectionElement> instead of Dictionary. I also removed some redundant attributes.
[DataContract]
public class Request
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class Response
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class MyResponse : Response
{
[DataMember]
public MyEnumHere Result { get; set; }
}
public class ParamCollectionElement
{
public string Key { get; set; }
public object Value { get; set; }
}
[CollectionDataContract(Name = "ParameterCollection")]
public class ParameterCollection : List<ParamCollectionElement>
{
}
public static class KnownTypesResolver
{
public static IEnumerable<Type> GetKnownTypes()
{
return
new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(Request),
typeof(Response),
typeof(MyResponse)
};
}
}
[DataContract]
public enum MyEnumHere
{
[EnumMember]
FirstValue,
[EnumMember]
SecondValue
}
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
[ServiceContract(Name = "Service")]
public interface IService
{
[OperationContract]
Response Execute(Request request);
}
public class Service : IService
{
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
{
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request)
{
var result = new MyResponse
{
Result = MyEnumHere.FirstValue,
ParameterCollection = new ParameterCollection()
};
result.ParameterCollection.Add(new ParamCollectionElement {Key = "one", Value = MyEnumHere.FirstValue});
result.ParameterCollection.Add(new ParamCollectionElement { Key = "two", Value = new Response() });
return result;
}
}
Make sure you have [DataContract] on your enum and [EnumMember] on each of the enum members:
[DataContract]
public enum MyEnumHere
{
[EnumMember]
SomeValue,
[EnumMember]
OtherValue,
[EnumMember]
OneMoreValue
}
That should cause the proxy-enum to be built out (with its member values) in your client without having to manually change the Reference.cs file.