5
votes

I expose a complex type through OData. The class is like this:

public class RemoteFile 
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Resource { get; set; }

    public virtual ICollection<RemoteFile> RelatedFiles { get; set; }
}

And I expose it through OData:

    var modelBuilder = new ODataConventionModelBuilder();
    modelBuilder.ComplexType<RemoteFile>();

Here is what I got when I start the project:

An exception of type 'System.ArgumentException' occurred in System.Web.Http.OData.dll but was not handled in user code

Additional information: The complex type 'RemoteFile' has a reference to itself through the property 'RelatedFiles'. A recursive loop of complex types is not allowed.

If there is a handler for this exception, the program may be safely continued.

Any suggestion is welcomed.

4

4 Answers

3
votes

It sounds like it makes more sense for RemoteFile to be an entity type, not a complex type. Entity types can have properties that point to the originating type, which is how you've set up RemoteFile. Your definition of the type also has a key property, which is used for entity types, not complex types. (Think of complex types as a convenient way to group a bunch of scalar properties. Entity types are the first-class types of your system where each instance can be uniquely identified.)

So instead of this:

modelBuilder.ComplexType<RemoteFile>();

Try this:

modelBuilder.EntitySet<RemoteFile>(“RemoteFiles”);

That line will create both the entity type RemoteFile and the entity set RemoteFiles. An entity set is the container for all the instances of an entity type.

So why is recursion allowed for entity types but not complex types? When you ask for an entity, by default the server won't fetch the data of referenced entities. You can explicitly ask for the data of a referenced entity by using $expand in the query, but you can't expand infinitely. On the other hand, complex values will always be included when you ask for their parent. So if you have a circular complex value, you'll create a stack overflow when you try to serialize it.

2
votes

Do you need to explicitly ignore the navigation property by any chance?

modelBuilder.ComplexType<RemoteFile>().Ignore(x => x.RemoteFile);

Hope that helps :)

2
votes

I had the same problem. I had a model with more than 100 entities, and I tried to add only two, for make tests.

The solution: ADD ALL ENTITIES to ODataConventionModelBuilder, something like this:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Entity1>("Entity1");
builder.EntitySet<Entity2>("Entity2");
builder.EntitySet<Entity3>("Entity3"); 

//... and thus for ALL YOUR ENTITIES.

// If you don't want to expose any entity like EntitySet, simply add to builder like EntityType:
builder.EntityType<Entity4>("Entity4");

Even if you do not add entities, the builder scans all types like Complex Types, and relationships fail. Therefore it is necessary to specify that all scanned types are Entities.

If you don't want to expose all like EntitySet, you can add to builder like EntityType, and your client reference will use this class but not will give you access to a EntitySet (CRUD Operations). This Entities only can be used indirectly through relationships of exposed entities.

0
votes

The error message "The complex type 'RemoteFile' has a reference to itself through the property 'RelatedFiles'. A recursive loop of complex types is not allowed." is due to a limitation of the Web API OData library, specifically the inner workings of the ODataConventionModelBuilder class. This is a blocking issue for many people, tracked here on GitHub.