Ignoring files in MVC Bundles - c#

We are using feature flags (set in Web.Config) to toggle the visibility in the UI of certain features that aren't yet complete. I'd also like my MVC bundles to NOT include the related JS files since they would just be useless files the client would have to download when the feature isn't enabled.
So far I've found IgnoreList.Ignore but I can't seem to get it to work. This is basically what I'm doing:
public static void RegisterBundles(BundleCollection bundles)
{
if (!appConfiguration.FeatureXEnabled)
{
//Skip these files if the Feature X is not enabled!
//When this flag is removed, these lines can be deleted and the files will be included like normal
bundles.IgnoreList.Ignore("~/App/Organization/SomeCtrl.js", OptimizationMode.Always);
bundles.IgnoreList.Ignore("~/App/Filters/SomeFilter.js", OptimizationMode.Always);
}
var globalBundle = new ScriptBundle("~/bundles/app-global").Include(
"~/App/RootCtrl.js",
"~/App/Directives/*.js",
"~/App/Services/*.js",
"~/App/Application.js"
);
bundles.Add(globalBundle);
var appOrgBundle = new ScriptBundle("~/bundles/app-org");
appOrgBundle.Include(
"~/App/Filters/*.js",
"~/App/Organization/*.js"
);
bundles.Add(appOrgBundle);
}
With the above code, the files in the ignore list are still being included! What am I doing wrong here?

I have fought the ignore list when using expressions as well. A simple work around I have found is to implement an IBundleOrderer that will exclude the files I do not want, and apply some ordering to how the files get included. Although this is not really its intended use, I find it fills the gap.
The IBundleOrderer gets access to the full list of files (expression expanded to what files it matched).
public class ApplicationOrderer : IBundleOrderer {
public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
{
if (!AppSettings.FeatureFlag_ServiceIntegrationsEnabled)
{
//Skip these files if the Service Integrations Feature is not enabled!
//When this flag is removed, these lines can be deleted and the files will be included like normal
var serviceIntegrationPathsToIgnore = new[]
{
"/App/ServiceIntegrations/IntegrationSettingsModel.js",
"/App/ServiceIntegrations/IntegrationSettingsService.js",
"/App/ServiceIntegrations/ServiceIntegrationsCtrl.js"
};
files = files.Where(x => !serviceIntegrationPathsToIgnore.Contains(x.VirtualFile.VirtualPath));
}
return files;
}
}
Example Usage:
public static void RegisterBundles(BundleCollection bundles)
{
var appBundle = new ScriptBundle("~/bundles/app")
.IncludeDirectory("~/App/", "*.js", true)
appBundle.Orderer = new ApplicationOrderer();
bundles.Add(appBundle);
}

I think that the bundling code only happens once - during the initialisation of the website. If you are switching the appConfiguration.FeatureXEnabled flag without restarting/republishing the website then your change will be ignored.
One solution would be to create 2 separate bundles and then use Razor to display the correct bundle reference in your HTML.
e.g.
#{if (appConfiguration.FeatureXEnabled){
<script type="text/javascript" src="~/bundles/bundle-large.js"></script>
}else{
<script type="text/javascript" src="~/bundles/bundle-small.js"></script>
}

I upvoted Gent's answer above because it helped me accomplish what I wanted.
But then I expanded upon it and created a generic IBundleOrderer that uses lambdas to exclude whatever items you want to exclude very simply.
Note the excludes parameter is a params list so you are not limited to a single exclusion pattern. This doesn't do any ordering, but it could be easily added with another parameter.
public class ExcludeItemsOrderer : IBundleOrderer
{
private Func<BundleFile, bool>[] _excludes;
public ExcludeItemsOrderer(params Func<BundleFile, bool>[] excludes)
{
_excludes = excludes;
}
public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
{
if(_excludes == null || _excludes.Length == 0)
{
return files;
}
foreach(var exclude in _excludes)
{
var exclusions = files.Where(exclude);
files = files.Except(exclusions);
}
return files;
}
}
And then I created an extension method to simplify using it:
public static class BundleExtensions
{
public static Bundle AsIsOrderExcluding(this Bundle bundle, params Func<BundleFile, bool>[] excludes)
{
bundle.Orderer = new ExcludeItemsOrderer(excludes);
return bundle;
}
}
So that it can be easily applied as you create your bundles, like this:
bundles.Add(
new ScriptBundle("~/Bundles/App/js")
.IncludeDirectory("~/App", "*.js", true)
.AsIsOrderExcluding(bf => bf.VirtualFile.VirtualPath.IndexOf("/skip_me/") >= 0)
);

Try defining the ignore list as follows (using wildcards instead of relative paths):
bundles.IgnoreList.Ignore("*/App/Organization/SomeCtrl.js", OptimizationMode.Always);
bundles.IgnoreList.Ignore("*/App/Filters/SomeFilter.js", OptimizationMode.Always);

Bundles.IgnoreList.Ignore(pattern) doesn't work like that. It compares just the Name part of files (i.e not the full (virtual)path ) with pattern in any bundle going to be returned.
For example you can ignore files starting with name old_style prefix. This filtering will be applied for files under any folder.
public IEnumerable<BundleFile> FilterIgnoredFiles(BundleContext context, IEnumerable<BundleFile> files) {
return files.Where(f => !ShouldIgnore(context, f.VirtualFile.Name));
}
See:
https://aspnetoptimization.codeplex.com/SourceControl/latest#src/System.Web.Optimization/IgnoreList.cs

Related

How to use Rx to monitor a project file and files for external changes?

I would like to reproduce the behavior of Visual Studio which informs you when a project file is touched externally and proposes to reload it!
Due to the requirements, I believe reactive is a great match to solve that problem.
I am using a modified reactive FileSystemWatcher described in this post: http://www.jaylee.org/post/2012/08/26/An-update-to-matthieumezil-Rx-and-the-FileSystemWatcher.aspx
public class FileWatcher
{
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static IObservable<FileChanged> ObserveFolderChanges(string path, string filter, TimeSpan throttle, Predicate<string> isPartOfProject)
{
return Observable.Create<FileChanged>(
observer =>
{
var fileSystemWatcher = new FileSystemWatcher(path, filter) { EnableRaisingEvents = true, IncludeSubdirectories = true };
var sources = new[]
{
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Created")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Added, SourceChangeTypes.FileSystem)),
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Deleted")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Deleted, SourceChangeTypes.FileSystem))
};
return sources.Merge()
.Throttle(throttle)
.Do(changed =>
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"FileWatcher event [{changed.FileChangeType}] {changed.FullPath}");
}
})
.Finally(() => fileSystemWatcher.Dispose())
.Subscribe(observer);
}
);
}
private static bool IsMaybeAProjectFile(EventPattern<FileSystemEventArgs> ev)
{
return ev.EventArgs.FullPath.EndsWith(".zip") || ev.EventArgs.FullPath.EndsWith(".skye");
}
}
public class FileChanged
{
public string FullPath { get; }
public FileChangeTypes FileChangeType { get; }
public SourceChangeTypes SourceChangeType { get; }
public FileChanged(string fullPath, FileChangeTypes fileChangeType, SourceChangeTypes sourceChangeType)
{
FullPath = fullPath;
FileChangeType = fileChangeType;
SourceChangeType = sourceChangeType;
}
}
[Flags]
public enum FileChangeTypes
{
Added = 1,
Deleted = 2
}
[Flags]
public enum SourceChangeTypes
{
FileSystem = 1,
Project = 2
}
Now in my application I created an event
private ProjectChangedEventHandler ProjectChanged { get; set; }
private void OnProjectChanged(FileChanged fileChanged)
{
ProjectChanged?.Invoke(this, fileChanged);
}
public delegate void ProjectChangedEventHandler(object sender, FileChanged fileChanged);
Which is used like this when I delete or a add a file from the project
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Deleted, SourceChangeTypes.Project));
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Added, SourceChangeTypes.Project));
Now I can start to leverage those two streams and with a join (which needs fine tuning for the left and right duration selector) I am able to detect which file was modified by my application:
private void ObserveProjectModifications(string projectFilePath)
{
_observeFolderChanges = FileWatcher.ObserveFolderChanges(Path.GetDirectoryName(projectFilePath), "*.*", TimeSpan.FromMilliseconds(500), IsPartOfProject);
_observeProjectChanges = Observable.FromEventPattern<ProjectChangedEventHandler, FileChanged>(h => ProjectChanged += h, h => ProjectChanged -= h).Select(pattern => pattern.EventArgs);
_changes = _observeProjectChanges.Join(_observeFolderChanges, _ => Observable.Never<Unit>(), _ => Observable.Never<Unit>(), ResultSelector).Where(changed => IsPartOfProject(changed.FullPath));
}
private FileChanged ResultSelector(FileChanged fileChanged, FileChanged projectChanged)
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"ResultSelector File [{fileChanged.FileChangeType}] {fileChanged.FullPath} # Project [{projectChanged.FileChangeType}] {projectChanged.FullPath}");
}
if (fileChanged.FullPath == projectChanged.FullPath)
{
if (fileChanged.FileChangeType == projectChanged.FileChangeType)
{
if (fileChanged.SourceChangeType != projectChanged.SourceChangeType)
{
return projectChanged;
}
return fileChanged;
}
return fileChanged;
}
return fileChanged;
}
private bool IsPartOfProject(string fullPath)
{
if (_projectFileManager.ProjectFilePath.Equals(fullPath)) return true;
return _archives.Values.Any(a => a.Filename.Equals(fullPath));
}
My issue is that I also want to know that a file was modified externally! Any idea would be really helpful! Thanks
Unfortunatelly the FileSystemWatcher doesn't provide information which process has modified the file, so you are bit out of luck there. There are few possibilities that I can think of:
Ignore flag - When your application is doing a change you can set a flag and ignore the events when the flag is set. This is the simplest way, but you might miss some external change if it happens concurrently when the flag is set and also it gets even more complicated due to throttling you have.
Tagging the file - whenever you do a change to the file you generate a guid (or similar) which you will use to tag the file. And then whenever the file change is fired, you check the file property (can be stored either as real filesystem file property - similar for example to jpeg metadata you see in details in file explorer, there are more ways to set such file property) and then if the tag is different from what you have or is missing then you know it is external - there you need to also take care due to throttling and the tag being outdated etc
Minifilter file system driver - This would be the cleanest solution and probably is very close to what Visual studio is using - just a guess though. It is basically a universal windows driver that monitors any I/O change. Microsoft has created reference implementation called minispy, which is small tool to monitor and log any I/O and transaction activity that occurs in the system. You don't have to implement the driver yourself as there is already a 3rd party FileSystemWatcher implemented using this approach on github. That file system watcher provides information which process has modified the file. The only problem here is that the driver itself needs to be installed, before it can be used, so you need admin privileged installer of sort.
At the moment that's all I can think of.

Move outlook item to archive

I have a C# program (actually it's just a C# library that is being used by NUnit) that I wish to modify slightly which is based off of this article: How to Programmatically move items in outlook. I'm currently faced with a folder that has bout 3500 messages all around 350kb and is taking FOREVER to move to my archive so I can send and receive emails again (since my inbox is currently at 1.5Gb out of 500Mb... lol) but for the life of me I can't figure out how to get my archive folder. I'm a little bit multitasking since I'm at work so I can edit as I go. So if you have any code readily available that finds the archive folder that would be great. Thank you
EDIT
ok to show that I do have some work in progress (based on negative feedback) here is the code I have in place right now (since yes I know I have a give me teh codez)
here is my NUnit test case that looks at a folder and gives me specific information
[Test]
public void CheckMessages()
{
List<EmailMessage> messages = new List<EmailMessage>();
using (var target = new EmailMessageProvider())
{
messages.AddRange(target.GetEmailMessages("UnexpectedErrors\\NotFindImage"));
}
Dictionary<int, string> asdf = new Dictionary<int, string>();
foreach (var item in messages)
{
var line = item.Body.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[2];
var revisionId = int.Parse(Regex.Match(line, #"\-*\d+").Value);
var path = line.Substring(line.IndexOf("\\\\"));
if (asdf.ContainsKey(revisionId))
{
Assert.That(path, Is.EqualTo(asdf[revisionId]));
}
else
{
asdf.Add(revisionId, path);
}
}
foreach (var item in asdf.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key} {item.Value}");
}
}
I use the same class to find messages (in another test) and move it to that subfolder which that test is using.
here is the code I have that does the moving
public void MoveSurveyPrintComponentsNotFound()
{
var destination = _baseFolder.Folders["UnexpectedErrors"].Folders["NotFindImage"];
foreach (var mailItem in _baseFolder.Folders["UnexpectedErrors"].Items.OfType<MailItem>())
{
mailItem.UseMailItem(x =>
{
if (x.Body.Contains("Foobar.Common.Exceptions.ImageNotFound"))
x.Move(destination);
});
}
}
EDIT 2
looks like I may have just about got it. I found that in the MAPI Namspace one of the subfolders is Archives. I'm going to try to change a few of the variables and see if it moves. Problem is just checking one folder takes over 31 seconds. oh well. better than never.
I figured it out. It wasn't as hard as I had thought either so I'll share what i have just incase someone else has this problem. In my program I did 2 things. One was to set _basefolder as my default email address's Folder. Second was to to set _mapi to the Outlook.GetNamespace("MAPI"). Those two thinsg I already had in my constructor.
private readonly OutlookApplication _outlook;
private readonly NameSpace _mapi;
private MAPIFolder _baseFolder;
public EmailMessageProvider()
{
_outlook = new OutlookApplication();
_mapi = _outlook.GetNamespace("MAPI");
_baseFolder = _mapi.Folders["robert#defaultEmail.com"];
}
Archives works just like any other MAPIFolder so it's just a matter of getting said folder. For me it was in _mapi.Folders["Archive"]. I would imagine this is fairly standard so if you copy and paste it should work just fine.
So now to list out all of my emails I want to go through and move tham appropriatly.
public void MoveSpecificEmailsToArchives()
{
var destination = _mapi.Folders["Archives"];
foreach (var mailItem in _baseFolder.Folders["Unexpected Error"].Items.OfType<MailItem>())
{
mailItem.UseMailItem(x =>
{
if (x.Body.Contains("offensiveProgram.exe ERROR "))
x.Move(destination);
});
}
Release(destination);
}
fyi the UseMailItem is an extension method. Looks like this
public static void UseMailItem(this MailItem item, Action<MailItem> mailItemAction)
{
mailItemAction(item);
Marshal.ReleaseComObject(item);
}

Is there a way to programmatically export/save generated javascript and css bundles?

I am using System.Web.Optimization BundleTable for CSS and JavaScript file bundling. I was able to save bundle files manually by going to Chrome Developer Tools, Sources tab then clicking on bundle I would like to save and then clicking right mouse button on code and Save as (screenshot below)
I was wondering is there a way to save them programmatically (javascript or .NET I don't mind ether) as I need to test changes in bundle files by comparing them with old versions to see if they have changed or not. The end product will be selenium test most likely.
Sorted it out myself.
public static string GetBundleContents(string virtualPath)
{
OptimizationSettings config = new OptimizationSettings()
{
ApplicationPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath,
BundleTable = BundleTable.Bundles
};
BundleResponse response = Optimizer.BuildBundle(virtualPath, config);
return response.Content;
}
public static void WriteBundlesToDisk(string path)
{
foreach (var bundle in BundleTable.Bundles)
{
var bundleContents = BundleConfig.GetBundleContents(bundle.Path);
File.WriteAllText(string.Format("{0}/{1}.{2}", path, bundle.Path.Split('/').Last(), BundleConfig.GetFileExtensionByBundleType(bundle)), bundleContents);
}
}
public static string GetFileExtensionByBundleType(Bundle bundle)
{
if (bundle is ScriptBundle)
return "js";
else if (bundle is StyleBundle)
return "css";
return "folderBundle";
}
usage:
BundleConfig.WriteBundlesToDisk("c://bundles");
sidenote: there's a type of bundle called System.Web.Optimization.DynamicFolderBundle that is not being handled properly by the solution above, it will save it as .folderBundle file type.

Get access to the URL's being used in System.Web.Optimization

Background: I'm using the HTML 5 Offline App Cache and dynamically building the manifest file. Basically, the manifest file needs to list each of the static files that your page will request. Works great when the files are actually static, but I'm using Bundling and Minification in System.Web.Optimization, so my files are not static.
When in the DEBUG symbol is loaded (i.e. debugging in VS) then the actual physical files are called from the MVC View. However, when in Release mode, it calls a virtual file that could look something like this: /bundles/scripts/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1
So my question: How can I get that URL in the code to add it to the offline app manifest?
I've tried:
var paths = new List<string>()
{
"~/bundles/styles/common",
"~/bundles/styles/common1024",
"~/bundles/styles/common768",
"~/bundles/styles/common480",
"~/bundles/styles/frontend",
"~/bundles/scripts/jquery",
"~/bundles/scripts/common",
"~/bundles/scripts/frontend"
};
var bundleTable = BundleTable.Bundles;
foreach (var bundle in bundleTable.Where(b => paths.Contains(b.Path)))
{
var bundleContext = new BundleContext(this.HttpContext, bundleTable, bundle.Path);
IEnumerable<BundleFile> files = bundle.GenerateBundleResponse(bundleContext).Files;
foreach (var file in files)
{
var filePath = file.IncludedVirtualPath.TrimStart(new[] { '~' });
sb.AppendFormat(formatFullDomain, filePath);
}
}
As well as replacing GenerateBundleResponse() with EnumerateFiles(), but it just always returns the original file paths.
I'm open to alternative implementation suggestions as well. Thanks.
UPDATE: (7/7/14 13:45)
As well as the answer below I also added this Bundles Registry class to keep a list of the required static files so that it works in debug mode in all browsers. (See comments below)
public class Registry
{
public bool Debug = false;
public Registry()
{
SetDebug();
}
[Conditional("DEBUG")]
private void SetDebug()
{
Debug = true;
}
public IEnumerable<string> CommonScripts
{
get
{
if (Debug)
{
return new string[]{
"/scripts/common/jquery.validate.js",
"/scripts/common/jquery.validate.unobtrusive.js",
"/scripts/common/knockout-3.1.0.debug.js",
"/scripts/common/jquery.timepicker.js",
"/scripts/common/datepicker.js",
"/scripts/common/utils.js",
"/scripts/common/jquery.minicolors.js",
"/scripts/common/chosen.jquery.custom.js"
};
}
else
{
return new string[]{
"/scripts/common/commonbundle.js"
};
}
}
}
}
I'm by no means happy with this solution. Please make suggestions if you can improve on this.
I can suggest an alternative from this blog post create your own token.
In summary the author suggests using web essentials to create the bundled file and then creating a razor helper to generate the token, in this case based on the last changed date and time.
public static class StaticFile
{
public static string Version(string rootRelativePath)
{
if (HttpRuntime.Cache[rootRelativePath] == null)
{
var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
var lastChangedDateTime = File.GetLastWriteTime(absolutePath);
if (rootRelativePath.StartsWith("~"))
{
rootRelativePath = rootRelativePath.Substring(1);
}
var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;
HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
}
return HttpRuntime.Cache[rootRelativePath] as string;
}
}
Then you can reference the bundled file like so...
#section scripts {
<script src="#StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>}
Then you have control of the token and can do what you want with it.

MVC Razor, Include JS / CSS files from another project

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>
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 src=#Url.Action("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!
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.
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.
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:
Create empty folder under my VS project: /ProjectName/Scripts/Common
Committed the empty folder to source control
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.
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.

Categories

Resources