【问题标题】:Get Areas associated with an MVC project获取与 MVC 项目关联的区域
【发布时间】:2010-09-01 15:56:44
【问题描述】:

有没有一种方法可以获取 MVC 项目中区域的名称?

以下是我能想到的几种方法:

a) 如果我有源代码,我可以浏览项目文件夹结构并枚举 Areas 文件夹下的文件夹。但这并不能保证所有文件夹都代表区域,除非我还枚举了每个子文件夹下的 Controllers 和 Views 文件夹。如您所知,这种方法很糟糕。

b) 从二进制文件中,我可以枚举所有符合条件RootNamespaceOfProject.Areas.* 的命名空间。

或者,我相信还有更优雅的方式。在 ASP.NET MVC 框架中必须有一些字典来记录所有领域。

其次,在 MVC 框架中是否还有一个程序化构造来表示一个区域?我似乎找不到一个。只有四种结构与区域相关

 1. AreaRegistration
 2. AreaRegistrationContext
 3. IRouteWithArea
 4. AreaHelpers (an internal class)

如果有,是否有可能枚举该区域内的所有控制器?

已编辑

我刚刚注意到文件夹 \Windows\Microsoft.NET\Framework\v4.x.x\Temporary ASP.NET Files\root\RandomlyGeneratedHash1\RandomlyGeneratedHash2\UserCache 中有一个名为 MVC-AreaRegistrationTypeCache.xml 的文件。

这个文件夹有两个文件:

a) MVC-AreaRegistrationTypeCache.xml:该文件列出了机器上所有程序集中有区域的所有区域。

b) MVC-ControllerTypeCache.xml:此文件列出程序集区域内的控制器。

现在,唯一需要确定的是是否有某种编程方式让 MVC 框架读取这些文件并告诉我二进制文件中是否存在某个区域。

我认为AreaRegistration 类可能就是其中之一。进一步探索它......

【问题讨论】:

  • 这两个想法都不是双赢的解决方案,因为它们取决于您无法控制的东西!区域可以放置在项目结构中的任何位置,只要它们被注册和映射。

标签: asp.net-mvc-2 asp.net-mvc-areas


【解决方案1】:

看来,您能够检索在项目中注册的路线的唯一方法是枚举项目中继承 AreaRegistration 的类型,似乎没有任何跟踪当前注册区域的私有或公共对象。

下面是长解释...

这里要记住的一个障碍是,区域只不过是任意字符串和命名空间列表之间的耦合。当一个区域被注册时,它只是扩展了应用程序的路由集合,其中包含一些可由唯一“区域”DataToken 识别的新规则。

如果查看注册区域的过程,则必须从System.Web.Mvc.AreaRegistration继承并覆盖RegisterArea()。 RegisterArea() 接收到一个AreaRegistrationContext,它定义了一个区域名称、路由集合和对象状态,但是如果您观察实现 RegisterArea() 的格式,它会返回 void 并且不做任何事情来保存上下文对象。此外,如果您查看在 RegisterArea() 被触发之前运行的代码(Reflector),您会发现传递给 RegisterArea() 的 AreaRegistrationContext 对象永远不会被永久跟踪。

internal static void RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state)
{
    foreach (Type type in TypeCacheUtil.GetFilteredTypesFromAssemblies("MVC-AreaRegistrationTypeCache.xml", new Predicate<Type>(AreaRegistration.IsAreaRegistrationType), buildManager))
    {
        ((AreaRegistration) Activator.CreateInstance(type)).CreateContextAndRegister(routes, state);
    }
}

internal void CreateContextAndRegister(RouteCollection routes, object state)
{
    AreaRegistrationContext context = new AreaRegistrationContext(this.AreaName, routes, state);
    string str = base.GetType().Namespace;
    if (str != null)
    {
        context.Namespaces.Add(str + ".*");
    }
    this.RegisterArea(context);
}

如您所见,对静态方法RegisterAllAreas() 的调用会调用内部RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state),然后调用内部CreateContextAndRegister(RouteCollection routes, object state),后者会创建AreaRegistrationContext 并将其传递给RegisterArea()

据我所知,永远不会为永久存储的每个区域创建 AreaRegistrationContext。

【讨论】:

  • 看准了,内森!我注意到了同样的事情。非常感谢。因此,我可以获取从 AreaRegistration 派生的所有类型,然后将对象的 AreaName 属性值与我要查找的区域的名称进行比较。伟大的!坏事是,区域和它的控制器之间实际上没有任何映射保存在任何地方。
  • @Water Cooler 您可以解析 Routes.RouteCollection 并检查每个路由的“命名空间”DataToken (string[]),并将这些值与所有注册控制器的命名空间进行比较。我很好奇此功能的预期用途是什么,因为可能有更好的选择尚未探索。
【解决方案2】:

您可以做 MVC 正在做的事情,迭代所有引用的程序集并查找从 AreaRegistration 继承的类。然后你可以简单地得到AreaRegistration.AreaName

我计划这样做是为了使用 Twitter Bootstrap 构建我的顶级导航栏。

【讨论】:

  • 如何迭代所有引用的程序集?
  • 类似new[] { Assembly.GetExecutingAssembly() }.Concat(Assembly.GetExecutingAssembly.GetReferencedAssemblies());
  • 你可以使用 AppDomain.CurrentDomain.GetAssemblies() 来获取我的所有想法
【解决方案3】:

这个帖子有助于找到答案,但这里没有人明确发布它。这是我想出的,但请注意,您可能需要根据您的所有区域是否都在同一个程序集中进行调整。

private IEnumerable<AreaRegistration> GetAllAreasRegistered()
{
    var assembly = this.GetType().Assembly;
    var areaTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(AreaRegistration)));
    var areas = new List<AreaRegistration>();
    foreach (var type in areaTypes)
    {
        areas.Add((AreaRegistration)Activator.CreateInstance(type));
    }
    return areas;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2019-10-07
    • 1970-01-01
    相关资源
    最近更新 更多