Pergunta

Estou tentando criar um aplicativo da web onde desejo poder conectar assemblies separados.Estou usando o MVC Preview 4 combinado com o Unity para injeção de dependência, que uso para criar os controladores a partir dos meus assemblies de plugins.Estou usando WebForms (aspx padrão) como meu mecanismo de visualização.

Se eu quiser usar uma view, fico preso naquelas que estão definidas no projeto principal, por causa da compilação dinâmica da parte ASPX.Estou procurando uma maneira adequada de incluir arquivos ASPX em um assembly diferente, sem precisar passar por toda a etapa de implantação.Estou perdendo algo óbvio?Ou devo recorrer à criação de minhas visualizações de forma programática?


Atualizar:Mudei a resposta aceita.Embora a resposta de Dale seja muito completa, optei pela solução com um provedor de caminho virtual diferente.Funciona perfeitamente e leva apenas cerca de 20 linhas de código, eu acho.

Foi útil?

Solução

Essencialmente, este é o mesmo problema que as pessoas tiveram com WebForms e tentaram compilar seus arquivos UserControl ASCX em uma DLL.Eu achei isto http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx isso pode funcionar para você também.

Outras dicas

Levei muito tempo para fazer isso funcionar corretamente a partir de vários exemplos parciais, então aqui está o código completo necessário para obter visualizações de uma pasta Views em uma biblioteca compartilhada estruturada da mesma forma que uma pasta Views normal, mas com tudo configurado para construir como incorporado recursos.Ele só usará o arquivo incorporado se o arquivo normal não existir.

A primeira linha de Application_Start:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider());

O VirtualPathProvider

   public class EmbeddedVirtualFile : VirtualFile
{
    public EmbeddedVirtualFile(string virtualPath)
        : base(virtualPath)
    {
    }

    internal static string GetResourceName(string virtualPath)
    {
        if (!virtualPath.Contains("/Views/"))
        {
            return null;
        }



        var resourcename = virtualPath
            .Substring(virtualPath.IndexOf("Views/"))
            .Replace("Views/", "OrangeGuava.Common.Views.")
            .Replace("/", ".");

        return resourcename;

    }


    public override Stream Open()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();


        var resourcename = GetResourceName(this.VirtualPath);
        return assembly.GetManifestResourceStream(resourcename);
    }




}

public class EmbeddedViewPathProvider : VirtualPathProvider
{


    private bool ResourceFileExists(string virtualPath)
    {

        Assembly assembly = Assembly.GetExecutingAssembly();


        var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath);
        var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename);
        return result;
    }

    public override bool FileExists(string virtualPath)
    {
        return base.FileExists(virtualPath) || ResourceFileExists(virtualPath);
    }


    public override VirtualFile GetFile(string virtualPath)
    {

        if (!base.FileExists(virtualPath))
        {
            return new EmbeddedVirtualFile(virtualPath);
        }
        else
        {
            return base.GetFile(virtualPath);
        }

    }

}

A etapa final para fazê-lo funcionar é que o Web.Config raiz deve conter as configurações corretas para analisar visualizações MVC fortemente tipadas, já que aquela na pasta de visualizações não será usada:

<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <controls>
    <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

Algumas etapas adicionais são necessárias para fazê-lo funcionar com o Mono.Primeiro, você precisa implementar GetDirectory, já que todos os arquivos na pasta views são carregados quando o aplicativo é iniciado, e não conforme necessário:

public override VirtualDirectory GetDirectory(string virtualDir)
    {
        Log.LogInfo("GetDirectory - " + virtualDir);
        var b = base.GetDirectory(virtualDir);
        return new EmbeddedVirtualDirectory(virtualDir, b);
    }

public class EmbeddedVirtualDirectory : VirtualDirectory
{
    private VirtualDirectory FileDir { get; set; } 

    public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir)
        : base(virtualPath)
    {
        FileDir = filedir;
    }

    public override System.Collections.IEnumerable Children
    {
        get { return FileDir.Children; }
    }

    public override System.Collections.IEnumerable Directories
    {
        get { return FileDir.Directories; }
    }

    public override System.Collections.IEnumerable Files
    {
        get {

            if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/"))
            {
                return FileDir.Files;
            }

            var fl = new List<VirtualFile>();

            foreach (VirtualFile f in FileDir.Files)
            {
                fl.Add(f);
            }


            var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");

            Assembly assembly = Assembly.GetExecutingAssembly();

            var rfl = assembly.GetManifestResourceNames()
                .Where(s => s.StartsWith(resourcename))
                .Select(s => VirtualPath + s.Replace(resourcename, ""))
                .Select(s => new EmbeddedVirtualFile(s));
            fl.AddRange(rfl);

            return fl;
        }
    }
}

Finalmente, visualizações fortemente tipadas funcionarão quase perfeitamente, mas não exatamente.O modelo será tratado como um objeto não digitado, portanto, para obter uma digitação forte, você precisa iniciar suas visualizações compartilhadas com algo como

<% var Model2 = Model as IEnumerable<AppModel>;  %>
protected void Application_Start()
{
    WebFormViewEngine engine = new WebFormViewEngine();

    engine.ViewLocationFormats = new[] { "~/bin/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
    engine.PartialViewLocationFormats = engine.ViewLocationFormats;

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(engine);

    RegisterRoutes(RouteTable.Routes);
}

Defina a propriedade 'Copiar para saída' da sua visualização como 'Copiar sempre'

Um complemento para todos vocês que ainda procuram o Santo Graal:Cheguei um pouco mais perto de encontrá-lo, se você não estiver muito apegado ao mecanismo de visualização de webforms.

Recentemente experimentei o viewengine Spark.Além de ser totalmente incrível e eu não voltaria aos webforms mesmo se estivesse ameaçado, ele também fornece alguns ganchos muito bons para a modularidade de um aplicativo.O exemplo em seus documentos é usar o Windsor como um contêiner IoC, mas não consigo imaginar que seja muito mais difícil se você quiser adotar outra abordagem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top