I have a Server/Client project with WCF communications and sharing a dll between both with Contracts and objects:
Service | Shared Objects | Client
For my Objects I added attributes for use in the propertygrid :
[DataMember]
[DisplayName("Javascript File Name")]
[Description("The browseable path of the Javascript file.")]
[Browsable(true)]
[Editor("BaseNS.NS.NS2.ObjectEditor", typeof(System.Drawing.Design.UITypeEditor))]
public String JavaScriptFileName { get; set; }
I used the (String,Type) constructor of the Editor attribute because the editor will be stored at the client and I dont want to embed it in the shared DLL
Should this work cause I cant get it to ?
I don't think it's a good idea to add UI code of any form to a data contract class. You are creating a dependency in your model with the UI.
I I would recommend you to use a simple view model class to expose the relevant properties from your model to the view and do the mapping manually (if you need, you can use an existing mapping service to avoid doing it manually).
Related
I have some POCO objects that are set up for use with Entity Framework Code First.
I want to return one of those objects from an ApiController in my ASP.NET MVC 4 website, and then consume it in a client application.
I originally had problems with the serialization of the object at the server end, because the Entity Framework was getting in the way (see Can an ApiController return an object with a collection of other objects?), and it was trying to serialize the EF proxy objects rather than the plain POCO objects. So, I turned off proxy generation in my DbContext to avoid this - and now my serialized objects look OK (to my eye).
The objects in question are "tags" - here's my POCO class:
public class Tag
{
public int Id { get; set; }
public int ClientId { get; set; }
public virtual Client Client { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsActive { get; set; }
}
Pretty standard stuff, but note the ClientId and Client members. Those are EF Code First "navigation" properties. (Every tag belongs to exactly one client).
Here's what I get from my ApiController:
<Tag xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Foo">
<Client i:nil="true"/>
<ClientId>1</ClientId>
<Id>1</Id>
<IsActive>true</IsActive>
<Name>Example</Name>
</Tag>
The Client member is nil because having disabled proxy generation I don't get automatic loading of the referenced objects. Which is fine, in this case - I don't need that data at the client end.
So now I'm trying to de-serialize those objects at the client end. I had hoped that I would be able to re-use the same POCO classes in the client application, rather than create new classes. DRY and all that. So, I'm trying:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Tag));
var tag = xmlSerializer.Deserialize(stream);
But I've run into two problems, both of which are due to EF Code First conventions:
Problem 1: Because my Tag class has a Client member, the XmlSerializer is complaining that it doesn't know how to de-serialize that. I guess that's fair enough (though I had hoped that because the member was Nil in the XML it wouldn't care). I could pass in extra types in the XmlSerializer constructor, when I tried that, it then complained about other classes that Client uses. Since Client references all sorts of other objects, I'd end up having to pass in them all!
I tried using the [DataContract] and [DataMember] attributes to remove the Client member from the XML (by not marking it as a DataMember). That did remove it from the XML, but didn't stop the XmlSerializer from whining about it. So I guess it's not the fact that it's in the XML that's the problem, but that it's in the class definition.
Problem 2: When I did try passing in typeof(Client) as an extra type, it also complained that it couldn't de-serialize that class because it contains an interface member. That's because - again due to EF Code First conventions - it has a Tags member as follows:
`public virtual ICollection<Tag> Tags { get; set; }`
So it looks like even if I get over the referenced-types problem, I'm still not going to be able to use my POCO classes.
Is there a solution to this, or do I have to create new DTO classes purely for use at the client side, and return those from my ApiController?
I just tried using DataContractSerializer instead of XmlSerializer, and for the Tag class that seems to work. I've yet to try it with a class that has a virtual ICollection<T> member...
Update: tried it, and it "works". It still manages to reconstruct the object, and leaves the ICollection member at null.
Update 2: OK, that turned out to be a dead end. Yes, it meant that I could correctly serialize and de-serialize the classes, but as everyone kept telling me, DTO classes were a better way to go. (DTO = Data Transfer Objects - classes created specifically for transferring the data across the wire, probably with a subset of the fields of the original).
I'm now using AutoMapper (thanks Cuong Le) so that I can easily transform my POCO entities into simpler DTO classes for serialization, and that's what I'd recommend to anyone faced with the same problem.
I have defined the following type in a class library project.
[CollectionDataContract()]
public class OperationException:System.Collections.Generic.Dictionary<string,ExceptionData>
{
[DataMember()]
public bool ExceptionExists { get; set; }
}
[DataContract()]
public class ExceptionData {[DataMember()] public string Msg;}
On my WCF service end, I am returning an object which contains the above class as a child member variable like this.
[DataContract()]
public class SaveClient
{
[DataMember()]
public string Id;
[DataMember()]
public OperationException ExceptionCollection;
}
I have the OperationException class library referenced on the client side. The problem is when I generate the proxy using Add Service Reference, a new definition of OperationException of type dictionary is generated. I do have the Reuse Types option set to true. I like to have Actual 'OperationException' type being used since I have to pass this object to other methods.
Thanks in Advance..!
Iftikhar.
I had the same issue and like you I had applied the CollectionDataContract attribute and told the proxy generator to reuse types from my shared assembly.
The fix was not obvious, you need to supply a hook in the Reference.svcmap file on your client to tell the generator to use your custom collection type.
In Reference.svcmap edit the CollectionMappings element as follows and then update the service reference:
<CollectionMappings>
<CollectionMapping TypeName="YourSharedAssemblyNamespace.OperationException" Category="List" />
</CollectionMappings>
I think the same objective can be achieved if you are using svcutil from the command line by supplying the collection type argument.
/collectionType:YourSharedAssemblyNamespace.OperationException
See these posts for more info:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/09eefbbc-bf63-4aa3-a0cb-01a9dbd7f496/
http://www.codeproject.com/KB/WCF/WCFCollectionTypeSharing.aspx
I am not sure why the WCF proxy generator doesn't just use it's common sense to find the shared collection types but there you go, chalk it up as another funny from the WCF tool design.
Does your client proxy assembly have a project reference to the class library where the type is added?
If the proxies generated by svcutil are not what you want, it's also very easy to write them by hand. Just create your own ClientBase-derived class and implement your service interface on it. Then you have control over which assembly types you want to reuse.
We have a 3rd party dll wich contains (among other things) our entities.
The entites are all marked with the [Serializeable] attribute.
We now need to creat a new WCF services which will expose some of this entities.
The problem is, since the entites are not declared with the DataContract and DataMember attributes, the property names are appended with __BackingField!
I know using the DataContarct\Member attributes will solve this issue, but given that I cannot modify the 3rd party dll with the entities, is there a different workaround?
Types decorated with the [Serializable] attribute have their fields serialized, not properties (that's the [Serializable] "contract"). If the 3rd party types use automatic properties (as shown below), the compiler will create a field with the k_BackingField suffix, and this is what will be serialized.
If you cannot change the types in the 3rd party library, one alternative would be to use that same library on the client. When creating the proxy for the service (using either svcutil or Add Service Reference), you can reference the 3rd party library, and the generated client won't create new types for the contracts, instead reusing the types from the library. This way you won't have to deal with types with public _BackingField property names.
Automatic properties:
[Serializable]
public class MyType
{
public string MyProp { get; set; }
}
The compiler will turn it into something similar to
[Serializable]
public class MyType
{
private string <MyProp>k_BackingField;
public string MyProp
{
[CompilerGenerated]
get { return this.<MyProp>k_BackingField; }
[CompilerGenerated]
set { this.<MyProp>k_BackingField = value; }
}
}
You can use the XmlSerializerFormatAttribute to use XmlSerializer instead of DataContractSerializer in the service implementation.
It will perform slower but it should sovle your problem.
I am assuming you want to expose these third party types from a service.
One solution which you may consider is to maintain a separate library which mirrors the types in the third party library.
This has the following benefits:
Ownership - You own the types you are exposing therefore you control the serialization/deserialization across your service boundary.
You are insulated from sudden changes to the other party's types and can change your interfaces in a controlled fashion.
From a SOA perspective if you are exposing another party's types on your service the other party should supply the types in a contractural format like XSD. I think your design calls for some fairly unreasonable hoop-jumping on your part.
It may be more work up front but it is kind of a one-off exercise.
Hope this helps some.
I am trying to seperate DataAnnotations from our POCOs within a Silverlight project. One way to do this outside of SL is to use buddy classes e.g:
[MetadataTypeAttribute(typeof(MyPOCO.POCOMetaData))]
public partial class MyPOCO
{
internal sealed class POCOMetaData
{
[Required(ErrorMessage="Requires name.")]
public string Name { get; set; }
[Required(ErrorMessage = "Requires age.")]
public string Age { get; set; }
}
}
However as of Silverlight 4, the MetadataType attribute does not exist within System.ComponentModel.DataAnnotations namespace. Has anyone found an alternative way to seperate DataAnnotation attributes from POCOs? I am looking into this as I was planning on using T4 templates to generate our basic POCO classes.
This scenario looks somehow strange, why wouldn't you want to decorate your POCOs themselves?
1 - If there is a matching server, consider using WCF RIA Services and declare your POCOs at the server side, the RIA engine will then generate for you all the proxies at the client side, including all its annotations, and many other goodies.
2 - If there is no matching server and you want to manipulate the data on client side, then I would go for WPF and have a wider range of desktop development capabilities.
3 - Unfortunately, TypeDescriptor is also not implemented in Silverlight, so you can't even add the attributes dynamically at runtime (in case you would want go that dirty-handed).
So I'm affraid your chances are:
Attach the MD to partial classes
Decorate your POCOs
Use WCF-RIA and declare the POCOs at the server-side
Go WPF
Please take a look on PEM. the EDMX designer can be extended, maybe the class designer can be extended too (oops, unfortunately seems it isn't).
Suggest to the SL team for this feature to be implemented.
Im am not new to WCF web services but there has been a couple of years since the last time I used one. I am certain that last time I used a WCF service you could determine the type of object returned from a service call when developing the code. EG;
MyService.Models.ServiceSideObjects.User user = myServiceClient.GetUser();
You were then free to use the 'user' object client-side. However now it seems as if the WCF service will not return anything more than objects containing basic value types (string, int ect). So far I have remedied this by defining transfer objects which contain only these basic value types and having the service map the complex 'User' objects properties to simple strings and int's in the transfer object.
This becomes a real pain when, for example you have custom type objects containing more complex objects such as my Ticket object.
public class Ticket
{
public Agent TicketAgent {get;set;}
public Client Owner {get;set;}
public PendingReason TicketPendingReason {get;set;}
}
As simply mapping this object graph to a single transfer class with a huge list of inter-related system-typed properties gives a very 'dirty' client-side business model. Am I wrong in thinking that I SHOULD be able to just receive my Ticket object from a service method call and deal with it client side in the same state it was server-side ?
I realise this is probably a violation of some SoA principal or similar but my desktop app currently consuming this service is the ONLY thing that will consume ever consume it. So i do not care if many other clients will be able to manage the data types coming back from the service and therefore require some hugely normalised return object. I just want my service to get an object of type Ticket from its repository, return this object to the client with all its properties intact. Currently all I get is an object with a single property 'ExtentionData' which is unusable client-side.
Hope this makes sense, thank you for your time.
I might've missed a memo, but I think you need to decorate your model classes with DataContractAttribute and your properties with DataMemberAttribute, like so:
[DataContract( Namespace = "http://example.com" )]
public class Ticket
{
[DataMember]
public Agent TicketAgent { get; set; }
[DataMember]
public Client Owner { get; set; }
[DataMember]
public PendingReason TicketPendingReason { get; set; }
}
This is why you probably want to set up a DTO layer, to avoid polluting your model classes.
As for ExtensionData, it's used for forward-compatibility: http://msdn.microsoft.com/en-us/library/ms731083.aspx
I have marked Niklas's response as an answer as it has solved my issue.
While it seems you do not NEED to use [DataContract] and [DataMember], in some cases, I believe it could cause the issues I was experiencing. When simply transferring custom typed objects which, in themselves, only have simply typed properties, no attributes needed. However, when I attempted to transfer a custom typed object which itself had collections / fields of more custom typed objects there attributes were needed.
Thank you for your time.