Archive
HowTo: Reset browser cache of CSS files upon ASP.net MVC app publish
On an ASP.net MVC webapp I’m maintaining, I had the issue that due to caching of older CSS (stylesheet) files in the browser, if the user didn’t press F5/refresh, it wasn’t showing you some message (since I had added the class .center-horiz-vert in the CSS that didn’t exist in the older cached css the browser had).
Instead of changing web.config to stop cachine of CSS files (in which case it would bring the CSS on every page load which is an overkill), I expanded on an idea mentioned by Maxim Kornilov on SO (https://stackoverflow.com/a/12992813/903783), on making the CSS URLs webapp version specific.
I added a fake version parameter to the URLs with the build number as value so that till I publish a new build the browser caches the CSS, but when I upload a new build it brings the new one since it cache with the url as a key (that now includes the build number as a dummy url parameter that the webserver will ignore and just fetch the CSS file when requested)
Maxim’s example was in ASP/ASP.net WebForms syntax instead of MVC’s and Razor Pages’ newer Razor syntax), so I contributed my solution for the case of an ASP.net MVC webapp that wants to serve a fresh copy of CSS files on every new build that you publish (will do this whether the CSS file has changed or not) so that browsers don’t use older cached copies of the file. Obviously this expands to any kind of files you link/load into your webpages via a URL.
1) Added to the webapp’s main class (was called MvcApplication) in Global.asax.cs
#region Versioning
public static string Version =>
typeof(MvcApplication).Assembly.GetName().Version.ToString();
//note: syntax requires C# version >=6
public static DateTime LastUpdated =>
File.GetLastWriteTime(typeof(MvcApplication).Assembly.Location);
#endregion
the someProperty => someReadOnlyExpression syntax is just shorthand for someProperty { get { return … ;} } possible since C# 6
2) in its Content/_Layout.cshtml file I used to have the following to show build number and build datetime (based on the webapp’s main assembly) on the page footer:
Version @ViewContext.Controller.GetType().Assembly.GetName().Version
(@string.Format("{0:yyyy/MM/dd-HH:mm:ss}",
@File.GetLastWriteTime(ViewContext.Controller.GetType().Assembly.Location)))
which I changed to the simpler:
Version @somewebappname.MvcApplication.Version
(@string.Format("{0:yyyy/MM/dd-HH:mm:ss}",
somewebappname.MvcApplication.LastUpdated))
3) it was loading the CSS via hardcoded link in _Layout.cshtml (still refactoring it) which I changed to:
<link href='@Url.Content("~/Content/Site.css?version=" +
somewebappname.MvcApplication.Version)' rel="stylesheet" type="text/css" />
so if one right-clicks in the webpage and they do view source they see:
<link href='/Content/Site.css?version=2.1.5435.22633'
rel="stylesheet" type="text/css" />
that is the CSS url is version specific thanks to the dummy parameter version
If a random number was used instead it would fetch the CSS at every page load which is usually undesired, especially if you are already pushing a new webapp build instead of individual page changes to the web server (so that you do have access to a build number that you can inject into URLs).
Note that to achieve auto-incrementing of build number, at Properties/AssemblyInfo.cs I have (see How to have an auto incrementing version number (Visual Studio)?):
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision
// and Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyFileVersion("1.0.*")]
// don't use boh AssemblyVersion and AssemblyFileVersion with auto-increment
HowTo: Use latest C# features in MVC5 Razor views (.cshtml)
Having recently updated an ASP.net MVC web app from MVC4 to MVC5 and from .NET 4.5 to .NET 4.7.2 I was expecting Razor views (.cshtml files) to use the latest C# compiler, especially since at Properties/Build/Advanced option for the web project one read “C# latest major version (default)”.
However that was not the case and trying to use newer C# language features like the ?. ternary conditional operator or interpolated strings (or nameof etc.) would show errors like
Feature ‘interpolated strings’ is not available in C# 5. Please use language version 6 or greater.
Luckily there is a workaround for using the latest C# compiler in MVC5. Just need to add the NuGet package https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/ to one’s project as explained at https://dusted.codes/using-csharp-6-features-in-aspdotnet-mvc-5-razor-views. Alternatively one could move their project to ASP.net Core, which is a more drastic move though.
After doing it I started seeing Intellisense issues in .cshtml like:
The type ‘Expression<>’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘System.Core …
Tried to add the System.Core assembly to the project, but wasn’t allowed (it said the Build system was adding it). Adding System.Core as a NuGet package would mean moving to .NET Core which I wasn’t ready to try with that project yet.
Seems there was an easy solution to that, just closed and reopened the Visual Studio solution and did a Rebuild and all was fine after that.
Fix: jQuery’s jqGrid search UI custom styling
Lately, I’ve got the task of maintaining/extending an ASP.net MVC web application that is using jQuery’s jqGrid for data grids on its UI. First thing I noticed was how confusing the search UI on the grid’s header was:
Those symbols on the left-side of each column’s searchbox are for the type of search (e.g. contains, doesn’t contain, equals, starts with, doesn’t start with, ends with, doesn’t end with).
Bit too many options and using programming-related symbols that probably intimidate several users in my opinion:
But the worse is the “x” button (that clears the searchbox) on the right of each searchbox, that combined with the search-type symbols makes the whole search bar look like some strange mathematical expression.
So using the browser dev tools (F12) and some CSS rules I quickly restyled that search bar to make it more appealing UI/UX wise:
Added a border around the “x” button that clears the searchbox and offseted using a negative margin so that the searchbox and it fuse together visually on their sides. Also made the search-type symbol (that opens the search-type selection popup when clicked) of lighter color. It may look a bit-like some disabled thing like that, but at least it should confuse average users less with its use of technical symbols like that.
Just need to add the rules above at the ASP.net MVC app’s Site.css (probably to be found at the Content subfolder of the webapp) and remember to press F5/Refresh in one’s browser in case the old styling still appears due to caching.
Update #1:
I noticed on older versions of Windows (other than Windows 10 that is) that bevels were showing at the text inputs, leading to this ugly effect:
So I had to add some more rules to remove the bevel borders and use a consistent border color.
Removing the bevels seemed to also remove the inner padding of the text inputs, so added a padding of 2px and some box-sizing rules to make sure the padding doesn’t affect the input’s size.
/* OS-independent styling for input and textarea borders */
textarea,
input[type="text"],
input[type="password"] {
border-style: solid;
border-width: 1px;
border-color: gray;
padding: 2px;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
Update #2:
After recently updating some jQuery related NuGet packages in that ASP.net project, I noticed the [x] button was showing a bit higher up at the bottom compared to the search box. The fix to that was to add padding-bottom: 1px; at the CSS declaration for clearsearchclass in Content/Site.css
a.clearsearchclass {
border-width: 1px;
border-style: solid;
/* border-left-style: none; */
margin-left: -3px;
padding-bottom: 1px; /* seems to be needed with newer jQuery.UI */
background: whitesmoke;
border-color: gray;
}
Workarround: IE11 changing download file extension to .zip
At ClipFlair Gallery, apart from opening a ClipFlair activity in ClipFlair Studio, downloading of an activity (.clipflair) file is also supported.
However, because the component serialization file format of ClipFlair Studio is XML plus media assets packed in .zip archive (with nesting allowed, where components and whole activities can be placed in other activities), Internet Explorer 11 (and probably other browsers too) was downloading .clipflair files as .zip (changing their file extension).
At first, I thought that occured because I was using MIME type “application/zip” at the IIS web server/site settings for that file extension. So then I tried to change it to “application/octet-stream” hoping that one would be treated as an “opaque” data stream.
However, eventually I ended up setting a custom MIME type “application/clipflair” for the file extension “.clipflair”, because even with “application/octet-stream” (as with the “application/zip” that I had before), IE11 was still saving the .clipflair file as .zip (obviously detecting the zip content in the download stream).
<?xml version=”1.0″?>
<configuration><system.webServer>
<directoryBrowse enabled=”true” showFlags=”Size, Extension”/>
<defaultDocument>
<files>
<clear/>
<add value=”index.html”/>
<add value=”Default.aspx”/>
<add value=”Default.html”/>
</files>
</defaultDocument><caching>
<profiles>
<add extension=”.log”
policy=”CacheUntilChange”
kernelCachePolicy=”CacheUntilChange”/>
</profiles>
</caching><staticContent>
<mimeMap fileExtension=”.log” mimeType=”text/plain”/>
<mimeMap fileExtension=”.clipflair” mimeType=”application/clipflair”/>
<mimeMap fileExtension=”.dzi” mimeType=”text/xml”/>
<mimeMap fileExtension=”.dzc” mimeType=”text/xml”/>
<mimeMap fileExtension=”.cxml” mimeType=”text/xml”/>
</staticContent></system.webServer>
<system.web>
<compilation debug=”true” targetFramework=”4.0.3″/>
</system.web></configuration>
HowTo: Use WordPress Permalinks on IIS
at http://zachis.it/blog/7-dangers-of-using-windows-server-on-a-wordpress-installation/
the thing that guy says about Permalinks isn’t accurate at all (not that the other things that he says are any accurate that is). WordPress Codex have documentation on how to configure URL rewriting in web.config that is necessery for Permalinks to work in IIS.
e.g. at http://ClipFlair.net, if you press the "about" icon you’re taken to a WordPress site that runs on IIS and uses permalinks fine and hides the index.php too from the URL
in its web.config I have the following:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer><httpErrors>
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath="" path="/index.php?error=404" responseMode="ExecuteURL" />
</httpErrors><!– Needed for WordPress Permalinks –>
<rewrite>
<rules><rule name="Main Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<!– <action type="Rewrite" url="index.php/{R:0}" /> –>
<action type="Rewrite" url="index.php" />
</rule></rules>
</rewrite><defaultDocument>
<files>
<clear />
<add value="index.html" />
<add value="index.php" />
<add value="default.aspx" />
</files>
</defaultDocument></system.webServer>
</configuration>
HowTo: Perform ASP.net action after page child controls are databound
While creating metadata entry/update pages for ClipFlair’s Activity and Clip Galleries I had the problem of figuring out how to do some initialization (from XML data, loaded from a filename based on the 1st item of a DataBound control), after all child controls of the ASP.net Page have been databound (from XmlDataSource).
Seems others have asked about that too, so I contributed my solution:
Based on ASP.net Page Life Cycle Events article I used:
protected void Page_PreRenderComplete(object sender, EventArgs e)
{
if (!IsPostBack) //only at 1st load
UpdateSelection();
}
protected void UpdateSelection()
{
UpdateSelection(listItems.SelectedValue);
}
protected void listItems_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateSelection();
}
where UpdateSelection was loading XML data from a file selected at a dropdown list (which at start is pointing to index 0) and needed some CheckBoxLists on the page to have first gotten their items from other XML files so that they would allow the code to check items on them based on the XML data (from then on UpdateSelection is just getting called at the dropdownlist SelectedIndexChanged event – those do PostBacks so at PreRenderComplete we ignore them to avoid doing UpdateSelection twice)
Gotcha: use server-side comment, not HTML comment in ASP.net
I try to remember to use <%– and –%> instead of <!– and –> for comments in server-side web pages, to avoid having implementation details (for security reasons that is) in the output HTML and to keep the download size smaller / have the pages load faster.
What came to me though the other day while making some metadata editing pages for ClipFlair Gallery, was that if you don’t use server-side comments and use classic HTML comments in server-side pages, the code inside the HTML comments is executed at the server side and its output is placed in HTML comments at the web page the client browser downloads.
So this can potentially lead to a very big download and to increased time to generate the page at the sever-side if you accidentally comment out some code block you were using for testing and don’t use server-side comments, but use HTML comments instead to comment it out.
What got me into suspission was the syntax highlighting of Visual Studio 2010. Note below that <%@ Register isn’t highlighted green (as it would be in a server-side comment).
Probably JavaEE’s JSP behaves similarly (uses the same symbols for server-side comments), but haven’t tested it.
Rate this:
Like this: