Roberto Luis Bisbé
Roberto Luis Bisbé

Reputation: 2090

WCF with Entity Framework Code First relationship

I'm learning WCF, and tried to make a small service that exposes a Project and its tasks (the standard Entity Framework hello world).

The class structure is the following:

public class Project
{
    public int ProjectId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public DateTime CreationDate { get; set; }
    public virtual ICollection<Task> Tasks { get; set; }
}

public class Task
{
    public int TaskId { get; set; }
    public string Title { get; set; }
    public virtual Project RelatedProject { get; set; }
}

The DB context comes after:

public class ProjectContext : DbContext
{
    public DbSet<Project> Projects { get; set; }
    public DbSet<Task> Tasks { get; set; }
}

Finally, the service endpoint:

    public IEnumerable<Project> getProjects()
    {
        ProjectContext p = new ProjectContext();
        return p.Projects.AsEnumerable();
    }

The problem is that this model will throw a System.ServiceModel.CommunicationException, but, If I remove the virtual properties from the model, It would work, but I would loose the entity framework links between Project and Task.

Anyone with a similar setup?

Upvotes: 1

Views: 1264

Answers (2)

fistix
fistix

Reputation: 83

I banged my head against the wall several hours with this one. After extensive debugging, google gave the answer and I feel right to post it here since this was the first result I got in google.

Add this class on top of your [ServiceContract] interface declaration (typically IProjectService.cs

public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {

    }

    public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
    {
        var dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver =
            new ProxyDataContractResolver();
    }

    public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
    {
        var dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver =
            new ProxyDataContractResolver();
    }

    public void Validate(OperationDescription description)
    {
        // Do validation.
    }
}

Requirements are

using System.ServiceModel.Description;
using System.Data.Objects;
using System.ServiceModel.Channels;

Then under the [OperationContract] keyword add [ApplyDataContractResolver] keyword and you are set!

Big thanks to http://blog.rsuter.com/?p=286

Upvotes: 2

Wouter de Kort
Wouter de Kort

Reputation: 39898

For sending data trough WCF you should disable lazy loading (dataContext.ContextOptions.LazyLoadingEnabled = false;).

To be sure the data you want is loaded you need to use eager loading ( trough the Include method).

You need to change your function to:

 public IEnumerable<Project> getProjects()
    {
        ProjectContext p = new ProjectContext();
        p.ContextOptions.LazyLoadingEnabled = false;

        return p.Projects.Include("Tasks").AsEnumerable();
    }

Upvotes: 0

Related Questions