I’m in the process of creating one central library for future ASP.NET libraries that will do all the setup and wiring of the infrastructure and also deliver core files (e.g. Twitter Bootstrap CSS + JS). In .NET Assemblies non-code (i.e. content files, such as .less files) can be embedded by changing the build action on the file to “Embedded Resource”.
Accessing file contents is easy once you remember that the filename is prefixed with the Namespace. So if you embed the file foo.txt in a Directory “bar” of a project configured with the default namespace “companydll” then the embedded resource will have the filename “companydll.bar.foo.txt”.
Simply pass that to the GetManifestResourceStream method and read the contents as follows:
public string GetFileContents(string file) {
using (Stream stream = this.GetType().Assembly.GetManifestResourceStream(file))
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}
So far so good. You can create MVC controller methods that load files from the manifest resource stream - just remember to always check the request file against a whitelist of allowed files.
In my case I embedded some .less files and wanted to deliver those to the browser. Working with less in ASP.NET projects couldn’t be easier. The dotlesscss project provides the engine for transforming less to css (and it can even be added using nuget). You simply add an HttpHandler and all .less requests are automatically parsed. But in my case I needed to run the less parser on files from the manifest. So basically I have a string in memory and not a real file path. The problem is the less parser provided by dotlesscss needs a file path to the .less file in order to import referenced files.
I started out by looking at IsolatedFileStorage as a temporary store for the .less files which I could pass to the engine, but there is only a private reflection hack to get to the real path of a file in IsolatedFileStorage, so I kept looking for something better. After a git clone of the dotlesscss source I produced this neat (and in retrospect very simple) solution to this problem. You can simply override the file provider for the less engine and read the files from the manifest stream.
public class DotLessEmbedded : IFileReader {
private LessEngine m_lessEngine;
private string m_filesNamespace;
public DotLessEmbedded(string filesNamespace) {
m_filesNamespace = filesNamespace;
if (!m_filesNamespace.EndsWith("."))
m_filesNamespace += ".";
m_lessEngine = new LessEngine();
// Changed for dotless v1.2.4 - IImporter was introduced
// but lacks the FileReader property, so cast required.
(m_lessEngine.Parser.Importer as Importer).FileReader = this;
}
public string TransformToCss(string file) {
return m_lessEngine.TransformToCss(GetFileContents(file), file);
}
public string GetFileContents(string fileName) {
using (Stream stream = this.GetType().Assembly.GetManifestResourceStream(m_filesNamespace + fileName))
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}
}
(Remember to reference and import dotless.Core.Input and dotless.Core.)
With this class you can simply transform any embedded less file by using:
var dotLess = new DotLessEmbedded("Web.Core.Assets.Bootstrap");
var out = dotLess.TransformToCss("bootstrap.less");