2010-01-29

Moving log files

I am using log4net and other logging in my apps, and the logs are filling up the disks of the test server, so I created a Windows Scheduled Task to move logs to an archive disk, and also delete very old logs from the archive. I move logs older than 2 weeks to the archive and delete logs older than a year from that archive. I created this vb-script (started from the scheduled task) to do the job:

'VBScript that moves old log files from C:\LogFiles to E:\LogArchive

'Folders
Const FOLDER = "C:\LogFiles" 
Const BACKUP_FOLDER = "E:\LogArchive" 

'Objects
Dim objFSO, objFolder, objFolder2, objFile 
Set objFSO = CreateObject("Scripting.FileSystemObject") 
Set objFolder = objFSO.GetFolder(FOLDER)

'Loop and move
For Each objFile In objFolder.Files 
  If objFile.DateLastModified < DateAdd("w", -2, Now) Then 
    objFile.Move BACKUP_FOLDER & "\" & objFile.Name 
  End If 
Next

'Delete very old files from target BACKUP_FOLDER
Set objFolder2 = objFSO.GetFolder(BACKUP_FOLDER)
For Each objFile In objFolder2.Files 
  If objFile.DateLastModified < DateAdd("w", -52, Now) Then 
    objFile.Delete
  End If 
Next

Plain old ASP-like code. No Types, everything is just Variant types.

2009-12-16

Check if any new properties have been added in EPiServer 4.x

The scenario for this is that you have a Test server and a Production server. You are rolling out all changes that have been tested on the Test server to the Production server. You did, being a pro and all, of course make a note of all the changes that were done to properties on the Test server since the last rollout to Production, but just to make sure you want to check what new properties have been added.

Step 1

Add the Production server as a linked server in SQL Server Management Studio on the Test server, in this example called [LinkedServer].

Step 2

Create a new query on the Test server and paste the following query into it. You will need to substitute the names of the database and linked server with names from your environment.

-- CHECKS IF ANY NEW FIELDS HAVE BEEN ADDED ON THE TEST SERVER
-- THAT DO NOT EXIST ON THE PRODUCTION SERVER
USE EPiServerDb;
GO

IF EXISTS(
    SELECT testTable.pkID, prodTable.pkID
    FROM tblPageDefinition testTable
    LEFT JOIN [LinkedServer].[EPiServerDb].[dbo].[tblPageDefinition] prodTable 
        ON testTable.pkID = prodTable.pkID
    WHERE prodTable.pkID IS NULL)
BEGIN
    PRINT 'NEW PAGE PROPERTIES DETECTED!'
    SELECT pt.Name, pd.Name
    FROM tblPageType pt JOIN tblPageDefinition pd ON pt.pkID = pd.fkPageTypeID
    WHERE pd.pkID IN (
    SELECT testTable.pkID
    FROM tblPageDefinition testTable
    LEFT JOIN [LinkedServer].[EPiServerDb].[dbo].[tblPageDefinition] prodTable 
        ON testTable.pkID = prodTable.pkID
    WHERE prodTable.pkID IS NULL)
END
ELSE BEGIN
    PRINT 'NO NEW PROPERTIES DETECTED.'
END

Step 3

If any new properties have been added, you will see a list of the names of page templates and what properties are new.

This script was tested on EPiServer 4.61/62 only, but may also work for newer versions of EPiServer.

2009-11-06

Generic Object Factory

The following class is a generic object factory. It is used for creating instances of objects from configured or otherwise provided strings.

The “User Guide” is in the comments.

/// <summary>
/// Generic object factory that creates instances of objects from configured or otherwise provided strings.
/// </summary>
public class ObjectFactory
{
    /// <summary>
    /// Creates an instance of a class from a string "[namespace.[...].className], [component without filname extension],[Version],[Culture],[…]".
    /// </summary>
    /// <typeparam name="T">The type to create.</typeparam>
    /// <param name="configuredClassAndAssembly">Format: [namespace.[...].className], [component without filname extension]</param>
    /// <returns>An object of type T.</returns>
    /// <remarks>This method uses generics!</remarks>
    public static T CreateClassInstance<T>(string configuredClassAndAssembly)
    {
        //Get type to instanciate
        Type tp = Type.GetType(configuredClassAndAssembly, true);

        //load assembly
        Assembly assembly = Assembly.GetAssembly(tp);

        //create class instance
        T instance = (T)assembly.CreateInstance(tp.FullName);

        //return instance
        return instance;
    }
}

I am using it to create instances of objects that implement certain interfaces, so that I can swap the implementation, or even mock it, by changing the configuration.

The string that tells which object to create uses the format of the Type.GetType(string) string.

Some error handling should be added.

2009-10-05

Skjermbrev (in Norwegian)

Jeg kom opp i en problemstilling på jobben der jeg trengte et ord for “bekreftelses-e-post”, og så lurte jeg på hva man da skal bruke, for jeg synes det ser litt rart ut med disse alternativene:

  • bekreftelsese-post
  • bekreftelses-e-post (bryter regel om bruk av bindestrek)
  • bekreftelses e-post (bryter regel om orddeling)

Det er jo anbefalt å bruke ordet “e-post” for det som på engelsk heter e-mail.

Jeg sendte derfor spørsmålet til Per Egil Hegge i Aftenposten, og fikk følgende svar: “Du løser dette ved å bruke mitt favorittord: skjermbrev.”

Så nå vet jeg (og dere) det. Fra nå av skal jeg prøve å snike inn ordet “skjermbrev” alle steder det er mulig :D

2009-10-02

Styles missing in EPiServer Edit/Admin mode

If you ever experience this, it’s probably because EPiServer uses a custom remapping for the 404 – Page Not Found error in IIS. To fix it do the following:

  1. Open IIS manager, and right click on the web site.
  2. Select “Properties” from the dropdown menu.
  3. On the “Custom Errors” tab, scroll down the list until you see “404” in the HTTP Error column.
  4. Double click the 404 entry to open the properties box.
  5. Set the Message type to “URL”.
  6. Type “/util/NotFound.aspx” in the URL text box.
  7. Click “OK”.
  8. Click “OK” in the web site properties dialog to close it.
  9. That’s it, now Bob’s your Uncle!

2009-09-21

EPiServer declines to support XForms on IE8 for CMS 4.x

According to this thread http://world.episerver.com/Forum/Pages/Thread.aspx?id=28931&epslanguage=en it seems that EPiServer is denying responsibility to make XForms on EPiServer 4.x work with IE8 since IE8 was released after EPiServer 4.x.

The customers are experiencing an error when loading XForms with radiobuttons and/or checkboxes. The error is “invalid form” (translated from the Norwegian “ugyldig skjema”).

UPDATE 2011-01-17: Also I have experienced that in IE8, the submit button will not submit the xform which is strange, since it should be calling a javascript function: “return(false)”, and then specifying an action string.

In the comments to this posting, Björn Sållarp has proposed a solution from his blog: http://blog.sallarp.com/episerver-xforms-ie8/

Save power on the cell phone

My HTC S730 is using a lot of power, and has little standby time. Recently it stopped working, so I played with the settings, and by setting the Band Select (“Båndvalg” in Norwegian, and number 3 on the Settings menu) to a fixed value in stead of using the “Auto” option, my phone now uses a lot less power. I selected the “GSM” network type and the “Euro band” band type. My standby time used to be like one to two days, but now its about twice that.

2009-09-11

Getting paths for an application

I am creating an application that needs to load an assembly dynamically from the bin-folder of the application. This is not as straight forward as one might think.

So I tried the following:

string s1 = Directory.GetCurrentDirectory();
string s2 = Environment.CurrentDirectory;
string s3 = Assembly.GetExecutingAssembly().Location;
string s4 = new DirectoryInfo("~/bin").FullName;
string s5 = new DirectoryInfo("/bin").FullName;
string s6 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string s7 = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string s8 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);

Results:

s1 = s2 = “C:\Windows\system32”

s3 = "C:\\WINDOWS\\Microsoft.NET\\Framework\\v2.0.50727\\Temporary ASP.NET Files\\root\\101551d3\\93890f66\\assembly\\dl3\\f2069ce7\\fe4f0aea_dc32ca01\\MyAssemblyName.DLL"

s4 = "C:\\WINDOWS\\system32\\~\\bin"

s5 = "C:\\bin"

s6 = "C:\\Documents and Settings\\MyMachineName\\ASPNET\\Local Settings\\Application Data" (getting desperate, I know)

s7 = "C:\\Documents and Settings\\MyMachineName\\ASPNET\\Application Data"

Finally found one that worked:

s8 = “file:\\C:\\DevProjects\\…\\MyAppDir\\bin

2009-08-24

Bare linefeeds in SMTP Messages, status 451

I was having a problem with mails that were not being sent as they were supposed to be, and the problem could be caused by the use of bare linefeeds in the message body.

A bare linefeed is a linefeed that has only a linefeed (LF = “\n” in C#/C++/Java, ASCII code 10 decimal) and no carriage return (CR = “\r”, ASCII code 13 decimal).

Internet e-mail standards forbid the use of bare linefeeds, and some mailservers will reject a mail using bare linefeeds, with the status 451 (other mailservers wil accept them and just correct the mistake itself).

In stead of using bare linefeeds, a linefeed should always come with a carriage return (CR + LF = “\r\n”).

Read the full story at http://www.dylanbeattie.net/docs/iis6_bare_linefeed.html.

2009-07-01

TIP: Defragment your VMs and VPCs

I have several VMs (VMWare) and VPCs (MS VPC), and one of them in particular has been performing worse and worse, so I thought it could be good to defragment it.

Now there’s internal fragmentation and there’s external fragmentation (just like indexes in SQL Server :P). The internal fragmentation is handled using the windows defragmentation tool inside the VM or VPC. External fragmentation occurs if you have set your disks to grow incrementally as needed. You could also say that all the space for your virtual disks should be reserved, and if your physical disks were defragmented in the first place, there would be no external fragmentation of your virtual disks. But if you let them grow incrementally, or your physical disk was fragmented when you created the virtual disk, there might be some fragmentation.

So I found this excellent tool from Sysinternals: http://technet.microsoft.com/nb-no/sysinternals/bb897428(en-us).aspx

Now I don’t need to defragment the whole physical disk. I can just defragment one file at a time. Should save me some time :)

How to check if an assembly has been built in DEBUG or RELEASE mode

I found this cool code that checks if an assembly has been built in debug or release mode: http://blogs.msdn.com/jb/archive/2006/06/14/631469.aspx 
It compiles and runs using .NET 1.1, and thats what I used for building it.

I used it on some assemblies in a solution I have, and were somewhat surprised to find that the following assemblies were reported as being debuggable:

  • EPiServer.dll (version 4.62)
  • log4net.dll
  • Microsoft.Web.Services2.dll

This is probably done on purpose (to enable debugging), but it makes me wonder if my applications would run faster if the assemblies were built using Release mode.

2009-06-16

MVVM Toolkit for WPF and Silverlight

Laurent Bugnion has created a toolkit for creating MVVM applications. He says:

“To make development of WPF and Silverlight applications according to the Model-View-ViewModel pattern easier, I have put together a small toolkit which should speed up the creation of such applications by automating certain tasks.”

Here’s the URL: http://geekswithblogs.net/lbugnion/archive/2009/06/14/mvvm-lsquolightrsquo-toolkit-for-wpf-and-silverlight.aspx

2009-06-03

The Web Platform Installer

ScottGu’s last blog post is about the Web Platform Installer: http://weblogs.asp.net/scottgu/archive/2009/06/02/microsoft-web-platform-installer.aspx

Looks like a very useful application for configuring your web server or web development server. It can be downloaded for free from this direct link to the installer.

I also like very much that it integrates with the new Windows Web Application Gallery: www.microsoft.com/web/gallery. I am very likely going to use it very soon.

2009-05-28

Are you mocking me?

I am learning to mock :D. I understand the principle, that you can write your unit tests without any finished methods or data sources (sometimes referred to as TDD). So just looking for nice places to start with Moq. Found a couple of nice links so far:

http://stephenwalther.com/blog/archive/2008/06/12/tdd-introduction-to-moq.aspx

http://blog.objectmentor.com/articles/2009/05/19/a-first-look-at-moq

2009-05-14

Getting started with S#arp Architecture

I am trying to get started with S#arp Architecture, and I found some nice (short) videos at Dime Casts.NET:

Introdction to S#arp Architecture

Another look at Sharp Architecture- Validation, Design Decisions and Automapping

Taking a look at how to modify the T4 templates used by Sharp Architecture

I’ll be looking at them and creating my own test project. Should be good :)

There should also be a good Northwind example available with the downloads from Google Code.

2009-05-12

Microsoft laying off 12 people in Norway

A sad day when even MS has to start downsizing: http://www.digi.no/812605/microsoft-norge-maa-nedbemanne (Norwegian). Good luck to the twelve people laid off in Norway, and to their families.

2009-05-08

TSQL: Checking if an ID is in a Comma Separated String

Ok, so the scenario is that we have a list of IDs, maybe from a checkboxlist, and we want to get the records that match those IDs from a table. So for the sake of this example, I just assume that the list of IDs is passed to my stored procedure as a varchar(8000) string. Using Northwind as an example database, heres an example of how an SP that gets products could look like:

CREATE PROCEDURE GetProducts
@ListOfProductsAsCSVString varchar(8000)

AS

SET @ListOfProductsAsCSVString = ',' + @ListOfProductsAsCSVString + ',';

SELECT ProductID, ProductName FROM Products
WHERE CHARINDEX(',' + CAST(ProductID as varchar(10)) + ',', @ListOfProductsAsCSVString ) > 0;

So what I do is to first append a comma before and after the CSV-list. This is because I need to search for somthing that starts with a comma and ends with a comma, and usually a CSV-list doesn’t have a comma before the first element or after the last one. Then, in my select, I search using the CHARINDEX function for the ProductID prefixed and postfixed by a comma.

Now, please be aware that this could lead to a possible SQL Injection attack, if you use this procedure uncritically without validating the input before passing it to this stored procedure, so use with caution.

2009-05-02

FileUpload for ASP.NET MVC 1.0

I worked my way through the free Nerd Dinner chapter from ASP.NET MVC 1.0, creating my own web from the example. My web is a Food Recipe application where one can search among 7000 recipes on words in the title or ingredients.

The web should also be able to have pictures of the food, so I needed to do some file uploading, and so I found Scott Hanselman’s article http://www.hanselman.com/blog/ABackToBasicsCaseStudyImplementingHTTPFileUploadWithASPNETMVCIncludingTestsAndMocks.aspx. I copied some of his code, and put the parts I needed into this function (the definition of the ViewDataUploadFilesResult class is in Scott’s article):

private List<ViewDataUploadFilesResult> uploadFiles()
{
    var r = new List<ViewDataUploadFilesResult>();

    foreach (string file in Request.Files)
    {
        HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase;
        if (hpf.ContentLength == 0)
            continue;
        string savedFileName = Path.Combine(
           string.Concat(AppDomain.CurrentDomain.BaseDirectory,"images\\upload"),
           Path.GetFileName(hpf.FileName));
        hpf.SaveAs(savedFileName);

        r.Add(new ViewDataUploadFilesResult()
        {
            Name = savedFileName,
            Length = hpf.ContentLength
        });
    }
    return r;
}

Now, from before I had an Edit-action for the posting of my Edit View in my Controller, and from this I called the function above, as hown in the following code:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection)
{
    try
    {
        var recipe = recipeRepository.GetRecipe(id);
        recipe.RecipeDescription = Request.Form["RecipeDescription"];

// code removed for brevity

        List<ViewDataUploadFilesResult> fileUploaded = uploadFiles();

        if (fileUploaded.Count > 0)
            recipe.RecipePictureUrl = Path.GetFileName(fileUploaded[0].Name);

        recipeRepository.Save();
        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

I also had to make some changes to my View:

First, I had to add an “enctype” to the form element. This is done like this with the Html-helper class:

<% using (Html.BeginForm("Edit", "<EntityController>", null, FormMethod.Post, new { @enctype = "multipart/form-data" })) {%>

Second, to be able to use a FileOpen dialog, I had to add an attribute to the text box for entering the file name. In plain old ASP/Html, you would use:

<input type=”file”>

And that is also what we need to do here, except we need to use the Html-helper class like this:

<%= Html.TextBox("RecipePictureUrl", Model.RecipePictureUrl, new { @type = "file" }) %>

2009-04-29

The old UDL-trick: Testing Database Connection Strings

If you have problems creating a connection string, here’s an old trick:

1. Create a text file and call it for instance test.udl. It’s the file type (extension) that’s important.

2. Click (double-click) on the file to open it. A “Data Link Properties” dialog box will open.

3. Select the provider you want from the first tab. Click “Next”.

4. Select or type the name of the server.

5. Provide username and password.

6. Select database. This will only be possible if the parameters provided above are correct.

7. Check “Allow saving passwords”.

8. Click the “Test Connection”. If you get a message box saying the test was successful, proceed to the next step.

9. Close the Data Link Properties.

10. Hold shift down while right clicking on the UDL-file, then select “Open with”, then select “Notepad”.

Voila! You now have your connection string to be copied and used in your application.

2009-04-27

Using TryParse

I just figured out a way to use int.TryParse in an if-sentence.

string myInputString = myTextBox.Text;
int i = –1;
if(int.TryParse(myInputString, out i) && i > 0)
{

//do cool stuff

}

This only works with the “&&” operator. If the parse fails, the second part that uses the “out” parameter from the parse, will not be run.