<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" version="2.0">
  <channel>
    <title>Ben's Quarters</title>
    <description>adventures in application development ...</description>
    <link>http://allben.net/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 2.5.0.8</generator>
    <language>en-US</language>
    <blogChannel:blogRoll>http://allben.net/opml.axd</blogChannel:blogRoll>
    <dc:creator>My name</dc:creator>
    <dc:title>Ben's Quarters</dc:title>
    <geo:lat>0.000000</geo:lat>
    <geo:long>0.000000</geo:long>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/allben" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="allben" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
      <title>BlogEngine.NET Theme Contest</title>
      <description>&lt;p&gt;If you haven't heard yet about the BE.NET theme contest, it started a couple of weeks ago, and continues on for about 1 more month -- until November 15th.&lt;/p&gt;
&lt;p&gt;The 1st BlogEngine.NET Theme Contest!&lt;a href="http://www.dotnetblogengine.net/post/The-1st-BlogEngineNET-Theme-Contest!.aspx"&gt;&lt;br /&gt;http://www.dotnetblogengine.net/post/The-1st-BlogEngineNET-Theme-Contest!.aspx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you know your way a bit around HTML/CSS and BE.NET themes, submit a theme, and you'll be contributing to the growing collection of BE.NET themes, and a chance to win one of the prizes.&lt;/p&gt;
&lt;p&gt;So far there hasn't been too many submissions, so winning a prize is easier odds than a lot of other contests out there.&lt;/p&gt;</description>
      <link>http://allben.net/post/2011/10/19/BlogEngineNET-Theme-Contest.aspx</link>
      <comments>http://allben.net/post/2011/10/19/BlogEngineNET-Theme-Contest.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=5fa11c8b-4158-45e9-bdcc-4a8a8558714c</guid>
      <pubDate>Wed, 19 Oct 2011 05:17:00 -0800</pubDate>
      <category>General</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=5fa11c8b-4158-45e9-bdcc-4a8a8558714c</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=5fa11c8b-4158-45e9-bdcc-4a8a8558714c</trackback:ping>
      <wfw:comment>http://allben.net/post/2011/10/19/BlogEngineNET-Theme-Contest.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=5fa11c8b-4158-45e9-bdcc-4a8a8558714c</wfw:commentRss>
    </item>
    <item>
      <title>Introducing Multiple Blogs in Single Instance for BlogEngine.NET</title>
      <description>&lt;p&gt;The much requested and highly anticipated "multiple blogs" feature has made it into BlogEngine.NET 2.5.&amp;nbsp; BE.NET 2.5 will be released at the end of June.&amp;nbsp; This post goes over how it works and some information on making themes, widgets, extensions and your code be multiple-blog compatible.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Blogs Management Page&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Each installation of BE.NET will have a "primary" blog instance.&amp;nbsp; By default this will be the normal blog you are accustomed to.&amp;nbsp; The primary blog has a new "Blogs" admin page.&amp;nbsp; That page looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://allben.net/image.axd?picture=2011%2f6%2fblogs_mgmt_1.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Here the blog instances (the primary blog instance and child blogs) can be managed.&amp;nbsp; A blog instance can be made Inactive.&amp;nbsp; An inactive blog instance will appear as if it does not exist.&amp;nbsp; Other properties that can be configured for each blog instance can be seen in the following "Add New Blog" dialog window:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://allben.net/image.axd?picture=2011%2f6%2fblogs_add_new_blog.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Let's look at the properties for each blog instance.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Any name you'd like to give the blog instance.&amp;nbsp; The name does not appear anywhere outside this Blogs management page.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Storage Container Name&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This is the name of the folder where the blog data will live.&amp;nbsp; Basically a copy of the files and folders for each blog instance will be made -- i.e. each blog instance will have its own separate folder of data.&amp;nbsp; This makes the blog instances portable and keeps the data separated.&amp;nbsp; Databases can also be used with multiple blogs.&amp;nbsp; Even when using a database, there is still some data (blog post images/files, and a few other small files) that is stored in the file system.&amp;nbsp; So a separate folder will be created for each blog instance even when using the DbBlogProvider.&lt;/p&gt;
&lt;p&gt;As in the past, the primary blog instance data is stored in App_Data.&amp;nbsp; There's now a new Blogs folder under App_Data.&amp;nbsp; Within App_Data\Blogs, a new folder matching the "Storage Container Name" will be created when a new blog instance is created.&amp;nbsp; The data that is initially put into this child blog storage folder is explained next.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Existing Blog to Create New Blog from&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This is partially related to the above Storage Container Name.&amp;nbsp; When a new blog instance is created, the "Existing Blog to Create New Blog From" dropdown list will contain all of the existing blog instances.&amp;nbsp; The files &amp;amp; folders from the existing blog instance you choose here will be copied into the storage folder for the new blog instance as "initial data".&amp;nbsp; BE.NET 2.5 also includes a "Template" blog instance (which is inactive), and can be used as the existing blog instance to create new blog instances from.&amp;nbsp; The Template blog instance has the same data and settings as the primary blog.&amp;nbsp; You can optionally change the Template blog data, or even create multiple template blog instances to choose from when creating new blog instances.&lt;/p&gt;
&lt;p&gt;If using a database, in addition to the storage container folder being copied for the new blog instance, the DB data from the existing blog instance will be copied in the DB for the new blog instance.&amp;nbsp; So for both XML and DB, the new blog instance created will start off using the exact data, files, etc that the blog instance you copied from has.&lt;/p&gt;
&lt;p&gt;Note, there is no Template blog included with the database.&amp;nbsp; You can still create your own template blog for the DB by creating a new blog instance of your Primary blog, naming it "Template" (or any name) and customizing it to your liking.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Host Name &amp;amp; Virtual Path&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;These two properties along with the "Accept Any Text before Host Name" checkbox are what is used for BE.NET to determine which blog instance a request to the application is being made for.&amp;nbsp; Virtual Path refers to the path following the root.&amp;nbsp; Host name is the domain name (plus any subdomain).&amp;nbsp; You can mix and match Hostname and Virtual Path to create many URL combinations.&amp;nbsp; For example, the following URLs can be used:&lt;/p&gt;
&lt;p&gt;http://www.example.com&lt;br /&gt;http://www.example.com/blog1&lt;br /&gt;http://blog1.example.com&lt;br /&gt;http://blog2.domain.com&lt;br /&gt;http://blog.domain.com/blog2&lt;br /&gt;... etc ...&lt;/p&gt;
&lt;p&gt;The 2nd and 5th examples above would be given a Virtual Path of ~/blog1 and ~/blog2 respectively.&amp;nbsp; Prefixing the virtual path with ~/ is required, and the ~/ will automatically be inserted for you in the Virtual Path field.&amp;nbsp; In the 3rd example above, the Host Name would be "blog1.example.com", in the 4th example the host name would be "blog2.domain.com" and in the 5th example, the host name would be "blog.domain.com".&lt;/p&gt;
&lt;p&gt;If you are only using a single hostname for your blog, you can omit the hostname.&amp;nbsp; Entering it is really only required if you will be using multiple hostnames.&lt;/p&gt;
&lt;p&gt;With the Virtual Paths above of blog1 and blog2 (examples 2 and 5), you do &lt;span style="text-decoration: underline;"&gt;not&lt;/span&gt; need to create physical directories named blog1 and blog2.&amp;nbsp; These are virtual directories that BE.NET will look for in the URL to treat that request to the web server as being on behalf of those blog instances.&amp;nbsp; At the same time, you will want to make sure that physical directories matching the virtual paths (blog1, blog2, etc) do not already exist.&lt;/p&gt;
&lt;p&gt;The "Accept Any Text before Host Name" checkbox is another option to allow any text (subdomains) to appear before the Host Name you enter.&amp;nbsp; If you leave this box unchecked, then the Host Name you enter will need to match exactly what is in the browser address bar.&lt;/p&gt;
&lt;h3&gt;&lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;Multiple-Blog Capable Themes, Widgets and Extensions&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;If you will be running a single instance blog, all of the themes, extensions and widgets that run under BE.NET 2.0 will still work perfectly fine for BE.NET 2.5.&lt;/p&gt;
&lt;p&gt;If you will be running multiple blog instances, existing themes, extensions and widgets may need some adjustments to work with this new multiple blogs feature.&amp;nbsp; The following types of issues need to be addressed.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Caching - HttpRuntime.Cache&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If an existing theme, widget or extension is storing data in the HttpRuntime.Cache, a likely needed change is to store that data separately for each blog instance.&amp;nbsp; With multiple blog instances, the same set of (physical) themes, extensions and widgets are being used across all the blog instances.&amp;nbsp; So if you have code in a widget that looks like:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;HttpRuntime.Cache["recent-posts"] = recentPostList;&lt;/pre&gt;
&lt;p&gt;This same data you are storing will be shared across all blog instances that are using this widget.&amp;nbsp; This is probably not what was intended.&amp;nbsp; You can either change that code so it uses a dynamic cache key based on the "current blog instance", or as a convenient shortcut, you can use a new Caching provider built into BE.NET 2.5.&amp;nbsp; That line of code above using the new caching provider would look like:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;Blog.CurrentInstance.Cache["recent-posts"] = recentPostList;&lt;/pre&gt;
&lt;p&gt;The cache provider will use your "recent-posts" cache key, prefixing it with the ID of the current blog instance (a GUID).&amp;nbsp; You can retrieve the current blog instance and/or the ID of the current blog instance via:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;Blog currentInstance = Blog.CurrentInstance;
Guid currentInstanceId = Blog.CurrentInstance.Id;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Caching - Static&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If your theme, widget or extension is storing data in static fields or properties, the cache provider described above cannot be used, however one approach is to convert your static data into a generic dictionary, with Guid as the key and your data type as the value.&amp;nbsp; The Guid key will represent the current blog instance ID.&amp;nbsp; You need to manage this dictionary by adding in each blog instance in the dictionary, ideally as the data needs to be stored the first time.&amp;nbsp; That code might look like:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;private static Dictionary&amp;lt;Guid, string&amp;gt; _recentPostsMarkup =
    new Dictionary&amp;lt;Guid, string&amp;gt;();

private static string RecentPostsMarkup
{
	get
	{
		if (!_recentPostsMarkup.ContainsKey(Blog.CurrentInstance.Id))
		{
			_recentPostsMarkup[Blog.CurrentInstance.Id] = GetRecentPostsMarkup();
		}

		return _recentPostsMarkup[Blog.CurrentInstance.Id];
	}
}&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;URL Paths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This is important and a problem you may notice, especially if you are using Virtual Paths for your blog instances.&amp;nbsp; Let's look at a simple line of code in BE.NET 2.0 that worked fine there:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;Response.Redirect("~/Account/login.aspx");&lt;/pre&gt;
&lt;p&gt;Simple enough.&amp;nbsp; However, if you're using Virtual Paths and you have a blog instance with a Virtual Path of "blog1", the homepage for that blog instance will be something like:&lt;/p&gt;
&lt;p&gt;http://www.example.com/blog1&lt;/p&gt;
&lt;p&gt;If you have code that uses that redirect shown above, you will end up at:&lt;/p&gt;
&lt;p&gt;http://www.example.com/Account/login.aspx&lt;/p&gt;
&lt;p&gt;When you probably wanted to end up at:&lt;/p&gt;
&lt;p&gt;http://www.example.com/blog1/Account/login.aspx&lt;/p&gt;
&lt;p&gt;So instead of redirecting to the login page for "blog1", you've incorrectly redirected the person to the login page for the primary blog instance.&amp;nbsp; That redirect should now look like the following to work well with multiple-blogs:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;Response.Redirect(Utils.RelativeWebRoot + "Account/login.aspx");&lt;/pre&gt;
&lt;p&gt;In the past, Utils.RelativeWebRoot would resolve to the root of the blog.&amp;nbsp; Starting with BE.NET 2.5, Utils.RelativeWebRoot will resolve to the homepage/root of the current blog instance.&amp;nbsp; So when you are in "blog1", Utils.RelativeWebRoot will resolve to:&lt;/p&gt;
&lt;p&gt;/blog1/&lt;/p&gt;
&lt;p&gt;Similarly, Utils.AbsoluteWebRoot also takes the relative path of the current blog instance into consideration.&lt;/p&gt;
&lt;p&gt;If you need the "real" web root of the application, you could use ~/ or you can use a new Utils.ApplicationRelativeWebRoot property.&lt;/p&gt;
&lt;p&gt;While on the topic, a couple of other similar new properties are Blog.CurrentInstance.StorageLocation which will return a storage location beginning with ~/ to the physical location of the storage container for the current blog instance.&amp;nbsp; So Blog.CurrentInstance.StorageLocation may return a value looking like:&lt;/p&gt;
&lt;p&gt;~/App_Data/Blogs/blog1/&lt;/p&gt;
&lt;p&gt;BlogConfig.StorageLocation will return the same type of path, but to the direct App_Data folder (which equates to the storage location of the primary blog instance), so BlogConfig.StorageLocation will generally return:&lt;/p&gt;
&lt;p&gt;~/App_Data/&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Extensions&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;With extensions, the primary blog instance has the "master" switch over whether extensions are enabled.&amp;nbsp; If the primary blog instance disables an extension, that extension is no longer available to any of the child blogs.&amp;nbsp; If the primary blog instance enables an extension, by default this extension will be enabled for all the child blogs.&amp;nbsp; A child blog can disable an extension.&lt;/p&gt;
&lt;p&gt;As long as the primary blog instance has an extension enabled, that extension will be loaded into memory and if it wires up any of the normal event handlers (Post.Serving, Commnet.Serving, etc), that extension is going to fire even if the event is occurring in a child blog that has explicilty disabled the extension.&amp;nbsp; In this scenario, the extension is handling an event for the child blog, but it should not do anything since the child blog disabled the extension.&amp;nbsp; In other words, we want the extension to "pretend" it was never called, or at least not take any action.&amp;nbsp; This is where a &lt;span style="text-decoration: underline;"&gt;change is required&lt;/span&gt; in extensions so they check to see if the blog instance the extension is firing for has the extension enabled or disabled.&amp;nbsp; If it is disabled, then the extension should not run any of it's normal code.&amp;nbsp; The extensions included with BE.NET 2.5 have a new line of code added to them for this.&amp;nbsp; Taking the ResolveLinks extensions as an example, it has this new line of code (technically 2 lines of code) added to the beginning of PostCommentServing:&lt;/p&gt;
&lt;pre class="brush: c-sharp;"&gt;private static void PostCommentServing(object sender, ServingEventArgs e)
{
	if (!ExtensionManager.ExtensionEnabled("ResolveLinks"))
		return;

	if (string.IsNullOrEmpty(e.Body))
		return;

	e.Body = LinkRegex.Replace(e.Body, new MatchEvaluator(Evaluator));
}&lt;/pre&gt;
&lt;p&gt;The new lines of code above are lines 3 and 4.&amp;nbsp; It makes a call into the Extension Manager, passing to it the name of the extension (in this case, ResolveLinks).&amp;nbsp; ExtensionEnabled() will return false if the extension is disabled for the current blog instance.&lt;/p&gt;
&lt;h3&gt;&lt;span style="text-decoration: underline;"&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, if you will be running a single instance of BE.NET, these changes described above will not need to be made -- although they can be made.&amp;nbsp; Overall, I think we maintained a lot of backwards compatibility as the changes you will need to make to have a multiple-blog capable BE.NET application are very minimal.&amp;nbsp; The themes, widgets and extensions included with BE.NET 2.5 already have these changes&amp;nbsp; made to them.&amp;nbsp; Enjoy the new multiple blog capability and if you run into any issues or have any suggestions, the best place to pass along your thoughts is in the &lt;a href="http://blogengine.codeplex.com/discussions"&gt;CodePlex discussion group&lt;/a&gt;.&lt;/p&gt;</description>
      <link>http://allben.net/post/2011/06/19/Introducing-Multiple-Blogs-in-Single-Instance-for-BlogEngineNET.aspx</link>
      <comments>http://allben.net/post/2011/06/19/Introducing-Multiple-Blogs-in-Single-Instance-for-BlogEngineNET.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=e2fcc91c-b610-4fcc-8e7a-8fa32a55879a</guid>
      <pubDate>Sun, 19 Jun 2011 13:00:00 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=e2fcc91c-b610-4fcc-8e7a-8fa32a55879a</pingback:target>
      <slash:comments>16</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=e2fcc91c-b610-4fcc-8e7a-8fa32a55879a</trackback:ping>
      <wfw:comment>http://allben.net/post/2011/06/19/Introducing-Multiple-Blogs-in-Single-Instance-for-BlogEngineNET.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=e2fcc91c-b610-4fcc-8e7a-8fa32a55879a</wfw:commentRss>
    </item>
    <item>
      <title>BlogEngine.NET 2.0 RC - Available now!</title>
      <description>&lt;p&gt;On Tuesday, a &lt;a href="http://blogengine.codeplex.com/releases/view/56309"&gt;release candidate&lt;/a&gt; for &lt;a href="http://dotnetblogengine.net/"&gt;BlogEngine.NET&lt;/a&gt; 2.0 was published.&amp;nbsp; Many nice features were added, with lots of great contributions from the community.&amp;nbsp; Switching to Mercurial for source code management made it very easy for people to create their own forks and for us to merge their code in.&lt;/p&gt;
&lt;p&gt;Here's the &lt;a href="http://dotnetblogengine.net/post/BlogEngine-20-Release-Candidate-is-Out!.aspx"&gt;official RC announcement&lt;/a&gt; that contains some additional details, including a short 5 minute video demo'ing the redesigned control panel.&lt;/p&gt;
&lt;p&gt;If you're already using BlogEngine, I think you'll really like the improvements.&amp;nbsp; These &lt;a href="http://blogengine.codeplex.com/wikipage?title=Upgrading%20to%20BlogEngine.NET%202.0"&gt;upgrade instructions&lt;/a&gt; will come in handy.&amp;nbsp; If you're not using BlogEngine, what are you waiting for?!&amp;nbsp; Stop copying your friends, throw away &lt;a href="http://dictionary.reference.com/browse/overrated" target="_blank"&gt;WordPress&lt;/a&gt; and get &lt;a href="http://dotnetblogengine.net/"&gt;BlogEngine&lt;/a&gt;! :)&lt;/p&gt;
&lt;p&gt;I expect we will get some good feedback on issues people run into while using the RC.&amp;nbsp; During my own testing, it's actually working very good even now.&amp;nbsp; Depending on how well the RC goes, the final RTW version may be out in December.&lt;/p&gt;
&lt;p&gt;If you download it and find any issues, the best place to report those are in the &lt;a href="http://blogengine.codeplex.com/workitem/list/basic"&gt;Issue Tracker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Give it a try and have fun with it!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogengine.codeplex.com/releases/view/56309"&gt;Download BlogEngine.NET 2.0 Release Candidate&lt;/a&gt;&lt;/p&gt;</description>
      <link>http://allben.net/post/2010/11/24/BlogEngineNET-20-RC-Available-now!.aspx</link>
      <comments>http://allben.net/post/2010/11/24/BlogEngineNET-20-RC-Available-now!.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=e2c6e7fd-bd87-4ac2-8449-416a393727c2</guid>
      <pubDate>Wed, 24 Nov 2010 00:36:00 -0800</pubDate>
      <category>General</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=e2c6e7fd-bd87-4ac2-8449-416a393727c2</pingback:target>
      <slash:comments>6</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=e2c6e7fd-bd87-4ac2-8449-416a393727c2</trackback:ping>
      <wfw:comment>http://allben.net/post/2010/11/24/BlogEngineNET-20-RC-Available-now!.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=e2c6e7fd-bd87-4ac2-8449-416a393727c2</wfw:commentRss>
    </item>
    <item>
      <title>BlogEngine 1.6 – Released!</title>
      <description>&lt;p&gt;Today &lt;a href="http://dotnetblogengine.net/" target="_blank"&gt;BlogEngine.NET&lt;/a&gt; 1.6 is available to everyone as announced &lt;a href="http://dotnetblogengine.net/post/BlogEngineNET-16-is-Released.aspx" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The two big features are a new Comment Management area to view, edit and delete comments across all posts.&amp;nbsp; This also includes a new automated comment moderation system.&amp;nbsp; Just like how you can build custom Extensions, custom filters can be built and plugged into the event system when a new comment is posted.&amp;nbsp; BE.NET 1.6 ships with two custom filters &amp;ndash; a filter for Akismet filter and one for StopForumSpam.com.&amp;nbsp; It&amp;rsquo;s quite slick.&lt;/p&gt;
&lt;p&gt;The other notable feature is Multiple Widget Zones.&amp;nbsp; This has actually been in the code base for several months now.&amp;nbsp; I blogged about &lt;a href="http://allben.net/post/2009/04/18/Multiple-WidgetZones-in-BENET.aspx" target="_blank"&gt;Multiple Widget Zones&lt;/a&gt; back in April.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re upgrading from BE.NET 1.5, there are a couple of changes to be aware of, including one change you &lt;span style="text-decoration: underline;"&gt;must make&lt;/span&gt; related to the ExtensionManager sub-folder in the App_Code folder.&amp;nbsp; Simple upgrade instructions are available &lt;a href="http://blogengine.codeplex.com/wikipage?title=Installation#version16upgrade" target="_blank"&gt;here&lt;/a&gt; in the documentation.&lt;/p&gt;
&lt;p&gt;A more complete list of features and changes in BE.NET 1.6 can be found &lt;a href="http://dotnetblogengine.net/page/BlogEngineNET-16-Release-Notes.aspx" target="_blank"&gt;here&lt;/a&gt;.&amp;nbsp; It&amp;rsquo;s definitely a worthwhile upgrade I recommend going to.&lt;/p&gt;</description>
      <link>http://allben.net/post/2010/02/01/BlogEngine-16-Released.aspx</link>
      <comments>http://allben.net/post/2010/02/01/BlogEngine-16-Released.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=4a7600dc-f27d-45a6-856f-5fbac1619033</guid>
      <pubDate>Mon, 01 Feb 2010 19:50:00 -0800</pubDate>
      <category>General</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=4a7600dc-f27d-45a6-856f-5fbac1619033</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=4a7600dc-f27d-45a6-856f-5fbac1619033</trackback:ping>
      <wfw:comment>http://allben.net/post/2010/02/01/BlogEngine-16-Released.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=4a7600dc-f27d-45a6-856f-5fbac1619033</wfw:commentRss>
    </item>
    <item>
      <title>CSS &amp; JavaScript Injection Bookmarklets</title>
      <description>&lt;p&gt;I like using bookmarklets to inject scripts into a page I’m on.&amp;#160; One of the popular ones for this is the &lt;a href="http://www.learningjquery.com/2006/12/jquerify-bookmarklet" target="_blank"&gt;jQueryify bookmarklet&lt;/a&gt; to inject jQuery into a page.&lt;/p&gt;  &lt;p&gt;After getting the jQuerify bookmarklet, I created a separate bookmarklet to inject my own JavaScript file into a document.&amp;#160; This is handy when I’m debugging a website I either don’t have the files for, or just want to test changes out within the confines of my own browser without touching any of the live files.&amp;#160; To do this, I simply Edit the bookmark in my browser, and change the URL embedded in the bookmarklet to point to the URL of the JavaScript file I want to inject into the page.&amp;#160; The URL to the script can even be a script on your own computer accessible via http://localhost, if you’re too lazy to upload the script to a public website :)&lt;/p&gt;  &lt;p&gt;Recently, I needed to inject a CSS stylesheet into the page I’m on.&amp;#160; The bookmarklet for this is very similar to the JavaScript injection bookmarklet.&amp;#160; The bookmarklet I created for this is at the bottom of this post.&lt;/p&gt;  &lt;p&gt;&lt;font color="#0d2fa4" size="4"&gt;Dynamic URL Bookmarklets&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Even though it’s not a big hassle to Edit the CSS/JS injection bookmarks to change the URL to the JS or CSS file embedded within the bookmarklet, I realized a very convenient bookmarklet would be one that would prompt me for the URL to the CSS/JS file, and then inject that URL.&amp;#160; By doing this, I don’t need to edit the bookmark, and can easily inject any CSS or JS file.&amp;#160; It’s also easy to inject multiple URLs by running the bookmark more than once, and entering a different URL each time.&lt;/p&gt;  &lt;p&gt;&lt;font color="#0d2fa4" size="4"&gt;Bookmarklets – For your Browser&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;For convenience’s sake, I have these 4 bookmarklets down below.&amp;#160; Feel free to use them.&amp;#160; Two of the bookmarklets are for JS injections and two are for CSS injections.&amp;#160; Within each pair, one bookmarklet has the URL already embedded within it, and the other one will prompt you for the URL.&lt;/p&gt;  &lt;p&gt;Just drag these links into your Bookmarks toolbar or menu area.&amp;#160; For reference, the bookmarklet code is under each link.&lt;/p&gt;  &lt;p&gt;&amp;gt; &lt;a href="javascript:(function(){var%20s=document.createElement('script');s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js');document.getElementsByTagName('body')[0].appendChild(s);alert('Script%20injected!');})();"&gt;Inject JS file&lt;/a&gt; &amp;lt;     &lt;br /&gt;javascript:(function(){var%20s=document.createElement('script');s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js');document.getElementsByTagName('body')[0].appendChild(s);alert('Script%20injected!');})();&lt;/p&gt;  &lt;p&gt;&amp;gt; &lt;a href="javascript:(function(){var%20sUrl=prompt('Enter%20URL%20to%20JavaScript%20file');if(sUrl){var%20s=document.createElement('script');s.setAttribute('src',sUrl);document.getElementsByTagName('body')[0].appendChild(s);alert('Script%20injected!');}})();"&gt;Inject JS file (get prompted)&lt;/a&gt; &amp;lt;     &lt;br /&gt;javascript:(function(){var%20sUrl=prompt('Enter%20URL%20to%20JavaScript%20file');if(sUrl){var%20s=document.createElement('script');s.setAttribute('src',sUrl);document.getElementsByTagName('body')[0].appendChild(s);alert('Script%20injected!');}})();&lt;/p&gt;  &lt;p&gt;&amp;gt; &lt;a href="javascript:(function(){var%20s=document.createElement('link');s.setAttribute('href','http://l.yimg.com/a/lib/arc/core_1.0.5.css');s.setAttribute('rel','stylesheet');s.setAttribute('type','text/css');document.getElementsByTagName('head')[0].appendChild(s);alert('Stylesheet%20injected!');})();"&gt;Inject CSS file&lt;/a&gt; &amp;lt;     &lt;br /&gt;javascript:(function(){var%20s=document.createElement('link');s.setAttribute('href','http://l.yimg.com/a/lib/arc/core_1.0.5.css');s.setAttribute('rel','stylesheet');s.setAttribute('type','text/css');document.getElementsByTagName('head')[0].appendChild(s);alert('Stylesheet%20injected!');})();&lt;/p&gt;  &lt;p&gt;&amp;gt; &lt;a href="javascript:(function(){var%20sUrl=prompt('Enter%20URL%20to%20Stylesheet');if(sUrl){var%20s=document.createElement('link');s.setAttribute('href',sUrl);s.setAttribute('rel','stylesheet');s.setAttribute('type','text/css');document.getElementsByTagName('head')[0].appendChild(s);alert('Stylesheet%20injected!');}})();"&gt;Inject CSS file (get prompted)&lt;/a&gt; &amp;lt;     &lt;br /&gt;javascript:(function(){var%20sUrl=prompt('Enter%20URL%20to%20Stylesheet');if(sUrl){var%20s=document.createElement('link');s.setAttribute('href',sUrl);s.setAttribute('rel','stylesheet');s.setAttribute('type','text/css');document.getElementsByTagName('head')[0].appendChild(s);alert('Stylesheet%20injected!');}})();&lt;/p&gt;</description>
      <link>http://allben.net/post/2010/01/30/CSS-JavaScript-Injection-Bookmarklets.aspx</link>
      <comments>http://allben.net/post/2010/01/30/CSS-JavaScript-Injection-Bookmarklets.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=320c42a5-33e9-4ad7-a156-234ce5c8e3ac</guid>
      <pubDate>Sat, 30 Jan 2010 23:34:00 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=320c42a5-33e9-4ad7-a156-234ce5c8e3ac</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=320c42a5-33e9-4ad7-a156-234ce5c8e3ac</trackback:ping>
      <wfw:comment>http://allben.net/post/2010/01/30/CSS-JavaScript-Injection-Bookmarklets.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=320c42a5-33e9-4ad7-a156-234ce5c8e3ac</wfw:commentRss>
    </item>
    <item>
      <title>Giving Comment Spammers Less Incentive to Spam You</title>
      <description>&lt;p&gt;The latest check-in of BE.NET (1.5.1.36) has a small, but important change.&amp;#160; The three themes included with BE.NET now include rel=”nofollow” on the links of commenter’s websites.&lt;/p&gt;  &lt;p&gt;This is a theme specific change.&amp;#160; So if you’re using a custom theme, and even if you upgrade to the latest build of BE.NET, there’s a good chance you might not have the NOFOLLOW instruction on these links.&amp;#160; It can simply be added in the CommentView.ascx file in your theme’s blog folder.&lt;/p&gt;  &lt;p&gt;As I’m using a custom theme myself, I just added NOFOLLOW to this blog too.&amp;#160; Wikipedia has a good &lt;a href="http://en.wikipedia.org/wiki/Nofollow" target="_blank"&gt;writeup on NOFOLLOW&lt;/a&gt;, in case you aren’t familiar with its purpose.&amp;#160; I’m a little surprised it’s taken this long to get NOFOLLOW into the themes that are included with BE.NET.&amp;#160; Better late than never!&lt;/p&gt;  &lt;p&gt;I get a lot of comment spam on this blog.&amp;#160; As I’m moderating comments, it ends up never showing up since I don’t approve any of it (TIP to spammers, stop wasting your time!).&lt;/p&gt;  &lt;p&gt;Comment spammers are a lot more likely to leave comments on blogs that do not include NOFOLLOW.&amp;#160; Yes, I’m sure a lot of the spammers actually look at these types of details when scoping out blogs to attack.&lt;/p&gt;  &lt;p&gt;Incidentally, the ResolveLinks extension that comes with BE.NET already includes the NOFOLLOW instructions.&amp;#160; This is the extension that will convert URLs in comments into hyperlinks.&amp;#160; If the extension finds a URL like www.google.com in the comment content, it will convert that into:&lt;/p&gt;  &lt;p&gt;&amp;lt;a href=&amp;quot;http://www.google.com&amp;quot; rel=&amp;quot;nofollow&amp;quot;&amp;gt;www.google.com&amp;lt;/a&amp;gt;&lt;/p&gt;  &lt;p&gt;This conversion is done as the comment is being served.&lt;/p&gt;  &lt;p&gt;I’m anxious to see what type of impact adding NOFOLLOW will have on my level of comment spam.   &lt;br /&gt;Fingers crossed ...&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/12/08/Giving-Comment-Spammers-Less-Incentive-to-Spam-You.aspx</link>
      <comments>http://allben.net/post/2009/12/08/Giving-Comment-Spammers-Less-Incentive-to-Spam-You.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=79b52f6b-90bd-4c7f-84cd-6e971dc5dcf0</guid>
      <pubDate>Tue, 08 Dec 2009 22:01:52 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=79b52f6b-90bd-4c7f-84cd-6e971dc5dcf0</pingback:target>
      <slash:comments>7</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=79b52f6b-90bd-4c7f-84cd-6e971dc5dcf0</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/12/08/Giving-Comment-Spammers-Less-Incentive-to-Spam-You.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=79b52f6b-90bd-4c7f-84cd-6e971dc5dcf0</wfw:commentRss>
    </item>
    <item>
      <title>App_offline.htm – Page Not Found</title>
      <description>&lt;p&gt;For a few years, .NET has had the built-in capability to easily take your entire application offline when you need to make an update or perform some maintenance on your site.&lt;/p&gt;  &lt;p&gt;By simply putting a file named app_offline.htm in the root directory of your site, ASP.NET will serve the app_offline.htm file, instead of the requested page.&lt;/p&gt;  &lt;p&gt;I recently employed this feature for probably the first time.&amp;#160; I put the app_offline.htm file on the site, and pulled up my site in Firefox.&amp;#160; The contents of app_offline.htm displayed as expected.&lt;/p&gt;  &lt;p&gt;However, if I were to pull my site in Chrome or IE, I would get a Page Not Found error that appeared as though my entire site did not exist.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;App_offline.htm result in IE8:&lt;/strong&gt;&lt;/p&gt;  &lt;p style="text-align: left"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="App_offline.htm in IE" border="0" alt="App_offline.htm in IE" src="http://allben.net/image.axd?picture=app_offline_ie_404.png" width="758" height="382" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;App_offline.htm result in Chrome:&lt;/strong&gt;&amp;#160;&lt;/p&gt;  &lt;p style="text-align: left"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="App_offline in Chrome" border="0" alt="App_offline in Chrome" src="http://allben.net/image.axd?picture=app_offline_chrome_404_1.png" width="764" height="298" /&gt; &lt;/p&gt;  &lt;p&gt;As mentioned above, in Firefox, the contents of App_offline.htm would display as expected.&lt;/p&gt;  &lt;p&gt;The problem is that when ASP.NET serves the App_offline.htm file, the HTTP Response code it passes out is 404.&amp;#160; Chrome will display the page shown above for 404 errors.&amp;#160; In IE, you can actually avoid that generic error page shown above if you turn off HTTP Friendly errors.&lt;/p&gt;  &lt;p&gt;But I obviously cannot expect IE visitors to my site to have HTTP Friendly errors turned off.&lt;/p&gt;  &lt;p&gt;The way ASP.NET has implemented app_offline.htm by passing out a 404 HTTP status code is not well designed, in my opinion.&amp;#160; A much better implementation would be for ASP.NET to return a normal 200 HTTP status code.&lt;/p&gt;  &lt;p&gt;To accomplish this, for this site, I created a simple HTTP Module that processed the beginning of each request.&amp;#160; It checks an “offline” appSetting in web.config to see if the application should be offline.&amp;#160; If the offline setting is turned on, the module will do a server transfer to my own app offline HTML file.&lt;/p&gt;  &lt;p&gt;One thing I found on an IIS7 server is requests for items such as JPG, GIF, CSS files, etc. will also go through this HTTP module.&amp;#160; This is normally a great benefit of IIS7’s integrated mode pipeline.&amp;#160; However, if the application offline HTML file includes an IMG tag for an image on the same site, or a link to a CSS file on the same site, the HTTP module is also going to do a server transfer for these other files (JPG, CSS, etc).&amp;#160; This will result in the image not displaying on the application offline page, or the CSS file not loading in the browser, etc.&lt;/p&gt;  &lt;p&gt;A simple filter in the HTTP module to only do a server transfer for actual pages is all that is required.&amp;#160; The fairly simple HTTP Module I ended up creating is below.&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Configuration;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;AppOffline&lt;/span&gt; : &lt;span class="type"&gt;IHttpModule&lt;/span&gt;
{
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Dispose()
    {
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Init(&lt;span class="type"&gt;HttpApplication&lt;/span&gt; context)
    {
        context.BeginRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;EventHandler&lt;/span&gt;(context_BeginRequest);
    }

    &lt;span class="kwrd"&gt;void&lt;/span&gt; context_BeginRequest(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;span class="type"&gt;EventArgs&lt;/span&gt; e)
    {
        &lt;span class="type"&gt;HttpContext&lt;/span&gt; context = ((&lt;span class="type"&gt;HttpApplication&lt;/span&gt;)sender).Context;

        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="type"&gt;ConfigurationManager&lt;/span&gt;.AppSettings[&lt;span class="str"&gt;&amp;quot;offline&amp;quot;&lt;/span&gt;] == &lt;span class="str"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;)
        {
            &lt;span class="kwrd"&gt;string&lt;/span&gt; extension = &lt;span class="type"&gt;Path&lt;/span&gt;.GetExtension(context.Request.Path);

            &lt;span class="rem"&gt;// Don't server transfer for extensions like .JPG, .CSS, etc.&lt;/span&gt;
            &lt;span class="kwrd"&gt;string&lt;/span&gt; targetedExtensions = &lt;span class="str"&gt;&amp;quot;.aspx.ashx.asmx&amp;quot;&lt;/span&gt;;
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (targetedExtensions.IndexOf(extension, &lt;span class="type"&gt;StringComparison&lt;/span&gt;.OrdinalIgnoreCase) == -1)
                &lt;span class="kwrd"&gt;return&lt;/span&gt;;
            
            context.Server.Transfer(&lt;span class="str"&gt;&amp;quot;~/application_offline.html&amp;quot;&lt;/span&gt;);
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;It’s a pretty simple, but effective HTTP module.&amp;#160; When the server transfer is done to my own application offline HTML file, the HTTP status code returned to the client is 200.&amp;#160; No more Page Not Found problems with browsers like IE and Chrome.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx</link>
      <comments>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</guid>
      <pubDate>Sat, 05 Sep 2009 18:11:00 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</wfw:commentRss>
    </item>
    <item>
      <title>Performance: Compiled vs. Interpreted Regular Expressions</title>
      <description>&lt;p&gt;When a regular expression in .NET will be used multiple times, it’s common to create that Regex with the Compiled flag, e.g. RegexOptions.Compiled.&amp;#160; Compiled regexp’s take a bit more time to create initially, but will run faster than a regexp created without the Compiled flag.&amp;#160; At least that’s what the documentation states!&lt;/p&gt;  &lt;p&gt;Without the Compiled flag, your regexp will be interpreted.&amp;#160; There’s even &amp;quot;precompiled” regular expressions.&amp;#160; You need to compile these regular expressions into an assembly before runtime.&amp;#160; This might be a good option if you have constant regexps that don’t change.&amp;#160; If your regexps are subject to change, pre-compiled is not a good option.&amp;#160; These three types of regexp’s (interpreted, compiled and pre-compiled) are explained with a few more technical details in &lt;a href="http://blogs.msdn.com/bclteam/archive/2004/11/12/256783.aspx" target="_blank"&gt;this&lt;/a&gt; somewhat dated MS blog article.&lt;/p&gt;  &lt;p&gt;Theory is great, but real benchmarks are more meaningful.&amp;#160; I’ve assembled some code that benchmarks the difference in speed it takes to create and run 5,000 regular expressions.&amp;#160; There’s actually a big difference in the time taken to run a compiled regular expression the first time, versus subsequent times.&amp;#160; So the results shown here will include the first run time as well as the subsequent run times.&lt;/p&gt;  &lt;p&gt;Here’s some code to get us started:&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; _expressions;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; _SyncRoot = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; GetExpressions()
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_expressions != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            &lt;span class="kwrd"&gt;return&lt;/span&gt; _expressions;

        &lt;span class="kwrd"&gt;lock&lt;/span&gt; (_SyncRoot)
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_expressions == &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            {
                &lt;span class="type"&gt;DateTime&lt;/span&gt; startTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;

                &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; tempExpressions = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt;();
                &lt;span class="kwrd"&gt;string&lt;/span&gt; regExPattern =
                    &lt;span class="str"&gt;@&amp;quot;^[a-zA-Z0-9]+[a-zA-Z0-9._%-]*@{0}$&amp;quot;&lt;/span&gt;;

                &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; 5000; i++)
                {
                    tempExpressions.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Regex&lt;/span&gt;(
                        &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(regExPattern,
                        &lt;span class="type"&gt;Regex&lt;/span&gt;.Escape(&lt;span class="str"&gt;&amp;quot;domain&amp;quot;&lt;/span&gt; + i.ToString() + &lt;span class="str"&gt;&amp;quot;.&amp;quot;&lt;/span&gt; +
                        (i % 3 == 0 ? &lt;span class="str"&gt;&amp;quot;.com&amp;quot;&lt;/span&gt; : &lt;span class="str"&gt;&amp;quot;.net&amp;quot;&lt;/span&gt;))),
                        &lt;span class="type"&gt;RegexOptions&lt;/span&gt;.IgnoreCase | &lt;span class="type"&gt;RegexOptions&lt;/span&gt;.Compiled));
                }

                _expressions = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt;(tempExpressions);
                &lt;span class="type"&gt;DateTime&lt;/span&gt; endTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;
                &lt;span class="kwrd"&gt;double&lt;/span&gt; msTaken = endTime.Subtract(startTime).TotalMilliseconds;
            }
        }

        &lt;span class="kwrd"&gt;return&lt;/span&gt; _expressions;
    }&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;We’re storing 5,000 regular expressions in a static list.&amp;#160; Notice the RegexOptions.Compiled flag is being used.&amp;#160; The regexp’s are just looking for email addresses with specific domain names – domain1.net, domain2.net, domain3.com, etc.&amp;#160; Not very useful, but I just wanted the regexp’s to vary.&amp;#160; You can see we’re also recording the number of milliseconds taken to create the regular expressions.&amp;#160; Now here’s the code that calls GetExpressions() and actually invokes the IsMatch function on each regexp.&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CheckForMatches(&lt;span class="kwrd"&gt;string&lt;/span&gt; text)
    {
        &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; expressions = GetExpressions();
        &lt;span class="type"&gt;DateTime&lt;/span&gt; startTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;

        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="type"&gt;Regex&lt;/span&gt; e &lt;span class="kwrd"&gt;in&lt;/span&gt; expressions)
        {
            &lt;span class="kwrd"&gt;bool&lt;/span&gt; isMatch = e.IsMatch(text);
        }

        &lt;span class="type"&gt;DateTime&lt;/span&gt; endTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;
        &lt;span class="kwrd"&gt;double&lt;/span&gt; msTaken = endTime.Subtract(startTime).TotalMilliseconds;
    }&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And we call CheckForMatches like so:&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;    CheckForMatches(&lt;span class="str"&gt;&amp;quot;some random text with email address, address@domain200.com&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;How much time does it take to create and run these 5,000 compiled expressions?&amp;#160; Here’s what I get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#0000ff"&gt;Compiled Regular Expressions&lt;/font&gt;&lt;/strong&gt; 

  &lt;br /&gt;&lt;strong&gt;CREATION TIME:&lt;/strong&gt; 1662 ms 

  &lt;br /&gt;&lt;strong&gt;FIRST RUN TIME:&lt;/strong&gt; 25137 ms 

  &lt;br /&gt;&lt;strong&gt;SUBSEQUENT RUN TIMES:&lt;/strong&gt; 41 ms&lt;/p&gt;

&lt;p&gt;Subsequent runs of all 5,000 expressions is very fast.&amp;#160; However, look how much time it takes the first time these 5,000 expressions are run in CheckForMatches() – &lt;u&gt;25 seconds&lt;/u&gt;!!!&lt;/p&gt;

&lt;p&gt;Let’s make ONE change.&amp;#160; Remove the RegexOptions.Compiled flag.&amp;#160; By doing this, our regular expressions will be interpreted.&amp;#160; Here’s what we get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#0000ff"&gt;Interpreted Regular Expressions&lt;/font&gt; 

    &lt;br /&gt;CREATION TIME:&lt;/strong&gt; 493 ms 

  &lt;br /&gt;&lt;strong&gt;FIRST RUN TIME:&lt;/strong&gt; 22 ms 

  &lt;br /&gt;&lt;strong&gt;SUBSEQUENT RUN TIMES:&lt;/strong&gt; 20 ms&lt;/p&gt;

&lt;p&gt;Interpreted regexp’s beat compiled in every category!&amp;#160; Running these tests several times produces similar results.&amp;#160; The BIG difference here is obviously the First Run Time.&amp;#160; 25 seconds versus .022 seconds.&lt;/p&gt;

&lt;p&gt;I’ve seen some benchmarks showing static regexp’s performing a little slower than instance regexp’s.&amp;#160; I ran the same tests without the static modifier on the fields and methods above.&amp;#160; Same results – using the Compiled flag takes around 25 seconds for the regular expressions to run the first time.&amp;#160; Without the Compiled flag, they run in hundredths of a second.&lt;/p&gt;

&lt;p&gt;Clearly, interpreted regexps are the winner.&amp;#160; Granted, if you’re only dealing with a small number of regular expressions, and you use the compiled flag, the first run time isn’t going to be anywhere near what I’ve shown here with 5,000 regexps.&amp;#160; However, even with just a few regular expressions, in .NET, you’ll see me sticking with interpreted regular expressions!&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx</link>
      <comments>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</guid>
      <pubDate>Thu, 06 Aug 2009 22:33:00 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</wfw:commentRss>
    </item>
    <item>
      <title>Show the MAX length Requirement Too!</title>
      <description>&lt;p&gt;When creating an account online and you need to enter your desired username and password, it's common for there to be a note regarding the minimum number of characters required for a username/password.&amp;#160; But the maximum character limit is often omitted in this note.&amp;#160; Particularly for passwords, when allowed, I try to make 36 character passwords.&amp;#160; My passwords are just random characters of letters, numbers and special characters.&amp;#160; Every time I spend 30 seconds creating one of these passwords, I always tell myself I need to get one of those random password generators -- but end up never getting one! &lt;/p&gt;  &lt;p&gt;Back to my point -- all sites should be making use of the &amp;quot;maxlength&amp;quot; attribute on a text input element.&amp;#160; Not just for usernames/passwords, but for every piece of data that is accepted through a text input.&amp;#160; At the very very least, indicating the maximum length in a note next to the input field would be appreciated. &lt;/p&gt;  &lt;p&gt;On numerous sites I've registered at (including large sites), there is no note, and there is no maxlength on the input field.&amp;#160; I spend my 30 seconds typing in a 36 character password to find out when I click the Submit button that the password is too long.&amp;#160; In a couple of cases, the site doesn't even tell you the password is too long.&amp;#160; They just report &amp;quot;Invalid Password&amp;quot;.&amp;#160; I'll try shortening it in chunks until they take it. &lt;/p&gt;  &lt;p&gt;This may just be a small user experience point, but adding a maxlength attribute takes no time at all and provides immediate knowledge you've reached the max character limit.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx</link>
      <comments>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</guid>
      <pubDate>Sun, 12 Jul 2009 02:08:00 -0800</pubDate>
      <category>Opinion</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</wfw:commentRss>
    </item>
    <item>
      <title>Logging &amp; Improved Error Reporting in BlogEngine</title>
      <description>&lt;p&gt;There’s two new features in the latest build of BE, 1.5.1.11.&amp;#160; These features, Logging and Improved Error Reporting, are separate but related features.&amp;#160; I think both features will turn out to be very helpful – especially when trying to diagnose a problem.&amp;#160; I’ll explain both features and how they can be used independently of each other, as well as with each other.&lt;/p&gt;  &lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Logging&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;There’s a new event handler in BE that any extension or other component can subscribe to: &lt;strong&gt;Utils.OnLog&lt;/strong&gt;.&amp;#160; It can be subscribed to in an extension, like:&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="type"&gt;Utils&lt;/span&gt;.OnLog += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;&lt;span class="type"&gt;EventArgs&lt;/span&gt;&amp;gt;(OnLog);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;In this case, there’s an OnLog event handler that will fire every time any piece of code in BE.NET logs a message.&amp;#160; I created a simple Logger extension that is now included in BE 1.5.1.11 that subscribes to log notifications, and writes the log message to a logger.txt file in the App_Data folder.&amp;#160; Anyone can write a similar extension that will save log messages to a database.&amp;#160; The code for the this new Logger extension can be viewed below.&lt;/p&gt;

&lt;p class="toggleCodeLink"&gt;&lt;a onclick="return toggleCode(200906131);" href="#"&gt;Logger Extension 1.0 (view code)&lt;/a&gt;&lt;/p&gt;

&lt;div id="divCode200906131" class="showcode collapseCode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="preproc"&gt;#region&lt;/span&gt; using

&lt;span class="kwrd"&gt;using&lt;/span&gt; System;
&lt;span class="kwrd"&gt;using&lt;/span&gt; BlogEngine.Core;
&lt;span class="kwrd"&gt;using&lt;/span&gt; BlogEngine.Core.Web.Controls;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;

&lt;span class="preproc"&gt;#endregion&lt;/span&gt;

&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="rem"&gt;/// Subscribes to Log events and records the events in a file.&lt;/span&gt;
&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
[&lt;span class="type"&gt;Extension&lt;/span&gt;(&lt;span class="str"&gt;&amp;quot;Subscribes to Log events and records the events in a file.&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;BlogEngine.NET&amp;quot;&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Logger&lt;/span&gt;
{
    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;Logger&lt;/span&gt;()
    {
        &lt;span class="type"&gt;Utils&lt;/span&gt;.OnLog += &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;EventArgs&lt;/span&gt;&amp;gt;(OnLog);
    }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// The event handler that is triggered every time there is a log notification.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnLog(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;span class="type"&gt;EventArgs&lt;/span&gt; e)
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (sender == &lt;span class="kwrd"&gt;null&lt;/span&gt; || !(sender &lt;span class="kwrd"&gt;is&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;))
            &lt;span class="kwrd"&gt;return&lt;/span&gt;;

        &lt;span class="kwrd"&gt;string&lt;/span&gt; logMsg = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)sender;

        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(logMsg))
            &lt;span class="kwrd"&gt;return&lt;/span&gt;;

        &lt;span class="kwrd"&gt;string&lt;/span&gt; file = GetFileName();

        &lt;span class="type"&gt;StringBuilder&lt;/span&gt; sb = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;StringBuilder&lt;/span&gt;();

        &lt;span class="kwrd"&gt;lock&lt;/span&gt; (_SyncRoot)
        {
            &lt;span class="kwrd"&gt;try&lt;/span&gt;
            {
                &lt;span class="kwrd"&gt;using&lt;/span&gt; (&lt;span class="type"&gt;FileStream&lt;/span&gt; fs = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;FileStream&lt;/span&gt;(file, &lt;span class="type"&gt;FileMode&lt;/span&gt;.Append))
                {
                    &lt;span class="kwrd"&gt;using&lt;/span&gt; (&lt;span class="type"&gt;StreamWriter&lt;/span&gt; sw = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;StreamWriter&lt;/span&gt;(fs))
                    {
                        sw.WriteLine(&lt;span class="str"&gt;@&amp;quot;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*&amp;quot;&lt;/span&gt;);
                        sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Date: &amp;quot;&lt;/span&gt; + &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now.ToString());
                        sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Contents Below&amp;quot;&lt;/span&gt;);
                        sw.WriteLine(logMsg);

                        sw.Close();
                        fs.Close();
                    }
                }
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt;
            {
                &lt;span class="rem"&gt;// Absorb the error.&lt;/span&gt;
            }
        }
    }

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _FileName;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; _SyncRoot = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetFileName()
    { 
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_FileName != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            &lt;span class="kwrd"&gt;return&lt;/span&gt; _FileName;

        _FileName = System.Web.Hosting.&lt;span class="type"&gt;HostingEnvironment&lt;/span&gt;.MapPath(&lt;span class="type"&gt;Path&lt;/span&gt;.Combine(&lt;span class="type"&gt;BlogSettings&lt;/span&gt;.Instance.StorageLocation, &lt;span class="str"&gt;&amp;quot;logger.txt&amp;quot;&lt;/span&gt;));
        &lt;span class="kwrd"&gt;return&lt;/span&gt; _FileName;
    }
}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;Any code within BE.NET, a widget, extension, etc. can now log any message like:&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="type"&gt;Utils&lt;/span&gt;.Log(&lt;span class="str"&gt;&amp;quot;some message to log&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;If more than one event handler is subscribed to the OnLog notifications, each event handler will of course fire.&amp;#160; It’s worth noting that Utils.Log() accepts a parameter of type object.&amp;#160; The Logger extension is designed to receive messages of a string type (it actually casts the object type parameter to a string type).&amp;#160; If Logger receives a non-string type, it doesn’t log the message – because the Logger extension is designed to receive simple string-based messages.&amp;#160; If an extension or other piece of code wants to pass a non-string type message to a logger, a different extension could be created that is equipped to handle log messages that are of a type different than string.&lt;/p&gt;

&lt;p&gt;If you don’t want logging to take place, either because you don’t want to worry about having a log file that keeps growing, or because you prefer not to store data in the App_Data folder (as the Logger extension does), you can simply disable the Logger extension on the Extensions tab in the control panel.&amp;#160; As of right now, even if you leave the Logger extension enabled, there’s going to be virtually no messages logged as there isn’t any code that currently calls Utils.Log().&lt;/p&gt;

&lt;p&gt;This new logging feature is going to help keep track of events going on.&amp;#160; Although logging can be used for many purposes, one of the reasons I wanted to have this in BE.NET was to be able to record unhandled errors.&lt;/p&gt;

&lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Error Handling &amp;amp; Reporting&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;There’s now an event handler in the Global.asax file that catches all unhandled exceptions.&amp;#160; Up till now, if a 500 server error occurred, you would be redirected to error404.aspx, as defined by the &amp;lt;customErrors&amp;gt; tag in the web.config file.&amp;#160; While this is a nice catch-all method to handling errors, people just getting started with BE.NET are often confused why they are seeing a “Page cannot be found” message when they are trying to do something like save changes and an unhandled error occurs -- which they don’t know has occurred when all they see is a “Page cannot be found” message.&amp;#160; To be fair, most people just getting started with BE.NET are not getting errors.&amp;#160; But some do, and adjusting folder permissions or other settings fixes the errors they are seeing.&amp;#160; In order to fix the problem, one must first know an error is actually occurring, and they need to know what the actual error is!&lt;/p&gt;

&lt;p&gt;The new Application_Error event handler in Global.asax does up to three things for non-404 errors:&lt;/p&gt;

&lt;ol class="list"&gt;
  &lt;li&gt;It generates a summary, including details, of the unhandled error that just occurred. &lt;/li&gt;

  &lt;li&gt;If error logging is turned on (a new option, explained below), it makes a call to Utils.Log(), passing the error summary to any event handlers registered to receive log notifications. &lt;/li&gt;

  &lt;li&gt;It does a Server.Transfer() to a new error.aspx page in the root of the BE.NET web folder. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For #1, the summary generated includes a stack trace, inner exceptions, and the URL the page occurred on (both Request.Url and Request.RawUrl).&lt;/p&gt;

&lt;p&gt;The summary that is generated is stored in the Items collection of HttpContext.Current.&amp;#160; Why?&amp;#160; When Application_Error in Global.asax does a Server.Transfer to the new error.aspx page, the error.aspx page checks to see if the person is logged in (i.e. if they’re authenticated).&amp;#160; If they are logged in, error.aspx will display the error summary generated within Global.asax.&amp;#160; The error summary is retrieved out of the HttpContext.Current.Items collection.&amp;#160; If the person isn’t logged in, they just see a message similar to error404.aspx, indicating an “unexpected error has occurred”, and the developer will be tortured, blah blah. :-)&lt;/p&gt;

&lt;p&gt;Displaying the error details directly on error.aspx for logged in users is helpful for two reasons.&amp;#160; (a) Immediate knowledge of the error, and (b) even if the new error logging option is turned off, you still can see the error message in your browser when you’re transferred to error.aspx.&lt;/p&gt;

&lt;p&gt;I’ve mentioned this new error logging option twice now.&amp;#160; On the Settings tab in the control panel, in the Advanced Settings section, there is a new checkbox labeled “Enable error logging”.&amp;#160; By default, it’s turned off.&amp;#160; While this is turned off, the only notifications the Logger extension will receive (if you leave the Logger extension enabled) will be messages coming from some piece of code that explicitly makes a call to Utils.Log().&amp;#160; If you turn this new Enable Error Logging feature on, then when an unhandled exception occurs, Global.asax will pass the error details to Utils.Log() for any registered event handlers to deal with.&lt;/p&gt;

&lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Result &amp;amp; Extensibility Possibilities&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;From version 1.5.1.11, any extension, widget or even BE.NET itself can now simply make a call to Utils.Log() to have any message logged.&amp;#160; Logging can be done with the built-in Logger extension, or messages can be logged to a database, sent via email, etc. by any other extension someone creates.&amp;#160; I’m also very excited about the new error handling mechanism which will give administrators a lot more information about errors that may be occurring in their blog.&lt;/p&gt;

&lt;p&gt;Please &lt;a href="http://blogengine.codeplex.com/SourceControl/ListDownloadableCommits.aspx" target="_blank"&gt;download&lt;/a&gt; and test out the latest build.&amp;#160; If any problems show up or you have any ideas for improvements, you can leave a comment here, or post a message on the CodePlex &lt;a href="http://blogengine.codeplex.com/Thread/List.aspx" target="_blank"&gt;discussion boards&lt;/a&gt;.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx</link>
      <comments>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=b11777f3-f539-4914-8943-82b8e713ae44</guid>
      <pubDate>Sat, 13 Jun 2009 18:26:00 -0800</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=b11777f3-f539-4914-8943-82b8e713ae44</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=b11777f3-f539-4914-8943-82b8e713ae44</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=b11777f3-f539-4914-8943-82b8e713ae44</wfw:commentRss>
    </item>
  </channel>
</rss>

