SySc0d3r
SySc0d3r

Reputation: 662

WebAPI response causes "System.OutOfMemoryException"

I developed a WebAPI service which returns in its response an array of complex custom objects. Some of their fields have many-to-many relations. For example:

[Table("OEReferences", Schema = "public")]
public class OEReference
{

    [NotMapped]
    public IList<IAReference> IAReferences{ get; set; }

}

[Table("IAReferences", Schema = "public")]
public class IAReference
{

    [NotMapped]
    public IList<OEReference> OEReferences{ get; set; }

}

Each OEReference object has a list of IAReferences, which at the same time each IAReference object has a list of OEReference (including the first one) with its corresponding list of IAReferences, etc. And it never stops.

Sometimes I'm getting an out of memory exception. Is this the reason? How can I prevent it?

It's breaking with just ~50 OEReference containing each of them a couple of IAReferences. The rest of the fields are quite simple. I use HttpClient class in .Net 4.7.1.

Upvotes: 1

Views: 1276

Answers (1)

Peter B
Peter B

Reputation: 24147

It looks like you are returning Entity Framework objects directly from the Web API (through its built-in JSON Serializer). The general rule is to NEVER do that, because the JSON Serializer will try to serialize everything it can find. But with EF Objects that have relations such as yours, it won't know where to stop, leading to Reference Loop errors or even Out of Memory errors.

Instead you need to take exactly from the EF objects what you need, or more precisely: what your API caller needs.

The solution is to create Objects/ViewModels that copy the parts of the EF Objects that the caller needs, fill those from the EF Objects, and then return them.

A quick-and-dirty way is to use anonymous objects, for example:

// Instead of "return EF_Item":
return new
{
    Item = new
    {
        Id = EF_Item.Id,
        Description = EF_Item.Description,
        Things = MapThings(EF_Item.Things) // helper function that you need to provide
    }
};

A good rule-of-thumb is to only assign simple properties (number, bool, string, datetime) from the EF Objects to the ViewModel items. As soon as you encounter an EF Object property that is yet another EF Object (or a collection of EF Objects), then you need to translate those as well to 'simple' objects that are not linked to EF.

A more structured approach is by using ViewModel classes, and you can make that even more structured by using a tool such as AutoMapper that (once configured) will do the mapping for you whenever and wherever you need it.

Upvotes: 2

Related Questions