2013-04-02 13 views
7

Attualmente sto valutando se AutoMapper può essere di beneficio per il nostro progetto. Sto lavorando su un'API Web RESTful utilizzando l'API Web ASP.NET e una delle cose che devo restituire è una risorsa che contiene collegamenti. Considerate questo esempio semplificato, utilizzando il seguente oggetto di dominio:Proiezione utilizzando valori contestuali in AutoMapper

public class Customer 
{ 
    public string Name { get; set; } 
} 

ho bisogno di mappare questo in un oggetto risorsa, come una sorta di DTO, ma con proprietà aggiunti per facilitare REST. Ecco come può essere il mio oggetto risorsa:

public class CustomerResource 
{ 
    public string Name { get; set; } 
    public Dictionary<string, string> Links { get; set; } 
} 

La proprietà Collegamenti dovrà contenere collegamenti a risorse correlate. In questo momento, ho potuto costruirli utilizzando il seguente approccio:

public IEnumerable<CustomerResource> Get() 
{ 
    Func<Customer, CustomerResource> map = customer => 
     new CustomerResource 
     { 
      Name = customer.Name, 
      Links = new Dictionary<string, string>() 
      { 
       {"self", Url.Link("DefaultApi", new { controller = "Customers", name = customer.Name })} 
      } 
     } 

    var customers = Repository.GetAll(); 
    return customers.Select(map); 
} 

... ma questo è abbastanza noioso e ho un sacco di risorse annidate e così via. Il problema che vedo è che non riesco ad utilizzare AutoMapper perché non mi consente di fornire alcune cose necessarie durante la proiezione che sono circoscritte al punto in cui viene eseguita l'operazione di mappatura. In questo caso, la proprietà Url di ApiController fornisce l'istanza UrlHelper di cui ho bisogno per creare i collegamenti per me, ma potrebbero esserci altri casi.

Come risolverebbe questo enigma?

P.S. Ho digitato questo codice in modo specifico per questa domanda, che è stato compilato nella tua testa ma potrebbe non riuscire nel tuo IDE preferito.

+0

Al momento sono propenso a creare la mappa sul sito di chiamata ma non so se sia una buona idea. –

+0

Quando vengono definiti i collegamenti? Durante il runtime? – carlpett

+0

Per essere più precisi, i collegamenti sono definiti 1) al momento della compilazione, 2) al momento dell'avvio o 3) al tempo di mappatura/risoluzione? – carlpett

risposta

2

Vorrei utilizzare un Custom Type Converter. Il convertitore di tipi potrebbe avere informazioni contestuali iniettate tramite un contenitore IOC. Oppure, poiché il convertitore viene istanziato al momento della configurazione, potrebbe avere un riferimento a una fabbrica che restituirebbe informazioni contestuali ogni volta che viene eseguito il convertitore di tipi.

Semplice esempio

Si potrebbe definire un'interfaccia per ottenere il vostro "contesto" corrente (cosa ciò significhi dipende da quello che stai facendo e come implementare le cose così per questo esempio mi limiterò a corrente HttpContext che ottiene l'accesso alla sessione, Server, Articoli, ecc ...):

public interface IContextFactory 
{ 
    HttpContext GetContext(); 
} 

e l'implementazione è semplicemente:

public class WebContextFactory : IContextFactory 
{ 
    public HttpContext GetContext() 
    { 
     return HttpContext.Current; 
    } 
} 

Il convertitore del tipo personalizzato potrebbe richiedere un'istanza di IContextFactory dal contenitore IOC e ogni volta che viene eseguita la mappatura, è possibile chiamare GetContext() per ottenere il contesto per la richiesta corrente.

Accesso alla proprietà URL

L'UrlHelper deriva dall'oggetto Request allegata al contesto del regolatore di corrente. Sfortunatamente, questo non è disponibile in HttpContext. Tuttavia, è possibile sovrascrivere il metodo Initialize su ApiController e memorizzare ControllerContext in HttpContext.Articoli collezione:

protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext) 
{ 
    HttpContext.Current.Items["controllerContext"] = controllerContext; 
    base.Initialize(controllerContext); 
} 

È quindi possibile accedere a tale dalla HttpContext corrente:

var helper = ((HttpControllerContext) HttpContext.Current.Items["controllerContext"]).Request.GetUrlHelper(); 

io non sono sicuro che sia la migliore soluzione, ma si può ottenere l'istanza UrlHelper all'interno del vostro personalizzato tipo mapper.

+0

Come otterresti il ​​contesto con IoC? Supponiamo che tu stia eseguendo 'Mapper.Map' in un controller MVC. Come si inietterebbe l'istanza in cui si sta eseguendo l'esecuzione nel resolver/convertitore? – carlpett

+0

@carlpett, ho aggiornato con lo stesso di afferrare l'attuale contesto Http tramite una fabbrica. – PatrickSteele

+0

Ma HttpContext non è quello che mi serve per la risoluzione. –

2

Questa non è una soluzione carina, ma dopo aver letto i documenti sembra che non ce ne sia uno ... Stiamo attualmente lanciando contenuti contestuali mappando Tuple<TDomainType, TContextStuff> a TDataTransfer. Quindi nel tuo caso avresti Mapper.CreateMap<Tuple<Customer, Controller>, CustomerResource>.

Non carino, ma funziona.

+0

Non è carino, ma è creativo. Sto riflettendo su questo, ma sto già pensando al disastro che creerebbe durante la configurazione dei mapping nidificati. –

Problemi correlati