Я пытаюсь собрать простой игрушечный проект, используя Entity Framework, WebAPI, OData и клиент Angular. Все работает нормально, за исключением того, что свойство навигации, которое я наложил на одну из моих моделей, похоже, не работает. Когда я вызываю свой API с помощью $expand, возвращаемые объекты не имеют своих навигационных свойств.
Мои классы - Собака и Владелец, и выглядят так:
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 для обработки запросов:
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.
Запрос для списка собак, использующих это, отлично работает:
http://localhost:49382/odata/Dogs
Я получаю список объектов Dog без свойства навигации Owner.
Запрос для собак с их владельцами с использованием OData $expand НЕ работает:
http://localhost:49382/odata/Dogs?$expand=Owner
Мой ответ - 200 со всеми сущностями Dog, но ни один из них не обладает свойством Owner в JSON.
Если я запрошу свои метаданные, я обнаружил, что 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>
Что я могу потерять, что мешает моей навигации preoprty вернуться к остальной части моей модели?
ИЗМЕНИТЬ
Чтобы дополнительно изолировать проблему, которую я пробовал, включая владельцев на С# на стороне сервера. Я добавил эту строку в метод Get моего контроллера Dog:
var test = db.Dogs.Include("Owner").ToList();
С этим я могу отлаживать и видеть, что связанные владельцы включены. У каждой собаки есть владелец, связанный с ним в этом списке.
Использование .Include( "Владелец" ) в том, что действительно возвращается, не устраняет проблему - свойства все еще не доходят до клиента.
Это означает, что свойства навигации работают, но не отправляются обратно клиенту. Кажется, что это рана указывает на проблему с OData или WebAPI, я бы предположил, но я не уверен, что.
Кроме того, я добавил следующие строки в Application_Start в файле Global.asax, чтобы обрабатывать свойства круговой навигации:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
Я сделал это, если круговая ссылка была каким-то образом виновником, но это ничего не меняет.
UPDATE
Я заметил, что вызов
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();
}