2010-04-15 11 views
6

que tiene una interfaz:RuntimeBinderException con dinámica en 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 

    } 
} 

Entonces, tengo un montón de diferentes clases que todos implementan RequiresAuthorization, y en consecuencia, una Authorizer<T> para cada uno de ellos (cada objeto de negocio en mi el dominio requiere una lógica diferente para ejecutarse una vez que el registro ha sido autorizado).

También estoy usando un UnityContainer, en el cual registro varios Authorizer<T> 's. entonces tengo un cierto código de la siguiente manera para encontrar el registro directamente de la base de datos y autorizarlo:

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); 
} 

Básicamente, estoy usando la identificación en el objeto de recuperar fuera de la base de datos. En el fondo NHibernate se encarga de averiguar qué tipo de Requiere Autorización es. Entonces quiero encontrar el Autorizador adecuado para ello (no sé en tiempo de compilación qué implementación de Authorizer<T> necesito, así que tengo un poco de reflexión para obtener el tipo completo). Para lograr esto, uso la sobrecarga no genérica del método Resolve de UnityContainer para buscar el autorizador correcto desde la configuración.

Finalmente, deseo llamar a Autorizar al autorizador, pasando el objeto que he recibido de NHibernate.

Ahora, para el problema:

En la Beta 2 de VS2010 el código anterior funciona perfectamente. En RC y RTM, tan pronto como realizo la llamada a Authorize(), obtengo una excepción RuntimeBinderException que dice "La mejor coincidencia de método sobrecargado para 'Foo.Authorizer<Bar>.Authorize(Bar)' tiene algunos argumentos no válidos". Cuando inspecciono el autorizador en el depurador, es el tipo correcto. Cuando llamo a GetType(). GetMethods() en él, puedo ver el método Authorize que toma una barra. Si hago GetType() en dbItem, es una barra.

Porque esto funcionó en Beta2 y no en RC, asumí que era una regresión (parece que debería funcionar) y retrasé la clasificación hasta después de haber tenido la oportunidad de probarla en la versión RTM de C# 4.0. Ahora lo he hecho y el problema aún persiste. ¿Alguien tiene alguna sugerencia para hacer que esto funcione?

Gracias

Terence

Respuesta

11

Terence, necesitaría más información acerca de los tipos aquí en juego y sus definiciones para saber cuál es el problema realmente es, pero este error es básicamente te dice que no pudo convertir dbItem en Bar. Hay dos posibilidades:

1) RetrieveRequiresAuthorizationById() devuelve dynamic, y por lo tanto el tipo de tiempo de compilación de dbItem se infiere a ser dynamic. Si este es el caso, entonces el enrutador de tiempo de ejecución seleccionará un tipo para dbItem en tiempo de ejecución que es esencialmente el mejor tipo accesible si puede encontrarlo. Este tipo no es convertible a Bar dada la accesibilidad. Por ejemplo, podría ser que el tipo de tiempo de ejecución de dbItem es algún tipo inaccesible con una clase base directa de object, y obviamente object no es convertible a Bar.

2) RetrieveRequiresAuthorizationById() devuelve un tipo estático. En ese caso, el tipo estático no es convertible a Bar en tiempo de ejecución.

Supongo que (2) es el caso, y que el tipo de dbItem es RequiresAuthorization. Lo cual también estoy adivinando es una interfaz. Y eso no va a ser convertible a ningún tipo de clase.

Si estoy en lo cierto, lo que quiere hacer es hacer dbItem dinámico. Si lo hace, la carpeta de tiempo de ejecución seleccionará el tipo apropiado para dbItem, que es, presumiblemente, la razón por la que desea hacer esto.

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

Hola, Chris, el código que publicaste funciona perfectamente. ¡Gracias! –