I want to inherit from a class which is located in a WCF Service. The Inheritance works fine (I see the properties of the base class in my child class), my problem occurs when I try to call a method from the service and pass the childtype as a parameter and not the basetype.
Base class in WCF Service (Pet):
[ServiceContract]
public interface IService
{
[OperationContract]
void BringPet(Pet pet);
[OperationContract]
void TakePet(Pet pet);
[OperationContract]
List<Pet> GetAllPets();
}
[DataContract]
public class Pet
{
private string _name;
private string _color;
[DataMember]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember]
public string Color
{
get { return _color; }
set { _color = value; }
}
}
Class on the client (Dog inherits from Pet):
[DataContract()]
class Dog : PetService.Pet
{
[DataMember()]
public bool HasPedigree { get; set; }
[DataMember()]
public string Race { get; set; }
}
When I try calling something like this:
Dog dog = new Dog()
{
Color = "Black",
Name = "Pluto",
HasPedigree = true,
Race = "Travolta"
};
_client.BringPet(dog);
I get a CommunicationException which says that the type Dog is not expected by the method BringPet(Pet pet).
I would solve this problem by setting the KnownType attributes on the service side, but because my service must not know the type Dog I can't set the KnownType or ServiceKnownType attributes.
Can someone help me out?
If you want to have inherited classes that get returned instead of the ones defined in your service contract, you need to make this fact known to WCF by means of the ServiceKnownType attribute:
[ServiceContract]
[ServiceKnownType(typeof(Dog))]
public interface IService
{
[OperationContract]
void BringPet(Pet pet);
[OperationContract]
void TakePet(Pet pet);
[OperationContract]
List<Pet> GetAllPets();
}
This basically tells the WCF service to also allow classes of type Dog to be used in your service.
Or you can decorate your data contract with the KnownType attribute:
[DataContract]
[KnownType(typeof(Dog))]
public class Pet
{
.....
}
to associate your data contract type with additional types that could be used in its place.
One way or the other: you have to make your extra type known to the WCF runtime - WCF cannot figure out that fact by just checking .NET type inheritance.
I think this is very similar to this question: Service - client interface, architecture advice
As you will see, there is no really easy way to do this without making your service operation more generic.
Related
I'm looking for the best method of doing this.
I have a series of data types which I want to return in the WCF, under one method. This is why I have a MarkerInterface, so that I don't need 100s of ServiceContracts/Methods to just simply transform the data.
The unfortunate part is, all the DataContracts (such as currentbatch) are all unique, they have their unique set of properties. The reason I have a Transformer is that they all require some sort of Data Transformation based off of the dataset.
The transformer interface is as follows:
public interface IDataTransformer
{
IMarkerInterface Transform(DataSet inDataSet_);
}
Then i have a transformer library for each datacontract (which i will explain below)
public class CurrentBatch_Transformer : IDataTransformer
{
}
This is One of the many datacontracts i will have, that implement IMarkerInterface.
[DataContract]
public class CurrentBatch : IMarkerInterface
{
[DataMember]
public string GroupName { get; set; }
[DataMember]
public bool FlagLocked { get; set; }
}
So, because i have an IMarkerInterface; I can place everything in one method (GetDataUsingDataContract), and take in their respective transformer and a DataSet.
public IMarkerInterface GetDataUsingDataContract(IDataTransformer composite, DataSet inData_)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
return composite.Transform(inData_);
}
Again, my questions are:
1) Is this a good practice? If this isn't; why, and is there a better way of doing it?
2) Will the Interface get serialized during the WCF serialization? (I don't want it to get serialized, but wouldn't mind if it did).
Yes, it's acceptable solution, but you must let WCF know what kind of types you are going to use, because it should form WSDL document. You can use either ServiceKnownType attribute for ServiceContract, KnownType for data contract or add known types in config.
Here are examples:
ServiceContract:
[ServiceKnownType("GetKnownTypes", typeof(Helper))]
[ServiceContract()]
public interface IService
{
[OperationContract]
IMarkerInterface GetMarker();
}
static class Helper
{
static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
return new Type[] { typeof(CurrentBatch) };
}
}
KnownType:
[DataContract]
[KnownType("GetKnownType")] //there are few option of usage, you can apply for one concrete class: [KnownType(typeof(InheritedClass))]
public class BaseClass
{
private static Type[] GetKnownType()
{
return return new Type[] { typeof(InheritedClass) };;
}
}
Config file:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyProject.IMarkerInterface, MyProjectAssembly">
<knownType type="MyProject.CurrentBatch, MyProjectAssembly"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
I have library with class in it looks like next:
[DataContract]
class MyBaseClass
{
public string Name { get; set; }
}
Also, I have another project with ref of this library and class in it looks like:
[DataContract]
class MyDeliveredClass : MyBaseClass
{
public string SecondName { get; set; }
}
I wonder how could I serialize list<MyBaseClass> which could contain objects of MyBaseClassand MyDeliveredClass ?
May be I should use [KnownType(MyDeliveredClass)]... but I have no reference to MyDeliveredClass in library...
If you declare both MyBaseClass and MyDeliveredClass as Service known types on the service contract interface then that should do the trick:
[ServiceContract(SessionMode = SessionMode.Required, ...)]
[ServiceKnownType(typeof(MyBaseClass ))]
[ServiceKnownType(typeof(MyDeliveredClass ))]
public interface IMySerivceContract {
...
}
Generally you have to choice between delclaring as KnownTypes on the class declaration or as ServerKnownTypes on the service interface. However sometimes the KnownType route is not an option for various reasons - one of which is your situation where you dont have access to a class declaration. Another case where you are forced to use ServiceKnownTypes is if your contract uses interfaces instead of the base class i.e if your contract had List<IMyBaseInterface>.
I need invoke webservice operations using standard wsdl, but data objects must be different in client and in the server.
Using interfaces for data objects in a common library, making proxy classes for it in client and in server.
Then, I'm declaring operation contract using the interface, but WCF don't recognize it.
I yet tried use DataContractSerializerBehavior and set knownTypes, no success yet.
Someone can help-me? I've attached a complete solution with more details.
public interface Thing
{
Guid Id {get;set;}
String name {get;set;}
Thing anotherThing {get;set;}
}
[DataContract]
public class ThingAtServer: BsonDocument, Thing // MongoDB persistence
{
[DataMember]
Guid Id {get;set;}
//...
}
[DataContract]
public class ThingAtClient: Thing, INotifyPropertyChanged // WPF bindings
{
[DataMember]
Guid Id {get;set;}
//...
}
[ServiceContract]
public interface MyService
{
[OperationContract]
Thing doSomething(Thing input);
}
Click here do see a Sample project on GitHub with TestCases
I've created WCF Service with contract:
[OperationContract]
CompositeTypeServer GetDataUsingDataContract( CompositeTypeServer composite );
My CompositeTypeServer looks like this:
[DataContract( Namespace = "http://enes.com/" )]
public class CompositeTypeServer
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
Then I've created client project with type CompositeTypeClient:
[DataContract( Namespace = "http://enes.com/" )]
public class CompositeTypeClient
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
Then I've added the reference to my service and selected to reuse types. Everything worked like charm. I was able to use CompositeTypeClient on client side.
So the trick was to specify Namespace for DataContract so they would match on both client and service.
[DataContract( Namespace = "http://enes.com/" )]
PS. I can provide full working VS solution on request.
Based on ServiceKnownTypeAttribute (MSDN documentation), I changed what types expected depending on the situation. The main idea is implemented in the class XHelper, responsible to return the correct Type[] according to the situation:
public static class XHelper
{
public static Boolean? IsClient = null;
public static Type[] ClientTypes;
public static Type[] ServerTypes;
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider pProvider)
{
if (!IsClient.HasValue)
throw new Exception("Invalid value");
if (IsClient.Value)
return ClientTypes;
return ServerTypes;
}
}
You must include the ServiceKnownType tag in the interface that has the ServiceContract to know XHelper class.
[ServiceContract(Namespace = MyProxyProvider.MyNamespace)]
[ServiceKnownType("GetKnownTypes", typeof(XHelper))]
public interface MyService
{
[OperationContract]
Thing2 CopyThing(Thing1 input);
}
At the beginning of the test unit, which was informed of the right Type[] for every situation:
[AssemblyInitialize]
public static void TestInitialize(TestContext pContext)
{
XHelper.ClientTypes = new Type[] { typeof(Thing1ProxyAtClient), typeof(Thing2ProxyAtClient), typeof(Thing2ProxyAtClient) };
XHelper.ServerTypes = new Type[] { typeof(Thing1ProxyAtServer), typeof(Thing2ProxyAtServer), typeof(ThingNProxyAtServer) };
}
Click here do see the final code Sample project on GitHub with TestCases
How can this work as a WCF Service?
public class BusinessObject<T> where T : IEntity
{
public T Entity { get; set; }
public BusinessObject(T entity)
{
this.Entity = entity;
}
}
public interface IEntity { }
public class Student : IEntity
{
public int StudentID { get; set; }
public string Name { get; set; }
}
I want to expose the BusinessObject <T> class and the all the class that inherits the IEntity interface in the WCF Service.
My code is in C#, .NET Framework 4.0, build in Visual Studio 2010 Pro.
While exposing BusinessObject to the clients via WCF, you must do that by using closed generic type.
[DataContract]
public class BusinessObject<T> where T : IEntity
{
[DataMember]
public T Entity { get; set; }
public BusinessObject(T entity)
{
this.Entity = entity;
}
}
[ServiceContract]
interface IMyContract {
[OperationContract]
BusinessObject<Student> GetStudent(...) // must be closed generic
}
KnownType attribute is a way to ensure that the type data for the contract is added to the wsdl metadata. This only works for classes it will not work for an interface. An interface cant store data and is not univerally understood by all languages out there so its not really exposable over wcf. See this here- http://social.msdn.microsoft.com/forums/en-US/wcf/thread/7e0dd196-263c-4304-a4e7-111e1d5cb480
You need to register a DataContractResolver behavior with your host so WCF can (de)serialize as yet unknown types dynamically. See more information here:
http://msdn.microsoft.com/en-us/library/ee358759.aspx
That said, the type still needs to be closed generic type, albeit a common base class AFAIK.
I have this ServiceContract
[OperationContract(IsOneWay=true)]
void ProcessMessage(Message message);
and these objects
[DataContract]
public class Message
{
[DataMember]
public long Id { get; set; }
[DataMember]
public string Body { get; set; }
}
[DataContract]
public class ExtendedMessage : Message
{
[DataMember]
public NameValueCollection AdditionalData { get; set; }
}
Will WCF be able to handle if I pass in the subclass to the service method? Or will it drop all extra properties that aren't on the base class?
ExtendedMessage msg = new ExtendedMessage();
...
ProcessMessage(msg);
I think if you didn't specify ExtendedMessage via the KnownType attribute, you would get an error. Once you've told WCF about ExtendedMessage via KnownType, it will work without losing data.
By the way, you don't need to know the set of possible types at compile time because the KnownType attribute can reference a method that will return the set of possible types at runtime.
You could also use the ServiceKnownType attribute to narrow down the scope of the KnownType attribute. You can apply the ServiceKnownType attribute on a specific operation or on your service contract.