Gunnar Peipman's ASP.NET blog

ASP.NET, C#, SharePoint, SQL Server and general software development topics.

Sponsors

News

 
 
 
DZone MVB

Links

Social

February 2009 - Posts

Links 2009-02-28

SharePoint

Other development topics

Refactoring: extract interface

Extract interface is one of the most common refactoring techniques. Motivation behind extract interface refactoring method is to avoid direct dependencies between classes. Instead of using classes in method calls we use interfaces so we can also use subclasses of those classes we used before. Also we can create brand new classes that use follow these interfaces and we can use these classes instead of current ones.

Let’s say you have a class that takes some parameters with its constructor. And let’s suppose you want to make this class easily testable.


public class PaymentImporter

{

    private readonly LogProvider _logProvider;

 

    public PaymentImporter(LogProvider logProvider)

    {

        _logProvider = logProvider;

    }

 

    public void Import()

    {

        _logProvider.WriteMessage("Starting payments import");

 

        try

        {

            // Perform importing tasks here

        }

        catch(Exception ex)

        {

            _logProvider.WriteException(ex);

        }

 

        _logProvider.WriteMessage("Payments import finished");

    }

}


You cannot test this class easily because you need to provide it with log provider. And you have to use real log provider. But you need to unit test your class. Of course, you can write test that uses current log provider but it is not unit test anymore. It is mix of unit test and integration test and therefore results of this test are pointless. Why? Because this test fails if you have logic errors in your class and also if log provider produces exceptions. You want to remove this dependency and use fake log provider for unit tests.

Extract interface

First step is to create interface for log provider. To be sure that nothing gets broken we create interface using exactly the same public members as original log provider has. If original log provider is something like this


public class LogProvider

{

    public void WriteMessage(string message)

    {

        // Write message to log

    }

 

    public void WriteException(Exception exception)

    {

        // Write exception to log

    }

}


then our interface will look like this


public interface ILogProvider

{

    void WriteMessage(string message);

    void WriteException(Exception ex);

}


Of course, we have to make original LogProvider class also to follow this interface. Otherwise we are not able to use it in class constructor.

Break dependency

Second step is to break dependency in class constructor. We replace original log provider class name with interface name so our class can accept also other log providers that follow the same interface.


public class PaymentImporter

{

    private readonly ILogProvider _logProvider;

 

    public PaymentImporter(ILogProvider logProvider)

    {

        _logProvider = logProvider;

    }

 

    // ...

}


Now we have broken dependency between our class and logger class and we can use arbitrary number of log provider implementations instead of original one as long as they follow ILogProvider interface.

Create fake log provider

Now we can create fake log provider using ILogProvider interface. We need fake log provider only for testing purposes and therefore it has to do nothing. If there are properties or methods that return values we make sure that these properties and methods return “correct” values. This way we can be sure that log provider is not able to garble our test results.

Our fake log provider is something like this.


public class FakeLogProvider : ILogProvider

{

    public void WriteMessage(string message) { }

    public void WriteException(Exception exception) { }

}


Now we are able to write tests for our class because we have no direct dependencies with other classes that may produce their own errors and make our test results messy this way.


kick it on DotNetKicks.com pimp it Shout it
SharePoint: creating SharePoint accounts for Live ID users

Couple of weeks ago I started adding Windows Live ID authentication support to SharePoint. I used Community Kit for SharePoint and I also made here some notes about it. There was one problem – although user is authenticated there is no SharePoint user and it is not possible assign it to any roles. Here is some advices how to create SharePoint user account when new user is registered after logging in using Live ID.

  1. Open Windows Live ID Authentication project with Visual Studio.
  2. Open file Code/liveinfo.cs.
  3. Move to Submit_Click() method and find the following line:

    bup.Update();


  4. Add the following code after line mentioned above. This code runs method AddNewSPUser then creates user account also to SharePoint. This way you achieve link between LiveID authenticated user account and SharePoint user account.

    var sec = new SPSecurity.CodeToRunElevated(

        delegate()

        {

            AddNewSPUser(User.Identity.Name, tbDisplayName.Text);

        });

    SPSecurity.RunWithElevatedPrivileges(sec);


  5. Add the following method to the end of the class:

    private static void AddNewSPUser(string userName, string name)

    {

        using (SPSite site = new SPSite(SPContext.Current.Web.Url))

        {

            using (SPWeb web = site.RootWeb)

            {

                site.AllowUnsafeUpdates = true;

                web.AllowUnsafeUpdates = true;

     

                SPUser user = web.EnsureUser(userName);

                user.Name = name;

                user.Update();

                web.SiteGroups["MySite visitor"].AddUser(user);

                web.Update();

                web.AllowUnsafeUpdates = false;

            }

            site.AllowUnsafeUpdates = false;

        }

    }


  6. Compile the code and deploy it to server. Also don’t forget to recycle application bool of web application to where you deployed the solution.

Now, after new user joins your site, the new user account is also created to SharePoint. By default the new user account is assigned to “MySite visitor” group. Make sure you have this group created or change the name of default group before compiling the code.

Posted: Feb 27 2009, 06:23 PM by DigiMortal | with 4 comment(s)
Filed under: ,
ASP.NET MVC: Ignore requests to favicon.ico

One problem I faced when writing my example pet portal on ASP.NET MVC was Google Chrome’s request to favicon.ico that doesn’t exist. IE8 that I use (8.0.7000.0) doesn’t make these blind requests to discover that favicon.ico is not there. Solution to my problem was very simple. Just add the ignore rule for favicon.ico to Global.asax file, in the beginning of RegisterRoutes method:


public static void RegisterRoutes(RouteCollection routes)

{

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });

 

    // Other route definitions follow here

}


The rule for favicon works for all folders in your application. There is also one other interesting thing. If you ignore some route then ASP.NET MVC lets those requests to files directly to web server. It doesn’t direct them to controllers.

kick it on DotNetKicks.com Shout it
Posted: Feb 26 2009, 09:07 PM by DigiMortal | with 11 comment(s)
Filed under: ,
SharePoint: Writing messages to ULS (Unified Logging System)

If you want to make your SharePoint solution more SharePoint way you can write your tracing, debug and error messages to SharePoint log. Easiest way to do it is to write logger by yourself. Btw, SharePoint has class called ULS to write messages to logs but this class is internal and we cannot use it. But it is there.

Let’s write our own logging class. I use my dirty draft here because different contracts doesn’t allow me to publish “The Perfect Solution”. But this code works and you can refactor it to structure it better.


using System;

using System.Runtime.InteropServices;

using System.Web;

using Microsoft.SharePoint;

using Microsoft.SharePoint.Administration;

 

namespace Util

{

    public static class TraceProvider

    {

        public static void LogException(Exception ex)

        {

            Log(ex.ToString(), TraceSeverity.Exception);

        }

        public static void LogInfo(string message)

        {

            Log(message, TraceSeverity.InformationEvent);

        }

        public static void Log(string message, TraceProvider.TraceSeverity severity)

        {

            string product = "My Trace Provider";

            string category = "Runtime";

            string exeName = "MyTraceWriter";

 

            Guid guid = Guid.Empty;

            object guidObject = null;

            if (HttpContext.Current != null)

            {

                guidObject = HttpContext.Current.Items["TraceGuid"];

            }

            if (guidObject == null)

            {

                guid = Guid.NewGuid();

                HttpContext.Current.Items.Add("TraceGuid", guid);

            }

            else

            {

                guid = (Guid)guidObject;

            }

 

            SPSecurity.RunWithElevatedPrivileges(

                delegate()

                {

                    RegisterTraceProvider();

                    WriteTrace(0, severity, guid, exeName, product, category, message);

                    UnregisterTraceProvider();

                }

            );

        }

 

        private static UInt64 hTraceLog;

        private static UInt64 hTraceReg;

 

        internal enum TraceFlags

        {

            Start = 1,

            End = 2,

            Middle = 3,

            IdAsASCII = 4

        }

 

        [StructLayout(LayoutKind.Sequential)]

        internal struct EventTraceHeaderClass

        {

            internal byte Type;

            internal byte Level;

            internal ushort Version;

        }

 

        [StructLayout(LayoutKind.Sequential)]

        internal struct EventTraceHeader

        {

            internal ushort Size;

            internal ushort FieldTypeFlags;

            internal EventTraceHeaderClass Class;

            internal uint ThreadId;

            internal uint ProcessId;

            internal Int64 TimeStamp;

            internal Guid Guid;

            internal uint ClientContext;

            internal uint Flags;

        }

 

        internal static class NativeMethods

        {

            internal const int TRACE_VERSION_CURRENT = 1;

            internal const int ERROR_SUCCESS = 0;

            internal const int ERROR_INVALID_PARAMETER = 87;

            internal const int WNODE_FLAG_TRACED_GUID = 0x00020000;       

 

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

            internal struct ULSTraceHeader

            {

                internal ushort Size;

                internal uint dwVersion;

                internal uint Id;

                internal Guid correlationID;

                internal TraceFlags dwFlags;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]

                internal string wzExeName;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]

                internal string wzProduct;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]

                internal string wzCategory;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 800)]

                internal string wzMessage;

            }

 

            [StructLayout(LayoutKind.Sequential)]

            internal struct ULSTrace

            {

                internal EventTraceHeader Header;

                internal ULSTraceHeader ULSHeader;

            }

 

            // Copied from Win32 APIs

            internal enum WMIDPREQUESTCODE

            {

                WMI_GET_ALL_DATA = 0,

                WMI_GET_SINGLE_INSTANCE = 1,

                WMI_SET_SINGLE_INSTANCE = 2,

                WMI_SET_SINGLE_ITEM = 3,

                WMI_ENABLE_EVENTS = 4,

                WMI_DISABLE_EVENTS = 5,

                WMI_ENABLE_COLLECTION = 6,

                WMI_DISABLE_COLLECTION = 7,

                WMI_REGINFO = 8,

                WMI_EXECUTE_METHOD = 9

            }

 

            // Copied from Win32 APIs

            internal unsafe delegate uint EtwProc(NativeMethods.WMIDPREQUESTCODE requestCode, IntPtr requestContext, uint* bufferSize, IntPtr buffer);

 

            // Copied from Win32 APIs

            [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]

            internal static extern unsafe uint RegisterTraceGuids([In] EtwProc cbFunc, [In] void* context, [In] ref Guid controlGuid, [In] uint guidCount, IntPtr guidReg, [In] string mofImagePath, [In] string mofResourceName, out ulong regHandle);

 

            // Copied from Win32 APIs

            [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]

            internal static extern uint UnregisterTraceGuids([In]ulong regHandle);

 

            // Copied from Win32 APIs

            [DllImport("advapi32.dll", CharSet = CharSet.Unicode)]

            internal static extern UInt64 GetTraceLoggerHandle([In]IntPtr Buffer);

 

            // Copied from Win32 APIs

            [DllImport("advapi32.dll", SetLastError = true)]

            internal static extern uint TraceEvent([In]UInt64 traceHandle, [In]ref ULSTrace evnt);

        }

 

        public enum TraceSeverity

        {

            Unassigned = 0,

            CriticalEvent = 1,

            WarningEvent = 2,

            InformationEvent = 3,

            Exception = 4,

            Assert = 7,

            Unexpected = 10,

            Monitorable = 15,

            High = 20,

            Medium = 50,

            Verbose = 100,

        }

 

        internal static void WriteTrace(uint tag, TraceSeverity level, Guid correlationGuid, string exeName, string productName, string categoryName, string message)

        {

            const ushort sizeOfWCHAR = 2;

            NativeMethods.ULSTrace ulsTrace = new NativeMethods.ULSTrace();

 

            // Pretty standard code needed to make things work

            ulsTrace.Header.Size = (ushort)Marshal.SizeOf(typeof(NativeMethods.ULSTrace));

            ulsTrace.Header.Flags = NativeMethods.WNODE_FLAG_TRACED_GUID;

            ulsTrace.ULSHeader.dwVersion = NativeMethods.TRACE_VERSION_CURRENT;

            ulsTrace.ULSHeader.dwFlags = TraceFlags.IdAsASCII;

            ulsTrace.ULSHeader.Size = (ushort)Marshal.SizeOf(typeof(NativeMethods.ULSTraceHeader));

 

            // Variables communicated to SPTrace

            ulsTrace.ULSHeader.Id = tag;

            ulsTrace.Header.Class.Level = (byte)level;

            ulsTrace.ULSHeader.wzExeName = exeName;

            ulsTrace.ULSHeader.wzProduct = productName;

            ulsTrace.ULSHeader.wzCategory = categoryName;

            ulsTrace.ULSHeader.wzMessage = message;

            ulsTrace.ULSHeader.correlationID = correlationGuid;

 

            // Pptionally, to improve performance by reducing the amount of data copied around,

            // the Size parameters can be reduced by the amount of unused buffer in the Message

            if (message.Length < 800)

            {

                ushort unusedBuffer = (ushort)((800 - (message.Length + 1)) * sizeOfWCHAR);

                ulsTrace.Header.Size -= unusedBuffer;

                ulsTrace.ULSHeader.Size -= unusedBuffer;

            }

 

            if (hTraceLog != 0)

                NativeMethods.TraceEvent(hTraceLog, ref ulsTrace);

        }

 

        public static unsafe void RegisterTraceProvider()

        {

            SPFarm farm = SPFarm.Local;

            Guid traceGuid = farm.TraceSessionGuid;

            uint result = NativeMethods.RegisterTraceGuids(ControlCallback, null, ref traceGuid, 0, IntPtr.Zero, null, null, out hTraceReg);

            System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);

        }

 

        public static void UnregisterTraceProvider()

        {

            uint result = NativeMethods.UnregisterTraceGuids(hTraceReg);

            System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);

        }

 

        internal static uint TagFromString(string wzTag)

        {

            System.Diagnostics.Debug.Assert(wzTag.Length == 4);

            return (uint)(wzTag[0] << 24 | wzTag[1] << 16 | wzTag[2] << 8 | wzTag[3]);

        }

 

        internal static unsafe uint ControlCallback(NativeMethods.WMIDPREQUESTCODE RequestCode, IntPtr Context, uint* InOutBufferSize, IntPtr Buffer)

        {

            uint Status;

            switch (RequestCode)

            {

                case NativeMethods.WMIDPREQUESTCODE.WMI_ENABLE_EVENTS:

                    hTraceLog = NativeMethods.GetTraceLoggerHandle(Buffer);

                    Status = NativeMethods.ERROR_SUCCESS;

                    break;

                case NativeMethods.WMIDPREQUESTCODE.WMI_DISABLE_EVENTS:

                    hTraceLog = 0;

                    Status = NativeMethods.ERROR_SUCCESS;

                    break;

                default:

                    Status = NativeMethods.ERROR_INVALID_PARAMETER;

                    break;

            }

 

            *InOutBufferSize = 0;

            return Status;

        }

    }

}


Okay, it is a little bit messy, but let’s see how to use it. This is primitive example and don’t try to find any good practices or guidance from here. It is only example and it illustrates how to use the previous class to log exception.


try

{

    list = SPContext.Current.Web.Lists[myListId];

}

catch (Exception ex)

{

    TraceProvider.LogException(ex);

    throw;

}


One thing I want you to be warned about. This class may show error messages on server screen when something goes wrong. Make sure you debug and test this class before using it in live environment.

You can find more examples about ULS logging if you follow these links:

Happy logging! :)

SharePoint: Access denied error when adding new page to page library

On one of my servers I detected very weird error. There are authenticated users who can add content to some page libraries and modify pages they have made. One day suddenly they were not able to add pages to page library anymore because of Access Denied error. Well, thanks Google, God and Clinton Cherry for saving my ass this time.

Clinton Cherry has very helpful blog entry about this issue: Fixing 'Error: Access Denied' error on creating a new page in MOSS.

  1. Go to Site Actions -> Site Settings ->Modify all site settings
  2. Go to Galleries -> Master pages and page layouts
  3. From the list toolbar, select Settings -> Document library settings
  4. Select permissions for this document library
  5. Add 'Restricted Read' access to the required groups.

To make robots to swallow this blog entry I give some bold keywords: SharePoint add page access denied

Posted: Feb 24 2009, 04:25 PM by DigiMortal | with 14 comment(s)
Filed under:
Links 2009-02-24

SharePoint

Other development topics

Other stuff

Starting with ASP.NET MVC

About a week ago I started learning ASP.NET MVC framework. It is pretty cool entertainment for cold winter nights as I found out. Here are the steps I made to get started:

  • I installed last versions of Visual Web Developers 2008 Express Edition and ASP.NET MVC framework.
  • I tried out some simpler examples to find out how things work and how code is organized.
  • I decided to write a little, simple and minimalistic pet portal that looks like real thing. This keeps me away from simple-sample-code mode, so I have to deal with every detail.
  • As a first thing I played with routings to find out how they work and how it is related to code. Okay, I had my own idea what kind of URLs I like and I achieved it without any additional extensions.
  • Then I started to play with controllers and views. I wanted to find out how the code is executed and how I can write my own code to make things happen. It is also important to play with views because things are different than in case of usual ASP.NET pages and user controls.
  • Next thing was including models, so instead of VC I have MVC in my application.
  • Currently I’m integrating AJAX and validation framework to my MVC application.

It is good to learn new stuff as much as possible. I refactored my code more than one time – it is really good refactoring training, believe me.

Why should one consider using ASP.NET MVC framework? There are some reasons I found to be important enough to write up:

  • MVC pattern keeps your code very well layered.
  • Your code can be easily tested (by example: unit tests, integration tests).
  • You can easily use JavaScript selectors (jQuery and others) and components built on them.
  • You have better control over your mark up and you can optimize it the way you like it.

Of course, there are scenarios when classic ASP.NET web applications are better choice. If you have data intensive applications like intranets or other special purpose applications then you may want to use also very powerful UI components from 3rd party providers. Of course, all these things are also possible on MVC framework – just find components you need or write your own components and go live. :)

Posted: Feb 24 2009, 11:49 AM by DigiMortal | with 5 comment(s)
Filed under: ,
Show your Twitter log in SharePoint

About year ago I wrote how to display blog feeds in SharePoint sites using XML Web Part. Today is Twitter very popular and we can use XML Web Part to render Twitter feeds. To see how to configure XML Web Part check out blog feeds entry referred above. This time you have to insert user Twitter feed as XML link, by example http://twitter.com/statuses/user_timeline/12358522.rss. Click XSL Editor button and insert the following XSL to dialog window.


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="xsl">

<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>   

<xsl:template match="/">       
    <xsl:value-of select="rss/channel/title" />
    <div>
        <xsl:apply-templates select="rss/channel"/>
    </div>   
</xsl:template>   

<xsl:template match="rss/channel">
    <xsl:variable name="link" select="link"/>
    <xsl:variable name="description" select="description"/>       

    <ul>
             <xsl:apply-templates select="item"/>
    </ul>
</xsl:template>   

<xsl:template match="item">       
    <xsl:variable name="owner">
        <xsl:value-of select="substring(/rss/channel/title,11)" />
    </xsl:variable>
    <xsl:variable name="item_link" select="link"/>
    <xsl:variable name="item_title" select="substring(description,string-length($owner)+3)"/>       

    <li>
        <xsl:call-template name="getDate">
            <xsl:with-param name="dateTime" select="pubDate" />
        </xsl:call-template> |
        <a href="{$item_link}" title="{$item_title}">
            <xsl:value-of select="$item_title"/>
        </a>
    </li>
</xsl:template>

<xsl:template name="getDate">
    <xsl:param name="dateTime" />
    <xsl:variable name="monthName">
        <xsl:value-of select="substring($dateTime,9,3)" />
    </xsl:variable>
    <xsl:variable name="day">
        <xsl:value-of select="substring($dateTime,6,2)" />
    </xsl:variable>
    <xsl:variable name="month">
        <xsl:choose>
        <xsl:when test="$monthName = 'Jan'">01</xsl:when>
        <xsl:when test="$monthName = 'Feb'">02</xsl:when>
        <xsl:when test="$monthName = 'Mar'">03</xsl:when>
        <xsl:when test="$monthName = 'Apr'">04</xsl:when>
        <xsl:when test="$monthName = 'May'">05</xsl:when>
        <xsl:when test="$monthName = 'Jun'">06</xsl:when>
        <xsl:when test="$monthName = 'Jul'">07</xsl:when>
        <xsl:when test="$monthName = 'Aug'">08</xsl:when>
        <xsl:when test="$monthName = 'Sep'">09</xsl:when>
        <xsl:when test="$monthName = 'Oct'">10</xsl:when>
        <xsl:when test="$monthName = 'Nov'">11</xsl:when>
        <xsl:when test="$monthName = 'Dec'">12</xsl:when>
        </xsl:choose>
    </xsl:variable>

    <xsl:variable name="year">
        <xsl:value-of select="substring($dateTime,13,4)" />
    </xsl:variable>

    <xsl:variable name="time">
        <xsl:value-of select="substring($dateTime,18,8)" />
    </xsl:variable>

    <xsl:value-of select="concat($day,'.',$month,'.',$year,' ',$time)" />
</xsl:template>
</xsl:stylesheet>


Now apply or save changes and check out the result. You should see something like this.

SharePoint displaying Twitter feed

If you find any bugs in this code or you know some tricks how to make this XSL shorter then please feel free to drop me a line. :)

Please help - Visual Studio SharePoint development environment is very slow

I run SharePoint development environment on virtual machine. Virtual machine has 2GB RAM and 30GB disk space. Also I have no complaints over processor (I can run here two usual ASP.NET development virtual machines parallel). All other Visual Studio projects are working well except those that use VSeWSS 1.2. Hard performance problems occur usually at these moments:

  • when opening, modifying or saving aspx, ascx or xml files,
  • Visual Studio is doing something after building,
  • deployments are pretty slow.

Here is what I have installed:

  • SharePoint 2007 Standard,
  • Visual Studio Team System 2005,
  • Visual Studio Team System 2008,
  • .Net Framework 3.5.

These problems occur on both Visual Studio installations. I have no problems like these when I don’t use VSeWSS. Here’s screenshot of Task Manager. Check out what devenv.exe is doing.

Visual Studio / SharePoint resource problem

What may be the problem here?

More Posts Next page »