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.
Related
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.
I have following Service Interface
[ServiceContract]
public interface ICacheService
{
[OperationContract]
IEnumerable<CacheResponse> GetCache(IEnumerable<CacheRequest> requests);
}
[DataContract]
[KnownType(typeof(CacheItem))]
[KnownType(typeof(TreeItem))]
[KnownType(typeof(TreeTopGroup))]
[KnownType(typeof(TreeGroup))]
[KnownType(typeof(TreeView))]
[KnownType(typeof(ITreeItem))]
public class CacheResponse
{
[DataMember]
public IDictionary<string, CacheItem> CacheItems { get; set; }
[DataMember]
public IEnumerable<KCSLuceneDocument> LuceneDocuments { get; set; }
}
The request works so i won't post it. The Response only contains a DateTime, a String and a List of TreeItem.
Important part of the TreeItem Class
public class TreeItem : ITreeItem
{
public ITreeItem Parent { get; set; }
.
. more stuff
}
As soon as the parent Property is set to something other then null, the client gets a System.ServiceModel.Dispatcher.NetDispatcherFaultException.
Stating that the elment
\"http://schemas.datacontract.org/2004/07/Core.Base:_x003C_Parent_x003E_k__BackingField\" contains Data of the Type \"http://schemas.datacontract.org/2004/07/Core.Base:TreeItem\" and that the deserializere doesn't know any Type with that name.
I tried using the KnownType Attribute as well as the ServiceKnownType Attribute.
Both didn't help.
The only thing that worked is changing the type of Parent to TreeItem which i really don't want to do. Especially as it can contain ITreeItems in some other Locations which most likely also break the Service.
Any idea how to solve the problem?
Do you have the [DataContract] attribute on TreeItem? That would be required. if the TreeItem does have a [DataContract] attribute set, I have a few other ideas, but it may require a bit more of the code to be posted. So give it a try and let me know if I can be of more help.
A hint as to what the other issues may be can be found on the MSDN for Data Contract Known Type.
The KnownType is needed when the class marked with the Data Contract attribute meets one of the following:
The sent data contract is derived from the expected data contract. For more information, see the section about inheritance in Data Contract Equivalence). In that case, the transmitted data does not have the same data contract as expected by the receiving endpoint.
The declared type for the information to be transmitted is an interface, as opposed to a class, structure, or enumeration. Therefore, it cannot be known in advance which type that implements the interface is actually sent and therefore, the receiving endpoint cannot determine in advance the data contract for the transmitted data.
The declared type for the information to be transmitted is Object. Because every type inherits from Object, and it cannot be known in advance which type is actually sent, the receiving endpoint cannot determine in advance the data contract for the transmitted data. This is a special case of the first item: Every data contract derives from the default, a blank data contract that is generated for Object.
Some types, which include .NET Framework types, have members that are in one of the preceding three categories. For example, Hashtable uses Object to store the actual objects in the hash table. When serializing these types, the receiving side cannot determine in advance the data contract for these members.
source
If it is marked with [DataContract] you will likely need to add a [ServiceKnownType] attribute on the TreeItem class itself to show possibilities for the TreeItem.Parent property. That is just a conjecture, I would need to see a bit more code first.
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.
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();
}
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).