开发者

RuntimeBinderException - C# .NET 4 Dynamic Keyword - Help Me Understand Why Method Isn't Matching

I've built a generic config system for an HttpModule that allows pluggable HTTP header inspectors. For reference, here is the basic layout of the code -- this should be enough to get a feel for what I'm doing:

public interface IHttpHeaderInspectingAuthenticatorFactory<T>
    where T: HttpHeaderInspectingAuthenticatorConfigurationElement
{
    IHttpHeaderInspectingAuthenticator<T> Construct(T config);
}

public class BasicAuthenticationInspectingAuthenticatorFactory :
    IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    public IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement> Construct(BasicAuthenticationHeaderInspectorConfigurationElement config)
    {
        return new BasicAuthenticationInspectingAuthenticator(config);
    }
}

public class BasicAuthenticationInspectingAuthenticator : HttpHeaderInspectingAuthenticatorBase<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    internal BasicAuthenticationInspectingAuthenticator(BasicAuthenticationHeaderInspectorConfigurationElement config) 
        : base(config) {}

 //snip -- IHttpHeaderInspectingAuthenticator<T> and IHttpHeaderInspectingAuthenticator implementation
}


public class BasicAuthenticationHeaderInspectorConfigurationElement : HttpHeaderInspectingAuthenticatorConfigurationElement
{
 //extra properties
}


public class HttpHeaderInspectingAuthenticatorConfigurationElement : ConfigurationElement
{
 protected override void PostDes开发者_如何学运维erialize()
 {
     base.PostDeserialize();

     //simple verification of info supplied in config
     var t = Type.GetType(Factory);
     if (null == t)
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] cannot be found - check configuration settings", Factory ?? ""));

     if (!typeof(IHttpHeaderInspectingAuthenticatorFactory<>).IsGenericInterfaceAssignableFrom(t))
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] must derive from {1} - check configuration settings", Factory ?? "", typeof(IHttpHeaderInspectingAuthenticatorFactory<>).Name));

     var c = t.GetConstructor(Type.EmptyTypes);
     if (null == c)
         throw new ConfigurationErrorsException(String.Format("The factory type specified [{0}] must have a parameterless constructor - check configuration settings", Factory ?? ""));
 }

 [ConfigurationProperty("factory", IsRequired = true)]
 public string Factory
 {
  get { return (string)this["factory"]; }
  set { this["factory"] = value; }
 }

 //this allows us to use types derived from HttpHeaderInspectingAuthenticatorConfigurationElement
    protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
    {                    
        ConfigurationProperty property = new ConfigurationProperty(name, typeof(string), value);
        Properties.Add(property);
        base[property] = value;            
        return true;
    }

 public IHttpHeaderInspectingAuthenticator GetInspector()
 {
     dynamic factoryInstance = Activator.CreateInstance(Type.GetType(Factory));
     return factoryInstance.Construct(this);
 }
}

Now the problem occurs in the GetInspector call -- all instances of the Factory name supplied must implement IHttpHeaderInspectingAuthenticatorFactor<> (as spec'd in PostDeserialize). Therefore, they will all have a Construct method, where the type T supplied will actually be the actual type of the class implementing the GetInspector() method. So for instance, GetInspector will be called on an instance of BasicAuthenticationHeaderInspectorConfigurationElement -- so 'this' will be an instance of BasicAuthenticationHeaderInspectorConfigurationElement.

However, the call to Construct fails. It finds the method fine, but apparently the parameter type is NOT matching up based on the RuntimeBinderException thrown. It would appear, based on the CallSite.Target that the expected type that the dynamic proxy expects is HttpHeaderInspectingAuthenticatorConfigurationElement -- the 'this' I'm passing is a BasicAuthenticationHeaderInspectorConfigurationElement, derived from that base.

So what gives? Am I not getting something here? I tried passing (this as dynamic) or ((HttpHeaderInspectingAuthenticatorConfigurationElement)this) -- but both fail with the same problem.

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'Redcated.Authentication.BasicAuthenticationInspectingAuthenticatorFactory.Construct(Redcated.Authentication.Configuration.BasicAuthenticationHeaderInspectorConfigurationElement)' has some invalid arguments
  at CallSite.Target(Closure , CallSite , Object , HttpHeaderInspectingAuthenticatorConfigurationElement )
  at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
  at Redcated.Authentication.Configuration.HttpHeaderInspectingAuthenticatorConfigurationElement.GetInspector() in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\Configuration\HttpHeaderInspectingAuthenticatorConfigurationElement.cs:line 79
  at Redcated.Authentication.Configuration.HttpHeaderInspectingAuthenticatorConfigurationElementCollection.<GetInspectors>b__0(HttpHeaderInspectingAuthenticatorConfigurationElement i) in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\Configuration\HttpHeaderInspectingAuthenticatorConfigurationElementCollection.cs:line 78
  at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
  at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
  at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
  at Redcated.Authentication.HttpHeaderInspectingAuthenticationModule.AuthenticateRequest(Object sender, EventArgs e) in D:\Users\ebrown\Documents\Visual Studio 2010\Projects\Utility\source\Authentication Library\HttpHeaderInspectingAuthenticationModule.cs:line 49
  at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
  at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

FYI -- I have worked around this issue by altering the architecture of the interfaces a tad:

public interface IHttpHeaderInspectingAuthenticatorFactory
{
    IHttpHeaderInspectingAuthenticator Construct(HttpHeaderInspectingAuthenticatorConfigurationElement config);
}

public interface IHttpHeaderInspectingAuthenticatorFactory<T> : IHttpHeaderInspectingAuthenticatorFactory
    where T: HttpHeaderInspectingAuthenticatorConfigurationElement
{
    IHttpHeaderInspectingAuthenticator<T> Construct(T config);
}

public abstract class HttpHeaderInspectingAuthenticatorFactoryBase<T> : IHttpHeaderInspectingAuthenticatorFactory<T>
            where T : HttpHeaderInspectingAuthenticatorConfigurationElement
{
    protected static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public abstract IHttpHeaderInspectingAuthenticator<T> Construct(T config);

    public IHttpHeaderInspectingAuthenticator Construct(HttpHeaderInspectingAuthenticatorConfigurationElement config)
    {
        return Construct((T)config);
    }
}

 public class BasicAuthenticationInspectingAuthenticatorFactory :
    HttpHeaderInspectingAuthenticatorFactoryBase<BasicAuthenticationHeaderInspectorConfigurationElement>
{
    public override IHttpHeaderInspectingAuthenticator<BasicAuthenticationHeaderInspectorConfigurationElement> Construct(BasicAuthenticationHeaderInspectorConfigurationElement config)
    {
        return new BasicAuthenticationInspectingAuthenticator(config);
    }
}

And then of course, the GetInspector call becomes

public IHttpHeaderInspectingAuthenticator GetInspector()
{
    var factoryInstance = (IHttpHeaderInspectingAuthenticatorFactory)Activator.CreateInstance(Type.GetType(Factory));
    return factoryInstance.Construct(this);
}

So I assume there must be a lapse in my understanding here... hoping someone can shed some light.

Thanks!


I think you need to cast this as dynamic. for example factoryInstance.Construct((dynamic)this); I think the issue is that the dynamic invocation uses compile time information for the cached delegate (Target)'s signature by default.

Since you implementation of the dynamic call is in the base class it is what it is using for the signature, but because your late bound signature is a subclass you can't go HttpHeaderInspectingAuthenticatorConfigurationElement -> BasicAuthenticationHeaderInspectorConfigurationElement, there fore by casting the argument to dynamic you are telling the DLR that the arguments type is determined at runtime.


Are you saying that:

 dynamic factoryInstance = Activator.CreateInstance(Type.GetType(Factory));
 dynamic parameter = this;
 return factoryInstance.Construct(parameter); 

Did not work?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜