Расширение коллекций с помощью контроллера Web Api OData
Я использую OData Framework 5.0.0 и Web API 5.0.0 и EntityFramework 5.0.0. И есть проблема с расширением на свойство навигации, которое является коллекцией. Я всегда получаю следующее исключение:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.
The given key was not present in the dictionary.
at System.Web.Http.OData.Query.Expressions.SelectExpandWrapper`1.GetEdmType()
at System.Web.Http.OData.Formatter.Serialization.ODataSerializerContext.GetEdmType(Object instance, Type type)
at System.Web.Http.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperty(KeyValuePair`2 navigationPropertyToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)
at System.Web.Http.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperties(IDictionary`2 navigationPropertiesToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)
at System.Web.Http.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.WebHost.HttpControllerHandler.
Раскрыть свойства навигации вне коллекции работает как шарм.
Это мои классы EF:
public partial class ElementTemplate
{
public ElementTemplate()
{
this.Elements = new HashSet<Element>();
this.Derived = new HashSet<ElementTemplate>();
this.TemplateAttributes = new HashSet<ElementTemplateAttribute>();
}
public System.Guid ID { get; set; }
public string InheritancePath { get; set; }
public string Name { get; set; }
public short Level { get; set; }
public string Description { get; set; }
public string Type { get; set; }
public bool AllowElementToExtend { get; set; }
public Nullable<System.Guid> DefaultElementTemplateAttributeID { get; set; }
public Nullable<System.Guid> BaseElementTemplateID { get; set; }
public string SecurityDescriptor { get; set; }
public Nullable<System.DateTime> CheckOutTime { get; set; }
public string CheckOutUserName { get; set; }
public string CheckOutMachineName { get; set; }
public virtual ICollection<Element> Elements { get; set; }
public virtual ICollection<ElementTemplate> Derived { get; set; }
public virtual ElementTemplate Base { get; set; }
public virtual ElementTemplateAttribute DefaultTemplateAttribute { get; set; }
public virtual ICollection<ElementTemplateAttribute> TemplateAttributes { get; set; }
}
public partial class Element
{
public Element()
{
this.Attributes = new HashSet<ElementAttribute>();
}
public System.Guid ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public int Revision { get; set; }
public bool HasChildren { get; set; }
public bool HasMultipleVersions { get; set; }
public Nullable<System.Guid> ElementTemplateID { get; set; }
public Nullable<System.Guid> DBReferenceTypeID { get; set; }
public string SecurityDescriptor { get; set; }
public System.DateTime Created { get; set; }
public string CreatedBy { get; set; }
public System.DateTime Modified { get; set; }
public string ModifiedBy { get; set; }
public Nullable<System.DateTime> CheckOutTime { get; set; }
public string CheckOutUserName { get; set; }
public string CheckOutMachineName { get; set; }
public virtual ICollection<ElementAttribute> Attributes { get; set; }
public virtual ElementTemplate Template { get; set; }
}
Метаданные для ElementTemplate выглядят следующим образом:
<EntityType Name="ElementTemplate">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false" />
<Property Name="InheritancePath" Type="Edm.String" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Level" Type="Edm.Int16" Nullable="false" />
<Property Name="Description" Type="Edm.String" />
<Property Name="Type" Type="Edm.String" />
<Property Name="AllowElementToExtend" Type="Edm.Boolean" Nullable="false" />
<Property Name="DefaultElementTemplateAttributeID" Type="Edm.Guid" />
<Property Name="BaseElementTemplateID" Type="Edm.Guid" />
<Property Name="SecurityDescriptor" Type="Edm.String" />
<Property Name="CheckOutTime" Type="Edm.DateTime" />
<Property Name="CheckOutUserName" Type="Edm.String" />
<Property Name="CheckOutMachineName" Type="Edm.String" />
<NavigationProperty Name="Elements" Relationship="Entity_EntityModel_ElementTemplate_Elements_EntityModel_Element_ElementsPartner" ToRole="Elements" FromRole="ElementsPartner" />
<NavigationProperty Name="Derived" Relationship="Entity_EntityModel_ElementTemplate_Derived_Entity_EntityModel_ElementTemplate_DerivedPartner" ToRole="Derived" FromRole="DerivedPartner" />
<NavigationProperty Name="Base" Relationship="Entity_EntityModel_ElementTemplate_Base_Entity_EntityModel_ElementTemplate_BasePartner" ToRole="Base" FromRole="BasePartner" />
<NavigationProperty Name="DefaultTemplateAttribute" Relationship="Entity_EntityModel_ElementTemplate_DefaultTemplateAttribute_Entity_EntityModel_ElementTemplateAttribute_DefaultTemplateAttributePartner" ToRole="DefaultTemplateAttribute" FromRole="DefaultTemplateAttributePartner" />
<NavigationProperty Name="TemplateAttributes" Relationship="Entity_EntityModel_ElementTemplate_TemplateAttributesEntity_EntityModel_ElementTemplateAttribute_TemplateAttributesPartner" ToRole="TemplateAttributes" FromRole="TemplateAttributesPartner" />
</EntityType>
Соответствующие методы в контроллере выглядят следующим образом:
// GET odata/ElementTemplates
[Queryable]
public IQueryable<ElementTemplate> GetElementTemplates()
{
return Db.ElementTemplates;
}
// GET odata/ElementTemplates(guid'...')
[Queryable]
public SingleResult<ElementTemplate> GetElementTemplate([FromODataUri] Guid key)
{
return SingleResult.Create(Db.ElementTemplates.Where(elementtemplate => elementtemplate.ID == key));
}
// GET odata/ElementTemplates(guid'...')/Elements
[Queryable]
public IQueryable<Element> GetElements([FromODataUri] Guid key)
{
return Db.ElementTemplates.Where(m => m.ID == key).SelectMany(m => m.Elements);
}
Работают следующие запросы:
/odata/ElementTemplates(guid'...')/Elements
/ OData/ElementTemplates?$ = Расширение базы
/ OData/ElementTemplates?$ Развернуть =DefaultTemplateAttribute
Но при доступе к коллекции я получаю ошибку, упомянутую выше:
/ OData/ElementTemplates?$ = Развернуть элементы
/odata/ElementTemplates(guid'...')?$expand=Elements
Если я меняю контроллер таким образом, он предварительно загружает данные:
// GET odata/ElementTemplates
[Queryable]
public IQueryable<ElementTemplate> GetElementTemplates()
{
return Db.ElementTemplates.ToList().AsQueryable();
}
// GET odata/ElementTemplates(guid'...')
[Queryable]
public SingleResult<ElementTemplate> GetElementTemplate([FromODataUri] Guid key)
{
return SingleResult.Create(Db.ElementTemplates.Where(elementtemplate => elementtemplate.ID == key).ToList().AsQueryable());
}
Тогда все запросы работают, но это, очевидно, убивает производительность.
Кажется, что проблема похожа на Расширение коллекций с EntitySetController в MVC Web Api, но они утверждают, что проблема в платформе NHibernate, которую я, очевидно, не использую, так что должно быть что-то еще, есть идеи?
Я также не испытываю проблемы при использовании той же модели EF через WCF DataServices.
Спасибо!
1 ответ
Оказалось, что Web API добавляет в запрос выражение CASE, содержащее идентификатор модели. Мой код не оценил это выражение CASE правильно.