If you want to share your application resources(like css, javascript, images, etc) between different projects then embedded resource is a great choice. Embedded resource is also good for component/control writers because it allows component/control writers to distribute all the application resources with just a single assembly. A lot of vendors are already using this approach. It will great for component/control writers if they can leverage the ASP.NET bundling and minification for improving the performance. So, in this article we will show you how to write a very basic component(helper) which will use the ASP.NET bundling and minification feature on embedded javascript/css files.

About ASPHostPortal.com
ASPHostPortal.com is Microsoft No #1 Recommended Windows and ASP.NET Spotlight Hosting Partner in United States. Microsoft presents this award to ASPHostPortal.com for ability to support the latest Microsoft and ASP.NET technology, such as: WebMatrix, WebDeploy, Visual Studio 2012, ASP.NET 4.5, ASP.NET MVC 4.0, Silverlight 5 and Visual Studio Lightswitch. Click here for more information
First of all create a new Class Library project and install the Microsoft.AspNet.Mvc, WebActivator and Microsoft ASP.NET Web Optimization Framework 1.1.0-alpha1(make sure to include the -Pre parameter in Package Manager Console) nuget packages. Next, add a reference of System.Web assembly. Then, create your control/component/helper. For demonstration purpose, We will use this sample helper:
01. public static class HtmlHelpers
02. {
03. public static MvcHtmlString NewTextBox(this HtmlHelper html, string name)
04. {
05. var js = Scripts.Render("~/Test/Embedded/Js").ToString();
06. var css = Scripts.Render("~/Test/Embedded/Css").ToString();
07. var textbox = html.TextBox(name).ToString();
08. return MvcHtmlString.Create(textbox + js + css);
09. }
10. }
In this helper, I am just using a textbox with a style and script bundle. Style bundle include 2 css files and script bundle include 2 js files. So, just create 2 css files(NewTextBox1.css and NewTextBox2.css) and 2 javascript files(NewTextBox1.js and NewTextBox2.js) and then mark these files as embedded resource. Next, add a AppStart.cs file and add the following lines inside this file:
01. [assembly: WebActivator.PostApplicationStartMethod(typeof(AppStart), "Start")]
02. namespace Test
03. {
04. public static class AppStart
05. {
06. public static void Start()
07. {
08. ConfigureRoutes();
09. ConfigureBundles();
10. }
11. private static void ConfigureBundles()
12. {
13. BundleTable.VirtualPathProvider = new
EmbeddedVirtualPathProvider(HostingEnvironment.VirtualPathProvider);
14. BundleTable.Bundles.Add(new ScriptBundle("~/Test/Embedded/Js")
15. .Include("~/Test/Embedded/NewTextBox1.js")
16. .Include("~/Test/Embedded/NewTextBox2.js")
17. );
18. BundleTable.Bundles.Add(new StyleBundle("~/Test/Embedded/Css")
19. .Include("~/Test/Embedded/NewTextBox1.css")
20. .Include("~/Test/Embedded/NewTextBox2.css")
21. );
22. }
23. private static void ConfigureRoutes()
24. {
25. RouteTable.Routes.Insert(0,
26. new Route("Test/Embedded/{file}.{extension}",
27. new RouteValueDictionary(new { }),
28. new RouteValueDictionary(new { extension = "css|js" }),
29. new EmbeddedResourceRouteHandler()
30. ));
31. }
32. }
33. }
The above class using WebActivator's PostApplicationStartMethod, which allows your assembly to run some code after the Application_Start method of global.asax. The Start method simply register a custom virtual path provider and two bundles which are used in our NewText helper class. But ASP.NET optimization framework will only emit a bundle url when debug="false" or when BundleTable.EnableOptimizations = true. Therefore, we also need to handle the normal javascript and css requests. For this case, the above method has also register a specific route for handling embedded resource requests using EmbeddedResourceRouteHandler route handler. Here is the definition of this handler:
01. public class EmbeddedResourceRouteHandler : IRouteHandler
02. {
03. IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
04. {
05. return new EmbeddedResourceHttpHandler(requestContext.RouteData);
06. }
07. }
08. public class EmbeddedResourceHttpHandler : IHttpHandler
09. {
10. private RouteData _routeData;
11. public EmbeddedResourceHttpHandler(RouteData routeData)
12. {
13. _routeData = routeData;
14. }
15. public bool IsReusable
16. {
17. get { return false; }
18. }
19. public void ProcessRequest(HttpContext context)
20. {
21. var routeDataValues = _routeData.Values;
22. var fileName = routeDataValues["file"].ToString();
23. var fileExtension = routeDataValues["extension"].ToString();
24. string nameSpace = typeof(EmbeddedResourceHttpHandler)
25. .Assembly
26. .GetName()
27. .Name;// Mostly the default namespace and assembly name are same
28. string manifestResourceName = string.Format("{0}.{1}.{2}", nameSpace, fileName, fileExtension);
29. var stream =
typeof(EmbeddedResourceHttpHandler).Assembly.GetManifestResourceStream(manifestResourceName);
30. context.Response.Clear();
31. context.Response.ContentType = "text/css";// default
32. if (fileExtension == "js")
33. context.Response.ContentType = "text/javascript";
34. stream.CopyTo(context.Response.OutputStream);
35. }
36. }
EmbeddedResourceRouteHandler returns EmbeddedResourceHttpHandler http handler which will be used to extract embedded resource file from the assembly and then write the file to the response body. Now, the only missing thing is EmbeddedVirtualPathProvider:
01. public class EmbeddedVirtualPathProvider : VirtualPathProvider
02. {
03. private VirtualPathProvider _previous;
04. public EmbeddedVirtualPathProvider(VirtualPathProvider previous)
05. {
06. _previous = previous;
07. }
08. public override bool FileExists(string virtualPath)
09. {
10. if (IsEmbeddedPath(virtualPath))
11. return true;
12. else
13. return _previous.FileExists(virtualPath);
14. }
15. public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime
utcStart)
16. {
17. if (IsEmbeddedPath(virtualPath))
18. {
19. return null;
20. }
21. else
22. {
23. return _previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
24. }
25. }
26. public override VirtualDirectory GetDirectory(string virtualDir)
27. {
28. return _previous.GetDirectory(virtualDir);
29. }
30. public override bool DirectoryExists(string virtualDir)
31. {
32. return _previous.DirectoryExists(virtualDir);
33. }
34. public override VirtualFile GetFile(string virtualPath)
35. {
36. if (IsEmbeddedPath(virtualPath))
37. {
38. string fileNameWithExtension = virtualPath.Substring(virtualPath.LastIndexOf("/") + 1);
39. string nameSpace = typeof(EmbeddedResourceHttpHandler)
40. .Assembly
41. .GetName()
42. .Name;// Mostly the default namespace and assembly name are same
43. string manifestResourceName = string.Format("{0}.{1}", nameSpace,
fileNameWithExtension);
44. var stream =
typeof(EmbeddedVirtualPathProvider).Assembly
.GetManifestResourceStream(manifestResourceName);
45. return new EmbeddedVirtualFile(virtualPath, stream);
46. }
47. else
48. return _previous.GetFile(virtualPath);
49. }
50. private bool IsEmbeddedPath(string path)
51. {
52. return path.Contains("~/Test/Embedded");
53. }
54. }
55.
56. public class EmbeddedVirtualFile : VirtualFile
57. {
58. private Stream _stream;
59. public EmbeddedVirtualFile(string virtualPath, Stream stream)
60. : base(virtualPath)
61. {
62. _stream = stream;
63. }
64. public override Stream Open()
65. {
66. return _stream;
67. }
68. }
EmbeddedVirtualPathProvider class is self explanatory. It simply maps a bundling url(used above) and return the embedded request as stream. Note, this class will be invoked by ASP.NET optimization framework during bundling and minifying process. Now, just build your component/control/helper assembly. Then, create a sample ASP.NET(MVC) application and then use this component/control/helper in your page. For example, like,
1.@using Test.Helpers
[email protected]("New")