asp.net - tutorial - Web API Abfragbar-Wie man AutoMapper anwendet?



asp.net web api tutorial deutsch (3)

Ich habe eine einfache WebApi-Methode wie diese, die mit dem abfragbaren OData-Attribut dekoriert ist.

    [Queryable]
    public virtual IQueryable<PersonDto> Get()
    {
        return uow.Person().GetAll()); // Currently returns Person instead of PersonD
    }

Was ich tun möchte, ist das Transformieren des Ergebnisses der Abfrage vom Typ Person in PersonDto mit AutoMapper, bevor WebAPI das Ergebnis in JSON konvertiert.

Weiß jemand, wie ich das machen kann? Mir ist bewusst, ich könnte Mapper.Map nach dem GetAll () - Aufruf anwenden und dann wieder in IQueryable konvertieren, jedoch würde dies dazu führen, dass die gesamte Tabelle zurückgegeben und zugeordnet wird, bevor der OData-Filter angewendet wird (nicht gut!).

Es scheint, dass diese Frage ASP.NET-Web-API abfragbare DTOs zurückgeben? deckt das gleiche Problem ab (siehe zweite Antwort für eine bessere Antwort), wo der Vorschlag ist, AutoMapper am Ende der Kette mit einem benutzerdefinierten MediaTypeFormatter zu verwenden, aber ich habe keine Ahnung, wie man das anhand des Beispiels, das ich gesehen habe, macht.

Jede Hilfe wird dankbar angenommen!

-- Weitere Informationen

Ich habe mir den Quellcode von IQueryable angesehen, aber leider kann ich keine Möglichkeit sehen, den Code für diesen Zweck zu verwenden. Ich habe es geschafft, einen zusätzlichen Filter zu schreiben, der zu funktionieren scheint, aber es ist sicherlich nicht elegant.

public class PersonToPersonDtoConvertAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
    {
        HttpResponseMessage response = actionExecutedContext.Response;

        if (response != null)
        {
            ObjectContent responseContent = response.Content as ObjectContent;
            var query = (responseContent.Value as IQueryable<Student>).ToList();
            response.Content = new ObjectContent<IEnumerable<StudentResource>>(query.ToList().Select(Mapper.Map<Person, PersonDto>), responseContent.Formatter);
        }
    }
}

Dann habe ich die Aktion gerne dekoriert

    [Queryable]
    [PersonToPersonDtoConvert]
    public IQueryable<Person> Get()
    {
        return uow.GetRepo<IRepository<Person>>().GetAll();
    }

Answer #1

Es gibt eine bessere Lösung. Versuche dies:

public virtual IQueryable<PersonDto> Get(ODataQueryOptions<Person> query)
{
    var people = query.ApplyTo(uow.Person().GetAll());
    return ConvertToDtos(people);
}

Dadurch wird sichergestellt, dass die Abfrage auf Person statt auf PersonDTO ausgeführt wird. Wenn Sie möchten, dass die Konvertierung über ein Attribut statt über Code erfolgt, möchten Sie dennoch einen Aktionsfilter implementieren, der dem entspricht, was Sie aufstellen.


Answer #2

IMHO die akzeptierte Lösung ist nicht korrekt. Wenn Ihr Dienst DTOs verwendet, möchten Sie im Allgemeinen die zugrunde liegenden Entitäten (Person) dem Dienst nicht verfügbar machen. Warum würden Sie das Person abfragen und Objekte von PersonDTO ?

Da Sie es bereits verwenden, verfügt Automapper über Queryable Extensions, mit denen Sie nur Ihre DTOs verfügbar machen und die Filterung auf den zugrunde liegenden Typ in der Datenquelle anwenden können. Beispielsweise:

public IQueryable<PersonDto> Get(ODataQueryOptions<PersonDto> options) {
    Mapper.CreateMap<Person, PersonDto>();
    var persons = _personRepository.GetPersonsAsQueryable();
    var personsDTOs = persons.Project().To<PersonDto>();  // magic happens here...

    return options.ApplyTo(personsDTOs);
}

In Bezug auf das Laden von Navigationseigenschaften ...

@philreed: Ich konnte in dem Kommentar keine vernünftige Antwort geben, also habe ich es hier hinzugefügt. Es gab einen Post darüber, wie man das here aber ich bekomme heute 403s. Hoffentlich ist das vorübergehend.

Im Grunde untersuchen Sie die Select- und Expand-Klauseln für Ihre Navigationseigenschaft. Wenn es vorhanden ist, dann teilen Sie EF mit, dass es über die Erweiterungsmethode IQueryable<T> Include eifrig geladen werden IQueryable<T> Include .

Regler

public IQueryable<MyDto> GetMyDtos(ODataQueryOptions<MyDto> options)
{   
  var eagerlyLoad = options.IsNavigationPropertyExpected(t => t.MyNavProperty);
  var queryable = _myDtoService.GetMyDtos(eagerlyLoad);

  // _myDtoService will eagerly load to prevent select N+1 problems
  // return (eagerlyLoad) ? efResults.Include(t => t.MyNavProperty) : efResults;

  return queryable;
}

Erweiterungsmethode

public static class ODataQueryOptionsExtensions
{
  public static bool IsNavigationPropertyExpected<TSource, TKey>(this ODataQueryOptions<TSource> source, Expression<Func<TSource, TKey>> keySelector)
  {
    if (source == null) { throw new ArgumentNullException("source"); }
    if (keySelector == null) { throw new ArgumentNullException("keySelector"); }

    var returnValue = false;
    var propertyName = (keySelector.Body as MemberExpression ?? ((UnaryExpression)keySelector.Body).Operand as MemberExpression).Member.Name;
    var expandProperties = source.SelectExpand == null || string.IsNullOrWhiteSpace(source.SelectExpand.RawExpand) ? new List<string>().ToArray() : source.SelectExpand.RawExpand.Split(',');
    var selectProperties = source.SelectExpand == null || string.IsNullOrWhiteSpace(source.SelectExpand.RawSelect) ? new List<string>().ToArray() : source.SelectExpand.RawSelect.Split(',');

    returnValue = returnValue ^ expandProperties.Contains<string>(propertyName);
    returnValue = returnValue ^ selectProperties.Contains<string>(propertyName);

    return returnValue;
  }
}

Answer #3

Verwenden Sie die abfragbaren Erweiterungen des AutoMapper .

Definieren Sie zuerst das Mapping.

Mapper.CreateMap<Person, PersonDto>();

Dann können Sie etwas wie folgt verwenden:

[EnableQuery]
public IQueryable<PersonDto> Get() {
    return this.dbContext.Persons.Project().To<PersonDto>();
}




asp.net-web-api