views:

192

answers:

3

Say you have a one to one relationship in your entity model. The code generator will decorate it with the following attributes:

[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public RelatedObject Relationship { get {...} set {...} }

I want to serialize my parent object together with all its properties for which data has been loaded through an XML web service. Obviously, these related properties do not get serialized because of these attributes.

So for my purposes I just want to remove these "don't serialize me" attributes. I can do a find and replace in the designer code, but any modifications I make in the designer will put these attributes back in.

In my query I'm .Include()ing and explicitly loading only the child objects that I require for serialization. So I will make sure there are no circularities in my query. Some of the child properties are not required, so I won't Include() them, so they won't be serialized.

Else how do I achieve what I want to do? Make a separate call from my application for each child object? Say I'm returning hundreds of parent objects; I'd have to make hundreds of separate calls to get each child too.

How do I permanently get rid of these attributes?

VS 2008 / EF 3.5.

+4  A: 

Just don't do it. It's that simple.

You state on your post that you want to serialize the parent of your object, right?

Now let's see what happens when you do something like that...

  1. The serializer starts converting your object and it's properties
  2. When it finds the parent of your object, it starts serializing it
  3. While serializing the parent if finds the child object that it was serializing and goes back to 1.

And it will never get out, without some encouragement.

So those attributes are there for a good reason.

Paulo Santos
Nope - I'm serializing the parent object, which is working fine, but its children aren't being serialized. I just added some more info to the original question to indicate how in my query I am only Including()ing those properties that should be serialized, so I will guarantee that there are no circularities.
Mikey Cee
+2  A: 

Paulo is right (+1) but he didn't tell you how to fix the problem. Because XML serialization is a legitimate use case, even if serializing entities is the wrong way to do it.

Project onto anonymous types, and serialize that. E.g., to serialize to JSON, I do:

var q = from e in Context.MyEntities
        where // ...
        select new
        {
            Id = e.Id,
            Children = from c in e.Children
                       select new 
                       {
                           Id = c.Id,
                           // etc.
                       },
            // etc.
        };
return Json(q);

This guarantees no circular references, etc., and it works.

Craig Stuntz
Thanks for this Craig.I can't believe this is the only way to do it though. What we have gained with the EF in speed from having the data layer generated automatically, we have now lost in having to manually create these temporary objects for serialization. I must be missing something here, but it seems like we've taken one step forward then one step back again....
Mikey Cee
...The nicest thing from before when I rolled my own data layer was that the serialization/deserialization was completely abstracted by the web service proxy class. My service just returned normal objects and didn't have to worry about the serialization, and my application just received normal .NET objects and didn't have to worry about the deserialization. Now both ends have to take care of the serialization manually?Isn't this a huge oversight on the design of the EF?
Mikey Cee
The limitation is not with the EF. It's the XML Serializer which can't handle circular references, not the EF. You are free to write/find your own serializer if you don't like the limitations of the one you're using. Also, using the EF does not mean you are required to return entity types; you can project onto POCOs instead.
Craig Stuntz
OK thanks Craig. This is all very useful information.
Mikey Cee
+2  A: 

Hello Mike,

Here is a little known fact... Entity Framework + Web Services = :'(

There are three(3) approaches that can be taken to solve your problem (namely the XML graph serialization problem... or the lack of it).

I will list each approach in order of least development time and complexity of implementation required ["Bang-For-Buck"] vs. scalability, maintainability and performance ["Future Proofing"].

  1. Create POCO classes for each Entity for projection when sending over the wire. This is the easiest (and monotonous) approach but will solve your problem. I have included a sample at the end.

  2. Use WCF to relay your data. Entity Framework and WCF are like 'brothers from a different mother'. They were designed to work together but share their differences. You will notice that all EF generated Entity Objects are inherently [DataConctract] with all fields being [DataMember]. This makes use of the WCF DataContract Serializer with handles graphs very efficiently and maintains object reference even after deserialization. WCF DataContract Serializer is also proven to be 10% quicker than your default XML Serializer.

  3. Use EF 4.0 Self Tracking Entities (STE). This is still very new but it is workable. In Visual Studio 2010 you are given an option to generate Self Tracking Entities which are designed for N-Tier and SOA. The best thing about STE is the usage of T4 Transformed Text templates to generate the code. The classes generated by T4 is clean and very malleable, giving you plenty of room to insert your logic and validation. Here is a link to STE tutorial to get your started.

Best of luck and I hope you find the best approach for you.

POCO example.

public class CustomerPOCO
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<SubLocationPOCO> SubLocations { get; set; }
    // ...

    #region Ctors

    public CustomerPOCO() { }

    public CustomerPOCO(Customer customer)
    {
        // Inits
        if (customer.SubLocations != null)
            SubLocations = customer.SubLocations.Select(sl => new SubLocationPOCO(sl)).ToList();
    }

    #endregion

}


public class SubLocationPOCO
{
    public int ID { get; set; }
    public string Name { get; set; }

    #region Ctors

    public SubLocationPOCO() { }

    public SubLocationPOCO(SubLocation subLocation)
    {
        // Inits
    }

    #endregion

}

And your [WebMethod] is something like this.

[WebMethod]
public CustomerPOCO GetCustomerByID(int customerID)
{
    using (var context = new CustomerContext())
    {
        var customer = (from customer in context.Customers.Include("SubLocations")
                        where customer.ID == customerID
                        select new CustomerPOCO(customer)).FirstOrDefault();

        return customer;
    }
}
Tri Q
Great answer Tri, thanks!
Mikey Cee