C# ASP.NET MVC模块

C# ASP.NET MVC模块,c#,asp.net,asp.net-mvc,asp.net-mvc-4,nuget-package,C#,Asp.net,Asp.net Mvc,Asp.net Mvc 4,Nuget Package,我需要创建一个nuget包,其中将包含共享视图、控制器、js和css文件,以便在多个其他项目中使用。本质上是一组模块化的东西,比如可以放入其他站点项目的签出或搜索页面 到目前为止,我所做的所有研究都指向使用预编译视图,但没有对控制器、js和css文件做太多说明 理想情况下,模块的视图和其他文件应该能够被使用主体项目覆盖,但文件本身不应该在主体项目中直接编辑。与添加其他nuget包时引用的DLL非常相似 到目前为止,我发现关于这类主题的答案和帖子似乎有点过时 是否有一个更干净、更现代的解决方案来创

我需要创建一个nuget包,其中将包含共享视图、控制器、js和css文件,以便在多个其他项目中使用。本质上是一组模块化的东西,比如可以放入其他站点项目的签出或搜索页面

到目前为止,我所做的所有研究都指向使用预编译视图,但没有对控制器、js和css文件做太多说明

理想情况下,模块的视图和其他文件应该能够被使用主体项目覆盖,但文件本身不应该在主体项目中直接编辑。与添加其他nuget包时引用的DLL非常相似

到目前为止,我发现关于这类主题的答案和帖子似乎有点过时

是否有一个更干净、更现代的解决方案来创建ASP.NET MVC模块nuget包,以便能够在项目之间共享完整的工作页面?

使用区域并注册这些区域。这可能是本机不支持的,您可能需要覆盖mvc4中的某些部分。看看:

只要加载了dll,您就可以始终使用反射(在应用程序启动时)注册属于
控制器
子类的所有类

剃须刀 预编译是可能的,但只有在dotnetcore中才真正值得建议,因为它是一级公民

您还可以将视图作为内容添加到项目中

缺点:

  • 更新时,它会覆盖视图(如果更改了视图,则会丢失更改)
优点:

  • 更新时,您可以在git中合并这两个更改
  • 轻松更改已存在的razor页面

    • 我找到了一个适合我们的解决方案。我们还没有完全实施这一计划,因此可能仍会出现一些不可预见的问题

      首先,创建一个MVC项目,其中包含与包需求相匹配所需的视图、控制器、javascript等。每个静态文件和视图都必须设置为项目中的嵌入式资源

      然后,添加一个类以在虚拟路径提供程序上为这些文件提供服务。这将允许使用项目访问静态文件和视图,就像它们位于同一项目中一样

      要启用自定义路由,需要实现
      RouteBase
      类。此实现需要接受虚拟路由所基于的
      字符串
      属性,以允许主机应用所需的路由前缀。对于我们的示例,该属性将默认为预订与项目中视图的关联体系结构相匹配

      RouteBase
      实现和
      VirtualPath
      类都将在setup方法中实例化。这将允许消费项目调用单个方法来设置预订引擎。此方法将采用站点路由集合和动态路由属性来附加自定义路由。该方法还将
      VirtualPathProvider
      注册到
      HostingEnvironment
      对象

      消费主机还可以通过在主机项目中的某个位置放置与预订引擎中的文件或视图路径匹配的文件来覆盖视图和任何其他静态文件

      一些代码示例

      RouteBase
      方法,如果传入路由与虚拟路由匹配,则返回正确的路由值

      public override RouteData GetRouteData(HttpContextBase httpContext)
      {
          RouteData result = null;
      
          // Trim the leading slash
          var path = httpContext.Request.Path.Substring(1);
      
          // Get the page that matches.
          var page = GetPageList(httpContext)
              .Where(x => x.VirtualPath.Equals(path))
              .FirstOrDefault();
      
          if (page != null)
          {
              result = new RouteData(this, new MvcRouteHandler());
      
              // Optional - make query string values into route values.
              AddQueryStringParametersToRouteData(result, httpContext);
      
              result.Values["controller"] = page.Controller;
              result.Values["action"] = page.Action;
          }
      
          // IMPORTANT: Always return null if there is no match.
          // This tells .NET routing to check the next route that is registered.
          return result;
      }
      
      RouteBase
      虚拟路由到NuGet包路由映射。新建
      PageInfo
      使用动态虚拟路径字符串和对真实控制器和操作名称的引用创建的对象。然后将它们存储在http上下文缓存中

      private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext)
      {
          string key = "__CustomPageList";
          var pages = httpContext.Cache[key];
          if (pages == null)
          {
              lock (synclock)
              {
                  pages = httpContext.Cache[key];
                  if (pages == null)
                  {
                      pages = new List<PageInfo>()
                      {
                          new PageInfo()
                          {
                              VirtualPath = string.Format("{0}/Contact", BookingEngine.Route),
                              Controller = "Home",
                              Action = "Contact"
                          },
                      };
      
                      httpContext.Cache.Insert(
                          key: key,
                          value: pages,
                          dependencies: null,
                          absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
                          slidingExpiration: TimeSpan.FromMinutes(1),
                          priority: System.Web.Caching.CacheItemPriority.NotRemovable,
                          onRemoveCallback: null);
                  }
              }
          }
      
          return (IEnumerable<PageInfo>)pages;
      }
      
      嵌入虚拟文件

      public override CacheDependency GetCacheDependency(string virtualPath,  virtualPathDependencies, DateTime utcStart)
      {
          string embedded = _GetEmbeddedPath(virtualPath);
      
          // not embedded? fall back
          if (string.IsNullOrEmpty(embedded))
              return base.GetCacheDependency(virtualPath,
                  virtualPathDependencies, utcStart);
      
          // there is no cache dependency for embedded resources
          return null;
      }
      
      public override bool FileExists(string virtualPath)
      {
          string embedded = _GetEmbeddedPath(virtualPath);
      
          // You can override the embed by placing a real file at the virtual path...
          return base.FileExists(virtualPath) || !string.IsNullOrEmpty(embedded);
      }
      
      public override VirtualFile GetFile(string virtualPath)
      {
          // You can override the embed by placing a real file at the virtual path...
          if (base.FileExists(virtualPath))
              return base.GetFile(virtualPath);
      
          string embedded = _GetEmbeddedPath(virtualPath);
      
          if (string.IsNullOrEmpty(embedded))
              return null;
      
          return new EmbeddedVirtualFile(virtualPath, GetType().Assembly
              .GetManifestResourceStream(embedded));
      }
      
      private string _GetEmbeddedPath(string path)
      {
          if (path.StartsWith("~/"))
              path = path.Substring(1);
      
          path = path.Replace(BookingEngine.Route, "/");
      
          //path = path.ToLowerInvariant();
          path = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + path.Replace('/', '.');
      
          // this makes sure the "virtual path" exists as an embedded resource
          return GetType().Assembly.GetManifestResourceNames()
              .Where(o => o == path).FirstOrDefault();
      }
      
      嵌套虚拟文件类

      public class EmbeddedVirtualFile : VirtualFile
      {
          private Stream _stream;
      
          public EmbeddedVirtualFile(string virtualPath,
              Stream stream) : base(virtualPath)
          {
              if (null == stream)
                  throw new ArgumentNullException("stream");
      
              _stream = stream;
          }
      
          public override Stream Open()
          {
              return _stream;
          }
      }
      
      我们正在使用的许多代码来自以下链接

      嵌入文件-


      路由基础实现-

      这是一个很好的问题,但对于堆栈溢出来说太广泛了。试着把这个问题分成几个单独的问题。我过去曾研究过这一点,并得出结论,这并不是那么简单。但我认为,如果有人主动创建一个开源项目,将所有核心功能放在一个可重用的软件和Visual Studio插件和外接程序中,使之易于管理,这是可能的。@NightOwl888我不同意,这是一个特定的问题。这是一个简单的解决方案,使控制器得到
      IHttpControllerFactory
      的认可,尽管Joshua本可以在MVC框架本身做一些更深入的研究,也不需要问这么广泛的问题question@JoelHarkes-您的答案不包括OP要求的css或js文件,它也没有解决制作NuGet软件包的问题。唯一合乎逻辑的做法是将内容文件嵌入到嵌入式资源中(因为NuGet可以部署代码文件,但不够聪明,无法合并代码文件中的更改),然后您将讨论深入研究MVC的更多扩展,以使所有这些都能工作(因此存在多个问题)。控制器很容易转包给其他DLL,但内容文件和视图需要更多的努力才能确保它们能被MVC正确加载并提供服务。Joel和@NightOwl888,经过一些额外的研究后,我发现了一种适合我们目前需要的东西的组合。如果您对实现感兴趣,请参阅下面的答案。
      public class EmbeddedVirtualFile : VirtualFile
      {
          private Stream _stream;
      
          public EmbeddedVirtualFile(string virtualPath,
              Stream stream) : base(virtualPath)
          {
              if (null == stream)
                  throw new ArgumentNullException("stream");
      
              _stream = stream;
          }
      
          public override Stream Open()
          {
              return _stream;
          }
      }