c# - OData 和 WebAPI : Navigation property not present on model

标签 c# entity-framework asp.net-web-api odata navigation-properties

我正在尝试使用 Entity Framework、WebAPI、OData 和 Angular 客户端组合一个简单的玩具项目。一切正常,除了我在其中一个模型上放置的导航属性似乎不起作用。当我使用 $expand 调用我的 API 时,返回的实体没有它们的导航属性。

我的类是 Dog 和 Owner,看起来像这样:

    public class Dog
{
    // Properties
    [Key]
    public Guid Id { get; set; }
    public String Name { get; set; }
    [Required]
    public DogBreed Breed { get; set; }
    public int Age { get; set; }
    public int Weight { get; set; }


    // Foreign Keys
    [ForeignKey("Owner")]
    public Guid OwnerId { get; set; }

    // Navigation
    public virtual Owner Owner { get; set; }
}

    public class Owner
{
    // Properties
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public DateTime SignupDate { get; set; }

    // Navigation
    public virtual ICollection<Dog> Dogs { get; set; } 
}

我还设置了 Dog Controller 来处理查询:

public class DogsController : ODataController
{
    DogHotelAPIContext db = new DogHotelAPIContext();
    #region Public methods 

    [Queryable(AllowedQueryOptions = System.Web.Http.OData.Query.AllowedQueryOptions.All)]
    public IQueryable<Dog> Get()
    {
        var result =  db.Dogs.AsQueryable();
        return result;
    }

    [Queryable(AllowedQueryOptions = System.Web.Http.OData.Query.AllowedQueryOptions.All)]
    public SingleResult<Dog> Get([FromODataUri] Guid key)
    {
        IQueryable<Dog> result = db.Dogs.Where(d => d.Id == key).AsQueryable().Include("Owner");
        return SingleResult.Create(result);
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }

}

我在数据库中植入了一些样本数据。所有的狗记录都有一个 OwnerId,它与 Owners 表中所有者的 ID 相匹配。

使用这个查询狗的列表工作正常:

http://localhost:49382/odata/Dogs

我得到了 Dog 实体的列表,没有 Owner 导航属性。

使用 OData $expand 查询狗及其主人不起作用:

http://localhost:49382/odata/Dogs?$expand=Owner

我的响应是包含所有 Dog 实体的 200,但它们在 JSON 中都没有 Owner 属性。

如果我查询我的元数据,我发现 OData 似乎确实知道它:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="DogHotelAPI.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Dog">
        <Key>
          <PropertyRef Name="id" />
        </Key>
        <Property Name="id" Type="Edm.Guid" Nullable="false" />
        <Property Name="name" Type="Edm.String" />
        <Property Name="breed" Type="DogHotelAPI.Models.Enums.DogBreed" Nullable="false" />
        <Property Name="age" Type="Edm.Int32" Nullable="false" />
        <Property Name="weight" Type="Edm.Int32" Nullable="false" />
        <Property Name="ownerId" Type="Edm.Guid" />
        <NavigationProperty Name="owner" Type="DogHotelAPI.Models.Owner">
          <ReferentialConstraint Property="ownerId" ReferencedProperty="id" />
        </NavigationProperty>
      </EntityType>
      <EntityType Name="Owner">
        <Key>
          <PropertyRef Name="id" />
        </Key>
        <Property Name="id" Type="Edm.Guid" Nullable="false" />
        <Property Name="name" Type="Edm.String" />
        <Property Name="address" Type="Edm.String" />
        <Property Name="phone" Type="Edm.String" />
        <Property Name="signupDate" Type="Edm.DateTimeOffset" Nullable="false" />
        <NavigationProperty Name="dogs" Type="Collection(DogHotelAPI.Models.Dog)" />
      </EntityType>
    </Schema>
    <Schema Namespace="DogHotelAPI.Models.Enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="DogBreed">
        <Member Name="AfghanHound" Value="0" />
        <Member Name="AmericanStaffordshireTerrier" Value="1" />
        <Member Name="Boxer" Value="2" />
        <Member Name="Chihuahua" Value="3" />
        <Member Name="Dachsund" Value="4" />
        <Member Name="GermanShepherd" Value="5" />
        <Member Name="GoldenRetriever" Value="6" />
        <Member Name="Greyhound" Value="7" />
        <Member Name="ItalianGreyhound" Value="8" />
        <Member Name="Labrador" Value="9" />
        <Member Name="Pomeranian" Value="10" />
        <Member Name="Poodle" Value="11" />
        <Member Name="ToyPoodle" Value="12" />
        <Member Name="ShihTzu" Value="13" />
        <Member Name="YorkshireTerrier" Value="14" />
      </EnumType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityContainer Name="Container">
        <EntitySet Name="Dogs" EntityType="DogHotelAPI.Models.Dog">
          <NavigationPropertyBinding Path="owner" Target="Owners" />
        </EntitySet>
        <EntitySet Name="Owners" EntityType="DogHotelAPI.Models.Owner">
          <NavigationPropertyBinding Path="dogs" Target="Dogs" />
        </EntitySet>
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

我可能遗漏了什么阻止我的导航优先权与我的模型的其余部分一起返回?

编辑

为了进一步隔离问题,我尝试在服务器端包括 C# 中的所有者。我在 Dog Controller 的 Get 方法中添加了这一行:

var test = db.Dogs.Include("Owner").ToList();

有了这个我可以调试并看到相关的所有者被包括在内。在此列表中,每只狗都有与之关联的主人。

对实际返回的内容使用 .Include("Owner") 并不能解决问题 - 属性仍然无法到达客户端。

这似乎意味着导航属性正在工作,但没有被发送回客户端。我猜这似乎表明 OData 或 WebAPI 存在问题,但我不确定是什么。

此外,我已将以下行添加到我的 Global.asax 文件中的 Application_Start 以处理循环导航属性:

            var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.All;

我这样做是为了防止循环引用是罪魁祸首,但这并没有改变什么。

更新

我注意到调用

http://localhost:49382/odata/Dogs(abfd26a5-14d8-4b14-adbe-0a0c0ef392a7)/owner

有效。这将检索与该狗关联的所有者。这进一步说明我的导航属性设置正确,它们只是没有包含在对使用 $expand 的调用的响应中。

更新 2

这是我的WebApiConfig文件的注册方法:

        public static void Register(HttpConfiguration config)
    {
        //config.Routes.MapHttpRoute(
        //    name: "DefaultApi",
        //    routeTemplate: "api/{controller}/{id}",
        //    defaults: new { id = RouteParameter.Optional }
        //);

        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        builder.EnableLowerCamelCase();
        builder.EntitySet<Dog>("Dogs");
        builder.EntitySet<Owner>("Owners");

        config.EnableQuerySupport();

        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: "odata",
            model: builder.GetEdmModel());


        // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
        // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
        // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
        //config.EnableQuerySupport();

        // To disable tracing in your application, please comment out or remove the following line of code
        // For more information, refer to: http://www.asp.net/web-api
        config.EnableSystemDiagnosticsTracing();
    }

最佳答案

我找到了我的问题的解决方案,这最终是由三件事引起的:

1.) 我在我的 Controller 方法上使用了 [Queryable] 属性,该属性已被弃用。我需要使用较新的 [EnableQuery] 属性。

2.) 在我的 WebApiConfig.cs 文件中,我使用默认的 config.EnableQuerySupport() 启用查询。这已被弃用,并且已被删除

3.) 我需要的扩展调用采用 $expand=Owner 的形式,但需要采用 $expand=owner 的形式,因为我在我的 ODataConventionModelBuilder 上启用了小驼峰式大小写。非常感谢 Mark Bennetts,他的回答指出了这一点!

进行所有这些更改后,相关的 Owner 实体将与 Dog 实体一起返回。

关于c# - OData 和 WebAPI : Navigation property not present on model,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30357046/

相关文章:

c# - 如何将 Entity Framework 查询写成 **like 'lap%' **

c# - 如何禁用泛型复杂类型字段的延迟加载?

c#-4.0 - Web APi 中的每个操作后触发特定方法

asp.net-mvc - WebApi 操作路线

c# - 允许 .NET WebApi 忽略 DOCTYPE 声明

c# - 为什么 gridview 行的颜色没有改变?

c# - 添加对 web 服务的引用

c# - 从另一个类更新进度条

c# - 将 Sharepoint App 2013 部署到 Windows Azure(提供商托管)

c# - 如何强制 Entity Framework 生成更高效的 SQL 代码?