UPDATED: Code improved after feedback from Henrik Tengelin.

It is much easier to read large numbers with with thousands separator but how to get them to work with page validation in a good way?

The solution in this case was to sprinkle some jQuery on the page!

Test how it works here

JavaScript code for adding thousands separator as watermark

$(function() {
    var numberInputs = $("input.number");
    var convertToCurrencyDisplayFormat = function(str) {
        var regex = /(-?[0-9]+)([0-9]{3})/;
        str += '';
        while (regex.test(str)) {
            str = str.replace(regex, '$1 $2');
        }
        str += ' kr';
        return str;
    };
    var stripNonNumeric = function(str) {
        str += '';
        str = str.replace(/[^0-9]/g, '');
        return str;
    };
    numberInputs.each(function() {
        this.value = convertToCurrencyDisplayFormat(this.value);
    });
    numberInputs.blur(function() {
        this.value = convertToCurrencyDisplayFormat(this.value);
    });
    numberInputs.focus(function() {
        this.value = stripNonNumeric(this.value);
    });
    $("form").submit(function() {
        numberInputs.each(function() {
            this.value = stripNonNumeric(this.value);
        });
    });
});

Bookmark and Share

Tags: , , , ,

It is annoying when a trial expires before you had time testing the software. Sometimes it is just enough to reinstall to get a new trial period but usually they hide the information so it is persistent.

I used Sysinternal’s Process Monitor to find out what resources the license manager (PsiService_2.exe) used and cleared them away to get a new fresh trial period.

If you have licensed products using this service you should be careful and keep backups of all values/files.

These instructions are valid for Windows 7 64-bit but just remove “Wow6432Node” for 32 bit systems:

  • Stop “Protexis Licensing V2”-service and the application you are evaluating.
  • Clear registry:
    • HKEY_USERS\.DEFAULT\Software\Protexis\Licenses
    • HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Protexis\Licenses
  • Delete files:
    • C:\ProgramData\KGyGaAvL.sys
    • C:\ProgramData\88AD984CE6.sys (filename varies)
  • Start service
  • Start the application
Bookmark and Share

Tags: , ,

You get a warning when you try to delete a file or EPiServer page if it is referenced form another page.

EPiServer uses EPiServer.DataAbstraction.SoftLink for this feature and all links to files, other EPiServer pages and external urls are stored here every time you publish a page.

It is also worth to notice that EPiServer built-in keyword table that is used for text searching pages also is updated during publish.

Corrupt data = Broken Links

The index can be corrupt or out of sync and then it is possible to delete a file or a page without getting a warning that it has incoming links. Text search can also fail or return wrong data.

I have seen this very often after a migration from EPiServer CMS 4. EPiServer CMS 4 databases tends to have really poor data for soft links.

The index is updated when you publish a page with a delay controled by indexingDelayAfterPublish setting. If the delay it is very large and the application restarts before the page is indexed, neither soft links or keywords will be updated.

How to repair EPiServer CMS soft link and keyword index

I have created an admin tool that we always run after migration from EPiServer 4 and also if we want to repair the index if the settings has been strange.

You sould set indexingDelayAfterPublish to a very large delay during the time you run the tool because if you try to use LaxyIndexer.IndexPage() in more than one thread at the same time you are guaranteed to get deadlocks or get other strange SQL exceptions sooner or later.

System.Data.SqlClient.SqlException:Violation of PRIMARY KEY constraint ‘PK_tblPageKeyword’. Cannot insert duplicate key in object ‘dbo.tblPageKeyword’.

Example code

[GuiPlugIn(DisplayName = "Reindex pages", Description = "Tool to index keywords and soft links", Area = PlugInArea.AdminMenu, Url = "~/Common/FixIndexing.aspx")]
public partial class FixIndexing : System.Web.UI.Page
{
    private static readonly ILog log;
    private static long QueueLength;
    private static long IndexedPages;
    private static DateTime Started = DateTime.MinValue;
    private static readonly Queue<PageData> Queue = new Queue<PageData>();
    private static object LockRoot = new object();

    static FixIndexing()
    {
        log = LogManager.GetLogger(typeof(FixIndexing));
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        UpdateStatus();
    }

    private void UpdateStatus()
    {
        long q = Interlocked.Read(ref QueueLength);
        long n = Interlocked.Read(ref IndexedPages);
        if (Started != DateTime.MinValue)
        {
            StatusLabel.Text = string.Format("Started: {0}  Indexed pages: {1}  Queue: {2}", Started, n, q);
        }
        FixPageSoftLinkButton.Enabled = q <= 0;
    }

    protected void FixPageSoftLinkButton_Click(object sender, EventArgs e)
    {
        log.Info("Start LazyIndex of all pages!");
        Started = DateTime.Now;
        ThreadPool.QueueUserWorkItem(ThreadProc);
        UpdateStatus();
    }

    // This thread procedure performs the task.
    static void ThreadProc(Object stateInfo)
    {
        if (!Monitor.TryEnter(LockRoot)) return; //Job was already running
        try
        {
            IndexedPages = 0;
            PageData page;
            Queue.Enqueue(DataFactory.Instance.GetPage(PageReference.RootPage, LanguageSelector.MasterLanguage()));
            Interlocked.Increment(ref QueueLength);
            while ((page = Queue.Dequeue()) != null)
            {
                Interlocked.Decrement(ref QueueLength);
                try
                {
                    log.Debug(string.Format("Indexing page {0}:{1}...", page.PageLink.ID, page.PageName));
                    LazyIndexer.IndexPage(page.PageLink.ID);
                    Interlocked.Increment(ref IndexedPages);
                }
                catch (Exception ex)
                {
                    int id = (stateInfo as PageData) != null ? ((PageData)stateInfo).PageLink.ID : -1;
                    log.Error(string.Format("Error while indexing {0}", id), ex);
                }
                try
                {
                    PageDataCollection pages = DataFactory.Instance.GetChildren(page.PageLink, LanguageSelector.MasterLanguage());
                    log.Debug(string.Format("Done indexing page {0}. Adding {1} children to Queue...", page.PageLink.ID, pages.Count));
                    foreach (PageData data in pages)
                    {
                        Queue.Enqueue(data);
                        Interlocked.Increment(ref QueueLength);
                    }
                }
                catch (Exception ex)
                {
                    int id = (stateInfo as PageData) != null ? ((PageData)stateInfo).PageLink.ID : -1;
                    log.Error(string.Format("Error while adding children for {0}", id), ex);
                }

            }
        }
        finally
        {
            Monitor.Exit(LockRoot);
        }

    }

}

Index as Scheduled task

Another way to use the code above is to just add an Execute() method that calls ThreadProc and register it as a Scheduled Job plug-in.

Bookmark and Share

Tags: , ,

I got a question in the mail today how to get started with EPiServer CMS as a developer. Here are my tips:

Bookmark and Share

Tags: ,

I got a question how to find what was wrong when you only got an error “Exception has been thrown by the target of an invocation. [The server committed a protocol violation The server response was: …]” from a junior developer and I want to share my answer with all of you since it can be a little tricky.

The Exception “thrown by the target of invocation” indicated that an error has occurred in code called by using invoke through dot net reflection. This technique is common when you have plug-ins (like scheduled jobs in EPiServer) and other late binding in run-time.

The text in square brackets are the original exception (caught by the Invoke() method) and gives a clue what goes wrong but it is usually not enough to find the error.

How to find more clues when debugging

As always when searching for the cause of a problem or error condition the first rule is to get more clues.  Usually this process consist of finding a reproducible test scenario that triggers the condition and hook up the debugger to see what is happening inside the application.

We must make sure it halts on thrown exceptions even if it outside our code to get a call stack that can help us. Open your options dialog and uncheck ”Enable Just My Code”.

clip_image002

With default settings the debugger will only break on uncaught exceptions. We need the debugger to break when an exceptions are thrown regardless of they are handled by a try-catch statement.

This can be changed in your Exceptions dialog (Ctrl+Alt+E). You can either set it to catch all managed exceptions as I have in the screen shot below or if you know what exception you are looking for you can halt on that one alone to minimize noise since it will probably halt on exceptions a lot of places that are normal before reaching your area of intrest.

clip_image004

With this setup we are now ready to run the test case again to trigger the error. If everything is correctly configured it will break on the exception and show you a call stack.

Use the debugger windows in Visual Studio to examine the Call Stack. Inspect parameters and Local Variables at each step in the call stack. This should give enough clues so to find what is causing the error.

How to find more clues when you do not have the source code

If the exception is raised in code that is not our own we can use several strategies to find out more. One of the best is to use Lutz Roeder’s .NET Reflector and click File>Open to load all assemblies in your web applications bin-folder. Search for the class or method and Disassemble the methods outside your code and get more clues. (Another way, if the exception is inside Microsoft code, is to configure VS to use Microsoft source server to download source code.)

clip_image006

Here is an example of when I caught a “Exception has been thrown by the target of an invocation.  [NullReferenceException: Object reference not set to an instance of an object.]“ and found TransformCategoryForExport in the call stack.

By analyzing the call stack we can usually also find the point in our own code that is starting the chain of calls causing the exception. I would recommend setting a break point there and rerunning the test case to see if the parameters to the call are invalid.

Need more help?

God luck with you bug hunting! If you are stuck you are welcome to contact me at INEXOR and buy my services.

Bookmark and Share

Tags:

« Older entries § Newer entries »