Mit Web-API und OData habe ich einen Dienst, der Datentransferobjekte statt der Entity Framework-Entitäten verfügbar macht.Verwenden von DTOs mit OData & Web API
I verwenden AutoMapper EF Entities in ihre Gegenstücke DTO zu transformieren unter Verwendung von ProjectTo()
:
public class SalesOrdersController : ODataController
{
private DbContext _DbContext;
public SalesOrdersController(DbContext context)
{
_DbContext = context;
}
[EnableQuery]
public IQueryable<SalesOrderDto> Get(ODataQueryOptions<SalesOrderDto> queryOptions)
{
return _DbContext.SalesOrders.ProjectTo<SalesOrderDto>(AutoMapperConfig.Config);
}
[EnableQuery]
public IQueryable<SalesOrderDto> Get([FromODataUri] string key, ODataQueryOptions<SalesOrderDto> queryOptions)
{
return _DbContext.SalesOrders.Where(so => so.SalesOrderNumber == key)
.ProjectTo<SalesOrderDto>(AutoMapperConfig.Config);
}
}
AutoMapper (V4.2.1) konfiguriert ist wie folgt, siehe den ExplicitExpansion()
die Serialisierung Autonavigations verhindert Eigenschaften erweitern, wenn sie werden nicht angefordert:
cfg.CreateMap<SalesOrderHeader, SalesOrderDto>()
.ForMember(dest => dest.SalesOrderLines, opt => opt.ExplicitExpansion());
cfg.CreateMap<SalesOrderLine, SalesOrderLineDto>()
.ForMember(dest => dest.MasterStockRecord, opt => opt.ExplicitExpansion())
.ForMember(dest => dest.SalesOrderHeader, opt => opt.ExplicitExpansion());
ExplicitExpansion()
dann ein neues Problem schafft, wo die folgende Anforderung einen Fehler wirft:
/odatademo/SalesOrders('123456')?$expand=SalesOrderLines
The query specified in the URI is not valid. The specified type member 'SalesOrderLines' is not supported in LINQ to Entities
Die Navigationseigenschaft SalesOrderLines
ist EF unbekannt, also ist dieser Fehler ziemlich genau das, was ich erwartet habe. Die Frage ist, wie gehe ich mit dieser Art von Anfrage um?
Die ProjectTo()
Methode macht eine Überlast, die ich in einer Reihe von Eigenschaften passieren läßt, die Expansion erforderlich, fand ich & die Erweiterungsmethode modifizierte ToNavigationPropertyArray
um zu versuchen, die Anfrage in ein String-Array zu analysieren:
[EnableQuery]
public IQueryable<SalesOrderDto> Get([FromODataUri] string key, ODataQueryOptions<SalesOrderDto> queryOptions)
{
return _DbContext.SalesOrders.Where(so => so.SalesOrderNumber == key)
.ProjectTo<SalesOrderDto>(AutoMapperConfig.Config, null, queryOptions.ToNavigationPropertyArray());
}
public static string[] ToNavigationPropertyArray(this ODataQueryOptions source)
{
if (source == null) { return new string[]{}; }
var expandProperties = string.IsNullOrWhiteSpace(source.SelectExpand?.RawExpand) ? new List<string>().ToArray() : source.SelectExpand.RawExpand.Split(',');
for (var expandIndex = 0; expandIndex < expandProperties.Length; expandIndex++)
{
// Need to transform the odata syntax for expanding properties to something EF will understand:
// OData may pass something in this form: "SalesOrderLines($expand=MasterStockRecord)";
// But EF wants it like this: "SalesOrderLines.MasterStockRecord";
expandProperties[expandIndex] = expandProperties[expandIndex].Replace(" ", "");
expandProperties[expandIndex] = expandProperties[expandIndex].Replace("($expand=", ".");
expandProperties[expandIndex] = expandProperties[expandIndex].Replace(")", "");
}
var selectProperties = source.SelectExpand == null || string.IsNullOrWhiteSpace(source.SelectExpand.RawSelect) ? new List<string>().ToArray() : source.SelectExpand.RawSelect.Split(',');
//Now do the same for Select (incomplete)
var propertiesToExpand = expandProperties.Union(selectProperties).ToArray();
return propertiesToExpand;
}
Diese für erweitern funktioniert, jetzt, damit ich eine Anfrage wie folgt umgehen:
/odatademo/SalesOrders('123456')?$expand=SalesOrderLines
oder eine kompliziertere Anfrage wie:
/odatademo/SalesOrders('123456')?$expand=SalesOrderLines($expand=MasterStockRecord)
jedoch kompliziertere Anforderung, die versucht, $ wählen zu kombinieren mit $ erweitern fehl:
/odatademo/SalesOrders('123456')?$expand=SalesOrderLines($select=OrderQuantity)
Sequence contains no elements
So ist die Frage: bin ich dies den richtigen Weg nähern? Es fühlt sich sehr stinkend an, dass ich etwas schreiben müsste, um die ODataQueryOptions zu parsen und in etwas zu transformieren, das EF verstehen kann.
Es scheint dies ein ziemlich beliebtes Thema ist:
- odata-expand-dtos-and-entity-framework
- how-to-specify-the-shape-of-results-with-webapi2-odata-with-expand
- web-api-queryable-how-to-apply-automapper
- how-do-i-map-an-odata-query-against-a-dto-to-another-entity
Während die meisten von ihnen empfehlen die Verwendung ProjectTo
, scheinen keine Adresse Serialisierung automatisch erweitern ding Eigenschaften, oder wie mit der Erweiterung umzugehen, wenn ExplictExpansion
konfiguriert wurde.
Klassen und Config unter:
Entity Framework (V6.1.3) Einheiten:
public class SalesOrderHeader
{
public string SalesOrderNumber { get; set; }
public string Alpha { get; set; }
public string Customer { get; set; }
public string Status { get; set; }
public virtual ICollection<SalesOrderLine> SalesOrderLines { get; set; }
}
public class SalesOrderLine
{
public string SalesOrderNumber { get; set; }
public string OrderLineNumber { get; set; }
public string Product { get; set; }
public string Description { get; set; }
public decimal OrderQuantity { get; set; }
public virtual SalesOrderHeader SalesOrderHeader { get; set; }
public virtual MasterStockRecord MasterStockRecord { get; set; }
}
public class MasterStockRecord
{
public string ProductCode { get; set; }
public string Description { get; set; }
public decimal Quantity { get; set; }
}
OData (V6.13.0) Datenübertragungs Objekte:
public class SalesOrderDto
{
[Key]
public string SalesOrderNumber { get; set; }
public string Customer { get; set; }
public string Status { get; set; }
public virtual ICollection<SalesOrderLineDto> SalesOrderLines { get; set; }
}
public class SalesOrderLineDto
{
[Key]
[ForeignKey("SalesOrderHeader")]
public string SalesOrderNumber { get; set; }
[Key]
public string OrderLineNumber { get; set; }
public string LineType { get; set; }
public string Product { get; set; }
public string Description { get; set; }
public decimal OrderQuantity { get; set; }
public virtual SalesOrderDto SalesOrderHeader { get; set; }
public virtual StockDto MasterStockRecord { get; set; }
}
public class StockDto
{
[Key]
public string StockCode { get; set; }
public string Description { get; set; }
public decimal Quantity { get; set; }
}
OData Config:
var builder = new ODataConventionModelBuilder();
builder.EntitySet<StockDto>("Stock");
builder.EntitySet<SalesOrderDto>("SalesOrders");
builder.EntitySet<SalesOrderLineDto>("SalesOrderLines");
Hallo Dave zurückkehren, ich bin bereits explizit die Navigationseigenschaften erweitert. Der obige Code zeigt den Code, den ich benutze, um dies zu konfigurieren und die Odata zu parsen, um zu erhalten, welche Navigationseigenschaften eine Erweiterung erfordern. Dies ist der Zweck der 'ToNavigationPropertyArray()' Extension-Methode, deren Ergebnis dann an das 'ProjectTo()' übergeben wird. Meine Frage war/ist darüber, ob die Verwendung der Erweiterungsmethode zum Analysieren der ODATA-Abfragezeichenfolge der richtige Ansatz war, da es unordentlich wird, wenn mehrere Navigationseigenschaften erweitert werden müssen. Ich habe daran gearbeitet, indem ich Aktionen und Funktionen für kompliziertere Abfragen verwendet habe. – philreed