14 Stimmen

ASP.NET WebApi OData Unterstützung für DTOs

Ich habe das Projekt-Entity und das ProjektDTO. Ich versuche, eine WebAPI-Controller-Methode zu erstellen, die ProjectDTOs akzeptieren und zurückgeben kann und OData unterstützt.

Das Problem ist, dass ich ein ORM verwende, das die Datenbankabfrage mit dem Projekt-Entity durchführt und nicht mit dem Projekt-DTO. Gibt es eine Möglichkeit, Filtern/Sortieren/Paging von OData basierend auf ProjectDTO auf die Projekt-Entity-Abfrage anzuwenden?

public ODataQueryResult GetProjects(ODataQueryOptions query)
{
    var context = new ORM_Context();

    var projects = context.Projects; // IQueryable
    var projectDtos = query.ApplyTo(projectDTOs)); // <-- Ich möchte hier etwas ähnliches erreichen
    var projectDTOs =
        projects.Select(
            x =>
            new ProjectDTO
                {
                    Id = x.Id,
                    Name = x.Name
                });

    var projectsQueriedList = projectDtos.ToList();

    var result = new ODataQueryResult(projectsQueriedList, totalCount);

    return result;
}

4voto

qujck Punkte 14150

Etwas wie dies (Ich habe nicht versucht, es zu kompilieren)

using(var dataContext = new ORM_Context())
{
    var projects = dataContext.Projects; // IQueryable

    // Erstellen eines ODataQueryOptions-Sets für die interne Klasse
    ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
    modelBuilder.EntitySet("Project"); 
    var context = new ODataQueryContext(
         modelBuilder.GetEdmModel(), typeof(Project));
    var newOptions = new ODataQueryOptions(context, Request);

    var t = new ODataValidationSettings() { MaxTop = 25 };
    var s = new ODataQuerySettings() { PageSize = 25 };
    newOptions.Validate(t);
    IEnumerable internalResults =
        (IEnumerable)newOptions.ApplyTo(projects, s);

    int skip = newOptions.Skip == null ? 0 : newOptions.Skip.Value;
    int take = newOptions.Top == null ? 25 : newOptions.Top.Value;

    var projectDTOs =
            internalResults.Skip(skip).Take(take).Select(x =>
                new ProjectDTO
                    {
                        Id = x.Id,
                        Name = x.Name
                    });

    var projectsQueriedList = projectDtos.ToList();
    var result = new ODataQueryResult(
        projectsQueriedList, totalCount);
    return result;
}

2voto

avitenberg Punkte 1711

Ich denke, der einfachste Weg, das zu tun, ist die Verwendung von AutoMapper. Also für dein DTO

    [DataContract(Name = "Products")]
    public class ProductDTO
    {
        [Key]
        [DataMember]
        public string MyProductMember1 { get; set; }

        [DataMember]
        public string MyProductMember2 { get; set; }
        ...
    }

solltest du irgendwo in der AutoMapper-Konfiguration schreiben:

Mapper.CreateMap();

und irgendwo beim Erstellen von IEdmModel für OData:

builder.EntitySet("Products");

und der Code für deinen Controller wird so aussehen

public class ProductsController : ODataController
{
    [EnableQuery]
    public IHttpActionResult Get()
    {
        var products = context.Products; // IQueryable
        return Ok(products.Project().To());
    }
}

Auf diese Weise müssen Sie Ihre ORM-Entitäten nicht direkt freigeben und können OData für Filterung, Paging, Count und sogar das Erweitern verschachtelter Sammlungen verwenden. Für EF wird es in entsprechende SQL-Abfragen übersetzt, die auf die Tabelle, zu der das Produkt zugeordnet ist, verweisen. Aber seien Sie vorsichtig: Für kompliziertere Fälle (verschachtelte Sammlungen zum Beispiel) kann dies zu nicht optimalen SQL-Abfragen führen.

0voto

Zar Shardan Punkte 5257

Versuchen Sie das:

    public object GetProjects(ODataQueryOptions query)
    {
        var context = new ORM_Context();

        var projects = query.ApplyTo(context.Projects);
        var projectDTOs = projects.Select(
                x =>
                new ProjectDTO
                    {
                        Id = x.Id,
                        Name = x.Name
                    });

        return new
        {
            TotalCount =  Request.GetInlineCount(), //vor dem Paging
            Results = projectDTOs.ToList() 
        };
    }

Offensichtlich ist hier das Wichtigste, den richtigen Typ an ODataQueryOptions<> zu übergeben und dann funktioniert es wie von Zauberhand. Dies liegt daran, dass es diesen speziellen Typ verwendet, um Ihre Sammlung/DB-Context abzufragen, sodass er vom Typ sein muss, der tatsächlich in der Sammlung/im Context gespeichert ist und nicht das, was Sie zurückgeben möchten.

Offensichtlich sollten Ihre DTOs Ihren ORM-Objekten ähnlich sein (und sie sind in Ihrem Ausschnitt), sonst funktioniert dies aus Benutzer-/Clientperspektive nicht sehr gut.

Ich habe den obigen Code nicht kompiliert, weil ich Ihre Klassen und andere Infrastruktur nicht habe, aber es sollte ausreichen, um die Idee zu vermitteln.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X