28 votes

Vues MEF et Razor dans la bibliothèque de classes

J'ai une application composite ASP.NET MVC 3 Razor utilisant le MEF. Tout se passe bien si je déploie les plugins sous forme de fichiers DLL et les vues (CSHTML) sous la forme habituelle. Views de l'application. Mais ce n'est pas très propre et ce ne sera pas un vrai plugin si je ne place pas les vues comme ressources intégrées dans les fichiers DLL (avec les contrôleurs et les modèles).

J'ai suivi de nombreux articles (la plupart d'entre eux sont périmés). En fait, il y en a un assez bon ici sur Stack Overflow : Contrôleurs et vues dans une bibliothèque de classes

J'ai également vérifié les documents relatifs à VirtualPathProvider et j'ai pu en construire un personnalisé qui trouve le fichier dans l'assemblage et le charge parfaitement (ou du moins obtient le flux vers lui). Pour cela, j'ai suivi la méthode VirtualPathProvider documentation sur MSDN .

Il existe également une implémentation pour VirtualFile mais pas encore pour VirtualDirectory.

Voici le problème. Je travaille avec des vues Razor. Je sais qu'elles ont besoin de spécifications de configuration de la part de l'utilisateur. web.config pour que Razor les construise. Mais si je les intègre dans la DLL, cette configuration est tout simplement perdue.

Je me demande si c'est pour ça que je continue à avoir l'erreur :

La vue située dans '~/Plugins/CRM.Web.Views.CRM.Index.cshtml' doit dériver de WebViewPage ou de WebViewPage. de WebViewPage, ou de WebViewPage.

Peut-être dois-je simplement ajouter du code pour que cela fonctionne ? Vous avez des idées ?

7voto

sky-dev Points 1990

Ma méthode préférée pour intégrer des vues Razor dans une bibliothèque de classes consiste à les copier dans les dossiers Views/Areas du site MVC avec un événement post-construction. Il est possible de spécifier des emplacements de vue personnalisés si vous modifiez le ViewEngine ou le VirtualPathProvider.

La partie la plus délicate pour moi a été de faire fonctionner intellisense dans ces bibliothèques View Class. Tout d'abord, vous devez ajouter un Web.Config à votre assemblage View. Notez que vous n'avez pas besoin de l'inclure dans votre assemblage. Il doit seulement être dans le répertoire racine de l'assemblage (ou le dossier views). Voici un exemple. Tenez compte de l'importante section Assemblages/Compilation.

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>

  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>

  <system.web>
    <compilation targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>

    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>

    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <controls>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>
</configuration>

Ensuite, vous devez modifier le fichier vbproj de votre bibliothèque de classes de manière à ce que tous les éléments OutputPath pointent vers 'bin' au lieu de 'Debug'. \bin\ ou "Release \bin\ '. C'est la principale différence que j'ai trouvée entre les bibliothèques de classes et les types de projets web ASP.Net qui peut causer des bugs d'intellisense.

Si vous recevez toujours l'erreur "must inherits", envisagez d'utiliser @Inherits System.Web.Mvc.WebViewPage dans vos vues. Si vous ne copiez pas vos vues dans votre projet de site Web, il se peut que vous les chargiez à partir de ressources intégrées en utilisant un ViewEngine / VirtualPathProvider personnalisé. Si c'est le cas, vous avez définitivement besoin des Inherits pour que Razor sache quelle est la classe de base de votre vue, malheureusement.

Bonne chance.

3voto

Darin Dimitrov Points 528142

Vous pourriez jeter un coup d'œil à la article de blog suivant .

2voto

Nick Bork Points 3551

Hossam,

Le poste dont vous parlez est ce que Darin a déjà suggéré. Le principal inconvénient de cette approche est l'utilisation du compilateur personnalisé MvcRazorClassGenerator pour convertir les fichiers de vues CSHTML en fichiers de classe. Pour ce faire, vous devez définir chaque vue CSHTML de votre projet sur Content et définir l'outil personnalisé sur MvcRazorClassGenerator.

Je ne peux pas parler pour LordALMMa mais j'ai téléchargé le source du compilateur et je l'ai essayé et il ne fonctionne pas exactement comme je l'espérais.

Mon autre approche consistait à inclure les fichiers CSHTML en tant que ressources intégrées dans la DLL externe, à lire le contenu brut du fichier et à exécuter la vue sous forme de chaîne (voir l'exemple de RazorEngine sur CodeProject) : http://razorengine.codeplex.com/ )

Je ne voulais pas dépendre entièrement du RazorEngine dans une application d'entreprise, car je ne sais pas dans quelle mesure il est compatible avec l'ensemble de la syntaxe Razor, alors j'ai abandonné cette idée pour le moment.

Je viens d'un prototype que j'ai construit en ASP.NET MVC 2.0 et qui est une application multi-locataires. Sur une ferme de serveurs, nous avons une instance d'une application en cours d'exécution où tous les clients partagent la même base de code. Dans mon prototype MVC 2.0, j'ai été en mesure de déterminer pour quel "client" la demande a été faite, de vérifier si un contrôleur personnalisé remplace la base (pour les personnalisations du code de base) et également de vérifier si des vues personnalisées (pour les personnalisations de la vue de base). Cela nous permet de déployer un "plugin" pour chaque client. Le logiciel détecte si le client a un contrôleur personnalisé qui correspond à la demande ainsi qu'une action personnalisée qui correspond et si c'est le cas, il utilise le contrôleur/action personnalisé à la place.

Lorsque j'ai commencé à migrer mon prototype vers MVC 3, j'ai rencontré le même problème que LordALMMa, l'erreur "The view at '...Index.cshtml' must derive from WebViewPage, or WebViewPage". Je vais chercher à placer "@inherits System.Web.Mvc.WebViewPage" sur mes vues CSHTML et voir si cela me rapproche de la solution.

Puisque j'ai un prototype MVC 2.0 fonctionnel, l'utilisation de MVC 3 Razor n'est pas une priorité absolue et je ne perds pas beaucoup de temps à ce sujet. Je suis sûr que je peux porter le MVC 2.0 vers MVC 3.0 en utilisant le moteur WebForms si nous avons besoin de tirer parti du Framework 4.0.

0voto

Zasz Points 3933

Je pense que vous avez de bonnes raisons de vouloir des vues dans les DLL. Cependant, il faut aussi considérer que c'est une façon inhabituelle de tout regrouper en une seule entité.

Si vous développez un plugin, les gens optent aujourd'hui pour un conditionnement au format NUGET, qui résout également votre type de problème, entre autres. Il possède une structure .nupkg qui est également un moyen de distribuer des plugins sous forme de paquets et de bibliothèques.

Une autre solution que les communautés suivent généralement (si elles ne veulent pas quelque chose d'aussi élaboré que nuget) est de coder les DLL des plugins de telle sorte qu'ils n'utilisent pas de moteurs de visualisation comme razor, mais qu'ils produisent eux-mêmes du HTML en utilisant la vieille méthode primitive de Response.Write et deviennent ainsi indépendants des fichiers cshtml. Si vous voulez toujours utiliser cshtml - voir cet article de blog pour les précompiler en classes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X