Use Fiddler to monitor IIS request

by Viktor 11. November 2010 10:40

I often use Fiddler to debug or just see what is sent between the browser and server but today I needed a tool to see what EPiServer sent to a remote site and received back.
After an hour of searching and not finding anything I liked I come upon a blog post (using-fiddler-with-iis) that solved everything for me, just set the application pool to run as your user and all requests from your code to external servers will show up in Fiddler!

Tags: , ,

blog comments powered by Disqus

Move content to another language in EPiServer

by Viktor 1. November 2010 10:25

In the latest project I was involved in we created a site that would be in Norwegian, during the development we did all texts in English and as the site would not be globalized we did no changes in in EPiServer, just a standard installation. When we then received the Norwegian translations we noticed that we had made the site for the English language branch and was not able to get the Norwegian translations to show up on the site.

With the help of this blog post, http://andrewgunn.blogspot.com/2008/01/change-page-language-in-episerver.html, we ware able to solve the problem.
The first thing we did was enable Norwegian in Admin mode.
Then we ran the following SQL script in the EPiServer database,

DECLARE @PageID INT
DECLARE @RowNum INT 
DECLARE PageList CURSOR FOR
	SELECT pkID FROM tblPage
OPEN PageList
FETCH NEXT FROM PageList INTO @PageID
WHILE @@FETCH_STATUS = 0
BEGIN
	DECLARE @OriginalLanguageBranchID INT
	DECLARE @NewLanguageBranchID INT
	DECLARE @NewLanguageID NVARCHAR(3)
		 
	SET @OriginalLanguageBranchID = Y
	SET @NewLanguageBranchID = Z
	SET @NewLanguageID = 'NO'
		 
	UPDATE tblPageLanguage
	SET fkLanguageBranchID = @NewLanguageBranchID, fkLanguageID = @NewLanguageID
	WHERE fkPageID = @PageID
		 
	UPDATE tblPage
	SET fkMasterLanguageBranchID = @NewLanguageBranchID, fkLanguageID = @NewLanguageID
	WHERE pkID = @PageID 
		 
	UPDATE tblProperty
	SET fkLanguageBranchID = @NewLanguageBranchID
	WHERE fkPageID = @PageID and fkLanguageBranchID = @OriginalLanguageBranchID
		 
	EXEC editPageVersionList @PageID = @PageID, @SID = NULL

	FETCH NEXT FROM PageList INTO @PageID
END
CLOSE PageList
DEALLOCATE PageList
The importent parts here are
SET @OriginalLanguageBranchID = Y
SET @NewLanguageBranchID = Z
SET @NewLanguageID = 'NO'

Here you set the id of the language you want to change from, the id of the language you want to change to and the new language id, in this case 'NO'.
Run this script in your EPiServer database to move all content from the old language to the new language.
All information you need can be found in the table tblLanguageBranch,

SELECT pkID, LanguageID FROM tblLanguageBranch

When this is done you will get a warning in EPiServer telling you that the page is not available in your current language, to into Admin mode and turn on Globalization, in the edit mode select the root page and language options for this page, change the default language to your new language, turn off Globalaization.

Finished! The site had now been transformed from a English site to a Norwegian one.

Tags: ,

Development

blog comments powered by Disqus

Kill all connections to a database in SQL Server

by Viktor 19. May 2010 08:32
When you want to rename a database or something like that you need to get exclusive access to the database and it can be hard to get this exclusive access because there are always someone who has a connection. This script will kill all connections to a database, just replace "DATABASE_NAME" with the name of the database you want to kill connections for,
DECLARE @SQL VARCHAR(8000)
 
SELECT @SQL=COALESCE(@SQL,'')+'Kill '+CAST(spid AS VARCHAR(10))+ '; ' 
FROM sys.sysprocesses 
WHERE DBID=DB_ID('DATABASE_NAME')
 
EXEC(@SQL) 

Tags: ,

blog comments powered by Disqus

Blog Page Provider for EPiServer CMS

by Viktor 25. March 2010 15:04

Blog Page Provider is a example project I have created to try out the page provider in EPiServer

As the name may suggest this is a EPiServer page provider for blogs that has the ability to create a blog page in EPiServer that connects with a blog and lists all blogs, posts and comments connected to that account as EPiServer pages. You can also create blog posts and comments in EPiServer by creating "Post" or "Comment" pages.

The page providers are added dynamically to EPiServer so there is no configuration to setup the providers just create a page of the right type ("Blog - Settings") in EPiServer and the page provider will be added for you.

Support for different blog types is handled by blog providers, right now there are two providers, one for Blogger and the other for WordPress. You can create your own provider for other blogs by just implementing the interfaces in the Dropit.BlogPageProvider.Blog namespace and add the provider in web.config.

You can find more information about the project and all code over at Codeplex, Blog Page Provider

Have fun with it!

Blog Post Provider in three short steps

Blog post both in EPiServer and on Blogger

Create a new blog post in EPiServer

The created blog post is shown both in EPiServer and on blogger

Tags: , ,

Development

blog comments powered by Disqus

Speed up your RDP connection

by Viktor 20. March 2010 08:56

For some time now I have had problems both at work and home with very slow RDP connections making it almost impossible to work with some servers (it was not affecting all servers just "some") and today I tried to find a solution for the problem and found it.

It turns out that all you have to do is run this command as administrator and your will see a very big speed difference,

netsh interface tcp set global autotuninglevel=disabled

I have basically no understanding of what the command dose but it seems to work for me and everything else still seems to work so I guess its not that dangerous.

I found this solution here, Remote Desktop slow problem solved

Tags:

General

blog comments powered by Disqus

Android demo

by Viktor 17. March 2010 21:24

During the last internal tech forum (tech meeting with all developers at Dropit) I had a short demo of how to make a Android application. I'm not a Android expert so forgive me for the none expert written code :)

The goal was to make a blog application where you could write a heading, main text, take a photo with you phones camera and then send this informtion together with the GPS/Network position of the phone to EPiServer and there publish a page to create a blog post. Unfortunately my harddrive crashed before I finished the project so I never had time to do the EPiServer implementation but here you can download the Android code that I put together for this demo.

AndroidBlog.zip (1.07 mb)

Tags: , ,

Development | General

blog comments powered by Disqus

INTRODUCTION TO POSTSHARP - PART 2

by Viktor 14. October 2009 21:22

Time for part two of Introduction to PostSharp, part one can be found here Introduction to PostSharp - Part 1. In part two we will focus on PostSharp.Core. With PostSharp.Laos we get nice base classes that we just add our code to and PostSharp dose the rest for us, what I did not tell you in the first part is that PostSharp.Laos is a easy to use addin to PostSharp that is written with PostSharp.Core and as you my expect PostSharp.Core is not as easy to get a handle on.

The code for this example can be downloaded here, CacheExample.zip (303.53 kb), you need Visual Studio 2008 and PostSharp 1.5 RTM to compile and run the code.

In this part we will create a cache plugin for PostSharp using PostSharp.Core. What it will do is that at method entry generate a cache key based on the parameters, check if there is a values saved for this cache key and if there is return that value otherwise continue with the method. At the end of the method, at successfull exit, we will save the return value, using the cache key generated at method entry, in the cache for future use.

Lets start with the basic example program we have,

public class Program
{
        static void Main(string[] args)
        {
                string data;
                do
                {
                        Console.Write("Number: ");
                        data = Console.ReadLine();
                        Console.WriteLine("And the magic number is: {0}", DoBigCalculation(data));
                } while (data != "quit");

        }

        private static int DoBigCalculation(string data)
        {
                int number;
                if (int.TryParse(data, out number))
                {
                        //This is just to simulate a big calculation/data operation you may have in your application
                        //that can be much fast with cache
                        System.Threading.Thread.Sleep(2000);
                        return number * 2;
                }
                return 0;
        }
}      

The DoBigCalculation method is the slow method that we want to cache because it's just to slow in it's current form. The cache system we will use is very simple,

public static class Cache
{
        private static Dictionary _cache = new Dictionary();

        public static object Get(object key)
        {
                if (_cache.ContainsKey(key))
                {
                        return _cache[key];
                }
                return null;
        }

        public static object GenerateKey(params object[] paramlist)
        {
                string key = string.Empty;
                foreach (object obj in paramlist)
                {
                        key += obj.ToString();
                }
                return key;
        }

        public static void Set(object key, object value)
        {
                if (_cache.ContainsKey(key))
                {
                        _cache.Remove(key);
                }
                _cache.Add(key, value);
        }
}

Right now we have everything we need to cache the DoBigCalculation method and we could rewrite the method to look like this,

private static int DoBigCalculation(string data)
{
        object cacheValue = Caching.Cache.Get(Caching.Cache.GenerateKey(data));
        if (cacheValue != null)
        {
                return (int)cacheValue;
        }
        int number;
        if (int.TryParse(data, out number))
        {
                //This is just to simulate a big calculation/data operation you may have in your application
                //that can be much fast with cache
                System.Threading.Thread.Sleep(2000);
                number = number * 2;
        }
        else
        {
                number = 0;
        }
        Caching.Cache.Set(Caching.Cache.GenerateKey(data), number);
        return number;
}

and that works perfecly fin, but now we have added caching code to our calculation code and the calculation code is now responsible to have both working caching code and working calculation code. What we want to do insted is be able to write the caching code sepereate from the calculation code and then just be able to mark the method for caching and the caching code will be added to the method automaticly during compile time, to do this we write a caching aspect with PostSharp.Core and when we are finished the resulting code, after compile, will look very much like the example above but when we look at the code in our project it will still look like the code in the first example and only contain calculation code.

Lets begin by creating the caching attribute we want to use,

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,
AllowMultiple = false, Inherited = false)]
[MulticastAttributeUsage(MulticastTargets.Method | MulticastTargets.Property, AllowMultiple = false)]
[RequirePostSharp("Aspects.Weaver", "Aspects.Weaver")]
public class CacheAttribute : MulticastAttribute
{
}

Now when we have a attribute its time to create the two classes that will form our aspect, the Task and Advice, as always for more information about exacly why and what check out PostSharp.org. Lets begin with the Task,

public class CacheTask : Task, IAdviceProvider
{
        protected override void Initialize()
        {
        }

        #region IAdviceProvider Members

        public void ProvideAdvices(PostSharp.CodeWeaver.Weaver codeWeaver)
        {
        }

        #endregion
}

and then continue with the advice,

public class CacheAdvice : IAdvice
{
        private readonly CacheTask _parent;
        private readonly CacheAttribute _attribute;
        private LocalVariableSymbol _cacheKeyVariable;


        public CacheAdvice(CacheTask parent, CacheAttribute attribute)
        {
                _parent = parent;
                _attribute = attribute;
        }

        #region IAdvice Members

        public int Priority
        {
                get { return 5; }
        }

        public bool RequiresWeave(WeavingContext context)
        {
                return true;
        }

        public void Weave(WeavingContext context, PostSharp.CodeModel.InstructionBlock block)
        {
        }

        private void WaveEntry(WeavingContext context, InstructionBlock block)
        {
        }

        private void WaveExit(WeavingContext context, InstructionBlock block)
        {
        }

        #endregion
}

One more thing needs to be added to the project, a file that binds the attribute and Task togerther, the Aspect.Weaver.psplugin file,



        
                
        

This plugin file has to be in the bin directory of the example program for it to work.

Lets start with the code for the task.

public void ProvideAdvices(PostSharp.CodeWeaver.Weaver codeWeaver)
{
        // Gets the dictionary of custom attributes.
        AnnotationRepositoryTask annotationRepository = AnnotationRepositoryTask.GetTask(this.Project);

        // Requests an enumerator of all instances of our CacheAttribute.
        IEnumerator customAttributeEnumerator = annotationRepository.GetAnnotationsOfType(typeof(CacheAttribute), false);
        // For each instance of our CacheAttribute.
        while (customAttributeEnumerator.MoveNext())
        {
                // Gets the method to which it applies.
                MethodDefDeclaration methodDef = customAttributeEnumerator.Current.TargetElement as MethodDefDeclaration;

                if (methodDef != null)
                {
                        // Build an advice based on this custom attribute.
                        CacheAdvice advice = new CacheAdvice(this, null);
                                                // Add join points at the start of the method and at the end of a successfull method
                        codeWeaver.AddMethodLevelAdvice(advice, new Singleton(methodDef), JoinPointKinds.BeforeMethodBody | JoinPointKinds.AfterMethodBodySuccess, null);
                }
        }
}

This code will call the advice for every instance of our cache attribute in the code and tell the advice that we want to add code to the method at the top of the method (BeforeMethodBody) to be able to check if the value requested can be found in our cache or not and then after a successfull running of the method to be able to add the return value to the cache and return the value. Now we need to add helper methods to the Task, this are instances of the cache methods that needs to be called in the advice,

internal IMethod CacheSetMethod;
internal IMethod CacheGetMethod;
internal IMethod CacheGenerateKeyMethod;

internal ITypeSignature ObjectType;
internal ITypeSignature ObjectArrayType;

This are variables that will containe methods and types that are needed by the advice. We override the initialize method to initialize the variables,

protected override void Initialize()
{
        ModuleDeclaration module = this.Project.Module;

        ObjectType = module.FindType(typeof(object), BindingOptions.Default);
        ObjectArrayType = module.FindType(typeof(object[]), BindingOptions.Default);
        this.CacheSetMethod = module.FindMethod(typeof(Caching.Cache).GetMethod("Set", new Type[]
        {
                typeof(object),
                typeof(object)
        }), BindingOptions.Default);

        this.CacheGetMethod = module.FindMethod(typeof(Caching.Cache).GetMethod("Get", new Type[]
        {
                typeof(object)
        }), BindingOptions.Default);
        this.CacheGenerateKeyMethod = module.FindMethod(typeof(Caching.Cache).GetMethod("GenerateKey", new Type[]
        {
                typeof(object[])
        }), BindingOptions.Default);
}

We use the FindType and FindMethod methods to find the types and methods we need to work with later. Now when the Task class is ready its time to move on to the advice. First we add code to our Weave method,

switch (context.JoinPoint.JoinPointKind)
{
        case JoinPointKinds.BeforeMethodBody:
                this.WaveEntry(context, block);
                break;
        case JoinPointKinds.AfterMethodBodySuccess:
                this.WaveExit(context, block);
                break;
        default:
                throw new ArgumentException(string.Format("Unexpected join point kind: {0}", context.JoinPoint.JoinPointKind));
}

This is the method that will be called from our Task whenever a attribute is found and when its time to add code to one of our defined Joinpoints. Now we get into the hard part, the code generation, lets start with WaveEntry, here we want to generate a cache key, check if the cache contains a object and return that object or if it's null continue with the method. I have added alot of code comments here so read them to get a unerstanding of what happens here,

private void WaveEntry(WeavingContext context, InstructionBlock block)
{
        // Create a new instruction sequence and add it to the block
        // dedicated to our advice. Attach the InstructionWriter.
        InstructionSequence entrySequence = context.Method.MethodBody.CreateInstructionSequence();
        // Create another instruction sequence that we will jump to if nothing was found in the cache
        InstructionSequence endSequence = context.Method.MethodBody.CreateInstructionSequence();

        // Add the instruction sequence at the top of the method
        block.AddInstructionSequence(entrySequence, NodePosition.Before, null);
        // Add the instruction sequence after the previus instruction sequence
        block.AddInstructionSequence(endSequence, NodePosition.After, entrySequence);
        // Attach the instruction sequence to the writer so that we can being writing code
        context.InstructionWriter.AttachInstructionSequence(entrySequence);
        // Hide the sequence from debuggers.
        context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);

        // Create a local variable of type object, this will hold the cache key we create for this method call and
        // will be used both there in Entry and in the Exit method
        _cacheKeyVariable = block.DefineLocalVariable(_parent.ObjectType, "~cache~key~{0}");
        // Create a local variable that will be used as the argument to the generate cache key method
        LocalVariableSymbol tmpCacheKey = block.DefineLocalVariable(_parent.ObjectArrayType, "~tmp~cache~key~{0}");
        // Create a local variable that will hold the object that is returned from the caches Get method
        LocalVariableSymbol objectFromCache = block.DefineLocalVariable(_parent.ObjectType, "~object~from~cache~{0}");
        // Get the number of parameters sent to this method
        int parameters = context.Method.Parameters.Count;
        // Push the size of the array to the stack
        context.InstructionWriter.EmitInstructionInt32(OpCodeNumber.Ldc_I4, parameters);
        // Create a new array object
        context.InstructionWriter.EmitInstructionType(OpCodeNumber.Newarr, _parent.ObjectType);
        // Save the new array to the variable
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Stloc, tmpCacheKey);

        //Loop all of the paramters for this method
        for (int i = 0; i < context.Method.Parameters.Count; i++)
        {
                // Push the array onto the stack
                context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, tmpCacheKey);
                // Push the position index where you want to save the object in the array onto the stack
                context.InstructionWriter.EmitInstructionInt32(OpCodeNumber.Ldc_I4, i);
                // Push the value you want to save onto the stack
                context.InstructionWriter.EmitInstructionInt32(OpCodeNumber.Ldarg_S, i);
                // Copy everyting into the array
                context.InstructionWriter.EmitInstruction(OpCodeNumber.Stelem_Ref);
        }
        // Push the array onto the stack as a parameter for the next method call
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, tmpCacheKey);
        // Call the GenerateKey method with the array as a parameter
        context.InstructionWriter.EmitInstructionMethod(OpCodeNumber.Call, _parent.CacheGenerateKeyMethod);
        // Copy the return value from GenerateKey to the variable
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Stloc, _cacheKeyVariable);

        // Push the cache key onto the stack
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, _cacheKeyVariable);
        // Call the cache Get method with the cacke key as a parameter
        context.InstructionWriter.EmitInstructionMethod(OpCodeNumber.Call, _parent.CacheGetMethod);
        // Get the return value from the cache Get method
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Stloc, objectFromCache);

        // Push the object we got from the cache method onto the stack
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, objectFromCache);
        // Push null to to stack
        context.InstructionWriter.EmitInstruction(OpCodeNumber.Ldnull);
        // Check if the value we got from the stack and null are equal
        context.InstructionWriter.EmitInstruction(OpCodeNumber.Ceq);

        // If they are equel we want to continue with the method as it's written
        context.InstructionWriter.EmitBranchingInstruction(OpCodeNumber.Brtrue_S, endSequence);
        // If they are not equal we want to return the value we got from the cache Get method back from the calling method
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc_S, objectFromCache);
        context.InstructionWriter.EmitInstruction(OpCodeNumber.Ret);
        context.InstructionWriter.DetachInstructionSequence();

        // If we did not get anything back from the cahce do nothing and return to the method and continue executing
        context.InstructionWriter.AttachInstructionSequence(endSequence);
        context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
        context.InstructionWriter.EmitInstruction(OpCodeNumber.Nop);
        context.InstructionWriter.DetachInstructionSequence();
}

And then the WaveExit method, here we want to take the value that's returned form the method and put it into our cache using our generated cache key as the key,

private void WaveExit(WeavingContext context, InstructionBlock block)
{
        //Create a instruction sequence
        InstructionSequence entrySequence = context.Method.MethodBody.CreateInstructionSequence();
        // Add the new instruction sequence at the end of the method body (but before the last return)
        block.AddInstructionSequence(entrySequence, NodePosition.After, null);
        // Attache the new instruction sequence to the current context
        context.InstructionWriter.AttachInstructionSequence(entrySequence);
        // Hide the code from debuggers
        context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);

        // Push the cachekey we created in the WaveEntry method onto the stack
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc_S, _cacheKeyVariable);
        // Push the return value onto the stack
        context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc_S, context.ReturnValueVariable);
        // Call the cache Set method with the two parameters we have pushed onto the stack
        context.InstructionWriter.EmitInstructionMethod(OpCodeNumber.Call, _parent.CacheSetMethod);

        context.InstructionWriter.DetachInstructionSequence();
}

And to put it all to good use lets update our example program to look like this,

[Aspect.Cache]
private static int DoBigCalculation(string data)
{
        int number;
        if (int.TryParse(data, out number))
        {
                //This is just to simulate a big calculation/data operation you may have in your application
                //that can be much fast with cache
                System.Threading.Thread.Sleep(2000);
                return number * 2;
        }
        return 0;
}

Now we have added a cache to the big calculation method and when you call it with the same argument again you should get the respnse much faster as its taken from the cache and not calculated again.

A couple of notes about the example program that I have included here, you have to copy the dll from the Weaver project to your bin directory and also the *.psplugin file, as there are not references to this project the files are not copied when you compile the application.

Example program for Part 2: CacheExample.zip (303.53 kb)

Tags: , , , ,

Development

blog comments powered by Disqus

Introduction to PostSharp - Part 1

by Viktor 12. October 2009 19:06

PostSharp is a tool to make Aspect-oriented programming (AOP) a reality on the .NET (C#) platform. In this two part blog post I will try to give you a quick start into using PostSharp in your own programs. I will present two examples and try to explain the code as best as I can and the focus will be on the examples and I will not try to explain what AOP or PostSharp is, for more information about that visit Wikipeida and www.postsharp.org.

The code for this example program can be downloaded here, TraceExample.zip (39.91 kb), you need Visual Studio 2008 and PostSharp 1.5 RTM to compile the example.

PostSharp has two ways for a developer to create there aspects, Laos and Core, in this first part I will focus on Laos. In our example we want to add trace output to our project so that every method call can be traced from a log file. Lets start with the code for our program, it's a simple console application that asks for your name and age,


public class Program
{
        static void Main(string[] args)
        {
                string name;
                string age;
                do
                {
                        Console.Write("What is your name? ");
                        name = Console.ReadLine();
                } while (!CheckName(name));
                do
                {
                        Console.Write("How old are you {0}? ", name);
                        age = Console.ReadLine();
                } while (!CheckAge(age));
                Console.Write("Your name is {0} and you are {1} years old!", name, age);
                Console.ReadLine();
        }

        private static bool CheckName(string name)
        {
                if (string.IsNullOrEmpty(name))
                {
                        return false;
                }
                else if (name.Length < 5)
                {
                        return false;
                }
                return true;
        }

        private static bool CheckAge(string age)
        {
                int intAge = int.Parse(age);
                if (intAge < 0 || intAge > 130)
                {
                        throw new ArgumentOutOfRangeException("No way your are that young/old");
                }
                return true;
        }
}

The program makes a couple of method calls to check if the input data is correct.

Now lets create our very simple tracing class,


public class Trace
{
        public static void Write(string message)
        {
                try
                {
                        StreamWriter sw = new StreamWriter("trace.txt", true);
                        sw.WriteLine(message);
                        sw.Close();
                }
                catch
                {
                }
        }
}

And now we can begin creating our tracing aspect that we will add to the program to be able to trace the calls to the methods and view input parameters, return values and any exceptions that are thrown from the methods. We create the aspect by inheriting from the abstract base class PostSharp.Laos.OnMethodBoundaryAspect,


[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
        }

        public override void OnException(MethodExecutionEventArgs eventArgs)
        {
        }

        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
        }
}

From OnMethodBoundaryAspect we get some very cool virtual functions that we can use to hook on our aspect code to the excisting code. For this example we want to print trace information when we enter and exit a method, for the exit we have two methods, OnException for when the method has exited with an exception and OnSuccess for when the method has exited as expected, with no exception.

Here is our aspect with the code needed for the tracing output we want,


[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
                if (eventArgs.GetReadOnlyArgumentArray() != null)
                {
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < eventArgs.GetReadOnlyArgumentArray().Count(); i++)
                        {
                                sb.AppendFormat(", {0}='{1}'", eventArgs.Method.GetParameters()[i], Convert.ToString(eventArgs.GetReadOnlyArgumentArray()[i]));
                        }
                        Trace.Trace.Write(string.Format("[Entering] {0}.{1}\tParameters: {2}", eventArgs.Method.ReflectedType.Name, eventArgs.Method.Name, sb.ToString()));
                }
                else
                {
                        Trace.Trace.Write(string.Format("[Entering] {0}.{1}", eventArgs.Method.ReflectedType.Name, eventArgs.Method.Name));
                }
        }

        public override void OnException(MethodExecutionEventArgs eventArgs)
        {
                Trace.Trace.Write(string.Format("[Exit] {0}.{1}\tException: {2}", eventArgs.Method.ReflectedType.Name, eventArgs.Method.Name, eventArgs.Exception.Message));
        }

        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
                Trace.Trace.Write(string.Format("[Exit] {0}.{1}\tReturn value: '{2}'", eventArgs.Method.ReflectedType.Name, eventArgs.Method.Name, eventArgs.ReturnValue));
        }
}

In the OnEntry method we check if the method has any parameters, if there are parameters we print the type, name and value of the parameter and then the name of the method that was called. In the OnException method we print the name of the method that has had the exception and the exception message. And last if the method has been executed successfully we print the name of the method and the value of what is returned from the method.

Now we are redy to apply this aspect to our program. As the name may indicate the aspect is added to the program as a attribute and this attribute can be added to a namespace, class or method depending on, in this example, what you want to trace. For this example we add the attribute to the Program class,


[Aspects.Trace]
public class Program
{
...
}

When we now run this program the trace output may look like this


[Entering] Program.Main Parameters: , System.String[] args='System.String[]'
[Entering] Program.CheckName    Parameters: , System.String name='Vik'
[Exit] Program.CheckName        Return value: 'False'
[Entering] Program.CheckName    Parameters: , System.String name='Viktor'
[Exit] Program.CheckName        Return value: 'True'
[Entering] Program.CheckAge     Parameters: , System.String age='131'
[Exit] Program.CheckAge Exception: Specified argument was out of the range of valid values.
Parameter name: No way your are that young/old
[Exit] Program.Main     Exception: Specified argument was out of the range of valid values.
Parameter name: No way your are that young/old

What we have achieved here is to add tracing to our program without bloating our own code with tracing code, the methods still only do what they are there to to, check the name and age of a person, all the code for the tracing of the methods are added at compile time without the developer of the method/class needing to worry about it.

There are two more things I would like to add to our aspect, timing of the method calls and the ability to recover from a exception, the last part is not somthing I would recomend using in a example like this but it's just a example to show what can be done :)

Lets start with the timing information, I will use the DateTime and TimeSpan classes for this, At the end of our OnEntry method of our Aspect we add this code,


eventArgs.InstanceTag = DateTime.Now;

And at the start of our OnException and OnSuccess methods we add this code,


TimeSpan ts = DateTime.Now - (DateTime)eventArgs.InstanceTag;
Trace.Trace.Write(string.Format("[Time] {0}.{1}\tTime: {2} ms", eventArgs.Method.ReflectedType.Name, eventArgs.Method.Name, ts.TotalMilliseconds));

And when we run the program now we get the following output in our trace,


[Entering] Program.Main Parameters: , System.String[] args='System.String[]'
[Entering] Program.CheckName    Parameters: , System.String name='Viktor'
[Time] Program.CheckName        Time: 1 ms
[Exit] Program.CheckName        Return value: 'True'
[Entering] Program.CheckAge     Parameters: , System.String age='31'
[Time] Program.CheckAge Time: 0 ms
[Exit] Program.CheckAge Return value: 'True'
[Time] Program.Main     Time: 5550,3174 ms
[Exit] Program.Main     Return value: ''

Now we also want the appiction to recover from a exception, or rather if we get a exception we return the default value from the method that got the exception. At the end of the OnException method add the following code,


eventArgs.FlowBehavior = FlowBehavior.Continue;

If you now compile and run the program and enter invalid data on the age question the question will just be repeted until you enter correct data, this is because when a exception is thrown from the CheckAge method we return the default value of bool, false, insted of throwing the exception. The exception is still printed to our trace file for debug purpuses.

I would highly recomand that you check the compiled code with Red Gates Reflector to see what PostSharp and Laos has done to your code and get a better understanding of what happens with a Aspect during compile and runtime.

In part 2 of this series we will look at PostSharp Core and give a example of what you can do with it. You can find part 2 here, Introduction to PostSharp - Part 2

TraceExample.zip (39.91 kb)

Tags: , ,

Development

blog comments powered by Disqus

Copy workspaces in Team Foundation Server

by Viktor 1. October 2009 14:20

With the command line tools shipped with Team Explorer you can copy a workspace from one user to another user, very useful when a new developer joins a existing project.

On the computer you want to copy the workspace to just open a Visual Studio Command Prompt and enter the following code

tf workspace /new /template:[Name of workspace to copy];[Domain]\[User Account] /server:[Server]

If you don't remember the name of the workspace you can always run the following command to list all workspaces for a specific user

tf workspaces /owner:[Domain]\[User Account] /server:[Server]

Tags: ,

Development

blog comments powered by Disqus

CodePad.NET

by Viktor 23. September 2009 21:31

CodePad.NET is a new tool that has been released over at CodePlex. It's a tool used to quickly test small segments of code without the need to start a new Visual Studio project. It currently only supports C# and .NET 2.0 and 3.5 but support for .NET 4.0 and VB.NET will be added.

Here is a screen shot of the main code window of CodePad.NET
CodePad.NET

Hopefully a stable release will be ready in the next two weeks.

Tags: , ,

blog comments powered by Disqus

Creative Commons License
This work is licensed under a Creative Commons Attribution-Share Alike 2.5 Sweden License.


Welcome to the Dropit blog!

Here we, the people that work at Dropit, will write about stuff that interests us. For example web development, especially with .NET and EPiServer - but we'll also talk about other techniques that interest us, marketing on the web, social phenomenons, pop culture, games and software development in general.