ASP.NET MVC:使用由MEF加载模型类型的视图不能由视图引擎找到 [英] ASP.NET MVC: Views using a model type that is loaded by MEF can't be found by the view engine

查看:111
本文介绍了ASP.NET MVC:使用由MEF加载模型类型的视图不能由视图引擎找到的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图允许控制器和视图动态地导入一个MVC应用程序创建一个框架。下面是它如何工作至今:


  • 我使用.NET 4,ASP.NET MVC 3 RC和剃刀的ViewEngine

  • 控制器出口和使用MEF每个项目进口 - 我称之为从某一项目的一组控制器和视图的模块

  • 使用MEF发现组件是动态的BuildManager使用pre-应用程序启动方法引用和 BuildManager.AddReferencedAssembly

  • 二进制文件(从出口项目)和视图使用的是生成事件复制到目标项目的文件夹结构

  • 控制器使用自定义控制器工厂从DefaultControllerFactory继承并重写GetControllerType()
  • 选中
  • 查看正在使用从RazorViewEngine继承并重写GetView()和GetPartialView(),以允许它寻找意见模块特定视图目录中的自定义视图引擎选择

一切工作至今的除了的使用强类型的模型视图。使用动态模型做工精细,但是当我使用 @model 指定型号,我得到一个YSOD,说意见视图索引或它的主人是不是找到。

在调试我的视图引擎的实现,我可以看到:
this.VirtualPathProvider.FileExists(的String.Format(this.ViewLocationFormats [2]的viewName,controllerContext.RouteData.GetRequiredString(控制器)))返回true,尽管

this.FileExists(controllerContext,的String.Format(this.ViewLocationFormats [2]的viewName,controllerContext.RouteData.GetRequiredString(控制器)))返回false。

展望反射镜,RazorViewEngine实施 FILEEXISTS的()最终卷起这样做:

 收益率(BuildManager.GetObjectFactory(virtualPath,FALSE)!= NULL);

不过,我无法查看 BuildManager.GetObjectFactory()从反射器,因为它隐藏在某种程度上。

我怀疑它是与事实模型类型是从MEF加载的类型,但因为我已经通过引用从BuildManager MEF发现的组件,我出线索。任何人都可以提供多一点洞察可能什么?


更新:
原来我使用反射的一个过时的版本,从.NET 4之前,我可以看到GetObjectFactory()了,但我真的不能似乎发现了什么帮助。我试着加入到这一点我FindView()过载:


 {
  VAR路径=的String.Format(this.ViewLocationFormats [2]的viewName,controllerContext.RouteData.GetRequiredString(控制器));
  VAR objFactory = System.Web.Compilation.BuildManager.GetObjectFactory(virtualPath:路径,throwIfNotFound:真正的);
 }
 抓住
 {
 }

不幸的是, objFactory 结束了零,没有例外被抛出。与编译错误处理的所有位都是私有方法或类型,所以我不能调试任何的一部分,但它甚至好像他们最终会抛出一个异常,这似乎并没有发生。貌似我在一个死胡同我一次。救命啊!


更新2

我发现在那里FindView()被调用,如果我叫 AppDomain.CurrentDomain.GetAssemblies()点,组装,该模型类型在的包括在内。不过,我可以用无法加载键入 Type.GetType()


更新3

下面就是我看到的:



更新4

下面是执行的ViewEngine:

 使用系统;
使用System.Linq的;
使用System.Web.Mvc;
使用System.Web.Hosting;
使用System.Web.Compilation;命名空间Site.Admin.Portal
{
    公共类ModuleViewEngine:RazorViewEngine
    {
        私人静态只读的String [] = viewLocationFormats新的String []
        {
            〜/查看/ {0} / {{1}} / {{0}}的.aspx
            〜/查看/ {0} / {{1}} / {{0}}。ASCX
            〜/查看/ {0} / {{1}} / {{0}}。CSHTML
            〜/查看/ {0} /共享/ {{0}}的.aspx
            〜/查看/ {0} /共享/ {{0}}。ASCX
            〜/查看/ {0} /共享/ {{0}}。CSHTML
        };        公共ModuleViewEngine(IModule的模块)
        {
            this.Module =模块;
            VAR格式= viewLocationFormats.Select(F =>的String.Format(F,module.Name))ToArray的()。            this.ViewLocationFormats =格式;
            this.PartialViewLocationFormats =格式;
            this.AreaViewLocationFormats =格式;
            this.AreaPartialViewLocationFormats =格式;
            this.AreaMasterLocationFormats =格式;
        }        公众的IModule模块{搞定;私人集; }        公众覆盖ViewEngineResult FindPartialView(ControllerContext controllerContext,字符串partialViewName,布尔useCache将)
        {
            VAR MODULENAME = controllerContext.RouteData.GetRequiredString(模块);
            如果(moduleName.Equals(this.Module.Name,StringComparison.InvariantCultureIgnoreCase))
            {
                返回base.FindPartialView(controllerContext,partialViewName,useCache将);
            }
            否则返回新ViewEngineResult(新的String [0]);
        }        公众覆盖ViewEngineResult FindView(ControllerContext controllerContext,字符串的viewName,字符串masterName,布尔useCache将)
        {
            VAR MODULENAME = controllerContext.RouteData.GetRequiredString(模块);
            如果(moduleName.Equals(this.Module.Name,StringComparison.InvariantCultureIgnoreCase))
            {
                VAR baseResult = base.FindView(controllerContext,的viewName,masterName,useCache将);
                返回baseResult;
            }
            否则返回新ViewEngineResult(新的String [0]);
        }
    }
}


解决方案

根据更新2,我猜你已经得到了你组装的明确载入副本(即,它是通过一些其他方法加载超过负荷,像LoadFrom)。显式加载组件掀起预留进入一个特殊的地方,因为他们不能满足隐式类型的要求。对Fusion(组装装载机)的规则可以被pretty晦涩难懂。

我同意这一点,得到这个工作,你的DLL将不得不将位于/ bin否则将永远无法满足的隐式类型要求马修的评估。

I'm attempting to create a framework for allowing controllers and views to be dynamically imported into an MVC application. Here's how it works so far:

  • I'm using .NET 4, ASP.NET MVC 3 RC and the Razor ViewEngine
  • Controllers are exported and imported using MEF per project - I call a set of controllers and views from a given project a "Module"
  • Assemblies discovered using MEF are dynamically referenced by the BuildManager using a pre-application start method and BuildManager.AddReferencedAssembly.
  • Binaries (from exporting project) and Views are copied into the target project's folder structure using a build event
  • Controllers are selected using a custom controller factory which inherits from DefaultControllerFactory and overrides GetControllerType()
  • Views are selected using a custom view engine which inherits from RazorViewEngine and overrides GetView() and GetPartialView() to allow it to look for views in Module-specific view directories

Everything works so far except for views using a strongly typed model. Views that use the dynamic model work fine, but when I specify a model type using @model, I get a YSOD that says "The view 'Index' or its master was not found".

When debugging my ViewEngine implementation, I can see that: this.VirtualPathProvider.FileExists(String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller"))) returns true, while

this.FileExists(controllerContext, String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller"))) returns false.

Looking in Reflector, the RazorViewEngine implementation of FileExists() ultimately winds up doing this:

return (BuildManager.GetObjectFactory(virtualPath, false) != null);

However, I can't view BuildManager.GetObjectFactory() from Reflector because it's hidden somehow.

I'm suspecting that it has something to do with the fact that the model type is a type that is loaded from MEF, but since I'm already referencing the assemblies discovered by MEF from BuildManager, I'm out of leads. Can anyone provide a little more insight into what might be going on?


Update: Turns out I was using an outdated version of Reflector from before .NET 4. I can see GetObjectFactory() now, but I can't really seem to find anything helpful. I've tried adding this into my FindView() overload:

try { var path = String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller")); var objFactory = System.Web.Compilation.BuildManager.GetObjectFactory(virtualPath: path, throwIfNotFound: true); } catch { }

Unfortunately, objFactory ends up null, and no exception gets thrown. All the bits that deal with compilation errors are part of private methods or types so I can't debug any of that, but it even seems like they'd end up throwing an exception, which doesn't seem to be happening. Looks like I'm at a dead end again. Help!


Update 2

I've discovered that at the point where FindView() is being called, if I call AppDomain.CurrentDomain.GetAssemblies(), the assembly that the model type is in is included. However, I cannot load the type using Type.GetType().


Update 3

Here's what I'm seeing:



Update 4

Here's the ViewEngine implementation:

using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Hosting;
using System.Web.Compilation;

namespace Site.Admin.Portal
{
    public class ModuleViewEngine : RazorViewEngine
    {
        private static readonly String[] viewLocationFormats = new String[]
        {
            "~/Views/{0}/{{1}}/{{0}}.aspx",
            "~/Views/{0}/{{1}}/{{0}}.ascx",
            "~/Views/{0}/{{1}}/{{0}}.cshtml",
            "~/Views/{0}/Shared/{{0}}.aspx",
            "~/Views/{0}/Shared/{{0}}.ascx",
            "~/Views/{0}/Shared/{{0}}.cshtml"
        };

        public ModuleViewEngine(IModule module)
        {
            this.Module = module;
            var formats = viewLocationFormats.Select(f => String.Format(f, module.Name)).ToArray();

            this.ViewLocationFormats = formats;
            this.PartialViewLocationFormats = formats;
            this.AreaViewLocationFormats = formats;
            this.AreaPartialViewLocationFormats = formats;
            this.AreaMasterLocationFormats = formats;
        }

        public IModule Module { get; private set; }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
        {
            var moduleName = controllerContext.RouteData.GetRequiredString("module");
            if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                return base.FindPartialView(controllerContext, partialViewName, useCache);
            }
            else return new ViewEngineResult(new String[0]);
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
        {
            var moduleName = controllerContext.RouteData.GetRequiredString("module");
            if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                var baseResult = base.FindView(controllerContext, viewName, masterName, useCache);
                return baseResult;
            }
            else return new ViewEngineResult(new String[0]);
        }        
    }
}

解决方案

Based on Update 2, I'm guessing what you've got is an explicitly loaded copy of your assembly (that is, it was loaded through some other method than Load, like LoadFrom). Explicitly loaded assemblies are set off aside into a special place, because they are not allowed to satisfy implicit type requirements. The rules for Fusion (the assembly loader) can be pretty arcane and hard to understand.

I agree with Matthew's assessment that, to get this to work, your DLL is going to have to be in /bin or else it will never be able to satisfy the implicit type requirement.

这篇关于ASP.NET MVC:使用由MEF加载模型类型的视图不能由视图引擎找到的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆