WebParts dynamisch aus Assemblies laden

Posted September 11, 2007

Ein nettes Feature bei dem freien ASP.NET Portal mojoPortal ist das dynamische hinzufügen von Assemblies, bei denen es sich meistens um kompilierte Klassenbibliotheken als DLL handelt. In diesen Assemblies können sich dann WebParts befinden, die automatisch erkannt und auch auf der Website erkannt werden können.

Der Code zum Laden von WebParts aus einer DLL könnte dann beispielsweise so aussehen:

Assembly assembly = Assembly.LoadFrom(assemblyFileName);
Type type = assembly.GetType("MyWebPart", true);
WebPart webPart = Activator.CreateInstance(type) as WebPart;
contentPanel.Controls.Add(webPart);

Wenn man sich allerdings eines WebPartManagers bedient. Dann wirft dieser nach dem Hinzufügen eines WebParts folgende Fehlermeldung:

The Type must be loadable by BuildManager.GetType(string typeName)

Wenn man ein Assembly erst zur Laufzeit hinzufügt, dann zählt es nicht mehr zum so genannten "Top_Level_Assembly" wo beispielsweise der kompilierte App_Code-Ordner drin liegt. Da der WebPartManager allerdings auch später noch immer an die Klassendefinitionen des WebParts herankommen muss, verbietet er alle WebParts, die in anderen Assemblies liegen, es sei denn, sie sind in der web.config eingetragen, doch aus diese kann man zur Laufzeit auch nur lesend zugreifen.

Die Fehlermeldung kommt ja aus der Methode WebPartManager.VerifyType() . Die Lösung ist also relativ einfach: Man überschreibt diese Methode und verhindert so eine Exception. Da diese Methode aber leider privat und nicht protected ist, muss man die aufrufende Methode überschreiben. Dabei handelt es sich um WebPartManager.CopyWebPart(WebPart). Ein modifizierter WebPartManager ohne Exception bei externen Assemblies könnte nun so aussehen:

using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
 
public class ModifiedWebPartManager : WebPartManager
{
    protected override WebPart CopyWebPart(WebPart webPart)
    {
        WebPart newPart;
        if (webPart is GenericWebPart) {
            Type childType = ((GenericWebPart)webPart).ChildControl.GetType();
            Control newControl = (Control)Internals.CreateObjectFromType(childType);
            newControl.ID = CreateDynamicWebPartID(childType);
            newPart = CreateWebPart(newControl);
        } else {
            newPart = (WebPart)Internals.CreateObjectFromType(webPart.GetType());
        }
        newPart.ID = CreateDynamicWebPartID(webPart.GetType());
        return newPart;
    }
}

(originally posted on 2007-09-12)