Serialize Generic type over WCF Service - c#

I have a C# class defined as follows:
public class GenericItem<T>
{
public List<T> Items { get; set; }
public DateTime TimeStamp { get; set; }
}
I am creating an instance of this class on my server. I am then trying to pass it over the wire via a WCF service as shown here:
[OperationContract]
public GenericItem<MyCustomType> GetResult()
{
GenericItem<MyCustomType> result = BuildGenericItem();
return result;
}
Everything compiles just fine at this point. When I "update service reference" in my Silverlight app an re-compile, I receive a compile-time error, similar to the following:
MyNamespace.GenericItemOfMyCustomType[extra chars] does not contain a public definition for 'GetEnumerator'
I have no idea why:
Extra chars are appearing. The seem to change everytime I update the service reference.
How to actually fix this.
What am I doing wrong?

Sleiman is correct, but one can use Bounded Generics as described in this article, and you may be able to achieve what you want. This allows you to create a generic type within the service and expose it. But the consumer will not view it as generic as the type is specified in the service operation.

You cannot define WCF contracts that rely on generic type parameters. Generics are
specific to .NET, and using them would violate the service-oriented nature of WCF. However, a data contract can include a collection as a data member because WCF offers dedicated marshaling rules for collections.

As sleiman has pointed out, Generics are not supported in SOAP.
WCF and generics -> http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/79585667-6b97-4ce4-93fa-3a4dcc7a9b86
related question -> WCF. Service generic methods

You can use the following on the client side instead of using the servicereference:
var myBinding = new BasicHttpBinding();
var myEndpoint = new EndpointAddress("");
var myChannelFactory = new ChannelFactory<IService>(myBinding, myEndpoint);
IService gks = myChannelFactory.CreateChannel();
Works for generics.

Related

ServiceRemoting V2_1 still throwing serialization exception when working with interface return types

over the last few days, I tried to equip my application with the service remoting IPC stack. I initially implemented the V2 version, but just noticed thanks to this post (https://github.com/Azure/service-fabric-issues/issues/735) that that does not support returning interfaces.
So, I just now made the switch to V2_1.
However, I still face this problem:
One or more errors occurred. (Type 'MooMed.Core.DataTypes.Session.SessionContext' with data contract name 'SessionContext:http://schemas.datacontract.org/2004/07/MooMed.Core.DataTypes.Session' is not expected. 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.)'
This is the service method that is being called on the endpoint:
[CanBeNull]
public Task<ISessionContext> GetSessionContext(int accountId):
The involved classes/intefaces look like this:
public interface ISessionContext
{
Account Account { get; set; }
}
[DataContract]
public class SessionContext : ISessionContext
{
[DataMember]
public Account Account { get; set; }
}
Also, as I mentioned, I changed my remoting version from V2 to V2_1 just now, so this is added to the Service class which contains the GetSessionContext method:
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1, RemotingClientVersion = RemotingClientVersion.V2_1)]
So, according to the docs (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#use-the-remoting-v2-interface-compatible-stack) I should now be fully equipped to have everything working properly.
The only thing I see myself doing differently than the tutorial is how I declare the endpoint listeners. I'm doing this via FabricTransportServiceRemotingListener like this:
public static ServiceReplicaListener CreateTypedListener([NotNull] IService service)
{
return new ServiceReplicaListener(context => new FabricTransportServiceRemotingListener(context, service),
$"I{service.GetType().Name}");
}
However, I don't see how this could be the culprit, as the request comes in properly and I don't think declaring endpoints does necessarily interfere with how responses are serialized.
So, what am I doing wrong here?
The DataContractSerializer can't serialize an interface. (see how to mark an interface as DataContract in WCF).
However, you can write your own custom serializer to achieve this. From that article you posted at the bottom are instructions on how to do this. You can also look at my example of implementing protobuf-net here:
https://github.com/mikeruhl/Frenetik.Fabric.Remoting.Protobuf
My example should not be used in production. It's a work in progress and there are zero tests written for it at the moment.

WCF service contract; calling a class method from client

Working with WCF service I met the following problem: calling the service from a client (simple console application), I can not access the method of a class marked with [DataContract] and [ServiceContract], even if this method is marked with [OperationContract] (actually, I've tried all the possible combinations of attributes so far :D ). Is there a way to resolve it? I'm missing some points here, I guess, but still can't handle it, need one's help ^^
Here is the code of a class:
[ServiceContract]
[DataContract]
public class AmountSpecification : IOrderSpecification
{
[DataMember]
public int Amount {get ; set;}
public AmountSpecification(int amount)
{
Amount = amount;
}
public bool IsSatisfiedBy(Order o)
{
return o.Amount >= Amount;
}
[OperationContract]
public IOrderSpecification And(IOrderSpecification specification)
{
return new AndSpecification(this, specification);
}
}
Your method returns IOrderSpecification.
So, on the client side, there is no indication on which implementation is used, (AmountSpecification , AndSpecification, etc) and so the client will not create the correct instance type.
You need to add Well Known Types attribute on your interface. This will allow the wsdl to contains definitions for the implementation, and so the client will have the information.
You're mixing up service contract and data contract in one class. You only need the DataContract and DataMember attributes to create a data contract, and those aren't even required.
However, when you generate a service reference from a service using this data contract, and the project you're adding the service reference to doesn't have a reference to the assembly where AmountSpecification is defined, it will generate a data contract with only the given name and properties.
If you want to use the class in its enitrety, you have to reference the assembly that class is defined in in your client appliaction, and check "Reuse types in referenced assemblies" in your service reference configuration. See Service reference complex types.

How to properly migrate from WCF DatacontractSerializer to Protobuf-net?

We have a huge application with a lot of classes. We are currently porting this .net application to IPad with Monotouch. We have some problems with the DataContractSerializer and we would like to use Marc Gravell's protobuf-net serializer.
The communication between the client and the server is managed by a WCF Service.
A WCF service is made of one Interface exposed to the client and the server, and one implementation of this interface on the server.
The interface looks like that:
[ServiceContract]
public interface IMyService
{
[OperationContract]
SomeObject MyFunction(SomeObject myObject);
}
The server side implementation looks like that:
[ServiceBehavior(...)]
public class MyService
{
public SomeObject MyFunction(SomeObject myObject)
{
}
}
Our classes looks like that:
[DataContract]
public class MyClass
{
[DataMember]
public int SomeProp {get; set;}
[OnSerialized]
public void OnSerialized(StreamingContext context)
{
}
}
So here are my questions:
What would be the changes to do to my classes, wcf interface and wcf implementation.
How would I replace the default WCF DataContractSerializer to the Protobuf Serializer.
Please note that on monotouch, I only have access to Protobuf and Protobuf.Meta namespaces.
[EDIT]
I found a way to swap the serializer runtime:
Custom WCF DataContractSerializer
The above solution uses the DataContractSerializerOperationBehavior. Does Protobuf-net provides such behavior?
In all honesty, I am unfamiliar with the WCF options available to you in monmotouch; they are very different between regular .NET and Silvelight, for example - and I see no reason to assume that monotouch has the ability to swap serializer at runtime (which "full" .NET does, at least under the MS version). This makes it hard to do the transition silently, as we can't wrestle control from DataContractSerializer.
As such, IMO the simplest option is to seize control of the data manually, and send raw byte[] - ideally with MTOM encoding enabled if monotouch can do that. Then once you have your byte[] the world is your mollusc, as they say.
Re changes to your types... well, MyFunction() is an oddity, in that it doesn't transfer any data, so I'm not sure what you want me to suggest on that one. With MyClass, all it needs is a unique number (unique within the type, not globally) per member, i.e.
[DataContract]
public class MyClass
{
[DataMember(Order=1)] // <==== this provides the 1 as the key
public int SomeProp {get; set;}
// see below re callback
}
You also have a serialization callback; these are fully supported, but it expects to find a familiar pattern - StreamContext is not one that I know of (although it should work with StreamingContext and a few others).
Finally, note that by default protobuf-net executes the constructor, which is different to DataContractSerializer. If you desire, you can suppress this via:
[DataContract(SkipConstructor=true)]
public class MyClass {...}
If I've missed the intent here, let me know.
Note there are also ways of doing all the configuration without changing/adding any attributes if you prefer.

WCF, Interface return type and KnownTypes

I'm creating a WCF service, and I'm having a lot of trouble with some Serialization issues. Perhaps there's just 1 way to do it, but i'd like to confirm it
Here's my sample code :
Contracts
public interface IAtm
{
[DataMember]
double Latitude { get; set; }
[DataMember]
double Longitude { get; set; }
}
[ServiceContract]
public interface IAtmFinderService
{
[OperationContract]
ICollection<IAtm> GetAtms();
}
Service Implementation :
[KnownType(typeof(Atm))]
[KnownType(typeof(List<Atm>))]
[ServiceKnownType(typeof(Atm))]
[ServiceKnownType(typeof(List<Atm>))]
public class AtmFinderService : IAtmFinderService
{
public ICollection<IAtm> GetAtms()
{
return new List<IAtm>()
{
new Atm() { Latitude = 1, Longitude = 1 },
new Atm() { Latitude = 2, Longitude = 2 }
};
}
}
I added all of the KnownType and ServiceKnownType attributes because i thought that there was something missing there..
So now, i've been doing some tests. I tried creating a console app, using the "add service reference" method to make VS create automatically the proxy. This way, I get a function like
object[] GetAtms();
When trying to call it, i get this error :
The InnerException message was 'Type
'WCFTest.Atm' with data contract name
'Atm:http://schemas.datacontract.org/2004/07/WCFTest'
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.'.
Very nice... So then, I think that VS's autogenerated code is crap. I did the following change in my service (and all the related classes and implementations) :
[OperationContract]
ICollection<Atm> GetAtms();
So now, i'm returning a concrete type. After updating the service reference, it creates a copy of the Atm class, with its members and stuff.
After calling the service, the call succeeds.
I thought that this was some bad behaviour related to the autogenerated code, so i tried creating a very simple host/client app. I started a console host listening on some port, then created a client that uses the ClientBase class to make a call to the service. Same behaviour... if the service is implemented returning an interface type, it fails. If i change it to return the concrete type, it works. I think that i have some problem with the KnownType attributes, i must be missing something that the serializer can't process. but i don't know what.
Ok, i managed to fix it
The problem, as I see it, was this
Since I'm returning an interface and not a concrete class, WCF doesn't know what to expect on the other end. So, it can be anything. When he gets a List, he's confused. The correct way to do it was to add the KnownType attributes where needed.
Who needs to know those types? the service implementation, to serialize and deserialize them correctly. However, the client talks with the interface of the service, not with the implementation itself. That's why adding theKnownType attribute in the service implementation didn't work
The problem here is that, interfaces don't allow KnownType attributes, but they do allow ServiceKnownType attributes. The solution to the problem was to add the expected type in the service interface contract, and voila, everything works ok and using interfaces
[ServiceContract]
[ServiceKnownType(typeof(Atm))]
[ServiceKnownType(typeof(List<Atm>))]
public interface IAtmFinderService
{
[OperationContract]
ICollection<IAtm> GetAtms();
}

Help Creating an Interface

I'm starting to see the value of Interfaces vs. lets say an Abstract class.
Currently I'm working on a PayPal Wrapper project. We'll also be probably doing a Google Payments, BillMeLater, and Amazon wrapper. I've been asked to identify some commonalities (methods, properties, whatever) that we could use across the board, in most any Web Service SOAP Wsdl Wrapper project for any web services we do.
So as I was coding out my PayPal wrappers, I created a new class to hold Errors received back from any PayPal response:
public class ApiError
{
#region Constructors
/// <summary>
/// Disallow default instantiation.
/// </summary>
private ApiError()
{
}
internal ApiError(ErrorType error)
{
if(error.ErrorCode != null)
{
this._errorCode = error.ErrorCode;
}
}
#endregion
#region member variables
private string _errorCode = string.Empty;
private string _erorMessage = string.Empty;
#endregion
#region Properties
public string ErrorCode
{
get { return _errorCode; }
set { _errorCode = value; }
}
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}
#endregion
}
Anyway, I said hey, these ErrorMessage and ErrorCode properties are most likely going to be in every third party API. So why not create an Interface in a new project called [MyCompany].WebServices.Common and in that interface add those 2 properties. Then any class wrapper that I create that has functionality to make API proxy calls can implement this interface and then I know any of those kinds of wrapper classes in any of our web service projects will be guaranteed to have these kind of properties in them that will be impolemented and filled with any errors that come back from an API response.
And if they do, then that's great because I can then start to create some helper methods that we can use across the board if I can somehow take in a generic response object and fill the array of errors and set the property.
Anyway, my problem is, I'm new to interfaces a litte. So the error array property from above for example is of a custom type.
Well if I create interface in a seperate physical project, I can't use that custom type because it doesn't exist..it only exists so far in my PayPal wrapper project.
So then when stubbing this interface out, how would I handle this?
namespace [MyCompany].WebServices.Common
{
interface IRequest
{
public ApiError Type { get; set; } //whoops, that's a custom type that this project does not know about (ApiError)
}
}
You should consider that the ApiErrors are going to depend on the particular web service implementation. So how would a client that is using only interfaces going to use the ApiErrors if they are implementation-specific?
It can't - because this would mean that its coupled to this particular implementation.
Instead, you need to abstract away from the specific API error codes, and define your own abstract error codes.
Every Problem in Software can be Solved with Another Layer of Indirection!!! Just add another interface called IAPIError that you implement for each pay service error type.
interface IRequest
{
public IApiError Type { get; set; }
}
You could put your shared types in a separate, shared assembly: [MyCompany].WebServices.Shared
Edit
Come to think of it, you already have that. [MyCompany].WebServices.Common should be as good a place as any for your shared types. Just move it there. Or am I misunderstanding what you want to do?
The exact error numbers and messages will be very specific for each provider. Therefore, while a common interface can be made, it will not provide enough abstraction to handle errors in a meaningful way.
I'd add another abstraction by defining the expected errors (one of which is "unknown error" as catch-all for unexpected errors) as separate class or even as set of Exceptions. Then, have each provider return or use those, which is already compatible with your application and allows you to handle common errors for all providers the same way.
Put those common types into a separate assembly, which is a pure "interface" assembly used by both the providers and the actual code using them. Therefore you'll be able to loosely couple the providers without having to add references to them in your main application (allowing you to add or modify providers without recompiling the application).

Categories