views:

195

answers:

1

I have an interface:

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

    }
}

Then, I have a bunch of different classes which all implement RequiresAuthorization, and correspondingly, an Authorizer<T> for each of them (each business object in my domain requires different logic to execute once the record has been authorized).

I'm also using a UnityContainer, in which I register various Authorizer<T>'s. I then have some code as follows to find the right record out of the database and authorize it:

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

Basically, I'm using the Id on the object to retrieve it out of the database. In the background NHibernate takes care of figuring out what type of RequiresAuthorization it is. I then want to find the right Authorizer for it (I don't know at compile time what implementation of Authorizer<T> I need, so I've got a little bit of reflection to get the fully qualified type). To accomplish this, I use the non-generic overload of UnityContainer's Resolve method to look up the correct authorizer from configuration.

Finally, I want to call Authorize on the authorizer, passing through the object I've gotten back from NHibernate.

Now, for the problem:

In Beta2 of VS2010 the above code works perfectly. On RC and RTM, as soon as I make the Authorize() call, I get a RuntimeBinderException saying "The best overloaded method match for 'Foo.Authorizer<Bar>.Authorize(Bar)' has some invalid arguments". When I inspect the authorizer in the debugger, it's the correct type. When I call GetType().GetMethods() on it, I can see the Authorize method which takes a Bar. If I do GetType() on dbItem it is a Bar.

Because this worked in Beta2 and not in RC, I assumed it was a regression (it seems like it should work) and I delayed sorting it out until after I'd had a chance to test it on the RTM version of C# 4.0. Now I've done that and the problem still persists. Does anybody have any suggestions to make this work?

Thanks

Terence

+2  A: 

Terence, I'd need more information here about the types in play and their definitions to know what the problem really is, but this error is basically telling you that it could not convert dbItem to Bar. There are two possibilities:

1) RetrieveRequiresAuthorizationById returns dynamic, and therefore the compile-time type of dbItem is inferred to be dynamic. If this is the case, then the runtime binder will select a type for dbItem at runtime that is essentially the best accessible type if can find. This type is not convertible to Bar given the accessibility. For example, it could be that dbItem's runtime type is some inaccessible type with a direct base class of 'object', and obviously object is not convertible to Bar.

2) RetrieveRequiresAuthorizationById returns some static type. In that case, the static type is not convertible to Bar at runtime.

My guess is that (2) is the case, and that the type of dbItem is RequiresAuthorization. Which I am also guessing is an interface. And that's not going to be convertible to any class type.

If I'm right, what you want to do is make dbItem dynamic. If you do, then the runtime binder will select the appropriate type for dbItem, which is presumably the whole reason you want to do this.

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);
}
Chris Burrows
Hi Chris, the code you posted works perfectly. Thanks!
Terence Lewis