2010-04-15 12 views
6

Ho un'interfaccia:RuntimeBinderException con dinamica in C# 4.0

public abstract class Authorizer<T> where T : RequiresAuthorization 
{ 
    public AuthorizationStatus Authorize(T record) 
    { 
     // Perform authorization specific stuff 
     // and then hand off to an abstract method to handle T-specific stuff 
     // that should happen when authorization is successful 

    } 
} 

Poi, ho un sacco di classi diverse, che tutti implementano RequiresAuthorization, e di conseguenza, un Authorizer<T> per ciascuno di essi (ogni oggetto di business nel mio il dominio richiede una logica diversa da eseguire una volta che il record è stato autorizzato).

Sto anche utilizzando un UnityContainer, nel quale registro vari Authorizer<T>. Ho poi qualche codice come segue per trovare il record a destra fuori del database e autorizzarla:

void Authorize(RequiresAuthorization item) 
{ 
    var dbItem = ChildContainer.Resolve<IAuthorizationRepository>() 
           .RetrieveRequiresAuthorizationById(item.Id); 
    var authorizerType = type.GetType(String.Format("Foo.Authorizer`1[[{0}]], Foo", 
          dbItem.GetType().AssemblyQualifiedName)); 
    dynamic authorizer = ChildContainer.Resolve(type) as dynamic; 

    authorizer.Authorize(dbItem); 
} 

In sostanza, sto utilizzando l'ID sull'oggetto per recuperare fuori del database. Sullo sfondo NHibernate si occupa di capire quale tipo di richiede l'autorizzazione. Quindi voglio trovare l'Authorizer giusto per questo (non so al momento della compilazione quale implementazione di Authorizer<T> mi serve, quindi ho un po 'di riflessione per ottenere il tipo completo). Per fare ciò, utilizzo l'overload non generico del metodo Resolve di UnityContainer per cercare la corretta configurazione dell'authorizer.

Infine, voglio chiamare Autorizza sul programma di autorizzazione, passando attraverso l'oggetto che ho ricevuto da NHibernate.

Ora, per il problema:

In Beta2 di VS2010 il codice precedente funziona perfettamente. Su RC e RTM, non appena faccio la chiamata a Authorize(), ottengo un RuntimeBinderException che dice "La migliore corrispondenza del metodo in overload per 'Foo.Authorizer<Bar>.Authorize(Bar)' ha alcuni argomenti non validi". Quando ispeziono l'autore nel debugger, è il tipo corretto. Quando chiamo GetType(). GetMethods() su di esso, posso vedere il metodo Authorize che accetta una barra. Se faccio GetType() su dbItem è una barra.

Poiché questo ha funzionato in Beta2 e non in RC, ho presupposto che fosse una regressione (sembra che dovrebbe funzionare) e ho ritardato l'ordinamento fino a dopo aver avuto la possibilità di testarlo sulla versione RTM di C# 4.0. Ora l'ho fatto e il problema persiste ancora. Qualcuno ha qualche suggerimento per farlo funzionare?

Grazie

Terence

risposta

11

Terence, avrei bisogno di maggiori informazioni qui sui tipi in gioco e le loro definizioni di sapere che cosa il problema è davvero, ma questo errore è in fondo si dice che non si poteva convertire dbItem a Bar. Ci sono due possibilità:

1) RetrieveRequiresAuthorizationById() restituisce dynamic, e quindi la fase di compilazione di dbItem viene dedotta per essere dynamic. In questo caso, il raccoglitore di runtime selezionerà un tipo per dbItem in fase di runtime che è essenzialmente il tipo più accessibile se è possibile trovarlo. Questo tipo non è convertibile in Bar in base all'accessibilità. Ad esempio, potrebbe essere che il tipo di runtime di dbItem sia un tipo inaccessibile con una classe di base diretta di object e che ovviamente object non sia convertibile in Bar.

2) RetrieveRequiresAuthorizationById() restituisce un tipo statico. In tal caso, il tipo statico non è convertibile in Bar in fase di runtime.

La mia ipotesi è che (2) è il caso, e che il tipo di dbItem è RequiresAuthorization. Il che sto anche indovinando è un'interfaccia. E questo non sarà convertibile in nessun tipo di classe.

Se ho ragione, quello che vuoi fare è rendere dinamico dbItem. Se lo fai, allora il raccoglitore di runtime selezionerà il tipo appropriato per dbItem, che è presumibilmente l'intero motivo per cui vuoi farlo.

void Authorize(RequiresAuthorization item) 
{ 
    var dbItem = ChildContainer.Resolve<IAuthorizationRepository>() 
           .RetrieveRequiresAuthorizationById(item.Id); 
    var authorizerType = type.GetType(String.Format("Foo.Authorizer`1[[{0}]], Foo", 
          dbItem.GetType().AssemblyQualifiedName)); 
    dynamic authorizer = ChildContainer.Resolve(type) as dynamic; 

    authorizer.Authorize(dbItem as dynamic); // <<<Note "as" here 
} 
+0

Ciao Chris, il codice che hai postato funziona perfettamente. Grazie! –