WebApi with EF Code First generates error when hav

2020-02-10 17:28发布

问题:

I am breaking my head over this issue. I did find something on the internet about it, but not a clear answer. My problem:

I have to classes in Model section of an MVC3 web app: ParentClass and ChildClass On ParentClass there is a property Children of type List

I used EF Code First, which neatly generates a parent table and a child table for me in the database.

Now I need a REST service that gives back a List or a single ParentClass.

When I remove the property Children from the ParentClass there is no problem. But with the propoerty Children there I keep getting an error.

Error: "The type System.Data.Entity.DynamicProxies.ParentClass_A0EBE0D1022D01EB84B81873D49DEECC60879FC4152BB115215C3EC16FB8003A was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."}

Some code:

Classes:

     public class ParentClass
{
    public int ID { get; set; }
    public string Name {get;set;}
    public virtual List<ChildrenClass> Children { get; set; }

}

public class ChildrenClass
{
    public int ID { get; set; }
    public string MyProperty { get; set; }
}

Service:

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true)] 
public class MyService
{

    static MyContext db;
    public MyService() { db = new MyContext(); }


    [WebGet(UriTemplate = "")]
    public List<ParentClass> GetParents()
    {
        var result = db.Parents.ToList();
        return result;

    }

I will not get the result when callinh this service. What am I doing wrong?

回答1:

I had to DisableProxyCreation in the context configuration:

[OperationContract] 
[WebGet(UriTemplate = "")] 
public List<ParentClass> GetParents() 
{ 
     using (DBContext context = new DBContext()) 
     {
         context.Configuration.ProxyCreationEnabled = false; 
         List<ParentClass> parents = context.Parents
             .Include("Children") 
             .ToList();
         return parents; 
      }
}

This worked out for me fine.



回答2:

It seems to be serialising the proxy classes for your POCOs, my first recommendation would be to use the proxydatacontractresolver: http://msdn.microsoft.com/en-us/library/system.data.objects.proxydatacontractresolver.aspx.

Also I would work on having things spelt out explicitly when loading data for sending over web service ... i.e.

Change Parent class to

public class ParentClass
{
    public int ID { get; set; }
    public string Name {get;set;}
    public List<ChildrenClass> Children { get; set; }

}

Alter your content to turn off lazy loading: Disable lazy loading by default in Entity Framework 4

And explicitly specify what you want to load when returning data being sent over the wire.

[WebGet(UriTemplate = "")]
public List<ParentClass> GetParents()
{
    var result = db.Parents.Include("Children").ToList();
    return result;

}

Have a look at the following answer: Entity Framework Code First - Eager Loading not working as expected? for more advanced Include calls.

Also a piece of advice from experience, I wouldn't return your data classes over the wire as they form a contract for the consumer of your web service. You are best to have another set of classes that you map your data values into.

That way if your data classes change, you don't have to change the web service client unless explicitly required.

And using paging is important if you are expecting 1000's of rows in the Parent or Child classes otherwise you end up with N+1 select, see : What is SELECT N+1?.



回答3:

In some situations a simple solution is to use a wrapper class so that all the properties exposed are known types.

Usually you won't be using an ObjectContext or a DbContext in your controller classes anyway so in an earlier layer (Business or Service layer) you can do a quick translation from the objects coming from the DB into ViewModel style objects, similar to what you'd do in an MVC app, but instead of passing them on to a View you return them to the caller.

Maybe you can't use it in all cases, but often it's a workable compromise.