Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I am learning WCF and do not understand the real advantage of KnowTypeAttribute. Could somebody explain me simply why we need it?
DataContractSerializer is contract-based, meaning it is not tied to any specific type model. All it has is data (typically xml). This means that if you have a model like:
Customer
SuperCustomer : Customer
AwesomeCustomer : Customer
then the serializer needs to know in advance what each type means if it sees it in the data; otherwise it won't know what type to create. This is done in a number of ways, the simplest of which is KnownTypeAttribute.
Consider the alternative; all the serializer knows is "Customer", which it expects to see as <customer>...</customer> in some xml. Instead, it gets something else (it doesn't matter what, but let's say <superCustomer>...</superCustomer>. Now what does it do? Does it start scrounging for likely looking types? that is very imprecise and risky. Also consider, it needs to be able to generate a WSDL/MEX export for this data - if all it knows about is "Customer", it can't possible warn callers to also expect SuperCustomer / AwesomeCustomer - which would mean the WSDL/MEX is incomplete and useless.
This same approach is used by XmlSerializer (XmlIncludeAttribute) and protobuf-net (ProtoIncludeAttribute), and probably my most contract based serializers.
The alternative is type based serializers (BinaryFormatter, NetDataContractSerializer, etc) - in this it includes the type in the data, meaning Your.Namespace.Type, Your.Assembly, blah - this means it doesn't need to know in advance (since it is explicit in the data), but also means that it can't possibly work for different models (or indeed, cross-platform).
KnownTypeAttribute enable you to designate acceptable derived class for a given Data Contract. It specifies types that should be recognized by the DataContractSerializer when serializing or deserializing a given type.
One simple example.
[ServiceContract()]
interface ITaskManager
{
[OperationContract()]
MyCollection<Task> GetTaskByAssignedName(string name);
}
[DataContract()]
[KnownType(typeof(DerivedTask))]
class Task
{
}
[DataContract()]
class DerivedTask
{
}
When working with polymorphic types in your service contract, the KnownTypeAttribute is required because polymorphism is outside the paradigm of the service orientation.
Apply the KnownTypeAttribute attribute to a type to indicate to the
DataContractSerializer types that should be recognized when
serializing or deserializing an instance of the type to which the
attribute is applied. This attribute could also be recognized by other
serializers that understand data contracts.
Look at here for more details.
This attribute is used to include additional classes in the metadata of the service so that clients could see them. Let's take for example the following:
[DataContract]
public class BaseModel
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class ChildModel: BaseModel
{
[DataMember]
public string Foo { get; set; }
}
and the following service contract:
[ServiceContract]
public interface IMyService
{
[OperationContract]
BaseModel Get();
}
and that you implement it like this:
public class MyService: IMyService
{
public BaseModel Get()
{
return new ChildModel();
}
}
Now when WCF exposes the metadata of this service it looks at the service contract and the operations being involved so it discovers the Get operation which returns the BaseModel type. So the BaseModel class is automatically exposed in the metadata. The problem is that when you try to invoke the service the actual implementation returns a ChildModel for WCF has no knowledge. The clients of the service neither have knowledge of this type.
So you need to explicitly indicate indicate this class that you are using in the implementation but is not part of the contract. This could be done by using the KnownType attribute:
[DataContract]
[KnownType(typeof(ChildModel))]
public class BaseModel
{
[DataMember]
public string Id { get; set; }
}
Another way to specify this known type is to do it by using the config file:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyCompany.BaseModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
<knownType type="MyCompany.ChildModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Related
I'm considering migrating current WCF-based based application to protobuf-net.Grpc. It seems to be doable, however I was not able to make protobuf-net serialize properties of (DTO classes) base class without including all derived classes with [ProtoInclude] attribute.
Simplified class hierarchy:
[DataContract]
public abstract class DtoBase
{
[DataMember(Order = 1)]
public int Id { get;set; }
[DataMember(Order = 2)]
public int Version { get;set; }
[DataMember(Order = 3)]
public EditState EditState { get;set; }
}
[DataContract]
public class PersonDto : DtoBase
{
[DataMember(Order=4)]
public string FirstName { get;set; }
[DataMember(Order=5)]
public string LastName { get;set; }
}
I have investigated related questions and it all boils down to the fact that specific type should be known during deserialization - or there should be a way to determine it. Our service methods already know the specific subclass to use, e.g. we have methods like
[ServiceContract]
public interface IPersonService
{
[OperationContract]
ScalarResult<PersonDto> GetById(personId);
}
DataContractSerializer can do that - deserialize base class properties, when specific subclass is already known. It needs hints (known types) when you deserialize subclass having base class signature, like returning PersonDto instead of DtoBase. But when specific subclass is known, known types are not needed and everything just works.
So the question is how to do the same with protobuf-net? And if it's not possible, why?
Protobuf-net, like any library, makes certain assumptions and compromises. If it wants to support additional scenarios, they need to be specified, designed, implemented, tested and supported - all of which takes time. So far, the scenario you describe: hasn't had that time invested.
It may be possible to configure the base-type properties using the RuntimeTypeModel API, but I must emphasize: whenever a question arises that is essentially:
My existing model isn't working well with my chosen serializer
my default response (based on literally decades of experience in this field) is:
If your existing model isn't a great fit for a different serializer: stop fighting the serializer. Instead, create a new model that works perfectly with your new choice of serializer, and shim between models at the point of (de)serialization
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.
This question already has answers here:
Is DataContract attributes required for WCF
(4 answers)
Closed 9 years ago.
I was wondering if there is any way to define a WCF Contract class without using the [DataContract] and [DataMember] annotation. The reason is that domain model we currently have is fairly clean so we would like to keep it this way. Whats the best practice here? Create a Transfer object and copy the domain model object into a transfer object (that has the required annotations and is the Contract transfered between Client and Server)? Or somehow not annotate the object model and specify the contract in a different way.
If you do not add any serialization attributes to your class, and use it as part of a WCF service contract method, WCF will use the default serialization rules to produce a data contract anyway. This means that the class will implicitly become a [DataContract] every public property that has both a get and set accessor will implicitly become a [DataMember].
The only time you need to apply the attributes is if you want to override the default behavior, e.g. hiding some attributes, applying namespaces, etc. It's generally considered good practice to do so anyway, because relying on the default behavior might get you in trouble later. (It also makes it explicit that your class is meant for use by WCF). But it's not strictly required, as long as the default behavior meets your needs.
In response to your follow-up:
As far as I know there's no completely external way to change the serialization behavior of the DataContractSerializer for a given class; every option requires at least some level of attribution on the class being serialized. As #Yair Nevet describes below, my preferred method for turning existing domain objects into data contracts is the MetadataType attribute.
Alternatively, you can bypass the whole issue by doing what you suggested in your question: don't serialize your domain objects, but create custom DTO objects and serialize them. I tend to do this whenever I'm using the Entity Framework, for example, because serializing those can be tricky. This is also a good approach to take if your domain objects have lots of behaviors built into them -- you get a clear separation of "data being passed around" vs. "objects participating in my business logic."
You often end up with lots of redundant code, but it does achieve your goal of zero changes to your existing objects.
You can use the MetadataType attribute and a metadata model class in order to separate the annotations from your model.
For example:
[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase {
... /* the current model code */
}
[DataContract]
public class MyModelMetadata {
[DataMember]
public string Name { get; set; }
}
WCF is capable of serializing your objects without the attributes. The attributes are there to allow for customization. For example, the two classes will serialize identically by the DataContractSerializer:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
[DataContract]
public class Customer
{
[DataMember] public string FirstName { get; set; }
[DataMember] public string LastName { get; set; }
}
It is worth mentioning that you really should mark your class with the attributes. They aren't as "messy" as you think. It will actually save you from headache in the future. For example:
[DataContract(Name = "Customer")]
public class Customer
{
[DataMember(Name = "FirstName")]
public string FirstName { get; set; }
[DataMember(Name = "LastName")]
public string LastName { get; set; }
}
In the previous code sample, I explicitly set the names of the class and members. This will allow me to refactor the names without breaking consumers code. So, if someone decides that my class should be named CustomerDetail instead of Customer, I can still leave the name as Customer so that consumers of my service continue to work.
You could always use DTOs. Make a separate class that has everything that is needed to serialize your objects. Then project your domain model on to the DTO. You could use something like AutoMapper to make this process a little easier.
Regarding Performance
Unless you have hundreds, probably thousands, or objects or a very large number of properties per class, the act of converting to and from DTOs probably isn't that much performance overhead.
If you are using something like EF, and you are not serializing every property, you might even be able to reduce some overhead by projecting your EF query directly on to your DTOs.
This is kind of a dramatic case, but I had (poorly designed) database models with 50+ properties per type. By changing to DTOs that only have the 10-15 properties I cared about, I was able to almost double the performance of a WCF service.
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 have a new question for WCF gurus.
So, I have a class User which is close to the 'User' representation from the DB which I use for database operations. Now, I would like to have 2 different service contracts that use this class as data contract, but each in their own way... I mean,
public class DBLayer
{
void InsertUsers(List<User> userList)
{
// both 'PropertyVisibleForService1' and 'PropertyVisibleForService2'
// are used HERE to be inserted into their columns
}
}
[DataContract]
public class User
{
[DataMember] public string PropertyVisibleOnlyForService1{...}
[DataMember] public string PropertyVisibleOnlyForService2{...}
}
[ServiceContract]
public interface IService1
{
List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService1' inside
}
[ServiceContract]
public interface IService2
{
List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside
}
So, the idea is that each service will get a different kind of user, subset of 'User'. Keeping in mind that I want to use the 'User' as is for DB operations, what would be my options to achieve this? Do I really need to create different data contracts or is there another smarter way?
Best would be to not only give me the solution, but also to explain me some best practices and alternatives.
Thank you in advance.
EDIT1:
I added a dummy DBLayer class here for a better overview and why I think the inheritance may not be good in this case.
A solution would be of having another 'UserForService1' and 'UserForService2' as data contracts which would map at the end from/into an 'User' but I wanted some other points of view.
EDIT2: Very good article which helped me in this case: http://bloggingabout.net/blogs/vagif/archive/2009/03/29/iextensibledataobject-is-not-only-for-backward-compatibility.aspx
You could create separate DTO's for each service but your case would actually be ideal for a Decorator pattern:
[DataContract]
public class UserForService1 : User
{
private User mUser;
public UserForService1(User u)
{
mUser = u;
}
//expose only properties you'd like the user of this data contract to see
[DataMember]
public string SomeProperty
{
get
{
//always call into the 'wrapped' object
return mUser.SomeProperty;
}
set
{
mUser.SomeProperty = value;
}
}
// etc...
}
and for Service2 similar code, that exposes only what you care for there...
If they are designed to represent different types of users, they should be different classes. I agree with phoog in the comments, you should derive the type you want from the shared User class and add the specific service properties to the derived classes.
Why don't you think inheritance would be good in this case? If you give us some more details, we could try to revise the suggestions to suit your actual problem.
As suggested in the comment, you can have two classes deriving from a base User then using Data Contract Known Types, you can accomplish your desired goal. See the following links for more examples.
http://www.freddes.se/2010/05/19/wcf-knowntype-attribute-example/
http://footheory.com/blogs/bennie/archive/2007/07/28/handling-data-contract-object-hierarchies-in-wcf.aspx
If you don't want to use inheritance, something like:
[DataContract]
public class User
{
}
[DataContract]
public class Service1User : User
{
[DataMember] public string PropertyVisibleOnlyForService1{...}
}
[DataContract]
public class Service2User : User
{
[DataMember] public string PropertyVisibleOnlyForService2{...}
}
[ServiceContract]
public interface IService1
{
List<Service1User> GetUsers(); // user with 'PropertyVisibleOnlyForService1' inside
}
[ServiceContract]
public interface IService2
{
List<Service2User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside
}
Then I'm not sure what you would do. Your sortof breaking the principals of an type declaration at that point. Think of it in a normal .NET way; if you define "User" in your application, then it is the same type everywhere. Some properties cant be hidden from certain other classes or methods.
WCF is also going to pack this type information into the generated WSDL, and it is only going to define the User type once, so it needs to know what properties are there.
Now, if all you care about is the actual SOAP message that is constructed, and you don't care about the WSDL or what any clients generated off the WSDL will see, then technically you can have it not emit that property into the SOAP message when it is null, by doing:
[DataMember(EmitDefaultValue=false)]
Then when that property is null, it wont be included in the serialization. That would make no real difference if the client was generated from the WSDL though, as its User type would still have to contain both properties. It would just change the serialization so that instead of sending the client something like:
<User>
<PropertyVisibleOnlyForService1 nil="true" />
<PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>
it would instead send:
<User>
<PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>