views:

115

answers:

2

I'm getting a peculiar exception using the Html.RenderPartial method. ASP.NET MVC seems to be unable to an object of type ClassA to an object of type ClassA. I was wondering if anyone knows what's going on.

Here's a little more background info. I'm having the following hierarchy in place:

public interface IInterface
{
    string Name { get; }
}

public class ClassA : IInterface
{
    public string Name
    {
        get
        {
            return "ClassA ";
        }
    }
}

Using these two in a view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<IInterface>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">

<% foreach (IInterface item in Model)
   {
       Html.RenderPartial(string.Concat(item.Name, "UserControl"), item);
   }
%>

</asp:Content>

And having a UserControl named ClassAUserControl with this header:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ClassA>" %>

Edit: TypeHelpers.IsCompatibleObject(Object value) decides that the two types are different:

public static bool IsCompatibleObject<T>(object value)
{
    return ((value is T) || ((value == null) && TypeAllowsNullValue(typeof(T))));
}

In the above case T is ClassA and the type of value is ClassA. That really makes me wonder why 'value is T' fails...

A: 

You should change:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<IInterface>>" %>

to

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<ClassA>>" %>

It does not know that your IInterface object is also a ClassA.

Or change:

Html.RenderPartial(string.Concat(item.Name, "UserControl"), item);

to casting (will fail if there exists objects which are not ClassA

Html.RenderPartial(string.Concat(item.Name, "UserControl"), (ClassA)item);

or properly the best solution (if you expect other types than ClassA)

foreach (IInterface item in Model)

to

foreach (ClassA item in Model.OfType<ClassA>())

this will only go through elements of type ClassA.

lasseespeholt
Thanks for your quick response but the whole idea was keeping the view unaware of the implementations of IInterface, and letting it delegate all specific rendering to partial views. This change would break my design :-)I know how to fix the problem (making my design a little more ugly..), still I was wondering why ASP.NET MVC is unable to cast an object of type 'ClassA' to an object of type 'ClassA'.
Jeroen
Hhm, what about the last solution I gave? it only takes the classes of type `ClassA`. But what does it matter when your usercontrol only takes `ClassA`? Maybe you should try: `ViewPage<IEnumerable<dynamic>>`
lasseespeholt
The view might not even be aware that ClassA exists, ClassA might be in another library that's loaded as a plugin. I'd like to keep the view completely in the dark about the implementations of IInterface. This allows me to add a second class, say ClassB, and add a second partial view (ClassBUserControl). The view will then be able to render both partial views without any changes.I'll try using the dynamic approach you suggested.
Jeroen
Using VS 2008, the dynamic keyword is not available.
Jeroen
Ahh I get it now ;) If nobody answers, I'll look at it later today :)
lasseespeholt
+1  A: 

Your usercontrol is expecting a ClassA Object whereas you give to it a IInterface object. He cannot downcast it to object A because he can't know it is a class A. You might do something of the kind using reflection to recast your IInterface to get back your ClassA type, but this would be so ugly I prefer not think about it another second...

Yoann