locked
Cannot get added (to partial EO class) DataMember property to serialize RRS feed

  • Question

  • Here are the requirements of my problem:

    1. I do not want to create a view in the database
    2. I want to add a property to the EntityObject
    3. I want that property to serialize to Atom or JSON
    4. I DO NOT care if I can't use that property in LINQ to Entities
    5. I DO NOT care if I have to split an EDMX and work the three files myself, I don't need designer support

    Now, let's start with adding a property to the Course object in the School example from the EF QuickStart:

    namespace SchoolModel  
    {  
      public partial class Course  
      {  
        [DataMember]  
        public string Slang  
        {  
          get 
          {  
            if (Title.Length % 2 == 0)  
              return Title.Substring(0, 4) + ", yo";  
            else 
              return Title.Substring(0, 1) + " to the " + Title.Substring(1);  
          }  
     
          set { }  
        }  
      }  

    This will successfully add a new property to the Course class that will return a modified version of whatever's in Title. I can see the values on the server side:

      [WebGet]  
      public IQueryable<SchoolModel.Course> DebugSlang()  
      {  
        foreach (SchoolModel.Course c in CurrentDataSource.Course)  
          Debug.WriteLine(c.Slang);  
     
        return CurrentDataSource.Course;  
      } 

    The above will write:

    Calc, yo  
    C to the hemistry  
    P to the hysics  
    C to the omposition  
    Poet, yo  
    Lite, yo  
    Trig, yo  
    Micr, yo  
    Macr, yo  
    Quan, yo 

    However, the Atom will not have a Slang property:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>  
    <feed xml:base="http://localhost:1584/EFTest/SchoolData.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">  
      <title type="text">DebugSlang</title> 
      <id>http://localhost:1584/EFTest/SchoolData.svc/DebugSlang</id> 
      <updated>2008-12-19T17:05:19Z</updated> 
      <link rel="self" title="DebugSlang" href="DebugSlang" /> 
      <entry> 
        <id>http://localhost:1584/EFTest/SchoolData.svc/Course(1045)</id> 
        <title type="text"></title> 
        <updated>2008-12-19T17:05:19Z</updated> 
        <author> 
          <name /> 
        </author> 
        <link rel="edit" title="Course" href="Course(1045)" /> 
        <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/OnlineCourse" type="application/atom+xml;type=entry" title="OnlineCourse" href="Course(1045)/OnlineCourse" /> 
        <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/OnsiteCourse" type="application/atom+xml;type=entry" title="OnsiteCourse" href="Course(1045)/OnsiteCourse" /> 
        <category term="SchoolModel.Course" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
        <content type="application/xml">  
          <m:properties> 
            <d:CourseID m:type="Edm.Int32">1045</d:CourseID> 
            <d:Title>Calculus</d:Title> 
            <d:Credits m:type="Edm.Int32">4</d:Credits> 
            <d:DepartmentID m:type="Edm.Int32">7</d:DepartmentID> 
          </m:properties> 
        </content> 
      </entry> 
      ...  
    </feed> 

    What do I need to do to add a fake property (i.e., nothing or some placeholder in storage schema) to an EntityObject?

    Friday, December 19, 2008 5:15 PM

Answers

  • hi Ryan,

    I got to break it to you, using the Entity framework and data services will not allow you to do what you want. Both dont allow custom properties and types to be used we can only get away really on the client side where we have control over what get serialized to the server. the classes that would have allowed you to actually do something about this are all internals and none of them, with out the use of reflection, have a mechanism that can be spoilted easily. My suggestion to you is that you look at other alternatives and think outside of the box about what you want to achieve.

    The only way i can think of it is to use a Custom Provider to force Data Services to use the "ReflectionServiceProvider" which doesnt look at any EDM instead uses reflection to read and write the objects. However that can be another marathon.

    I point you to a few threads that high light what EF and Data Services can and can not do and specially a good explanation from Pablo Castro of what Data Services is all about (do read that). 


    Daniel Portella - http://undocnet.blogspot.com - This posting is provided "AS IS" with no warranties, and confers no rights. If this post is answered your question please mark as the answer and if it is helpful do like wise.
    Wednesday, May 20, 2009 9:26 AM

All replies

  •  In V1, we don't have a way to modifying the model when you are working with EF. The only way to do this will be add this property to csdl i.e. change the underlying model.

    Lot of people have asked for this capability and we are thinking about this and hopefully will have something in the future release.

    Thanks
    Pratik
    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, December 19, 2008 5:28 PM
    Moderator
  • Would I just add it as a Property and not map it to anything?

            <EntityType Name="Course">  
              <Key> 
                <PropertyRef Name="CourseID" /> 
              </Key> 
              <Property Name="CourseID" Type="Int32" Nullable="false" /> 
              <Property Name="Title" Type="String" Nullable="false" MaxLength="100" Unicode="true" FixedLength="false" /> 
              <Property Name="Credits" Type="Int32" Nullable="false" /> 
              <Property Name="DepartmentID" Type="Int32" Nullable="false" /> 
              <Property Name="Slang" Type="String" /> 
              <NavigationProperty Name="OnlineCourse" Relationship="SchoolModel.FK_OnlineCourse_Course" FromRole="Course" ToRole="OnlineCourse" /> 
              <NavigationProperty Name="OnsiteCourse" Relationship="SchoolModel.FK_OnsiteCourse_Course" FromRole="Course" ToRole="OnsiteCourse" /> 
            </EntityType> 

    Maybe I can set it in the partial OnTitleChanging method?

    EDIT

    When I try to add the property to either the C-S of an EDMX, or just the CSDL of a broken-out model in an assembly, I get a mapping error saying that the new property has no mapping. It doesn't look like it's a warning that I can just ignore. Is there something I can put in the MSL that just marks it as a dummy property?

    I can run EdmGen, and the error in the console output. I can still compile but when I use the model, I get this error:

    "The Mapping and Metadata information for EntityContainer 'SchoolModel' no longer matches the information used to create the pre generated views."
    Friday, December 19, 2008 5:37 PM
  • I faced a similar problem, where I needed a property on the client that was not in the model.  The reason your partial shows up on the server is that it has access to it.  The dataservice only has the EF model to describe what is available in it.  I solved that particular problem by creating a partial on the client side and then using a [DoNotSerialize] attribute on the property that I added in the partial (nothing to it: just create the attribute in your project: )
    /// <summary>
        /// Properties marked with this Attribute are not serialized in the payload when sent to the server
        /// </summary>
        [AttributeUsage(AttributeTargets.Property)]
        public class DoNotSerializeAttribute : Attribute
        {
        }
    then use the attribute on the property you created in the partial.  The reason for this is that the data services server will bark at you if you send it an extra property when the xml is serialized and sent to it.  To use the attribute, subscribe to the WritingEntity event of your datacontext and in the handler for that event :

            private void DataServiceContext_WritingEntity(object sender, ReadingWritingEntityEventArgs e)
            {
                // e.Data gives you the XElement for the Serialization of the Entity 
                //Using XLinq  , you can  add/Remove properties to the element Payload  
                XName xnEntityProperties = XName.Get("properties", e.Data.GetNamespaceOfPrefix("m").NamespaceName);
                XElement xePayload = null;
                foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
                {
                    object[] doNotSerializeAttributes = property.GetCustomAttributes(typeof(DoNotSerializeAttribute), false);
                    if (doNotSerializeAttributes.Length > 0)
                    {
                        if (xePayload == null)
                        {
                            xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
                        }
                        //The XName of the property we are going to remove from the payload
                        XName xnProperty = XName.Get(property.Name, e.Data.GetNamespaceOfPrefix("d").NamespaceName);
                        //Get the Property of the entity  you don't want sent to the server
                        XElement xeRemoveThisProperty = xePayload.Descendants().Where<XElement>(xe => xe.Name == xnProperty).First<XElement>();
                        //Remove this property from the Payload sent to the server 
                        xeRemoveThisProperty.Remove();
                    }
                }
            }
    This will strip the property you have marked with the DoNotSerialize attribute from the xml before it's sent back to the server.  If I understand correctly, then that should help solve your problem.  I got the above code from a great article: http://blogs.msdn.com/phaniraj/archive/2008/12/11/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx
    • Proposed as answer by Scott Helm Tuesday, May 19, 2009 10:25 PM
    Tuesday, May 19, 2009 10:23 PM
  • Scott Ryan wants to serialize the property
    Daniel Portella - http://undocnet.blogspot.com - This posting is provided "AS IS" with no warranties, and confers no rights. If this post is answered your question please mark as the answer and if it is helpful do like wise.
    Wednesday, May 20, 2009 8:54 AM
  • hi Ryan,

    I got to break it to you, using the Entity framework and data services will not allow you to do what you want. Both dont allow custom properties and types to be used we can only get away really on the client side where we have control over what get serialized to the server. the classes that would have allowed you to actually do something about this are all internals and none of them, with out the use of reflection, have a mechanism that can be spoilted easily. My suggestion to you is that you look at other alternatives and think outside of the box about what you want to achieve.

    The only way i can think of it is to use a Custom Provider to force Data Services to use the "ReflectionServiceProvider" which doesnt look at any EDM instead uses reflection to read and write the objects. However that can be another marathon.

    I point you to a few threads that high light what EF and Data Services can and can not do and specially a good explanation from Pablo Castro of what Data Services is all about (do read that). 


    Daniel Portella - http://undocnet.blogspot.com - This posting is provided "AS IS" with no warranties, and confers no rights. If this post is answered your question please mark as the answer and if it is helpful do like wise.
    Wednesday, May 20, 2009 9:26 AM