Blog posts by Viktor Gars2010-11-11T10:40:00.0000000Z/blogs/Viktor-Gars/Optimizely WorldUse Fiddler to monitor IIS requesthttp://labs.dropit.se/blogs/post.aspx?id=b8b3203f-5d16-43fd-aae3-ba04bbd884a92010-11-11T10:40:00.0000000Z<p>
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.<br />After an hour of searching and not finding anything I liked I come upon a blog post (<a href="http://www.markhneedham.com/blog/2009/06/24/using-fiddler-with-iis/">using-fiddler-with-iis</a>) 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!
</p>Move content to another language in EPiServerhttp://labs.dropit.se/blogs/post.aspx?id=d39de884-1a3d-4e4e-97d6-22d11eade25b2010-11-01T10:25:00.0000000Z<p>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.</p>
<p>With the help of this blog post, <a href="http://andrewgunn.blogspot.com/2008/01/change-page-language-in-episerver.html">http://andrewgunn.blogspot.com/2008/01/change-page-language-in-episerver.html</a>, we ware able to solve the problem.<br />
The first thing we did was enable Norwegian in Admin mode.<br />
Then we ran the following SQL script in the EPiServer database,</p>
<pre class="language-csharp"><code>
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
</code></pre>
The importent parts here are
<pre class="language-csharp"><code>
SET @OriginalLanguageBranchID = Y
SET @NewLanguageBranchID = Z
SET @NewLanguageID = 'NO'
</code></pre>
<p>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'.<br />
Run this script in your EPiServer database to move all content from the old language to the new language.<br />
All information you need can be found in the table tblLanguageBranch,</p>
<pre class="language-csharp"><code>
SELECT pkID, LanguageID FROM tblLanguageBranch
</code></pre>
<p>
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.
</p>
<p>
Finished! The site had now been transformed from a English site to a Norwegian one.
</p>Kill all connections to a database in SQL Serverhttp://labs.dropit.se/blogs/post.aspx?id=3790ace3-3fb7-4bf3-a1aa-d808f2b143eb2010-05-19T08:32:00.0000000ZWhen 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,
<pre class="language-csharp"><code>
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)
</code></pre>Blog Page Provider for EPiServer CMShttp://labs.dropit.se/blogs/post.aspx?id=640702c5-d5a2-473f-b7d0-db393cebbcef2010-03-25T15:04:00.0000000Z<p>
Blog Page Provider is a example project I have created to try out the page provider in EPiServer
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
Support for different blog types is handled by blog providers, right now there are two providers, one for <a href="http://www.blogger.com">Blogger</a> and the other for <a href="http://www.wordpress.com">WordPress</a>. 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.
</p>
<p>
You can find more information about the project and all code over at Codeplex, <a href="http://bpp.codeplex.com/">Blog Page Provider</a>
</p>
<p>
Have fun with it!
</p>
<p>
Blog Post Provider in three short steps
</p>
<p>
Blog post both in EPiServer and on Blogger<br />
<img src="http://labs.dropit.se/blogs/image.axd?picture=2010%2f3%2fStep1.jpg" alt="" /><br />
Create a new blog post in EPiServer<br />
<img src="http://labs.dropit.se/blogs/image.axd?picture=2010%2f3%2fStep2.jpg" alt="" /><br />
The created blog post is shown both in EPiServer and on blogger<br />
<img src="http://labs.dropit.se/blogs/image.axd?picture=2010%2f3%2fStep3.jpg" alt="" /><br />
</p>Speed up your RDP connectionhttp://labs.dropit.se/blogs/post.aspx?id=b83a30a2-7b09-4752-863d-2a79bf3636222010-03-20T08:56:00.0000000Z<p>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.</p>
<p>It turns out that all you have to do is run this command as administrator and your will see a very big speed difference,</p>
<p>
<code> netsh interface tcp set global autotuninglevel=disabled </code></p>
<p>
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.
</p>
<p>
I found this solution here, <a href="http://blog.tmcnet.com/blog/tom-keating/microsoft/remote-desktop-slow-problem-solved.asp">Remote Desktop slow problem solved</a></p>Android demohttp://labs.dropit.se/blogs/post.aspx?id=25fbb244-d2c6-41d8-ad51-e4f8f3f4cccf2010-03-17T21:24:00.0000000Z<p>During the last internal tech forum (tech meating 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 :)
</p>
<p>
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.
</p>
<p><a href="http://labs.dropit.se/blogs/file.axd?file=2010%2f3%2fAndroidBlog.zip">AndroidBlog.zip (1.07 mb)</a></p>
<p><img src="http://labs.dropit.se/blogs/image.axd?picture=2010%2f3%2fBlogAndroid.jpg" alt="" /></p>INTRODUCTION TO POSTSHARP - PART 2http://labs.dropit.se/blogs/post.aspx?id=349eacb6-2619-4523-b48c-c074bfb7861b2009-10-14T22:22:00.0000000Z<p>
Time for part two of Introduction to PostSharp, part one can be found here <a href="http://labs.dropit.se/blogs/post/2009/10/12/Introduction-to-PostSharp-Part-1.aspx">Introduction to PostSharp - Part 1</a>.
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.
</p>
<p>
The code for this example can be downloaded here, <a href="http://labs.dropit.se/blogs/file.axd?file=2009%2f10%2fCacheExample.zip">CacheExample.zip (303.53 kb)</a>, you need Visual Studio 2008 and <a href="http://www.postsharp.org/download">PostSharp 1.5 RTM</a> to compile and run the code.
</p>
<p>
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.
</p>
<p>
Lets start with the basic example program we have,
</p>
<pre class="language-csharp"><code>
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;
}
}
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
public static class Cache
{
private static Dictionary<object, object=""> _cache = new Dictionary<object, object="">();
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);
}
}
</object,></object,></code></pre>
<p>
Right now we have everything we need to cache the DoBigCalculation method and we could rewrite the method to look like this,
</p>
<pre class="language-csharp"><code>
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;
}
</code></pre>
<p>
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.
</p>
<p>
Lets begin by creating the caching attribute we want to use,
</p>
<pre class="language-csharp"><code>
[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
{
}
</code></pre>
<p>
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 <a href="http://www.postsharp.org/">PostSharp.org</a>. Lets begin with the Task,
</p>
<pre class="language-csharp"><code>
public class CacheTask : Task, IAdviceProvider
{
protected override void Initialize()
{
}
#region IAdviceProvider Members
public void ProvideAdvices(PostSharp.CodeWeaver.Weaver codeWeaver)
{
}
#endregion
}
</code></pre>
<p>
and then continue with the advice,
</p>
<pre class="language-csharp"><code>
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
}
</code></pre>
<p>
One more thing needs to be added to the project, a file that binds the attribute and Task togerther, the Aspect.Weaver.psplugin file,
</p>
<pre class="language-xml"><code>
<?xml version="1.0" encoding="utf-8" ?>
<plugin xmlns="http://schemas.postsharp.org/1.0/configuration">
<tasktype name="Aspects.Weaver" implementation="Aspects.Weaver.CacheTask, Aspects.Weaver">
<dependency tasktype="CodeWeaver"></dependency>
</tasktype>
</plugin>
</code></pre>
<p>
This plugin file has to be in the bin directory of the example program for it to work.
</p>
<p>
Lets start with the code for the task.
</p>
<pre class="language-csharp"><code>
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<iannotationinstance> 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<methoddefdeclaration>(methodDef), JoinPointKinds.BeforeMethodBody | JoinPointKinds.AfterMethodBodySuccess, null);
}
}
}
</methoddefdeclaration></iannotationinstance></code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
internal IMethod CacheSetMethod;
internal IMethod CacheGetMethod;
internal IMethod CacheGenerateKeyMethod;
internal ITypeSignature ObjectType;
internal ITypeSignature ObjectArrayType;
</code></pre>
<p>
This are variables that will containe methods and types that are needed by the advice.
We override the initialize method to initialize the variables,
</p>
<pre class="language-csharp"><code>
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);
}
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
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));
}
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
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();
}
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
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();
}
</code></pre>
<p>
And to put it all to good use lets update our example program to look like this,
</p>
<pre class="language-csharp"><code>
[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;
}
</code></pre>
<p>
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.
</p>
<p>
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.
</p>
<p>Example program for Part 2: <a href="http://labs.dropit.se/blogs/file.axd?file=2009%2f10%2fCacheExample.zip">CacheExample.zip (303.53 kb)</a></p>Introduction to PostSharp - Part 1http://labs.dropit.se/blogs/post.aspx?id=3f3ecd78-1178-4be9-85de-ce5dc467f9ae2009-10-12T20:06:00.0000000Z<p>
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 <a href="http://en.wikipedia.org/wiki/Aspect_oriented">Wikipeida</a> and <a href="http://www.postsharp.org">www.postsharp.org</a>.
</p>
<p>
The code for this example program can be downloaded here, <a href="http://labs.dropit.se/blogs/file.axd?file=2009%2f10%2fTraceExample.zip">TraceExample.zip (39.91 kb)</a>, you need Visual Studio 2008 and <a href="http://www.postsharp.org/download">PostSharp 1.5 RTM</a> to compile the example.
</p>
<p>
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,
</p>
<pre class="language-csharp"><code>
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;
}
}
</code></pre>
<p>
The program makes a couple of method calls to check if the input data is correct.
</p>
<p>
Now lets create our very simple tracing class,
</p>
<pre class="language-csharp"><code>
public class Trace
{
public static void Write(string message)
{
try
{
StreamWriter sw = new StreamWriter("trace.txt", true);
sw.WriteLine(message);
sw.Close();
}
catch
{
}
}
}
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
}
public override void OnException(MethodExecutionEventArgs eventArgs)
{
}
public override void OnSuccess(MethodExecutionEventArgs eventArgs)
{
}
}
</code></pre>
<p>
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.
</p>
<p>
Here is our aspect with the code needed for the tracing output we want,
</p>
<pre class="language-csharp"><code>
[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));
}
}
</code></pre>
<p>
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.
</p>
<p>
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,
</p>
<pre class="language-csharp"><code>
[Aspects.Trace]
public class Program
{
...
}
</code></pre>
<p>
When we now run this program the trace output may look like this
</p>
<pre class="language-csharp"><code>
[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
</code></pre>
<p>
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.
</p><p>
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 :)
</p><p>
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,
</p>
<pre class="language-csharp"><code>
eventArgs.InstanceTag = DateTime.Now;
</code></pre>
<p>
And at the start of our OnException and OnSuccess methods we add this code,
</p>
<pre class="language-csharp"><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));
</code></pre>
<p>
And when we run the program now we get the following output in our trace,
</p>
<pre class="language-csharp"><code>
[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: ''
</code></pre>
<p>
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,
</p>
<pre class="language-csharp"><code>
eventArgs.FlowBehavior = FlowBehavior.Continue;
</code></pre>
<p>
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.
</p><p>
I would highly recomand that you check the compiled code with <a href="http://www.red-gate.com/products/reflector/index.htm">Red Gates Reflector</a> 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.
</p>
<p>
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, <a href="http://labs.dropit.se/blogs/post/2009/10/14/Introduction-to-PostSharp-Part-2.aspx">Introduction to PostSharp - Part 2</a>
</p>
<p><a href="http://labs.dropit.se/blogs/file.axd?file=2009%2f10%2fTraceExample.zip">TraceExample.zip (39.91 kb)</a></p>Copy workspaces in Team Foundation Serverhttp://labs.dropit.se/blogs/post.aspx?id=16e22e5b-8154-4839-bf02-fdcfad94302f2009-10-01T15:20:00.0000000Z<p>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.</p>
<p>
On the computer you want to copy the workspace to just open a Visual Studio Command Prompt and enter the following code
</p>
<code csharp="">
tf workspace /new /template:[Name of workspace to copy];[Domain]\[User Account] /server:[Server]
</code>
<p>
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
</p>
<code csharp="">
tf workspaces /owner:[Domain]\[User Account] /server:[Server]
</code>
<img src="http://labs.dropit.se/blogs/image.axd?picture=2009%2f10%2fImage3.jpg" alt="" />CodePad.NEThttp://labs.dropit.se/blogs/post.aspx?id=42ed4ba4-0a8c-4992-b3fd-67bfb84925732009-09-23T22:31:00.0000000Z<p><a href="http://codepadnet.codeplex.com">CodePad.NET</a> is a new tool that has been released over at <a href="http://www.codeplex.com/">CodePlex</a>. 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.</p>
<p>
Here is a screen shot of the main code window of CodePad.NET
<br /><img src="http://labs.dropit.se/blogs/image.axd?picture=2009%2f9%2fcodepad.jpg" alt="CodePad.NET" />
</p>
<p>Hopefully a stable release will be ready in the next two weeks.</p>
MSBuild error 5004http://labs.dropit.se/blogs/post.aspx?id=bcd3810e-086c-4fef-90e8-1cce0e668b392009-02-09T09:41:00.0000000ZCame back to work this morning to find that four automatic builds had failed during the weekend with the error message "MSB5004" which apperently mean "Error parsing the nested project section in solution file".<br />I had done a merge with a release branch back to the development branch late on friday with no problems and been able to build all projects without errors in Visual Studio but apperantly MSBuild reads the .sln file differently from Visual Studio 2005.<br />To solve my problem I compared my .sln files from before the merge and after the merge and found that one of the GUID's in the "NestedProjects" section had been changed so I just changed it back and all was good again.<br />I also found another solution to the problem over at <a href="http://blog.ashleyangell.com/index.php/2008/09/msbuild-error-msb5009/" target="_blank" title="Accumulative Effect">Accumulative Effect</a>.Finding the page id for a property value in Extension 2.6http://labs.dropit.se/blogs/post.aspx?id=747f92f6-c9e1-479c-8c37-3695692d83c02009-01-27T08:46:00.0000000ZToday I had to find on what page in EPiServer a Extension property string was set to a specific value and to do this I had to write four select statements to match fkID and pkID all the way up to the table extVariantData witch holds the holy grail, the EPiServerPageID. As this was not the first time this happend I wrote this stored procedure to make this task easyer.
This stored procedure is only tested on EPiServer 4.62 and Extension 2.6 and will not work with EPiServer CMS and X3.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css"><![CDATA[
.csharpcode, .csharpcode pre
{
color: #eee8aa;
font-family: Consolas, "Courier New", Courier, Monospace;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .kwrd { color: #40c4ff; }
]]></style>
<pre class="language-csharp"><code>
<span class="kwrd">CREATE</span> <span class="kwrd">PROCEDURE</span> FindExtensionData
@BooleanValue <span class="kwrd">BIT</span>=<span class="kwrd">NULL</span>,
@Number <span class="kwrd">INT</span>=<span class="kwrd">NULL</span>,
@FloatNumber <span class="kwrd">FLOAT</span>=<span class="kwrd">NULL</span>,
@PageType <span class="kwrd">INT</span>=<span class="kwrd">NULL</span>,
@PageLink <span class="kwrd">INT</span>=<span class="kwrd">NULL</span>,
@StringValue NVARCHAR(1500)=<span class="kwrd">NULL</span>,
@LongString NTEXT=<span class="kwrd">NULL</span>,
@DateValue DATETIME=<span class="kwrd">NULL</span>
<span class="kwrd">AS</span>
<span class="kwrd">BEGIN</span>
<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>;
<span class="kwrd">SELECT</span> EPiServerPageID
<span class="kwrd">FROM</span> extVariantData
<span class="kwrd">WHERE</span> ID <span class="kwrd">IN</span>
(
<span class="kwrd">SELECT</span> VariantID
<span class="kwrd">FROM</span> extInnerTemplateData
<span class="kwrd">WHERE</span> ID <span class="kwrd">IN</span>
(
<span class="kwrd">SELECT</span> InnerTemplateDataID
<span class="kwrd">FROM</span> extContentBlockData
<span class="kwrd">WHERE</span> ID <span class="kwrd">IN</span>
(
<span class="kwrd">SELECT</span> ContentBlockDataID
<span class="kwrd">FROM</span> extPropertyData
<span class="kwrd">WHERE</span>
(@BooleanValue <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> BooleanValue=@BooleanValue) <span class="kwrd">AND</span>
(@Number <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> Number=@Number) <span class="kwrd">AND</span>
(@FloatNumber <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> FloatNumber=@FloatNumber) <span class="kwrd">AND</span>
(@PageType <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> PageType=@PageType) <span class="kwrd">AND</span>
(@PageLink <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> PageLink=@PageLink) <span class="kwrd">AND</span>
(@StringValue <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> StringValue <span class="kwrd">LIKE</span> @StringValue) <span class="kwrd">AND</span>
(@LongString <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> LongString <span class="kwrd">LIKE</span> @LongString) <span class="kwrd">AND</span>
(@DateValue <span class="kwrd">IS</span> <span class="kwrd">NULL</span> <span class="kwrd">OR</span> DateValue=@DateValue)
)
)
)
<span class="kwrd">END</span>
GO</code></pre>
If you want to search a string just call the SP with the @StringValue or @LongString set to the string your looking for (you can use wildcards). If you look for a function pointing to a specific page call the SP with the @PageLink argument set to the EPiServer PageID.
Unit testing with Pexhttp://labs.dropit.se/blogs/post.aspx?id=f34f6d56-41be-4a70-ab82-058af6d84e2f2008-11-27T08:12:00.0000000Z<p>
</p>
<p>
Something that may not be as cool or fun making as a new web 2.0 function is making unit tests for that function, but they are just as important to help make sure that your function still works after one year when you rewrite some dependent code in your project.
</p>
<p>
Microsoft is trying to help us developers with this with their new Pex project that will be included in Visual Studio 2010. Pex is a system that will help you automatically generate unit tests for your code.
</p>
<p>
Pex will generate all this tests after analyzing the code as deep as it can and the limit today is that it can only analyze managed code.
</p>
<p>
</p>
<p>
A very quick example of how Pex can be used,
</p>
<p>
</p>
<p>
<img src="http://labs.dropit.se/blogs/image.axd?picture=2008%2f11%2fSimpleCode.gif" alt="" />
</p>
<p>
We have two classes, a data class named User with one public constructor that takes a username and then we have the static class with one method, IsUser to test if a user object is the specified user.
</p>
<p>
If we now want to run unit tests on the IsUser method we right click in the method and select "Run Pex Explorations"
</p>
<p>
<img src="http://labs.dropit.se/blogs/image.axd?picture=2008%2f11%2fRunPexTests.gif" alt="" />
</p>
<p>
Pex will then analyze your code (and underlying code called by your code) and find out how many different test cases there need to be to test the code.
</p>
<p>
When Pex is finished with this it will show a result window with the tests and results
</p>
<p>
<img src="http://labs.dropit.se/blogs/image.axd?picture=2008%2f11%2fGeneratedTests.gif" alt="" />
</p>
<p>
Here we can see that Pex found two paths through our code. Once with null as the user, this did not work, and once with a user object. Pex will also try to suggest a fix for your code if it fails a test. This was something that did not always work during my tests of Pex as it sometimes generated uncompilable code. From here you can save these generated tests in a test project, send a e-mail to the colleague responsible for the bug or add it as a work item in Team Foundation Server.
</p>
<p>
This has just been a very quick introduction to what Pex has to offer, for more information I suggest a visit to the Pex project page at http://research.microsoft.com/pex/.
</p>
<p>
Right now there are two preview versions of Pex released, one for Visual Studio 2010 that you can use on commercial projects and then a version for Visual Studio 2008 that can only be used for none commercial projects.
</p>