logo1

logo

Converting BlogEngine.NET 2.5 to a Web Application

6 Comments
Posted in .NET | ASP.NET | C# | Open Source

BlogEngine.NET 2.5 is currently distributed as a Web Site Project (WSP).  The project makes use of some of the WSP’s features to be more flexible for non technical end users.  For more technical users, who can recompile as necessary and don’t mind getting their hands dirty, a Web Application Project (WAP) conversion can provide some utility that the WSP can not.  You can find a guide of the pros and cons of each approach on MSDN: Web Application Projects versus Web Site Projects.

Aside from a general preference of WAPs over WSPs my main reasons for converting are as follows:

  • Deploying to Windows Azure is simpler with WAPs (I’ve been toying with the idea of putting this puppy up there, with the recent price drop)
  • A single compiled assembly is easier to manage during deployment
  • Web.config transforms can only be done on WAPs

At the time of this post there are a handful of forums posts, wiki articles and blog posts which somewhat describe the process of conversion (I’ve included links to all of these at the end of the post).  None of them capture all of the steps involved in conversion and the steps that were included weren’t always well detailed as one would like. 

After reviewing all of the available info and performing the conversion several times myself.  Here are the steps I’ve come up with and that I followed for the conversion of this site:

  1. Pull down the BlogEngine.NET source code and open the solution in Visual Studio.
  2. Remove the BlogEngine.Net Website project from the solution.
  3. Navigate to the solution directory in Windows Explorer and rename the Website project’s folder from BlogEngine.NET to BlogEngine.NETOld or something similar.  Leave Windows Explorer open, we’ll be back there again.

    02_RenameFolder
  4. Add an ASP.NET Web Application project called “BlogEngine.NET” to the solution.

    03_NewWebApplicationProject
  5. Remove most of the default files from the new Web Application project, such that it looks like the image below.

    04_DeleteDefaultFiles
  6. In Windows Explorer, copy all files except Bin & Obj from the renamed BlogEngine.NETOld folder  to the new created BlogEngine.NET folder.
  7. In Visual Studio, click the “Show All Files” icon in the Solution Explorer.
  8. Right click the newly displayed files (with the exception of the aspnet_client and obj folders) and select “Include in Project” until your Solution looks like the image below.  Note: don’t forget to include the files and folders below the Account, Add_Data, Scripts & Styles folders.

    05_IncludedFilesAfterCopy
  9. Add a new folder called Custom_Code and move everything except the Helpers folder from App_Code folder to Custom_Code folder. 

    The app code directory is one of the major differences between WAP and WSP. Most of the files in app code need to be moved out of app code, to a non-magic-name directory. The Helpers, however, are Razor helper classes and will only work if you paste the directly into the razor views, or leave them in the App_Code directory.

    One consequence of moving the Extensions folder out of App_Code and into Custom_Code is that it will no longer be possible to view or edit the source code of an extension from the browser. This is good from a security perspective.

    If, however, you want that functionality, create an App_Code/Extension folder and place those extensions in there. Do not, however, put all the extensions back because some of the extensions that ship with the product have dependencies and need to be included in the main dll (like the captcha extension).


    Quoted from:
    Converting BlogEngine.net 2.5 to Web Application Project
  10. Create a Helpers folder under Custom_Code and move RazorHelpers.cs there from the Helpers folder in App_Code.  After steps 8 & 9 your Solution should be structured like the below image.

    06_FilesAfterMoves
  11. After moving the files from App_Code to Custom_Code the build action may be incorrect on many of the class files.  For each of the files in Custom_Code change the build action to Compile in the Properties window.

    07_CustomCodeBuildActions
  12. In the BlogEngine.NET project make sure you have the following references (use the 1.0.0.0 versions if there are multiple versions available):

    08_NewReferences

  13. Add a project reference to BlogEngine.Core.
  14. Since we’ve technically moved the App_Code into a new assembly, we need to let the app know where it’s at.  Update the Web.Config pages section as follows:
    <pages enableSessionState="false" enableViewStateMac="true" enableEventValidation="true" 
      controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
        <controls>
        <!--WAP FIX-->
        <!--<add namespace="App_Code.Controls" tagPrefix="blog"/>-->
        <add namespace="App_Code.Controls" assembly="BlogEngine.NET" tagPrefix="blog"/>
      </controls>
    </pages>
  15. I had the async ctp installed on my pc, which makes async a reserved word and causes a handful of exceptions, so we’ll need to fix that.  Rename parameter async to asyncResult in BlogEngine.NET/Custom_Code/Controls/Blogroll.ProcessResponse.

    //WAP FIX
    //private static void ProcessResponse(IAsyncResult async)
    private static void ProcessResponse(IAsyncResult asyncResult)
    {
        //WAP FIX
        //GetRequestData data = (GetRequestData)async.AsyncState;
        GetRequestData data = (GetRequestData)asyncResult.AsyncState;
        Blog.InstanceIdOverride = data.BlogInstanceId;
        var blogReq = data.BlogRequest;
    
        try
        {
            //WAP FIX
            //using (var response = (HttpWebResponse)blogReq.Request.EndGetResponse(async))
            using (var response = (HttpWebResponse)blogReq.Request.EndGetResponse(asyncResult))
            {
                var doc = new XmlDocument();
                var responseStream = response.GetResponseStream();
  16. There’s a naming collision in themes between StandardSite and TitaniumX (WAP class names need to be unique). Rename BlogEngine.NET/themes/TitaniumX/site.master.cs from StandardSite to TitaniumSite class, change inherits in BlogEngine.NET/themes/TitaniumX/site.master as well.

    <%@ Master Language="C#" AutoEventWireup="true" Inherits="TitaniumSite " Codebehind="site.master.cs" %>
    <%@ Import Namespace="BlogEngine.Core" %>
    <% //WAP FIX 
       //<%@ Master Language="C#" AutoEventWireup="true" CodeFile="site.master.cs" Inherits="StandardSite " %>

    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using BlogEngine.Core;
    
    //WAP FIX
    //public partial class StandardSite : System.Web.UI.MasterPage
    public partial class TitaniumSite : System.Web.UI.MasterPage
    {
  17. Update the BlogEngine.Core/Utils.CodeAssemblies method by replacing assembly names with BlogEngine.NET.

    public static IEnumerable<Assembly> CodeAssemblies()
    {
        var codeAssemblies = new List<Assembly>();
        CompilationSection s = null;
        //WAP FIX
        //var assemblyName = "__code";
        var assemblyName = "BlogEngine.NET";
        try
        {
            try
            {
                s = (CompilationSection)WebConfigurationManager.GetSection("system.web/compilation");
            }
            catch (SecurityException)
            {
                // No read permissions on web.config due to the trust level (must be High or Full)
            }
    
            if (s != null && s.CodeSubDirectories != null && s.CodeSubDirectories.Count > 0)
            {
                for (var i = 0; i < s.CodeSubDirectories.Count; i++)
                {
                    assemblyName = string.Format("App_SubCode_{0}", s.CodeSubDirectories[i].DirectoryName);
                    codeAssemblies.Add(Assembly.Load(assemblyName));
                }
            }
            else
            {
                //WAP FIX
                //var assemblyName = "App_Code";
                assemblyName = "BlogEngine.NET";
                codeAssemblies.Add(Assembly.Load(assemblyName));
            }
  18. Update BlogEngine.Core/Web/Extensions/ManagedExtension.GetPathAndFilename  to point to the proper filename.

    public string GetPathAndFilename(bool checkExistence)
    {
        string filename = string.Empty;
        var appRoot = HostingEnvironment.MapPath("~/");
        var codeAssemblies = Utils.CodeAssemblies();
        foreach (Assembly a in codeAssemblies)
        {
            var types = a.GetTypes();
            foreach (var type in types.Where(type => type.Name == Name))
            {
                var assemblyName = type.Assembly.FullName.Split(".".ToCharArray())[0];
                assemblyName = assemblyName.Replace("App_SubCode_", "App_Code\\");
                var fileExt = assemblyName.Contains("VB_Code") ? ".vb" : ".cs";
                //WAP FIX
                //filename = appRoot + Path.Combine(Path.Combine(assemblyName, "Extensions"), Name + fileExt);
                filename += Path.Combine("Custom_Code\\Extensions", Name + fileExt);
            }
        }
    
  19. Although this may not be absolutely necessary, since in WAP we won’t be writing changes to cs files from the admin screens anyway, it’s a good idea to clean up loose ends just the same. Update BlogEngine.NET/admin/Extensions/Editor.cshtml.

    var extensionFilename = ext.GetPathAndFilename(false);
    //WAP FIX
    //var canWrite = Utils.CanWrite(Href(Utils.ApplicationRelativeWebRoot + "App_Code/Extensions"));
    var canWrite = Utils.CanWrite(Href(Utils.ApplicationRelativeWebRoot + "Custom_Code/Extensions"));
    var fileExists = !string.IsNullOrWhiteSpace(extensionFilename) && File.Exists(extensionFilename);
    
  20. Convert to Web Application.

    09_ConvertToWebApplication

    10_WarningYes
  21. Success!

All in all the conversion wasn’t the most straight forward process, but not excessively complex by any means.  The most difficult part was nailing down the correct steps and the correct sequence.  After that, the conversion could be performed in under 30 mins.  If there’s enough interest, I’ll upload a converted solution to GitHub.

For more information about BlogEngine.NET http://www.dotnetblogengine.net/ or http://blogengine.codeplex.com/

References

  • 6 Comments

Comments (6) -

Johnny Nouel

Hi Rich,

Great Post.  I'm very happy to see the instructions on making this conversion take shape in a very detailed and easy guide.  Thanks very much.

Wink

Cheers.

Johnny Nouel

Rich Czyzewski

Thanks Johnny!  It would of been a lot harder to piece together without your post on Converting BlogEngine.NET 2.0 to a WAP (listed in the references for anyone interested).

Vincent BIRET

Very useful! Thanks a lot!
http://danstoncloud.com/blogs/vincent/

Kyle

I can't see where I could have gone wrong following your steps, but I am getting the following errors. Any thoughts?
The name 'RecaptchaLogger' does not exist in the current context
The type or namespace name 'Recaptcha' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'RecaptchaControl' does not exist in the namespace 'App_Code.Controls' (are you missing an assembly reference?)
The type or namespace name 'RecaptchaControl' does not exist in the namespace 'App_Code.Controls' (are you missing an assembly reference?)
The type or namespace name 'RecaptchaLogItem' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'SimpleCaptchaControl' does not exist in the namespace 'App_Code.Controls' (are you missing an assembly reference?)

Rich Czyzewski

Hi Kyle,

There are 2 things I suspect this could be.  

First, since all of the types missing are types that were transferred from the original App_Code to Custom_Code, they may have been missed when updating the build actions in step 11. For step 11 you have to go down through every folder (and subsequent child folders) in Custom_Code and make the build action change to Compile.  To verify this go to BlogEngine.NET/Custom_Code/Extensions/Recaptcha and view the properties for one of the files to confirm the build action was changed to Compile.

Second, this could also be an issue with updating the config (step 14).  So you may want to verify the assembly attribute was added to the tagPrefix definition under the page element in Web.Config.  It should read <add namespace="App_Code.Controls" assembly="BlogEngine.NET" tagPrefix="blog"/> instead of <add namespace="App_Code.Controls" tagPrefix="blog"/>

Let me know if either of these options helps out.

Mike

Dear Rich,

Thank you very much for this helpful post.
I have 2 remarks

1- There are conflicts with the standard themes with StandardRTL themes ==> Solved by removing the StandardRTL as i figured out it's just a support for Right to left languages.
2- after running the application, navigating to admin then settings =>  i cannot find the Themes tab on the right side of the screen?

another question, using we application, will i be able to syncronize changes done in visual studio to the website which runs on a remote server and vesa versa?

Thanks ,
Mike
Thanks

Pingbacks and trackbacks (1)+

Comments are closed