3

I have the following code:

public class AwardTitle
{
    public int AwardTitleId
    {
        get;
        set;
    }

    public int? EpisodeId
    {
        get;
        set;
    }

    public virtual AwardEpisode Episode
    {
        get;
        set;
    }
}

public class AwardEpisode
{
    public int EpisodeId
    {
        get;
        set;
    }
}

public static class WebApiConfig
{
    public static void Register( HttpConfiguration config )
    {
        config.Routes.MapODataRoute( "ODataRoute", "api", GetImplicitEDM( ) );
    }
}

private static Microsoft.Data.Edm.IEdmModel GetImplicitEDM( )
{
    var builder = new ODataConventionModelBuilder( );
    builder.EntitySet<AwardTitle>( "AwardTitles" );

    return builder.GetEdmModel( );
}

Notice how I have only mapped the AwardTitle class... not the AwardEpisode class.

Now, when I browse to the controller, I would expect to get an error about not having AwardEpisode mapped. However, there is no error. In fact, in addition to AwardTitle being retrieved... AwardEpisode is also being retrieved... without any explicit calls to do so.

How is this possible??? Should this be possible??

I'm using ASP.Net Web API 2 on Windows 7.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Chris
  • 1,690
  • 2
  • 17
  • 24
  • I bet the edm generator uses reflection to reclusively create all dependent classes. Perhaps there is a setting to tell the template generator to ignore a class. – Ross Bush Nov 06 '14 at 21:43
  • It is by design this way. – vittore Nov 06 '14 at 21:48
  • vitore, I don't think it's by design. Every other time I implement the controller, the dependent classes are not included unless I specifically ask for them using $expand – Chris Nov 07 '14 at 00:43

1 Answers1

1

ODataConventionModelBuilder by default map a type T's primitive, complex, and navigation properties for the .EntitySet<TEntityType>(string name) api call. The code is:

private void MapEntityType(EntityTypeConfiguration entity)
{
    IEnumerable<PropertyInfo> properties = ConventionsHelpers.GetProperties(entity, includeReadOnly: _isQueryCompositionMode);
    foreach (PropertyInfo property in properties)
    {
        bool isCollection;
        StructuralTypeConfiguration mappedType;

        PropertyKind propertyKind = GetPropertyType(property, out isCollection, out mappedType);

        if (propertyKind == PropertyKind.Primitive || propertyKind == PropertyKind.Complex)
        {
            MapStructuralProperty(entity, property, propertyKind, isCollection);
        }
        else
        {
            // don't add this property if the user has already added it.
            if (!entity.NavigationProperties.Where(p => p.Name == property.Name).Any())
            {
                NavigationPropertyConfiguration addedNavigationProperty;
                if (!isCollection)
                {
                    addedNavigationProperty = entity.AddNavigationProperty(property, EdmMultiplicity.ZeroOrOne);
                }
                else
                {
                    addedNavigationProperty = entity.AddNavigationProperty(property, EdmMultiplicity.Many);
                }

                addedNavigationProperty.AddedExplicitly = false;
            }
        }
    }

An example of the test case:

public void ModelBuilder_Products()
{
    var modelBuilder = new ODataConventionModelBuilder();
    modelBuilder.EntitySet<Product>("Products");

    var model = modelBuilder.GetEdmModel();

    var product = model.AssertHasEntitySet(entitySetName: "Products", mappedEntityClrType: typeof(Product));
    product.AssertHasPrimitiveProperty(model, "ReleaseDate", EdmPrimitiveTypeKind.DateTime, isNullable: true);
    product.AssertHasComplexProperty(model, "Version", typeof(ProductVersion), isNullable: true);
    product.AssertHasNavigationProperty(model, "Category", typeof(Category), isNullable: true, multiplicity: EdmMultiplicity.ZeroOrOne);

To change the default behavior, we can ignore the property:

var builder = new ODataConventionModelBuilder();
builder.Entity<AwardTitle>().Ignore(a => a.Episode);
builder.EntitySet<AwardTitle>("AwardTitles");

return builder.GetEdmModel();
Congyong Su
  • 328
  • 2
  • 3