Returning subclass of DataContract - wcf

I have a DataContract which I use as a return type from a WCF service.
[DataContract]
public NameResult
{
[DataMember]
public string Name { get; set; }
}
However, I want to store additional information on the service side, so I create a subclass:
internal ServiceNameResult : NameResult
{
internal Guid ID { get; set; }
}
However, it seems I am unable to use instances of this as a result value (the error I get on the client isn't very helpful - Unrecognized error 109 (0x6d).
Basically, if I do;
NameResult GetName()
{
NameResult result = {...}
return result;
}
Then it works, but if I do;
NameResult GetName()
{
ServiceNameResult result = {...}
return result;
}
It doesn't. I don't really want to have to copy the properties from the ServiceNameResult to a new NameResult. Hopefully there is a way to make this work?
I've already put [IgnoreDataMember] on the subclass, but that makes no difference.
Thanks.

Here is one way to approach this problem. You could use composition to achieve what you're looking for:
internal class ServiceNameResult
{
object OtherInformation { get; set; }
NameResult Result { get; set; }
}
So your internal service implementation can hold a reference to the client return object as well as additional information, but you don't pollute your interface.

You'll need to define ServiceNameResult as a known type

Related

Deserialization of reference types without parameterless constructor is not supported

I have this API
public ActionResult AddDocument([FromBody]AddDocumentRequestModel documentRequestModel)
{
AddDocumentStatus documentState = _documentService.AddDocument(documentRequestModel, DocumentType.OutgoingPosShipment);
if (documentState.IsSuccess)
return Ok();
return BadRequest();
}
And this is my request model
public class AddDocumentRequestModel
{
public AddDocumentRequestModel(int partnerId, List<ProductRequestModel> products)
{
PartnerId = partnerId;
Products = products;
}
[Range(1, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int PartnerId { get; private set; }
[Required, MustHaveOneElement(ErrorMessage = "At least one product is required")]
public List<ProductRequestModel> Products { get; private set; }
}
so when I'm trying to hit the API with this body
{
"partnerId": 101,
"products": [{
"productId": 100,
"unitOfMeasureId": 102,
"quantity":5
}
]
}
this is the request : System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'
I don't need parameterless constructor,because it doesn't read the body parameters.Is there any other way for deserialization?
You can achieve your desired result. You need to switch to NewtonsoftJson serialization (from package Microsoft.AspNetCore.Mvc.NewtonsoftJson)
Call this in Startup.cs in the ConfigureServices method:
services.AddControllers().AddNewtonsoftJson();
After this, your constructor will be called by deserialization.
Extra info: I am using ASP Net Core 3.1
Later Edit: I wanted to give more info on this, as it seems that this can also be achieved by using System.Text.Json, although custom implementation is necessary. The answer from jawa states that Deserializing to immutable classes and structs can be achieved with System.Text.Json, by creating a custom converter (inherit from JsonConverter) and registering it to the converters collection (JsonSerializerOptions.Converters) like so:
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}
and then...
var serializeOptions = new JsonSerializerOptions();
serializeOptions.Converters.Add(new ImmutablePointConverter());
serializeOptions.WriteIndented = true;
Just in case someone have the same issue I had, I was using abstract class, once removed the abstract key word, it all worked just fine.
Just Add [JsonConstructor] before your constructor
like this
public class Person
{
public string Name { get; set; }
public int LuckyNumber { get; private set; }
[JsonConstructor]
public Person(int luckyNumber)
{
LuckyNumber = luckyNumber;
}
public Person() { }
}
There are still some limitations using System.Text.Json - have a look here: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson
Deserialization without parameterless constructor using a parameterized constructor is not supported yet (but it's on their plan). You can implement your custom JsonConverter (like in this example: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs) or - like Adrian Nasul above suggested: use Newtonsoft.Json and then you can use the [JsonConstructor] attribute
In my case I had set a class as internal and when I made it public it worked. The error message was really of little help with this specific circumstance.
Old (actual class name changed to ClassName in the example
internal class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = className?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
New:
public class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = branding ?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
In my case error, caused was inside InnerException. There is my class had a field with a custom class type that did not have a parameterless constructor. I've added a parameterless constructor to the inner class and the problem has gone away.

Combine wcf operation contracts

The common way in WCF is to have a service with several operation contracts. Once an operation contract is defined you better not change it anymore because there are a lot of cases in which an operation change will break an existing client. But how about having just one general or universal operation contract (One single instead of many operation contracts. In fact I know you won’t be able to combine all in one but most of them).
Here one example. That’s not the way I would finally realize it … just a simple illustration.
public enum Operation
{
Add,
Sub,
Mul,
Div
}
[DataContract]
public class Info
{
[DataMember]
public Operation Operation { get; set; }
[DataMember]
public object Data { get; set; }
}
[ServiceContract]
public interface IService
{
[OperationContract]
Info Do(Info info);
}
public class Service : IService
{
public Info Do(Info info)
{
var result = -1;
switch (info.Operation)
{
case Operation.Add:
// cast info.Data and calculate result
break;
case Operation.Sub:
// cast info.Data and calculate result
break;
}
return new Info { Operation = info.Operation, Data = result };
}
}
The main disadvantage on a universal contract is that you have to know how to pass data to the service and how to interpret the result. But this can be solved by documentation.
Currently I’m working on a project with a big client-server-application combined in one solution. It’s a big hassle to add one operation, update service references…
What speaks against to combine operation contracts? Do I have something more to consider?
Once an operation contract is defined you better not change it anymore
But how about having just one general or universal operation contract
[ServiceContract]
public interface IService
{
[OperationContract]
Info Do(Info info);
}
Though you have made an attempt to make it "universal" or generic you really haven't. That's because your service interface only accepts Info as an argument with no room for expansion.
Info has members of type Operation - an enumeration which you cannot change as you rightly stated. What happens if I want to do a square root operation down the track? Or maybe a factorial?
One approach for single method service contracts that allow for future request types is the request/response pattern. Essentially you define the WCF service contract once and it never changes even when you add new operations. There's actually no need to. The object that is passed in the Request parameter is essentially an a standard envelope in which the actual specific operation is serialised.
e.g. here I have renamed your Request to RequestEnvelope
[ServiceContract]
public interface IService
{
[OperationContract]
ResponseEnvelope Do(RequestEnvelope request);
}
[DataContract]
public class RequestEnvelope
{
[DataMember]
public IRequest Request { get; set; }
}
[DataContract]
public class ResponseEnvelope
{
[DataMember]
public IResponse Response { get; set; }
}
For example, I might want to calculate prime numbers so I would serialise an instance of CalculatePrimes into the Request member of RequestEnvelope.
public interface IRequest { }
public interface IResponse { }
[Serializable]
public class CalculatePrimes : IRequest
{
public int StartAt { get; set; }
public int CountToCalculate { get; set; }
public TimeSpan Timeout { get; set; }
}
[Serializable]
public class CalculatePrimesResponse : IResponse
{
public List<int> Primes { get; set; }
}
This approach works very well when refactoring large monolithic services with many many operations on a single WCF interface into something more manageable and significantly less long-term maintenance. Note how the actual requests and responses don't have to be actual WCF types but POCOs.

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method

Nested List serialization

I am having problem transfering my object from WCF to SL3.
interface IComposite {
ICollection<Child_A> Children{ get; set; }
}
[DataContract]
[knownType(typeof(ChildCollection))]
[knownType(typeof(ICollection<Child_A>))]
class Composite : IComposite {
ChildCollection c = null;
[DataMember]
public string Name { get;set;}
[DataMember]
public ICollection<Child_A> Children { get {
return c??(c=new ChildCollection());
} set;}
}
[CollectionDataContract]
class ChildCollection : List<Child_A> {
}
[DataContract]
class Child_A {
[DataMember]
string Name { get;set; }
}
[OperationContract]
Composite GetData(){
var data = new Composite();
data.Children.Add( new Child_A() { Name = "child_a_1" } );
return data;
}
When I call the service from SL3, I get the Composite object but no item in the list. There are other collection in Composite. When I set [DataMember(Order=0/1)] I get error nullreference error on client. And if I remove it, I get error Not Found. I tried KnowType and ServiceKnownType but did not work. I checked svcTrace, it simply says Serialization Error. Where I am doing wrong.
SVC TRACE
The InnerException message was 'Type 'xxxCoverageEntity' with data contract name 'xxxCoverageEntity : http://schemas.datacontract.org/2004/07/xxxBusinessEntities' is not expected. Add any types not known statically to the list of known types
Here xxxCoverageEntity is Child_A in sample
You need to annotate the collection with DataMember or it will not get serialized at all. You will also need to annotate the DataContract with KnownType(typeof(ChildCollection)) as otherwise it doesn't know what type of "thing" the ICollection is and therefore how to serialize it
Similarly you will need to add [DataMember] to Child_A Name property or it will not get serialized

Serialising classes that implement List<T> for transferring over WCF

I have spent some time writing code for my application assuming that the serialisation bit would be the easiest part of it. Pretty much both sides (client and server) are done and all I need to do is passing a class AccountInfo from the service to the client... The problem is that AccountInfo inherits List and therefore [DataContract] attribute is not valid. I tried using the [CollectionDataContract] attribute but then the class that is received on the other side (client) contains only generic List methods without my custom implemented properties such as GroupTitle...I have worked out a solution for this problem but I don't know how to apply it.
Basically everything works when I make a property instead of inheriting a List but then I can't bind this class to LongListSelector (WP7) because it's not a collection type.
There are three classes I'm on about. AccountInfo that contains multiple instances of: AccountInfoGroup that contains multiple instances of:AccountInfoEntry (this one does not inherit list therefore there are no problems serialising it and all properties are accessible).
Could someone help me using right attributes to serialise and transfer these classes using a WCF method?
Here is the code of 2 of these collection classes:
public class AccountInfo : List<AccountInfoGroup>
{
public AccountInfo()
{
UpdateTime = DateTime.UtcNow;
EntryID = Guid.NewGuid();
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
public Guid EntryID
{
get;
set;
}
public decimal GetTotalCredit()
{
decimal credit = 0;
foreach (AccountInfoGroup acg in this.Where(item => item.Class == AccountInfoEntry.EntryType.Credit))
{
acg.Where(item => item.ItemClass == AccountInfoEntry.EntryType.Credit).ToList().ForEach(entry =>
{ credit += entry.Remaining; }
);
}
return credit;
}
public bool UsedForCreditComparison = false;
public DateTime UpdateTime { get; private set; }
}
public class AccountInfoGroup : List<AccountInfoEntry>
{
public AccountInfoEntry.EntryType Class
{
get;
private set;
}
public string Title
{
get
{
return AccountInfoEntry.ClassToString(Class);
}
}
public AccountInfoGroup(AccountInfoEntry.EntryType groupClass)
{
this.#Class = groupClass;
}
public bool HasItems
{
get
{
return (Count != 0);
}
private set
{
}
}
}
Thank you for any suggestions... :)
The sample you had is quite painful for WCF in serialization.
What I suggest is you to revised and have a common models for your WCF messages (That means it only contains properties with getter and setter, serialization attributes).
If you have a problem in LongListSelector binding in WP7, you might want to convert the message to the actual type the WP7 object supports to use in binding.