views:

159

answers:

2

Hello.

I'm having a little trouble with some casts that i cant figure out out to solve.

I have this interface and the following implementations:

public interface IConfigureServiceOnServer
{
    void Configure(Service service);
}

public sealed class ServiceConfigurator : IConfigureServiceOnServer
{
 ...
}

I'm loading these types at runtime using a dictionary (that holds the assembly and type names) with the following method:

 public static T Resolve<T>(string type, params object[] values) where T : class
        {
            var split = refs[type].Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            var assembly = Assembly.LoadFrom(split[0] + ".dll");
            var instance = assembly.CreateInstance(
                split[1], // the name of the Type to instantiate
                true,
                BindingFlags.CreateInstance,
                null,
                values, // the params to use in the constructor
                null,
                null);
            T ret = (T)instance; //also tried with 'instance as T'
            return ret;
        }

I get an invalid cast exception when calling it like this

Resolver.Resolve<IConfigureServiceOnServer>("serviceconfiguration", "");

I can't quite figure out why i get this exception because the same code works for the next interface and its implementation:

public interface IServiceManager
{
    void Create();
    void Configure();
}

public sealed class Service : IServiceManager
{
...
}

Any ideas why the same code works for the 2nd example and not for the first?

Thanks in advance.

EDIT:

When i'm debugging and i stop execution right before the return in the Resolve method i can use the immediate window - i test the cast and it works.

EDIT2: This is the exception message:

Unable to cast object of type 'Codegarten.Controller.Configuration.Service.ServiceConfigurator' to type 'Codegarten.Controller.Configuration.IConfigureServiceOnServer '.

EDIT3: Some more info - Someone suggested the type loaded using reflection could be wrong so i did a little more debugging. I checked to see if the loaded type implemented the desired interface using the immediate window in visual studio:

>instance.GetType().GetInterfaces()
{System.Type[1]}
    [0]: {Name = "IConfigureServiceOnServer" FullName = "Codegarten.Controller.Configuration.IConfigureServiceOnServer"}

SOLUTION
Ok, the problem was caused by the Assembly.LoadFrom method, which loaded the assembly into a different context than the applications.

I solved this issue by using Assembly.Load instead.

+2  A: 

Are you absolutely sure that the type IConfigureServiceOnServer in the line

Resolver.Resolve<IConfigureServiceOnServer>("serviceconfiguration", "");

compiles to exactly the same type in the same namespace in the same assembly as the one in your class definition?

public sealed class ServiceConfigurator : IConfigureServiceOnServer {
    ...
}

I'm guessing from the names that you have some kind of web service in your solution. Might IConfigureServiceOnServer in one location refer to the actual interface defined in the service, and IConfigureServiceOnServer in the other location refer to the proxy copy defined in the service reference?

Alternatively, the ServiceConfigurator class might compile to a different type than the one actually being instantiated in your reflection code.

UPDATE:

I suggest you debug the AssemblyQualifiedName property of both the class and its interface for (a) the original defined type, and (b) the one instantiated by reflection.

Christian Hayter
Agreed, more than one IConfigureServiceOnServer type at work here.
Hans Passant
No, i'm not using web services. I'm just mapping services (as in other applications) into my c# app. I've look (ildasm) and the assembly which contains both types and interface built correctly.
Alka
Fair enough, but the fact remains that an exception saying "cannot cast X to Y", means that X does not inherit from or implement Y. For that to happen, either X != X or Y != Y, if you see what I mean.
Christian Hayter
I see what you mean - i've edited my post with some more information on that. The loaded type actually implements the IConfigureServiceOnServer interface. Also, using the immediate window i can cast it successfully.
Alka
Try debugging the `AssemblyQualifiedName` property instead of `FullName`.
Christian Hayter
> typeof(T).AssemblyQualifiedName"Codegarten.Controller.Configuration.IConfigureServiceOnServer, Codegarten.Controller, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" --//-->instance.GetType().AssemblyQualifiedName"Codegarten.Controller.Configuration.Apache.TracApacheConfigurator, Codegarten.Controller, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Alka
+1  A: 

I suspect the problem you are facing is that the types are loaded into different contexts. Using Assembly.LoadFrom will load the type into the LoadFrom context, while the interface you have defined I guess is in the Load context. See here for more info on the LoadFrom context.

Do you load the assemblies from different locations? If not you could try using Assembly.Load to load the assembly using the display name, this should load the type into the same context as the interface and your cast should work since the type identities will hopefully match. If this works, you might be better off just using the assembly qualified name and using Type.GetType to load the type.

Chris Taylor
Ok, i'll have to test it tomorrow and i'll get back to you. I think you're right on the context idea as the LoadFrom documentation says there can be InvalidCastException with its use. Is there a way to check the context on which a type is loaded?
Alka
It seems you were correct about the types being loaded into different contexts. I have used Assembly.Load instead and it works. Why does the LoadFrom fail if the assemblies are in the same directory as the .exe?
Alka