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.
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.
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 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).
I have a Silverlight application with a Silverlight-enabled WCF service. The service passes along a small POCO class with a few string properties, and a List<> of an enum defined in the class. Everything works fine when running using the ASP.NET development server, but when I move the service over to an IIS server (Windows 2003) I get the following error when I try to browse the .svc file:
Type 'MyProject.Web.MyClass' cannot be
serialized. Consider marking it with
the DataContractAttribute attribute,
and marking all of its members you
want serialized with the
DataMemberAttribute attribute.
Even though it's working development side, I've tried adding the decorations... but so far without effect.
Any ideas as to what might be causing this difference of outcomes between development workstation and server?
Make sure that (1) the .NET Framework 3.5 SP1 is installed on the server and (2) that the website is running in ASP.Net 2.0 mode and not 1.1 mode.
The Web Platform Installer is an easy way to install the updated framework if it isn't already installed.
Your data elements (POCO classes) ought to be marked as DataContracts for WCF, so that WCF knows explicitly what it'll need to serialize to send across the wire.
Contrary to the Xml Serializer, the DataContractSerializer in WCF uses an "opt-in" model - only things that you explicitly mark as [DataContract] and [DataMember] will be serialized - anything else will be ignored.
[DataContract]
class YourPocoClass
{
[DataMember]
private int _ID;
[DataMember]
string CustomerName { get; set; }
public decimal CustomerOrderAmount { get; set; }
}
In this example, from YourPocoClass, you'll have both the _ID field and the CustomerName property be serialized into the WCF message - but the CustomerOrderAmount will not be serialized - public or not.
So, best practice is: explicitly mark all your complex types that you need to send around in WCF with [DataContract] (for the class) and [DataMember] (for each member to be sent across inside that class).
Marc
My application is SL2 reading and writing data through an Entity Framework Model exposed via WCF. We have resisted writing any UI validation due to the exicting new validation controls coming from SL3.
...However after doing a trial update on our project yesterday, we realised that most of the standard practices for attaching validation properties to business objects can't readily be applied when the objects are created from the EF model.
Has anyone had any similiar experiences yet, if so how did you work around this?
Thanks,
Mark
You are correct, you have 2 options.
In your model, or viewmodel, depending on your implementation of MVVM, in the setters for your properties, do some validation there, and throw an exception if there is a problem, then use SL3 ValidatesOnException property in your databinding on the view for each control being validated.
use MetaDataClasses to provide addon functionality to ur existing domain model
[MetadataClass(typeof(MyMetadataClass))]
public partial class MyClass
{
public int MyProperty { get; set; }
}
public class MyMetadataClass
{
[Range(1,100)]
public int MyProperty{ get; set; }
}