18

I've got a C# MVC project that uses Razor syntax.
To be able to reuse some code, I want to put some of my JavaScript and CSS files in a different project and include them somehow.
This is how my scripts are included at the moment:

<script src="@Url.Content("~/Scripts/bootstrap-typeahead.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/bootstrap-dropdown.js")" type="text/javascript"></script>

At the moment, the scripts are in the same project as the cshtml file but they should be placed in the Common.Web project instead...
What I want to do is this (doesn't work though):

<script src="@Url.Content("Common.Web/Scripts/bootstrap-typeahead.js")" type="text/javascript"></script>
<script src="@Url.Content("Common.Web/Scripts/bootstrap-dropdown.js")" type="text/javascript"></script>
2
  • 1
    If someone has answered your question please mark it has such. Commented Aug 7, 2012 at 18:06
  • Did you ever come up with a good way of doing this? Commented Dec 31, 2012 at 9:22

4 Answers 4

13

I do this very thing. However I embed the Javascript files and other content in another DLL and then call them from my razor syntax like so. Here is the code I use. In the View: Script example:

        <script [email protected]("GetEmbeddedResource", "Shared", new { resourceName = "Namespace.Scripts.jquery.qtip.min.js", pluginAssemblyName = @Url.Content("~/bin/Namespace.dll") }) type="text/javascript" ></script>

Image Example:

@Html.EmbeddedImage("corporate.gif", new { width = 150, height = 50})

Here is my helper methods:

        public static MvcHtmlString EmbeddedImage(this HtmlHelper htmlHelper, string imageName, dynamic htmlAttributes)
    {
        UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
        var anchor = new TagBuilder("img");
        anchor.Attributes["src"] = url.Action("GetEmbeddedResource", "Shared",
                                              new
                                                  {
                                                      resourceName = "Namespace.Content.Images." + imageName,
                                                      pluginAssemblyName = url.Content("~/bin/Namespace.dll")
                                                  });

        if (htmlAttributes != null)
        {
            string width = "";
            string height = "";
            PropertyInfo pi = htmlAttributes.GetType().GetProperty("width");
            if (pi != null)
                width = pi.GetValue(htmlAttributes, null).ToString();

            pi = htmlAttributes.GetType().GetProperty("height");
            if (pi != null)
                height = pi.GetValue(htmlAttributes, null).ToString();

            if (!string.IsNullOrEmpty(height))
                anchor.Attributes["height"] = height;

            if (!string.IsNullOrEmpty(width))
                anchor.Attributes["width"] = width;
        }
        return MvcHtmlString.Create(anchor.ToString());
    }

Lastly my shared Controller:

        [HttpGet]
    public FileStreamResult GetEmbeddedResource(string pluginAssemblyName, string resourceName)
    {
        try
        {
            string physicalPath = Server.MapPath(pluginAssemblyName);
            Stream stream = ResourceHelper.GetEmbeddedResource(physicalPath, resourceName);
            return new FileStreamResult(stream, GetMediaType(resourceName));
            //return new FileStreamResult(stream, GetMediaType(tempResourceName));
        }
        catch (Exception)
        {
            return new FileStreamResult(new MemoryStream(), GetMediaType(resourceName));
        }
    }

    private string GetMediaType(string fileId)
    {
        if (fileId.EndsWith(".js"))
        {
            return "text/javascript";
        }
        else if (fileId.EndsWith(".css"))
        {
            return "text/css";
        }
        else if (fileId.EndsWith(".jpg"))
        {
            return "image/jpeg";
        }
        else if (fileId.EndsWith(".gif"))
        {
            return "image/gif";
        }
        else if (fileId.EndsWith(".png"))
        {
            return "image/png";
        }
        return "text";
    }

Resource Helper:

    public static class ResourceHelper
{
    public static Stream GetEmbeddedResource(string physicalPath, string resourceName)
    {
        try
        {
            Assembly assembly = PluginHelper.LoadPluginByPathName<Assembly>(physicalPath);

            if (assembly != null)
            {
                string tempResourceName = assembly.GetManifestResourceNames().ToList().FirstOrDefault(f => f.EndsWith(resourceName));
                if (tempResourceName == null)
                    return null;
                return assembly.GetManifestResourceStream(tempResourceName);
            }
        }
        catch (Exception)
        {

        }

        return null;
    }
}  

Plugin Helper

public static T LoadPluginByPathName<T>(string pathName)
{
    string viewType = typeof(T).GUID.ToString();

    if (HttpRuntime.Cache[viewType] != null)
        return HttpRuntime.Cache[viewType] is T ? (T)HttpRuntime.Cache[viewType] : default(T);

    object plugin = Assembly.LoadFrom(pathName);
    if (plugin != null)
    {
        //Cache this object as we want to only load this assembly into memory once.
        HttpRuntime.Cache.Insert(viewType, plugin);
        return (T)plugin;
    }

    return default(T);
}

Remember that I am using these as embedded content!

Sign up to request clarification or add additional context in comments.

6 Comments

sounds like this could be a decent solution. However, you didn't provide code for PluginHelper used in ResourceHelper. I can't test it out without it. Also, in order to make sure the resources are going to be embedded, is all I need to do is mark them as "Embedded Resource" in build action in properties?
I have edited the above for the plugin helper now. Correct all you need to to do is add mark as Embedded resource in the build action field under the properties of that file. Do this for all files.
This technique could cause some performance issues on a high traffic site. Personally, I wouldn't suggest using embedded resources. Orchard CMS has an interesting technique to include files from other projects so I would check that out!
@Bevan A link pointing to related Orchard documentation will be much appreciated.
I didn't try example code for images but rest of it is perfect. :)
|
7

To my knowledge you can't do this as the path would lie outside of the website.

You can however do the following:

1) Put all the scripts you want to share in Common.Web\Scripts
2) For each script file in your Web application 'Add as Link' to your Common.Web Scripts (you don't even need to do this step; it is however nice to see what scripts your web app uses in VS)
3) Add a post-build event to your Web application that copies the scripts from Common.Web\Scripts to your WebApp\Scripts folder:

copy $(ProjectDir)..\Common.Web\Scripts* $(ProjectDir)\Scripts

so from your perspective in Visual Studio you will only have a single place to update your .js files that can be used by multiple projects.

1 Comment

i havent used TFS but i would guess it would work because it would respect the post build event.
2

Instead of using Url helper use relative addressing. What you tried to do makes no sense, as helper is used to resolve paths relative to it's project.

Since you are trying to use resources of another project, it's ok to assume that you know upfront where you're going to deploy each project. Even though I don't like this practice, I can think of a pragmatic solution for this.


If your two applications are at urls:

http://www.mysite.com/app1
http://www.mysite.com/Common.Web

you could address like this:

<script src="@Url.Content("~")/../Common.Web/Scripts/bootstrap-typeahead.js")" type="text/javascript"></script>

meaning, resolve my app root folder, go up a level, and go down rest of the path.

Comments

-1

I just found this question while looking for the same answer. Thanks to the previous posters, who made it clear that this cannot be done using Visual Studio alone: you will always end up with a copy of the original file, or an incomplete set of shipping files.

I do not wish to reinvent the wheel whenever I have simple common functionality, so I have a folder for common scripts on my hard drive: /Common/Javascript

My web project is located in: /ProjectName

so my common scripts lie outside my web project.

I solved this via source control. Most source control repositories will have the functionality to show a file in another directory. My steps were:

  1. Create empty folder under my VS project: /ProjectName/Scripts/Common
  2. Committed the empty folder to source control
  3. Using Subversion (my source control), I set up an "extern" so that my common file was linked to the new folder. Other source control software may call this a "link" or some other such thing.
  4. Updated the new folder, and my common Javascript files came in, and could now be added to my web project in Visual Studio.

Obviously, I tested this by changing the files within Visual Studio and committing them. The changes were indeed reflected in the original files, sitting in /Common/Javascript.

I can now simply add an extern to any other web project which needs to use the same functionality: though, of course, changing those files comes with additional risk now, as I may unexpectedly break other projects which use them. Though I can configure an extern to use a specific revision of a file, that's not what I want to do at this point; I shall await such complexity as and when it occurs.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.