ASP.NET MVC模块

时间:2017-10-03 13:43:05

标签: c# asp.net asp.net-mvc asp.net-mvc-4 nuget-package

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

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

理想情况下,模块的视图和其他文件应该能够被消费主机项目覆盖,但文件本身不应该在主机项目中直接编辑。就像添加其他nuget包时引用的dll一样。

到目前为止,我发现的有关此类主题的答案和帖子似乎有点陈旧。

是否有更清晰,更现代的解决方案来创建ASP.NET MVC模块nuget包,以便能够跨项目共享完整工作的页面?

2 个答案:

答案 0 :(得分:2)

控制器

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

只要加载了dll,就可以随时注册所有带有反射的Controller子类的类(在应用程序启动时)。

剃刀

预编译是可能的,但只有在dotnet核心中才真正建议,因为它是那里的一等公民。

您还可以将视图添加为注入项目的内容。

缺点:

  • 在更新时,它会覆盖视图(如果您更改了它们,则会丢失更改)

优点:

  • 在更新时,您可以合并git中的两个更改
  • 轻松更改现有的剃须刀页面

答案 1 :(得分:1)

我找到了一个适合我们的解决方案。我们尚未完全实现这一点,因此可能仍会出现一些无法预料的问题。

首先,创建一个MVC项目,其中包含所需的视图,控制器,javascript等,以满足您的包需求。必须在项目中将每个静态文件和视图设置为嵌入式资源

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

要启用自定义路由,将需要RouteBase类的实现。此实现需要接受虚拟路由所基于的string属性,以允许主机应用所需的路由前缀。对于我们的示例,该属性将默认为 Booking ,并在项目中使用相关的视图架构进行匹配。

RouteBase实现和VirtualPath类都将在setup方法中实例化。这将允许消费项目调用单个方法来设置预订引擎。此方法将接受站点Route Collection和动态路由属性以附加自定义路由。该方法还会将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;
}

Booking Engine类设置方法,它执行程序集所需的所有实例化。

public class BookingEngine
{
    public static string Route = "Booking";

    public static void Setup(RouteCollection routes, string route)
    {
        Route = route;

        HostingEnvironment.RegisterVirtualPathProvider(
            new EmbeddedVirtualPathProvider());

        routes.Add(
            name: "CustomPage",
            item: new CustomRouteController());
    }
}

EmbeddedVirtualFile

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;
    }
}

我们使用的很多代码来自以下链接

嵌入式文件 - https://www.ianmariano.com/2013/06/11/embedded-razor-views-in-mvc-4/

RouteBase实施 - Multiple levels in MVC custom routing