views:

132

answers:

3

I built an assembly containing one js file. I marked the file as Embedded Resource and added it into AssemblyInfo file.

I can't refernce the Assembly from a web site. It is in the bin folder but I don't see the reference to it.

It seems like not having at least a class inside the assembly I can't reference it.

I would include the js file into my pages from the assembly.

How should I do this?

Thanks

+1  A: 

I do exactly the same thing in one of my projects. I have a central ScriptManager class that actually caches the scripts as it pulls them, but the core of extracting the script file from the embedded resource looks like this:

internal static class ScriptManager
{
    private static Dictionary<string, string> m_scriptCache = 
                                     new Dictionary<string, string>();

    public static string GetScript(string scriptName)
    {
        return GetScript(scriptName, true);
    }

    public static string GetScript(string scriptName, bool encloseInCdata)
    {
        StringBuilder script = new StringBuilder("\r\n");

        if (encloseInCdata)
        {
            script.Append("//<![CDATA[\r\n");

        }

        if (!m_scriptCache.ContainsKey(scriptName))
        {
            var asm = Assembly.GetExecutingAssembly();

            var stream = asm.GetManifestResourceStream(scriptName);

            if (stream == null)
            {
                var names = asm.GetManifestResourceNames();
                // NOTE: you passed in an invalid name.  
                // Use the above line to determine what tyhe name should be
                // most common is not setting the script file to be an embedded resource
                if (Debugger.IsAttached) Debugger.Break();

                return string.Empty;
            }

            using (var reader = new StreamReader(stream))
            {
                var text = reader.ReadToEnd();

                m_scriptCache.Add(scriptName, text);
            }
        }

        script.Append(m_scriptCache[scriptName]);

        if (encloseInCdata)
        {
            script.Append("//]]>\r\n");
        }

        return script.ToString();
    }
}

EDIT

To provide more clarity, I've posted my ScriptManager class. To extract a script file, I simply call it like this:

var script = ScriptManager.GetScript("Fully.Qualified.Script.js");

The name you pass in it the full, case-sensitive resource name (the exception handler gets a list of them by calling GetManifestResourceNames()).

This gives you the script as a string - you can then put it out into a file, inject it into the page (which is what I'm doing) or whatever you like.

ctacke
Thanks for the answer, I'm trying it.What is m_scriptCache ?
opaera
I can't figure how makes this working. Could you please provide an example? I have this WebResource specified in the AssemblyInfo file: 'TestResources.Assembly.CheckAnswer.js'.
opaera
I'm trying but GetManifestResourceNames() returns an empty collection.
opaera
You must be trying to load the scripts from an external assembly (I'm loading from the executing assembly). You should probably use Assembly.LoadFrom or similar to get the Assembly instance related to where your script files actually exist.
ctacke
Thank you very much, for the script and for the patience.It works now, with LoadFrom(), the problem is I have to specify the path to the assembly like this: 'c:\...\MySite\bin\myassembly.dll'.And then another question, how could embed the script into the page like a script include?Sorry for abusing of your patience.
opaera
You need to build the path for LoadFrom. How you do that depends on your usage, but maybe Application.StartupPath, or looking at the server path. Exactly how depends on your exact usage.Embedding the script is likewise very dependent on how you need it. For my purposes (a custom ASP.NET server I wrote for Windows CE) I'm overriding Render and writing it out to the page manually.
ctacke
A: 
Assembly myAssembly = // Get your assembly somehow (see below)...
IList<string> resourceNames = myAssembly.GetManifestResourceNames();

This will return a list of all resource names that have been set as 'Embedded Resource'. The name is usually the fully qualified namespace of wherever you put that JS file. So if your project is called My.Project and you store your MyScript.js file inside a folder in your project called Resources, the full name would be My.Project.Resources.MyScript.js

If you then want to use that JS file:

Stream stream = myAssembly.GetManifestResourceStream(myResourceName);

Where myResourceName argument might be "My.Project.Resources.MyScript.js". To get that JS file in that Stream object, you'll need to write it as a file to the hard drive, and serve it from your website as a static file, something like this:

Stream stream = executingAssembly.GetManifestResourceStream(imageResourcePath);
if (stream != null)
{
    string directory = Path.GetDirectoryName("C:/WebApps/MyApp/Scripts/");

    using (Stream file = File.OpenWrite(directory + "MyScript.js"))
    {
        CopyStream(stream, file);
    }

    stream.Dispose();
}

And the code for CopyStream method:

private static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8 * 1024];
    int len;
    while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, len);
    }
}

You might want to stick all this code in an Application_Start event in your Global.asax. You don't want it to run for each request


Now getting a reference to your Assembly is a different matter, there are many ways. One way is to include all the above code in your Assembly in question, then make sure you reference that Assembly from your main WebApp project in Visual Studio, then get a reference to the currently executing Assembly like so.

namespace My.Project
{
    public class ResourceLoader
    {
        public static void LoadResources()
        {
            Assembly myAssembly = Assembly.GetExecutingAssembly();

            // rest of code from above (snip)
        }
    }
}

Then call ResourceLoader.LoadResources() from your Application_Start event in your Global.asax.

Hope this helps

Sunday Ironfoot
Thanks for the answer, I'm trying it.I have my ClassLibrary project called TestResources.AssemblyI have one js file called CheckAnswer.js in the root of the assembly.What will be the first line of your code?I've tried:System.Reflection.Assembly myAssembly = TestResources.Assembly.CheckAnswer.js but it's doesn't work.Sorry but I'm new in these kind of code.
opaera
Try... Assembly myAssembly = Assembly.GetExecutingAssembly();- Stream stream = myAssembly.GetManifestResourceStream("TestResources.Assembly.CheckAnswer.js"); - Note: this code assumes it's running within the 'TestResources.Assembly' project otherwise the call to Assembly.GetExecutingAssembly() won't work.
Sunday Ironfoot
Thanks, but I don't want to use the resources within the assembly. I'd like to have the assembly just like a container of js files. I want to load the js files into my WebSite project. Any ideas?
opaera
If you have files embedded within an Assembly, this is the only way to get at them AFAIK. See my other other "Fully working example"
Sunday Ironfoot
Try: Assembly myAssembly = Assembly.LoadFile(@"C:\WebApps\MyApp\bin\TestResources.Assembly.dll");
Sunday Ironfoot
A: 

Fully working example (I hope):

namespace TestResources.Assembly
{
    public class ResourceLoader
    {
        public static void LoadResources()
        {
            Assembly myAssembly = Assembly.GetExecutingAssembly();

            Stream stream = myAssembly
                .GetManifestResourceStream("TestResources.Assembly.CheckAnswer.js");

            if (stream != null)
            {
                string directory = Path.GetDirectoryName("C:/WebApps/MyApp/Scripts/");

                using (Stream file = File.OpenWrite(directory + "MyScript.js"))
                {
                    CopyStream(stream, file);
                }

               stream.Dispose();
            }
        }

        private static void CopyStream(Stream input, Stream output)
        {
            byte[] buffer = new byte[8 * 1024];
            int len;

            while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, len);
            }
        }
    }
}

Some caveats:

  • Change "C:/WebApps/MyApp/" to wherever your web app is located, maybe write some code to work this out dynamically
  • Make sure the /Scripts folder exists in your webapp root
  • I think it will overwrite the 'MyScript.js' file if it already exists, but just in case you might want to add some code to check for that file and delete it

Then stick a call to this code in your Global.asax file:

protected void Application_Start()
{
    ResourceLoader.LoadResources();
}

Then the path for your web site will be /Scripts/MyScript.js eg:

<head>
    <!-- Rest of head (snip) -->
    <script type="text/javascript" src="/Scripts/MyScript.js"></script>
</head>
Sunday Ironfoot
I see, but in this way I have the file into my web app, just like I include the orginal CheckAnswer.js into my app. I'd like to reference the js files like they are in a different domain, but instead of this reading them from the assembly. I read something about WebResource but I can't make it work. Your approach could be useful for combining multiple files, is it right?
opaera
Yes you could use this approach for multiple files. If you don;t want to put this code in the same Assembly that has your files, try replace 'Assembly.GetExecutingAssembly()' with 'Assembly.LoadFile(@"C:\WebApps\MyApp\bin\TestResources.Assembly.dll")'
Sunday Ironfoot