views:

325

answers:

2

That title is quite a mouthful. Let me try to be as clear as I can...

I have a WCF REST Service written in .NET 4 that uses the entity framework to pull some data from SQL Server into a list of objects. The objects are then returned as XML to the client. The problem is that the XML have references to each other due to my model's relationships.

Here is some code to help illustrate the problem:

My model: http://bara.stardock.com/images/activity_model.png

The Activities class that handles the service logic:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class Activities : IActivities
{
    public ActivitiesList GetActivities(string titleId, string accountId, string numToReturn)
    {
        stardockActivitiesEntities sdActivitiesDb = new stardockActivitiesEntities();

        int accountIdInt = int.Parse(accountId);

        List<Activity> items = (from a in sdActivitiesDb.Activities
                                join ab in sdActivitiesDb.ActivityBridges
                                    on a.ActivityID equals ab.ActivityID
                                where ab.AccountID == accountIdInt
                                select a).ToList();

        ActivitiesList list = new ActivitiesList(items);

        return list;
    }
}

The interface for the above class:

[ServiceContract]
public interface IActivities
{
    [OperationContract]
    [WebGet(UriTemplate = "{titleId}/accounts/{accountId}/limits/{numToReturn}")]
    ActivitiesList GetActivities(string titleId, string accountId, string numToReturn);
}

The Activity class is auto-generated by the entity framework based on my model for the Activities table. However, I did extend on this class by creating an ActivitiesList object:

[XmlRoot(Namespace = "http://schemas.datacontract.org/2004/07/Stardock.CVP.Stats")]
public partial class Activity
{

}

[XmlRoot(Namespace = "http://schemas.datacontract.org/2004/07/Stardock.CVP.Stats")]
[DataContract(IsReference=false)]
public class ActivitiesList
{
    [DataMember]
    public List<Activity> Activities { get; set; }

    public ActivitiesList()
    {
        Activities = new List<Activity>();
    }

    public ActivitiesList(List<Activity> list)
    {
        Activities = new List<Activity>();

        foreach (Activity item in list)
        {
            Activities.Add(item);
        }
    }

    public void Add(Activity a)
    {
        Activities.Add(a);
    }
}

So to explain my problem again, my XML, rather than simply returning a list of Activity like it should, instead returns a list of Activity with some activities referencing other activities that are within the base activities. That sounds confusing, but look at the images below:

Returned XML: http://bara.stardock.com/images/activity_xml1.png

The activity with a reference of "i8" is referring to another activity that is actually inside the activity with the id of "i2": http://bara.stardock.com/images/activity_xml2.png

My question is, how can I remove all of those extra relationships from the Activity object? I'd prefer it to just be a list of Activity without the nested ActivityType, EntityKey, etc. that are auto-generated by the entity framework.

I hope I've sufficiently explained myself. And if not, let me know what other details you'd like to see and I'll provide them.

Bara

A: 

Hi, I guess if you use EF(4) you can remove the associations in the model (e.g. in the designer) that you don't need/want. That way, the code generator doesn't generate properties to navigate over the relations.

Edit: By associations I mean the real "associations" (lines in the diagram) and Navigation properties (on the bottom of the entities).

larsw
I thought about that... but then that would remove the usefulness of having those associations to begin with. Thank you for the suggestion though :)
Bara
A: 

There are two options here, however both of them require a bit of work.

My recommendation would be to create wrapper classes which expose the appropriate data and return them instead.

return new PersonWrapper() {Id = Person.Id, Name = Person.Name};

PersonWrapper only needs automatic properties, and you can control exacty what data you return, and how it is returned using the relevant properties.

The alternative suggestion comes from this walkthrough: http://blogs.msdn.com/b/endpoint/archive/2010/01/07/getting-started-with-wcf-webhttp-services-in-net-4.aspx

This suggests using POCO classes (which again you would have to manually create) instead of relying on the entity framework code generation, which map, by convention (in most cases) to the entities in your conceptual model (more information here: http://blogs.msdn.com/b/adonet/archive/2009/05/21/poco-in-the-entity-framework-part-1-the-experience.aspx)

The second option requires that you turn off code generation, so if you only plan on exposing a few classes, it would probably be a pain to have to create the POCO classes manually for every entity, which is why I would recommand creating wrapper classes only for the objects which need to be exposed

Martin

Martin Booth
A co-worker wrote some code generation tools that created the objects for me using the first method. Seems to be working well so far, but this is the closest I got to an answer, so it'll have to do.
Bara