Friday, November 15, 2013

Mongo Style Capped Collections In MS SQL Server

Suppose you want to store temporary data in MS SQL Server. In general it's not a good practice because of relatively low performance. Although when expected load isn't too high it can be desirable. The lifetime of each piece of data is short but you create the pieces often therefore you need to find a way of keeping the size of the storage small.

One possible way of solving this is removing multiple outdated records over a time interval using SQL Server Agent or a special console app launched as a service. The downside here is that we introduce one extra dependency which needs to be configured on every server you want to use your storage.

The second option is using a special data structure which prevents your storage from overflow. Mongo capped collections (which are basically circular buffers) are a good example of this kind of data structures. Unfortunately there is no anything similar in MS SQL Server but it's rather easy to build one.

Let's start by defining a table:

CREATE TABLE [dbo].[Storage] (
 [Key] int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Data] varbinary(MAX),
 [Version] rowversion NOT NULL
)

Retrieving data from storage by key should be fast so a clustered index on the Key column is very handy here. We can create an index on rowversion to find records quickly during updates but this should be done carefully. The amount of reads and writes is almost the same so while we gain some performance boost on reads we lose it on writes.

Next we should decide should we initialize the storage with empty values or not. I think it is beneficial because if the entire storage is filled we won't have to choose between an update and insert operation every time we want to store some bytes. We simply update the oldest record with a new value.

declare @MaxItems int = 999,
 @ItemIndex int = 0
while @ItemIndex < @MaxItems
begin
 INSERT INTO [dbo].[Storage] ([Data]) VALUES(null)
 SET @ItemIndex = @ItemIndex + 1
end

This will populate our storage with empty data. We use auto increment for simplicity; in fact keys can be generated whatever we want.

Last but not least we need to create a mechanism for inserting data to the storage. The following stored procedure will do the job:

CREATE PROCEDURE [dbo].[insertToStorage]
 @Data varbinary(MAX)
AS
BEGIN
 UPDATE [dbo].[Storage] WITH (UPDLOCK, READPAST)
 SET [Data] = @Data
 OUTPUT inserted.[Key]
 WHERE [Version] = (SELECT MIN([Version]) FROM [dbo].[Storage])
END

Thursday, October 31, 2013

Fixing Interface Segregation Principle Violations And Applying Dependency Injection Using Unity

Following SOLID principles in object oriented design is a good thing. It allows you to produce more maintainable and testable code.

Let's look at a common situation when the "I" letter principle in SOLID acronym is violated. Assume you have an application that reads various settings from different places (app.config, database, service etc.). For that purpose one can create a class that encapsulates all settings access logic along with some data type conversions. The class is then injected as a dependecy according to Dependency Inversion principle (letter "D" in SOLID acronym). Here is an example that is written on C# and uses Unity as an IoC container:

public static void Main()
{
 var container = new UnityContainer();
 container.RegisterType<ISiteSettings, SiteSettings>();
 container.RegisterType<EmailSender>();
 container.RegisterType<TitlePrinter>();
 container.RegisterType<ApplicationCache>();

 container.Resolve<EmailSender>().SendEmail();
 container.Resolve<TitlePrinter>().Print();
 container.Resolve<ApplicationCache>().Insert();
 Console.ReadKey();
}

First we register the settings and some helpers which use them: EmailSender, TitlePrinter and ApplicationCache. Right after the registration we resolve helpers and trigger their methods. Our IoC container injects the settings automaticaly so we don't have to resolve them and pass to helper classes manually. Here is what we have so far for settings:

public class SiteSettings : ISiteSettings
{
 public SiteSettings()
 {
  Console.WriteLine("----------New Site settings instance created------------");
 }

 public int CacheTimeoutMinutes
 {
  get { return 1; }
 }

 public string Title
 {
  get { return "My awesome site"; }
 }

 public string EmailSenderName
 {
  get { return "Vasya"; }
 }

 public string EmailSenderAddress
 {
  get { return "vasya@domain.com"; }
 }
}

public interface ISiteSettings
{
 int CacheTimeoutMinutes { get; }

 string Title { get; }

 string EmailSenderName { get; }

 string EmailSenderAddress { get; }
}

The helpers are all basicaly the same so I'm going to put only EmailSender here:

public class EmailSender
{
 private readonly ISiteSettings m_settings;

 public EmailSender(ISiteSettings settings)
 {
  m_settings = settings;
 }

 public void SendEmail()
 {
  Console.WriteLine(
   "Email is sent by {0} from {1}",
   m_settings.EmailSenderName,
   m_settings.EmailSenderAddress);
 }
}

The settings are injected via constructor and stored in m_settings field. I'm sure constructors are the best place to perform injection because of three things:
1. There is no way of missing a dependency as you have to explicitly specify all of them during creation.
2. It is clear for the class consumer what this class relies on.
3. If you find yourself using huge constructors it's time to refactor the class in order to follow Single Responsibility principle (letter "S" in SOLID acronym).
 Once we call SendEmail method we simply print a message based on m_settings. It's just an example and in a real app you would place some valuable code there. Let's look at the output:

----------New Site settings instance created------------
Email is sent by Vasya from vasya@domain.com
----------New Site settings instance created------------
My awesome site
----------New Site settings instance created------------
Item inserted into cache. Duration is set to 1

Good news is that the app works. However we have a few problems here. First of all ISiteSettings interface contains all application settings and helper classes are forced to pull all its members even if they don't need all of them. This is a typical violation of Interface Segregation principle and the code quickly becomes messy as application grows. Second we create a separate instance of SiteSettings on every injection which is redundant.

In order to address the first problem let's group all the settings by their purpose and split the ISiteSettings interface into three smaller ones:

public interface IAppearanceSettings
{
 string Title { get; }
}
 
public interface ICacheSettings
{
 int CacheTimeoutMinutes { get; }
}

public interface IEmailSettings
{
 string EmailSenderName { get; }

 string EmailSenderAddress { get; }
}

public class SiteSettings : IEmailSettings, ICacheSettings, IAppearanceSettings
{
 ...
}

Now for every helper class we have to provide only what it really needs.

public class EmailSender
{
 private readonly IEmailSettings m_settings;

 public EmailSender(IEmailSettings settings)
 {
  m_settings = settings;
 }

 public void SendEmail()
 {
  Console.WriteLine(
   "Email is sent by {0} from {1}",
   m_settings.EmailSenderName,
   m_settings.EmailSenderAddress);
 }
}

We've just fixed Interface Segregation principle violation. However we still create an instance of SiteSettings class every time we resolve a dependency. One possible way of solving the problem is to create a separate instance of SiteSettings and specify it during registration phase like this:

var container = new UnityContainer();
SiteSettings settings = new SiteSettings();
container.RegisterType<IEmailSettings>(new InjectionFactory(i => settings));
container.RegisterType<IAppearanceSettings>(new InjectionFactory(i => settings));
container.RegisterType<ICacheSettings>(new InjectionFactory(i => settings));

This will work. However you have to create the instance at the very begining of your application lifetime. In some circumstances it might not be a desirable option (for example if you wan't to perform a lazy initialization). In this case you could use the following approach:

var container = new UnityContainer();
container.RegisterType<SiteSettings>(new CustomLifetimeManager());
container.RegisterType<EmailSender>(new InjectionConstructor(new ResolvedParameter<SiteSettings>()));
container.RegisterType<TitlePrinter>(new InjectionConstructor(new ResolvedParameter<SiteSettings>()));
container.RegisterType<ApplicationCache>(new InjectionConstructor(new ResolvedParameter<SiteSettings>()));

Pay attention for SiteSettings registration as we pass an instance of CustomLifetimeManager there. Also we have to explicitly tell IoC container that we wan't to inject SiteSettings instance to our helpers. By default Unity tries to find registrations for IEmailSettings, IApprearanceSettings and ICacheSettings and fails (as we did not register anything with these types). Here is what CustomLifetimeManager looks like:

public class CustomLifetimeManager : LifetimeManager
{
 private object m_value;

 public override object GetValue()
 {
  return m_value;
 }

 public override void SetValue(object newValue)
 {
  m_value = newValue;
 }

 public override void RemoveValue()
 {
  m_value = null;
 }
}

We use m_value field to save SiteSettings instance and then return it when necessary. In web apps we could store the value somewhere else (in HttpContext for example). And here is the final console output:

----------New Site settings instance created------------
Email is sent by Vasya from vasya@domain.com
My awesome site
Item inserted into cache. Duration is set to 1

All the code mentioned above can be found in my github repository. Hope this helps.

Wednesday, July 17, 2013

How to stop, start or restart IIS site on a remote machine with powershell

Sometimes during your script execution you need to shut down a site, do some work and start it again. Powershell remoting is a good way to go. To allow a script to be executed on a remote machine you have to log on there as an administrator and run the following command: 

winrm quickconfig

Notice that confirmation is required. After that you should be able to run your commands on a remote machine. First approach is straightforward. You can stop entire IIS server and then start it again. Like this:

Invoke-Command -ComputerName $targetServer -ScriptBlock {iisreset /STOP}
# do your stuff here
Invoke-Command -ComputerName $targetServer -ScriptBlock {iisreset /START}

where $targetServer is a variable that contains the name of the server. To restart the server use iisreset without arguments. This approach is fine for a single site on a server. On the other hand you probably don't want to shut down all sites on a server. In this case you can stop a few like this:

Invoke-Command 
-ComputerName $targetServer 
-ScriptBlock {import-module WebAdministration; Stop-Website $args[0]; Stop-Website $args[1]} 
-ArgumentList @($site1, $site2)

where $site1 and $site2 are the sites you want to stop. To start the sites use a similar command:

Invoke-Command 
-ComputerName $targetServer 
-ScriptBlock {import-module WebAdministration; Start-Website $args[0]; Start-Website $args[1]} 
-ArgumentList @($site1, $site2)

Hope this helps.

Friday, June 21, 2013

Config transformations without msbuild

In my previous post I was writing about automation of build and deployment process. I used SlowCheetah to transform all configuration files (not just web.config) in a solution and some powershell scripting to push the result to target server.

In this post I'm going to tell you how transformation can be done without triggering a build. This might be helpful when you want to send result files for review before deployment.

I would like to thank Outcoldman and AlexBar as their discussion led me to this solution. Consider the following lines:

using Microsoft.Web.XmlTransform;

namespace ConfigTransformer
{
    public class Program
    {
        public static void Main(string[] args)
        {
            string sourceFile = args[0];
            string transformFile = args[1];
            string resultFile = args[2];

            var transformation = new XmlTransformation(transformFile);
            var transformableDocument = new XmlTransformableDocument();
            transformableDocument.Load(sourceFile);
            transformation.Apply(transformableDocument);
            transformableDocument.Save(resultFile);
        }
    }
}

I get input data from command line arguments and use Microsoft.Web.XmlTransform.dll library to perform transformations here. Now it's time to develop this app to a real life solution. Here is the list of arguments we need:
1. Source folder. A path to solution's directory (I want to transform all files in my solution);
2. Destination folder. The app should place transformed files here;
3. Build configuration name. There might be more than one transformation files for each config file (e.g. Release, Debug). It this case it is reasonable to specify which one to use.
4. A list of configuration files to skip. This should be an optional parameter.

Here is new code:

namespace ConfigTransformer
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args == null || args.Length < 3)
            {
                return;
            }

            var transformer = new SolutionConfigsTransformer(args[0], args[1], args[2]);
            if (args.Length > 3)
            {
                for (int i = 3; i < args.Length; i++)
                {
                    transformer.FilesToExclude.Add(args[i]);
                }
            }

            transformer.Transform();
        }
    }
}

In Main I validate input parameters and pass them to SolutionConfigTransformer. This class encapsulates transformation logic and exposes one method Transform(). Here is it's implementation:

public void Transform()
{
    if (!IsInputValid())
    {
        return;
    }

    IList<ConfigurationEntry> configurationEntries = GetConfigurationEntries();
    foreach (ConfigurationEntry entry in configurationEntries)
    {
        var transformation = new XmlTransformation(entry.TransformationFilePath);
        var transformableDocument = new XmlTransformableDocument();
        transformableDocument.Load(entry.FilePath);
        if (transformation.Apply(transformableDocument))
        {
            if (!string.IsNullOrWhiteSpace(entry.FileName))
            {
                var targetDirecory = Path.Combine(TargetDirectory, entry.ParentSubfolder);
                Directory.CreateDirectory(targetDirecory);
                transformableDocument.Save(Path.Combine(targetDirecory, entry.FileName));
            }
        }
    }
}

After some validation I get a list of configuraion files along with corresponding transformations and do almost the same thing as at the beginning (with some extra System.IO function calls). As you may guess GetConfigurationEntries() method plays a key role in program flow.

private IList<ConfigurationEntry> GetConfigurationEntries()
{
    string[] configs = Directory.GetFiles(SourceDirectory, "*.config", SearchOption.AllDirectories);
    var result = new List<ConfigurationEntry>();
    if (configs.Length == 0)
    {
        return result;
    }

    int i = 0;
    while (i < configs.Length - 1)
    {
        string config = configs[i];
        string transformation = configs[i + 1];
        var regex = new Regex(BuildSearchPattern(config.Remove(config.Length - 7, 7)), RegexOptions.IgnoreCase);
        bool found = false;
        while (regex.IsMatch(transformation))
        {
            Match match = regex.Match(transformation);
            if (IsTransformationFound(match) && !found)
            {
                found = true;
                if (FilesToExclude.Contains(config))
                {
                    m_logger.InfoFormat("{0} is in a black list. Won't be processed", config);
                }
                else
                {
                    var entry = new ConfigurationEntry
                    {
                        FilePath = config,
                        FileName = Path.GetFileName(config),
                        ParentSubfolder = GetParentSubfolder(config),
                        TransformationFilePath = transformation
                    };
                    result.Add(entry);
                }
            }

            i++;
            if (i < configs.Length - 1)
            {
                transformation = configs[i + 1];
            }
            else
            {
                break;
            }
        }

        i++;
    }

    return result;
}

I get all *.config files in a source directory. Then I iterate through the list searching for configuration files that have transformations. My main assumption here is that transformations are right after their configuration file in the list; for instance:

connectionStrings.config
connectionStrings.Relese.config
connectionStrings.Debug.config
Web.config
Web.Release.config
Web.Debug.config
Web.Test.config

I understand it's not a 100% accurate way. But it's rather simple and does what I need. I use regular expressions here to check transformations against particular pattern. If they match then use pattern's named group to check against build configuration name. If it matches too then put the configuration with the corresponding transformation to the result list.

It's just a high level description of my approach. If you are interested feel free to download the sources from my github repository.

Monday, April 22, 2013

Config Files Transformation And Web Application Deployment

My quest started with an innocent thought: "what if I had configuration file transformations instead of managing files via xcopy?" Previously I had already had some experience with this .net feature during my open source project deployment to appharbor. I didn't expect any difficulties mainly because the technology is rather straightforward and effective.

First I noticed that some web.config sections are extracted into separate files. So I tried implementing all the transforms in the web.release.config (with no luck of course). Each separate file required it's own transformation. That's how I met SlowCheetah. I read great introduction for the tool and realized that simply installing the VS extension is not enough - a way of propagating the MSBuild tasks to the build server is required. Fortunately here I found a detailed explanation.

At this point I had a few simple connection string transformations and all required changes in project files and  nuget packages. I triggered a build and examined the contents of the _PublishedWebsites folder in my build drop location. The transformations wasn't applied. After some searching I realized that this is exactly how it should work. Transformations are applied only when publishing the site. I added an extra argument /p:DeployOnBuild=True to the MSBuild call from my build definition and got packaged web sites in the _PublishedWebsites folder. All the transformations were in place this time. And here real troubles started.

The output of MSBuild with /p:DeployOnBuild=True is basically a zip archive with rather tricky hierarchy. The desired published site lied deep inside the archive and some of its folders were build version specific (dynamic). I realized that working with the package using common tools was something considered wrong.

The first and the most obvious solution was using generated sitename.deploy.cmd file to deploy the package. The file used MSDeploy internally and required the tool to be installed and configured on all target environments. By that time I had all my builds set up and running with powershell xcopy-style deployment strategy (which is generally speaking wrong). So I decided that it was too much work to redesign all the stuff just because of config transformations and continued searching.

What if I could extract the contents of the package with msdeploy and put it into shared location? I wrote this:
"c:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package=c:\Share\mysite.zip -dest:contentPath=\\my-pc\Share\Test -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension
and got the error
Error: Source (sitemanifest) and destination (iisApp) are not compatible for the given operation.
I didn't find the solution for the problem. Here are some links that might help: onetwo. Please notice the solution from the latter one:
This occurs because the iisApp provider specified in the destination argument is not expecting a Manifest.xml file in the source. To resolve this issue, use the auto provider instead
But "auto" - was actually the same as my first attempt, so no luck here.

The third option was triggering SlowCheetah during a build, replacing configuration files in _PublishedWebsites explicitly. The approach was described here in more detail. Unfortunately I noticed that all configuration files was locked during a build. I found one possible workaround here but wasn't excited with it at all.

At this point I decided to work with mysite.zip package using powershell. I had already had some powershell deployment steps by that time so I thought it wouldn't be too much overhead to add another one.

Here I'm going to show you two auxiliary powershell functions I used to achieve the goal. They are rather simple and are assembled from various pieces found in the Internet here and there.

# Copy published site from deployment package to destination folder
function Copy-PublishedSite
{
    param($zipFileName, $destination)
    if (!(Test-Path $zipFileName)) 
    {
        Throw "Deployment package is missing"
    }
    $shell = new-object -com shell.application
    Get-ZipChildFolders $shell.Namespace($zipFileName).Items()
}
The function above performs input check (destination check is omitted) and calls recursive search function:

# Search for published site inside a deployment package
function Get-ZipChildFolders
{
    param([object]$items) 
    $containerName = "PackageTmp"
    foreach($item in $items) 
    {
        if (($item.IsFolder -eq $true) -and ($item.Name -eq $containerName))
        {
            $shell.NameSpace($destination).CopyHere(($item.getfolder.Items()), 0x14)
            return
        }
        else 
        {
            if($item.getfolder -ne $null)
            {    
                Get-ZipChildFolders $item.getfolder.items()
            }
        }   
    } 
}
This function traverses archive hierarchy tree searching for "PackageTmp" folder which is assumed to be a container for a published site. If folder is found the function copies its contents to the destination folder.

These functions worked fine in command prompt window but they failed during TFS build. CopyHere wasn't copying anything and didn't throw any errors. I didn't manage to make it work. Instead I decided using command line version of 7zip. Here is the code I got:

# Copy published site from deployment package to destination folder
function Copy-PublishedSite
{
    param($zipFile, $currentSite)
    Print-LogMessage "Copying zip package..."
    Copy-Item $zipFile.FullName $deploymentFolder
    Print-LogMessage "Unzipping package..."
    $tempFolder = join-path $deploymentFolder "tmp"
    $tempZip = join-path $deploymentFolder $zipFile.Name
    & $zipUtilityPath x $tempZip ("-o" + $tempFolder) -aoa -r
    Print-LogMessage "Creating target directory..."
    $targetPath = Join-Path (Join-Path $deploymentFolder "MySitesFolder") $currentSite 
    Create-DirectoryStructure $targetPath
    Print-LogMessage "Moving package contents to target directory..."
    $moveFolder = Get-ChildItem $tempFolder -filter "PackageTmp" -r
    Move-Item (join-path $moveFolder.FullName "*") $targetPath -force
    Print-LogMessage "Deleting temp data..."
    Remove-Item $tempFolder -force -r
    Remove-Item $tempZip -force
}

Although it's not a complete solution but the main idea is quite clear. First I copied the entire package to deployment folder (transfering an archive as a single file over the network is faster). Then I unzipped the contents of the package to the temporary folder "tmp" (folder already exists check is ommited). Then I copied the contents of "PackageTmp" subfolder into my sites directory. Finally I did some cleanup.

With the approach above I keep my existing powershell deployment strategy and have all config transformation features I need. I realize this solution is far from ideal and someday I'll have to move to msdeploy. But right now I don't see a strong reason for doing that.

Friday, February 15, 2013

WCF Behaviors. Wildcard name or empty string name.

Few days ago I found a WCF behavior configuration section similar to this:

<behaviors>
    <endpointBehaviors>
        <behavior name="*">
     <webHttp/>
        </behavior>
    </endpointBehaviors>
</behaviors>

Does name="*" make any sense? .net 4 comes with great impovements in default configurations. And if one wants to specify a behavior for all endpoints then behavior with an empty name (or even without the name attribute at all) is used. So what about wildcards? Usually an asterics is used for exactly the same purpose. What is the difference then? This question marked as answered actually doesn't contain the answer.

First of all we can use an asterics as an argument during channel factory creation.

var factory = new ChannelFactory<IMyService>("*");

In this case first endpoint configuration is taken. The feature is described in more details here.
It has nothing to do with behaviors.

In the configuration above the "*" symbol acts as a regular behaviorConfiguration name. You can reference it in any of our endpoints.

<endpoint 
    address="http://localhost:9001/http/" 
    contract="Shared.IMyService" 
    binding="basicHttpBinding" 
    behaviorConfiguration="*"/>

But if you want apply a behavior to all of your enpoints just use an empty name for it.

<behavior name="">
    <webHttp/>
</behavior>

Or even

<behavior>
    <webHttp/>
</behavior>

You can read more about WCF configuration defaults in this article.

Thursday, February 14, 2013

WCF. Fighting your way through a proxy.

Recently I discovered that my desktop tool for memorizing English words doesn't work when a client is behind a proxy.

The problem was pretty common and I quickly found this awesome answer on stackoverflow. But still there were a few things to deal with:
1. Move all those hardcoded strings to config files.
2. Define proxy usage per binding.
3. Test the application.

First might be easily accomplished using ConfigurationManager class. Here is the code I got:

using System;
using System.Configuration;
using System.Net;
using log4net;

namespace VX.Desktop.Infrastructure
{
    public class CustomProxy : IWebProxy
    {
        private const string CustomProxyAddressKey = "CustomProxyAddress";
        private const string CustomProxyUserKey = "CustomProxyUser";
        private const string CustomProxyPassword = "CustomProxyPassword";

        private readonly ILog logger = LogManager.GetLogger(typeof (CustomProxy));
        
        public Uri GetProxy(Uri destination)
        {
            var proxyAddress = ConfigurationManager.AppSettings[CustomProxyAddressKey];
            if (string.IsNullOrEmpty(proxyAddress))
            {
                logger.Error("Error retrieving CustomProxyAddress from configuration file. Make sure you have a corresponding key in appSettings section of application config file.");
                return null;
            }
            
            logger.InfoFormat("Proxy address: {0}", proxyAddress);
            return new Uri(proxyAddress);
        }

        public bool IsBypassed(Uri host)
        {
            logger.InfoFormat("IsBypassed for host: {0} is false", host);
            return false;
        }

        public ICredentials Credentials
        {
            get
            {
                logger.InfoFormat("Getting proxy credentials");
                string userName = ConfigurationManager.AppSettings[CustomProxyUserKey];
                string password = ConfigurationManager.AppSettings[CustomProxyPassword];
                logger.InfoFormat("Done. {0}", userName);
                if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
                {
                    logger.Error(
                        "Error retrieving proxy credentials from configuration file. Make sure you have corresponding keys in appSettings section of application config file.");
                }

                return new NetworkCredential(userName, password);
            }
            set { }
        }
    }
}

Nice ways of addressing the second and the third issues are described here.
So we should use something like:

<bindings>
    <basicHttpBinding>
        <binding name="myBindingWithProxy" useDefaultWebProxy="true" />
    </basicHttpBinding>
</bindings>
instead of
<defaultProxy enabled="true" useDefaultCredentials="false">
  <module type = "SomeNameSpace.MyProxy, SomeAssembly" />
</defaultProxy>

Now we can use other bindings without any proxies. And to test all this stuff we can use a great tool - Fiddler. Just check the Rules -> Require Proxy Authentication option and you're done.

Monday, January 14, 2013

Using the Razor engine for rendering email templates

The problem. You want to have a flexible infrastructure to manage emails that are sent to users automatically.
The solution. First thing to deal with is making a text of an email body configurable. Hardcoding a template is not a good idea unless you want to recompile and redeploy your application each time you need to change the text. Templates can be stored in a database or in files, the exact solution depends on the situation. Second you need to find a way to insert a dynamic content to your emails. In this case you have to use placeholders in body text and replace them somewhere in the code.

Hello #FirstName# #LastName#!

string.Replace("#FirstName#", "Evgeny");
string.Replace("#LastName#", "Skurikhin");

This is an appropriate way only for very simple templates. But what if you need to render a table and you don't know the number of rows beforehand (only during runtime)? Well we still can use some kind of markers in email body. And what about conditional rendering? Hmm... another markers. But why reinvent the wheel? Let's use Razor.

After some research I found that the problem is quite common, there are multiple libraries that address the issue. RazorEngineRazorTemplates and RazorMachine are the most popular among developers. So I decided to try them all and choose the one that suits my needs.

Consider the following test class:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using RazorEngine;
using RazorTemplates.Core;
using Xipton.Razor;

namespace RazorEngineExample
{
    [TestClass]
    public class EngineTests
    {
        private const string CorrectResult =
            "<h1>FirstLevelField</h2><p>SecondLevelField</p><table><tr><td>ThirdLevelItem1</td></tr><tr><td>ThirdLevelItem2</td></tr><tr><td>ThirdLevelItem3</td></tr></table>";
        
        private readonly object fullyDynamicModel = new
        {
            FirstLevelValue = "FirstLevelField",
            NestedField = new
                {
                    SecondLevel = "SecondLevelField",
                    ThirdLevelList = new[]
                        {
                            new {Item = "ThirdLevelItem1"},
                            new {Item = "ThirdLevelItem2"},
                            new {Item = "ThirdLevelItem3"}
                        }
                }
        };

        private readonly object partiallyDynamicModel = new
        {
            FirstLevelValue = "FirstLevelField",
            NestedField = new
            {
                SecondLevel = "SecondLevelField",
                ThirdLevelList = new[]
                    {
                        new MyItem {Item = "ThirdLevelItem1"},
                        new MyItem {Item = "ThirdLevelItem2"},
                        new MyItem {Item = "ThirdLevelItem3"},
                    }
            }
        };

        private const string MyTemplate =
            "<h1>@Model.FirstLevelValue</h2><p>@Model.NestedField.SecondLevel</p><table>@foreach(var item in @Model.NestedField.ThirdLevelList){<tr><td>@item.Item</td></tr>}</table>";

        [TestMethod]
        public void RazorEngineFullyDynamicModelTest()
        {
            CheckResult(Razor.Parse(MyTemplate, fullyDynamicModel));
        }

        [TestMethod]
        public void RazorTemplatesFullyDynamicModelTest()
        {
            CheckResult(Template.Compile(MyTemplate).Render(fullyDynamicModel));
        }

        [TestMethod]
        public void RazorMachineFullyDynamicModelTest()
        {
            CheckResult(new RazorMachine().Execute(MyTemplate, fullyDynamicModel).Result);
        }

        [TestMethod]
        public void RazorEnginePartiallyDynamicModelTest()
        {
            CheckResult(Razor.Parse(MyTemplate, partiallyDynamicModel));
        }

        [TestMethod]
        public void RazorTemplatesPartiallyDynamicModelTest()
        {
            CheckResult(Template.Compile(MyTemplate).Render(partiallyDynamicModel));
        }

        [TestMethod]
        public void RazorMachinePartiallyDynamicModelTest()
        {
            CheckResult(new RazorMachine().Execute(MyTemplate, partiallyDynamicModel).Result);
        }

        private static void CheckResult(string actual)
        {
            Assert.AreEqual(CorrectResult, actual);
        }
    }

    public class MyItem
    {
        public string Item { get; set; }

    }
}

It contains two simple models. The first one is fully dynamic while the second one contains typed items in array. Each library is tested against both models using the same template.

All of them failed to render a fully dynamic model. RazorEngine and RazorMachine successfully rendered the template against the second model, while RazorTemplates didn't. In both cases RazorTemplates had problems with nested dynamic objects:


Test method RazorEngineExample.EngineTests.RazorTemplatesPartiallyDynamicModelTest threw exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a definition for 'SecondLevel'.


Unfortunately when I added RazorEngine 3.0.8beta to our main project it crashed. The problem looked similar to this one. I wasn't able to make things work for partially dynamic models. It worked fine for fully typed models though.

RazorMachine 2.4.1 worked fine either in test or main solutions. So I decided to go with it.

EPiServer Commerce. Loading MetaObject in meta controls explicitly.

The problem. Suppose you want to design a control for managing data in your custom meta field. You inherit CoreBaseUserControl (or BaseUserControl) class and implement IMetaControl interface. The interface contains two fields: MetaField and MetaObject. These fields are filled when you open an instance of certain meta class for edit. You use MetaField property to manage the meta field your control designed for. MetaObject is used to interact with other fields of the same item.
Sometimes you may have a dependency between fields e.g. the second control is rendered in different ways according to the value in the first field. But sometimes we get a MetaObject empty during control loading. It means that we can't access other fields without loading an object explicitly.

The solution. To address this problem we can enhance a MetaField getter in a folliwing way:

public MetaObject MetaObject
{
 get
 {
  if (this.m_metaObject == null)
  {
   const int metaObjectDefaultId = 0;
   int metaObjectId = ManagementHelper.GetIntFromQueryString("catalogentryid", metaObjectDefaultId);
   if (metaObjectId != metaObjectDefaultId && MetaField.OwnerMetaClass != null)
   {
    try
    {
     this.m_metaObject = MetaObject.Load(this.MDContext, metaObjectId, MetaField.OwnerMetaClass.Id);
    }
    catch (Exception exception)
    {
     s_logger.Error("Error retrieving MetaObject", exception);
    }
   }
  }

  return this.m_metaObject;
 }
 set
 {
  this.m_metaObject = value;
 }
}

Monday, December 17, 2012

Outlook 2007 addin. Distribution list based on AD look up.

Hello there! Today I'm going to describe my recent small project. The goal was simple: provide colleagues with a flexible way of creation of email distribution lists. Something like outlook groups but with possibility of excluding particular users from the groups. Useful thing if you want to notify the group of people about someone's birthday. Of course you don't want the guy whose name-day is celebrated be in the list.

So I scanned our company Active Directory with ADExporer and found all the details I need to build a small console application. I decided to use builder pattern and keep distribution list building logic in a separate assembly. Here is the class diagram:



And here is the corresponding code:

namespace DLExt.Builder
{
    public class AddressBuilder
    {
        private readonly IList<DirectoryEntry> containersToSearch;
        private readonly IList<Location> locationsToSearch;
        private readonly List<Person> extractedPersons;
        private readonly IList<Person> personsToExclude;
        private readonly List<Person> filteredPersons;

        public string Server { get; private set; }

        public string ResultAddress { get; private set; }

        public AddressBuilder(string server, IEnumerable<Location> locations, IEnumerable<Person> personsToExclude)
        {
            Server = server;
            locationsToSearch = locations.ToList();
            this.personsToExclude = personsToExclude.ToList();

            containersToSearch = new List<DirectoryEntry>();
            extractedPersons = new List<Person>();
            filteredPersons = new List<Person>();
            ResultAddress = string.Empty;
        }

        public void Build()
        {
            try
            {
                LocateContainersToSearch(locationsToSearch);
                GetPersons();
                ExcludePersons();
                BuildDistributionList();
            }
            finally
            {
                FinalizeBuilder();
            }
        }

        protected virtual void LocateContainersToSearch(IList<Location> locations)
        {
            foreach (Location location in locations)
            {
                containersToSearch.Add(new DirectoryEntry(string.Format("LDAP://{0}/{1},{2}", Server, "OU=Users", location.Path)));
            }
        }

        protected virtual void GetPersons()
        {
            foreach (DirectoryEntry container in containersToSearch)
            {
                using (var directorySearcher = new DirectorySearcher(container, "(objectCategory=Person)"))
                {
                    SearchResultCollection results = directorySearcher.FindAll();
                    if (results.Count == 0)
                    {
                        return;
                    }

                    extractedPersons.AddRange((from SearchResult result in results
                                select new Person(
                                    result.Properties["displayName"][0].ToString(), 
                                    result.Properties["mail"][0].ToString())).ToList());
                }
            }
        }

        protected virtual void ExcludePersons()
        {
            filteredPersons.AddRange(extractedPersons.Except(personsToExclude, new PersonEqualityComparer()));
        }

        protected virtual void BuildDistributionList()
        {
            var builder = new StringBuilder();
            foreach (Person person in filteredPersons)
            {
                builder.Append(person.Email).Append(';');
            }

            ResultAddress = builder.ToString();
        }

        protected virtual void FinalizeBuilder()
        {
            foreach (DirectoryEntry container in containersToSearch)
            {
                container.Dispose();
            }
        }
    }
}

namespace DLExt.Builder.Retrievers
{
    public class PersonsRetriever : IRetriever<Person>
    {
        public string Server { get; private set; }

        public PersonsRetriever(string server)
        {
            Server = server;
        }

        public IList<Person> Retrieve(string path)
        {
            var result = new List<Person>();
            try
            {
                using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", Server, path)))
                {
                    using (DirectorySearcher searcher = new DirectorySearcher(entry, "(objectCategory=Person)"))
                    {
                        SearchResultCollection locations = searcher.FindAll();
                        if (locations.Count == 0)
                        {
                            return result;
                        }

                        result.AddRange(from SearchResult location in locations
                                        orderby location.Properties["displayName"][0].ToString()
                                        select new Person(
                                            location.Properties["displayName"][0].ToString(),
                                            location.Properties["mail"][0].ToString()));
                    }
                }
            }
            catch
            {
            }

            return result;
        }
    }
}

namespace DLExt.Builder.Retrievers
{
    public class LocationsRetriever :IRetriever<Location>
    {
        public string Server { get; private set; }

        public LocationsRetriever(string server)
        {
            Server = server;
        }

        public virtual IList<Location> Retrieve(string path)
        {
            var result = new List<Location>();
            try
            {
                using (DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", Server, path)))
                {
                    using (DirectorySearcher searcher = new DirectorySearcher(entry, "(objectClass=organizationalUnit)") { SearchScope = SearchScope.OneLevel })
                    {
                        SearchResultCollection locations = searcher.FindAll();
                        if (locations.Count == 0)
                        {
                            return result;
                        }

                        result.AddRange(from SearchResult location in locations 
                                        select new Location(
                                            location.Properties["name"][0].ToString(), 
                                            location.Properties["distinguishedName"][0].ToString()));
                    }
                }
            }
            catch
            {
            }

            return result;
        }
    }
}

After the searching logic was implemented and the console application was finished I decided to move further and build the UI. One option was to create a small tray WPF application. But who wants to run an additional application for such a minor task? I guess nobody. So I decided to build an Outlook 2007 addin. After some searching I found two great articles on MSDN and Codeproject. I was very excited when discovered that I can use WPF for achieving my goal. Here is a code I got:

using System;
using System.Windows.Forms;
using Microsoft.Office.Core;

namespace DLExt.OutlookAddin
{
    public partial class ThisAddIn
    {
        static readonly Timer Timer = new Timer();
        
        private void ThisAddIn_Startup(object sender, EventArgs e)
        {
            // HACK: looks like it is the only way to deal with minimized outlook startup
            Timer.Interval = 100;
            Timer.Tick += (o, args) =>
                              {
                                  var activeExplorer = Application.ActiveExplorer();
                                  if (activeExplorer != null)
                                  {
                                      Timer.Stop();
                                      CreateToolbar();
                                  }
                              };
            Timer.Start();
        }

        private void ThisAddIn_Shutdown(object sender, EventArgs e)
        {
            RemoveToolbar();
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion

        private const string MenuToolbarTag = "Distribution List Addin";
        private CommandBar toolBar;
        private CommandBarButton toolBarButton;

        private CommandBar FindBar()
        {
            var activeExplorer = Application.ActiveExplorer();
            return activeExplorer != null
                       ? (CommandBar) activeExplorer.CommandBars.FindControl(missing, missing, MenuToolbarTag, true)
                       : null;
        }

        private void RemoveToolbar()
        {
            var commandBar = FindBar();
            if (commandBar != null)
            {
                commandBar.Delete();
            }
        }

        private void CreateToolbar()
        {
            try
            {
                toolBar = FindBar() 
                    ?? Application.ActiveExplorer().CommandBars.Add(MenuToolbarTag, MsoBarPosition.msoBarTop, false, true);

                toolBarButton = (CommandBarButton)toolBar.Controls.Add(MsoControlType.msoControlButton, missing, missing, 1, true);
                toolBarButton.Style = MsoButtonStyle.msoButtonIconAndCaption;
                toolBarButton.Caption = "Generate Distribution List";
                toolBarButton.FaceId = 65;
                toolBarButton.Tag = MenuToolbarTag;
                toolBarButton.Click += (CommandBarButton ctrl, ref bool @default) =>
                {
                    MainWindow window = new MainWindow(
                        "controller",
                        "OU=Sites,OU=Company,DC=domain,DC=corp",
                        "OU=Sites,OU=Company,DC=domain,DC=corp");
                    window.Show();
                };
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message, "Error Message");
            }
        } 
    }
}

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using DLExt.Builder;
using DLExt.Builder.Model;
using DLExt.Builder.Retrievers;
using Microsoft.Office.Interop.Outlook;

namespace DLExt.OutlookAddin
{
    public partial class MainWindow : INotifyPropertyChanged
    {
        private readonly BackgroundWorker loadingWorker;
        private readonly BackgroundWorker composeWorker;
        private AddressBuilder builder;
        private IList<Location> locationsList;
        private IList<Person> personsList;

        public event PropertyChangedEventHandler PropertyChanged;

        public string Server { get; private set; }

        public string LocationsRootPath { get; private set; }

        public string PersonsRootPath { get; private set; }

        private bool isProcessing;

        public bool IsProcessing
        {
            get { return isProcessing; }
            set
            {
                isProcessing = value;
                PropertyChanged(this, new PropertyChangedEventArgs("IsProcessing"));
            }
        }

        public MainWindow(string server, string locationsRootPath, string personsRootPath)
        {
            Server = server;
            LocationsRootPath = locationsRootPath;
            PersonsRootPath = personsRootPath;

            loadingWorker = new BackgroundWorker();
            loadingWorker.DoWork += (o, args) =>
            {
                IsProcessing = true;
                locationsList = new LocationsRetriever(Server).Retrieve(LocationsRootPath);
                personsList = new PersonsRetriever(Server).Retrieve(PersonsRootPath);
            };

            loadingWorker.RunWorkerCompleted += (sender, args) =>
            {
                locations.ItemsSource = locationsList;
                persons.ItemsSource = personsList;
                IsProcessing = false;
            };

            composeWorker = new BackgroundWorker();
            composeWorker.DoWork += (sender, args) =>
            {
                IsProcessing = true;
                builder = new AddressBuilder(
                    Server,
                    locations.Items.OfType<Location>().Where(loc => loc.IsSelected),
                    personsToExclude.Items.OfType<Person>());
                builder.Build();
            };

            composeWorker.RunWorkerCompleted += (sender, args) =>
            {
                IsProcessing = false;
                try
                {
                    var app = new Microsoft.Office.Interop.Outlook.Application();
                    var mailItem = (MailItem)app.CreateItem(OlItemType.olMailItem);

                    mailItem.To = builder.ResultAddress;
                    mailItem.Display(true);

                }
                catch (COMException)
                {
                }
            };

            loadingWorker.WorkerSupportsCancellation = true;
            DataContext = this;
            InitializeComponent();
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            loadingWorker.RunWorkerAsync();
        }

        private void ComposeEmail(object sender, RoutedEventArgs e)
        {
            composeWorker.RunWorkerAsync();
        }

        private void ExcludePerson(object sender, RoutedEventArgs e)
        {
            if (!personsToExclude.Items.Contains(persons.SelectedItem))
            {
                personsToExclude.Items.Add(persons.SelectedItem);
            }
        }

        private void CloseForm(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void WindowClosing(object sender, CancelEventArgs e)
        {
            if (loadingWorker.IsBusy)
            {
                loadingWorker.CancelAsync();
            }
        }
    }
}

The UI was completed in a first approximation. The main thing I wasn't satisfied with was execution of LDAP requests in the same thread as UI. So the next step was implementing multithreading with background worker.

public MainWindow(string server, string locationsRootPath, string personsRootPath)
{

 ...            

 loadingWorker = new BackgroundWorker();
 loadingWorker.DoWork += (o, args) =>
 {
  IsProcessing = true;
  locationsList = new LocationsRetriever(Server).Retrieve(LocationsRootPath);
  personsList = new PersonsRetriever(Server).Retrieve(PersonsRootPath);
 };

 loadingWorker.RunWorkerCompleted += (sender, args) =>
 {
  locations.ItemsSource = locationsList;
  persons.ItemsSource = personsList;
  IsProcessing = false;
 };


 loadingWorker.WorkerSupportsCancellation = true;

 ...

 InitializeComponent();
}

 ...

private void WindowLoaded(object sender, RoutedEventArgs e)
{
 loadingWorker.RunWorkerAsync();
}

The application was completed but unfortunately it was crushing during Outlook start up. After some research I found the problem was the absence of ActiveExplorer instance when outlook started minimized. No UI - no ActiceExplorer instance. Sounds fair but how to fix it? I had to recognize that the hack with a timer was the best solution for me.

private void ThisAddIn_Startup(object sender, EventArgs e)
{
 // HACK: looks like it is the only way to deal with minimized outlook startup
 Timer.Interval = 100;
 Timer.Tick += (o, args) =>
  {
    var activeExplorer = Application.ActiveExplorer();
    if (activeExplorer != null)
    {
     Timer.Stop();
     CreateToolbar();
    }
  };
 Timer.Start();
}

The app was completed this time. But I understood that I need to provide a way for easy distribution of the addin. So I decided to build a setup project to handle all prerequisites and registry stuff. The hardest part here was to find this great article for excel plugins distribution. I followed it to create a setup project for my outlook plugin.

Final sources can be found here.

Friday, November 9, 2012

Serialization of entities (Entity Framework)

I want to tell you about interesting issue I faced when tried to serialize an entity in Microsoft Entity Framework.

I'm going to use Northwind database for demonstration purposes. Suppose you've added the Northwind model to your solution. And you are interested in three entity classes: Customer, Order and Order_Detail. Here is the corresponding diagram:
 You want to serialize particular customer with all his orders and order details. Consider the following code:

using System.Data.Objects;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Serializing;

namespace EntitiesSerialization
{
    class Program
    {
        static void Main()
        {
            using (var context = new SerializingContext())
            {
                context.ContextOptions.LazyLoadingEnabled = false;
                var query = context.Customers.Include("Orders.Order_Details");
                var customer = query.Where("it.CustomerID = @Id", new ObjectParameter("Id", "ALFKI")).First();                
                var serializer = new XmlSerializer(typeof (Customers), new[] {typeof(Orders), typeof(Order_Details)});
                serializer.Serialize(new XmlTextWriter("test.xml", Encoding.UTF8), customer);                
            }
        }
    }
}

As you can see, I use XmlSerializer to serialize the customer to xml file. Just to make sure that LazyLoading doesn't affect the solution I've turned it off and included all entities explicitly. I pass Order and Order_Details types to XmlSerializer constructor as extraTypes. Unfortunatelly this solution won't work as it should. Result xml file contains only customer fields, no orders and order details are persisted:

<?xml version="1.0" encoding="utf-8"?>
<Customers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <EntityKey>
    <EntitySetName>Customers</EntitySetName>
    <EntityContainerName>SerializingContext</EntityContainerName>
    <EntityKeyValues>
      <EntityKeyMember>
        <Key>CustomerID</Key>
        <Value xsi:type="xsd:string">ALFKI</Value>
      </EntityKeyMember>
    </EntityKeyValues>
  </EntityKey>
  <CustomerID>ALFKI</CustomerID>
  <CompanyName>Alfreds Futterkiste</CompanyName>
  <ContactName>Maria Anders</ContactName>
  <ContactTitle>Sales Representative</ContactTitle>
  <Address>Obere Str. 57</Address>
  <City>Berlin</City>
  <PostalCode>12209</PostalCode>
  <Country>Germany</Country>
  <Phone>030-0074321</Phone>
  <Fax>030-0076545</Fax>
</Customers>

One way to make this work is described here. You can use DataContractSerializer to persist all the data to xml. Like this:

DataContractSerializer serializer = new DataContractSerializer(customer.GetType());
serializer.WriteObject(new XmlTextWriter("test.xml", Encoding.UTF8), customer);

Another option is to use binary formatter. In the following code I perform sequental serialization/deserialization of the customer object:

var binaryFormatter = new BinaryFormatter();
var stream = new MemoryStream();
binaryFormatter.Serialize(stream, customer);
stream.Position = 0;
var result = binaryFormatter.Deserialize(stream);

Thursday, November 1, 2012

Setting the value of new properties for old pages.

PageTypeBuilder is a great tool and I can't imagine developing against EPiServer CMS without it. But it has a well known issue: after a new property is added to page type old pages of this type have this property empty. It becomes worth if new property has required attribute set to true. You can't update old pages without filling this property.

In this post I want to share one possible solution. The idea is simple. Get all pages of certain type, find all required attributes with specified default value. Then iterate through these pages and set the required properties with their default values.

The first thing we need to do is to find all pages of certain type. It can be done using FindPagesWithCriteria method. To create search criteria we will need an ID of page type. So we have to use one of three overloads of PageType.Load method. Of course we can hardcode the guid or name, but more convenient way is to lookup this data. Here is what we've got for now:

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAccess;
using EPiServer.Filters;
using PageTypeBuilder;

public class DefaultValuesSetter<TPageData> where TPageData : PageData
{        
 private const string PageTypeCriteriaName = "PageTypeID";

 private readonly PageType pageType;
 private readonly IList<PageDefinition> targetPageDefinitions;

 public DefaultValuesSetter()
 {
  pageType = GetPageType();
  targetPageDefinitions = GetRequiredDefinitionsWithDefaultValue();
 }

 public virtual IList<TPageData> GetPages()
 {
  if (pageType == null)
  {
   return new List<TPageData>();
  }

  var criteria = new PropertyCriteria
         {
          Condition = CompareCondition.Equal,
          Name = PageTypeCriteriaName,
          Type = PropertyDataType.PageType,
          Value = pageType.ID.ToString(CultureInfo.InvariantCulture),
          Required = true
         };  

  return DataFactory.Instance
   .FindPagesWithCriteria(PageReference.RootPage, new PropertyCriteriaCollection {criteria})
   .OfType<TPageData>()
   .ToList();
 } 

 private static PageType GetPageType()
 {
  var pageTypeId = PageTypeResolver.Instance.GetPageTypeID(typeof (TPageData));
  return pageTypeId.HasValue 
       ? PageType.Load(pageTypeId.Value) 
       : null;
 } 
}

The next thing to deal with is searching for property attributes. As I mentioned before we are interested in required properties with not empty default value. Here is the code:

private IList<PageDefinition> GetRequiredDefinitionsWithDefaultValue()
 {
  return pageType.Definitions
   .Where(def => def.Required && 
    def.DefaultValueType == DefaultValueType.Value && 
    def.DefaultValue != null)
   .ToList();
 }

Finally we need to iterate through all found pages and set their required properties to default values:

public virtual void SetDefaultValues(IList<TPageData> pages)
 {
  foreach (TPageData page in pages)
  {
   var pageData = page.CreateWritableClone();
   foreach (PageDefinition definition in targetPageDefinitions)
   {
    if (pageData[definition.Name] == null)
    {
     pageData[definition.Name] = definition.DefaultValue;
    }
   }

   try
   {
    DataFactory.Instance.Save(pageData, SaveAction.Publish);
   }
   catch (Exception exception)
   {
    // TODO: add logging here    
   }
  }
 }

We're done with infrastructure. Now suppose you've created a new page type class:

[PageType(Name = "TestPageType")]
public class TestPageType : TypedPageData
{
}

You've created a page of this type in CMS and decided to add a property to it:

[PageType(Name = "TestPageType")]
public class TestPageType : TypedPageData
{
 [PageTypeProperty(            
  EditCaption = "Test Property",            
  Type = typeof(PropertyString),
  Required = true,            
  DefaultValue = "My default value",
  DefaultValueType = DefaultValueType.Value)]
 public virtual string TestProperty { get; set; }        
}

To update the page you can use the following code:

var setter = new DefaultValuesSetter<TestPageType>();
var pages = setter.GetPages();
setter.SetDefaultValues(pages);

Happy coding!

Wednesday, October 24, 2012

Byte arrays instead of strings when retrieving custom attributes from Active Directory.

Few days ago when I was working with Active Directory I noticed a strange thing. I was quering AD for user with a custom string attribute. The results were being varied on different dev boxes. On one of them I was getting attribute's value as a string but on another - as a byte array.

When I set application pool Identity with the same AD account user identity on both dev boxes I get the same behavior. So I thought that one of AD accounts doesn't have enough access rights to get AD schema.

The goal was to retrieve a Guid from this attribute so I decided to implement a method which can handle all major cases. Here what I got:

public static Guid GuidFromADAttribute(object attribute)
{
    if (attribute is byte[])
    {
        var bytes = (byte[])attribute;
        switch (bytes.Length)
        {
            case 16:
                return new Guid(bytes);
            case 36:
                var stringRepresentation = Encoding.Default.GetString(bytes);
                Guid result;
                return Guid.TryParse(stringRepresentation, out result) ? result : default(Guid);
        }
    }
    else if (attribute is string)
    {
        Guid result;
        return Guid.TryParse((string) attribute, out result) ? result : default(Guid);
    }

    return default(Guid);
}

I kept the opportunity to retrieve "normal" Guid from AD. By "normal" I mean 16 bytes long. Another option here is to pass byte array representation of guid string (it must be 36 bytes long in order to be parsed). The final option is to pass a string representation of guid.

The bad thing is that I didn't find the cause of such a strange AD behavior. Here is the list of similar posts:

Friday, October 19, 2012

Add link between contact and organization entities in Mediachase ecommerce framework

Yesterday I was trying to assign a contact to organization in my code. It seemed really natural to had such a requirement but unfortunatelly I didn't find any examples.

In Mediachase.Commerce.Customers.CustomerContext class there is a method called InnerGetAllCustomerContactsInOrganization. It lists all Contact entities filtered by OwnerId field. So we can set link between this two entities in a following way:
myContact.OwnerId = myOrganization.PrimaryKeyId;
Hope this helps. 

Tuesday, September 25, 2012

Launching Vocabulary Extender!

Today I'm starting my new open source service. It is called "Vocabulary Extender". The main purpose of it is to help people to learn foreign languages. As you probably know one of the most important aspects of mastering the new language is to extend your vocabulary. The more words you remember - the better. It will help you on all levels - from elementary to advance.

But we often don't want to spend our time sitting with a dictionary remembering a bunch of words. More than that such approach is usually inefficient because of simple fact: when you don't use the word you forget it. So I've decided that the process of learning should be continuous. It means that one should be able to repeat the words during his/her working day without loss of productivity.

One possible way of achieving this is to use small desktop application that popups one time in a couple of minutes and prompts you to translate one random word. It offers you few possible answers so you need only to choose one. That's it, only one click per 10 minutes (or pick any interval that suits you).

The other important thing in learning is socializing. I think it is much more fun when you have an opportunity to share what you have learnt with others. When you create something your friends should be noticed that they can use it. With "Vocabulary Extenter" it is also possible. You can use your friend's vocabularies or create your own ones.

But enough words. Just see it in action on this video (only on Russian for now):

Wednesday, September 12, 2012

EPiServer dual Active Directory multiplexing role provider

The problem: when use EPiServer multiplexing role provider with more than one active directory, one can see groups only from first AD.

Let's examine EPiServer.Security.ActiveDirectoryRoleProvider class (EPiServer.dll). The key method for us here is:

public override string[] GetAllRoles()
{
    ICollection<DirectoryData> collection = 
        this._factory.FindAll("(objectClass=group)", SearchScope.Subtree, this._roleNameAttribute);
    if (collection == null)
    {
        return new string[0];
    }
    List<string> list = new List<string>();
    foreach (DirectoryData current in collection)
    {
        DirectoryData entry = this._factory.GetEntry(current.DistinguishedName);
        if (entry != null)
        {
            list.Add(entry[this._roleNameAttribute][0]);
        }
    }
    return list.ToArray();
}

So we get the groups with FindAll method. The actual type of this._factory is AdsiDataFactory so let's look into it's FindAll method:

public override IList<DirectoryData> FindAll(
    string filter, 
    SearchScope scope, 
    string sortByProperty)
{
    string text = "EPiServer:DirectoryServiceFindAll:" + filter + scope.ToString();
    IList<DirectoryData> list = (IList<DirectoryData>)HttpRuntime.Cache[text];
    if (list != null)
    {
        return list;
    }
 
 ...
}

These are only first lines of this method, but it is enough to understand what's wrong. This method caches the results of requests to AD with a key. The key consists of a filter and a scope. Both of these variables are the same for all ADs. So we will be getting the same value for all requests from cache.

The solution: we have to implement our own DirectoryDataFactory and reference it from our own ActiveDirectoryRoleProvider. We also should change the way the cacheKey is built. For example by adding the ConnectionString to it. Let's start with the ActiveDirectoryRoleProvider. Actually we don't need to implement it from scratch. We can subclass the ActiveDerectoryRoleProvider and set it's DirectoryDataFactory property in constructor. Like this:

public class MyOwnActiveDirectoryRoleProvider : EPiServer.Security.ActiveDirectoryRoleProvider
{
    public MyOwnActiveDirectoryRoleProvider()
    {
        DirectoryDataFactory = new MyOwnAdsiDataFactory();
    }
}

Now its time to deal with DirectoryDataFactory implementation. First thought is to override the FindAll method. But this method accesses a private field _propertiesToLoad that is shared between couple of other methods. So we have two options here:
1. Inherit new class from DirectoryDataFactory and implement all methods and properties.
2. Inherit new class from AdsiDataFactory. Replace the _propertiesToLoad field and override all methods that use it.

Personally I have chosen the second approach. So I've ended up with the following code:


public class MyOwnAdsiDataFactory : EPiServer.Security.AdsiDataFactory
{
 private const string CacheKeyEntryPrefix = "EPiServer:DirectoryServiceEntry:";
 private const string CacheKeyFindOnePrefix = "EPiServer:DirectoryServiceFindOne:";
 private const string CacheKeyFindAllPrefix = "EPiServer:DirectoryServiceFindAll:";
 private const string DistingushedNameAttribute = "distinguishedName";
 private const string ObjectClassAttribute = "objectClass";

 private List<string> propertiesToLoad;

 public MyOwnAdsiDataFactory()
 {
 }

 public MyOwnAdsiDataFactory(
  string connectionString,
  string username,
  string password,
  AuthenticationTypes connectionProtection,
  TimeSpan absoluteCacheTimeout) : base(connectionString, username, password, connectionProtection, absoluteCacheTimeout)
 {
  Initialize();
 }

 public override void Initialize(NameValueCollection config)
 {
  base.Initialize(config);
  Initialize();
 }

 public override void AddPropertyToLoad(string propertyName)
 {
  if (propertiesToLoad.Contains(propertyName))
  {
   return;
  }

  propertiesToLoad.Add(propertyName);
  ClearCache();
 }

 public override DirectoryData GetEntry(string distinguishedName)
 {
  string cacheKey = CacheKeyEntryPrefix + distinguishedName;
  DirectoryData directoryData = (DirectoryData)HttpRuntime.Cache[cacheKey];
  if (directoryData != null)
  {
   return directoryData;
  }

  using (DirectoryEntry directoryEntry = CreateDirectoryEntry(distinguishedName))
  {
   directoryData = CreateDirectoryDataFromDirectoryEntry(directoryEntry);
  }

  if (directoryData != null)
  {
   StoreInCache(cacheKey, directoryData);
  }

  return directoryData;
 }

 public override DirectoryData FindOne(string filter, SearchScope scope)
 {
  string cacheKey = new StringBuilder(CacheKeyFindOnePrefix)
   .Append(filter)
   .Append(scope)
   .ToString();

  DirectoryData directoryData = (DirectoryData)HttpRuntime.Cache[cacheKey];
  if (directoryData != null)
  {
   return directoryData;
  }

  using (DirectorySearcher directorySearcher =
   new DirectorySearcher(CreateDirectoryEntry(), filter, propertiesToLoad.ToArray(), scope))
  {
   directoryData = CreateDirectoryDataFromSearchResult(directorySearcher.FindOne());
   if (directoryData == null)
   {
    return null;
   }
  }

  StoreInCache(cacheKey, directoryData);
  return directoryData;
 }

 public override IList<DirectoryData> FindAll(string filter, SearchScope scope, string sortByProperty)
 {
  string cacheKey = new StringBuilder(CacheKeyFindAllPrefix)
   .Append(filter)
   .Append(scope)
   .Append(ConnectionString)
   .ToString();

  IList<DirectoryData> list = (IList<DirectoryData>) HttpRuntime.Cache[cacheKey];
  if (list != null)
  {
   return list;
  }

  using (DirectorySearcher directorySearcher = new DirectorySearcher(CreateDirectoryEntry(), filter, propertiesToLoad.ToArray(), scope))
  {
   directorySearcher.PageSize = PageSize;
   using (SearchResultCollection all = directorySearcher.FindAll())
   {
    if (sortByProperty == null)
    {
     list = new List<DirectoryData>(all.Count);
     foreach (SearchResult result in all)
     {
      list.Add(CreateDirectoryDataFromSearchResult(result));
     }
    }
    else
    {
     SortedList<string, DirectoryData> sortedList = new SortedList<string, DirectoryData>(all.Count);
     foreach (SearchResult result in all)
     {
      DirectoryData fromSearchResult = CreateDirectoryDataFromSearchResult(result);
      sortedList.Add(fromSearchResult.GetFirstPropertyValue(sortByProperty), fromSearchResult);
     }
     list = sortedList.Values;
    }
   }
  }

  StoreInCache(cacheKey, list);
  return list;
 }

 protected new DirectoryData CreateDirectoryDataFromDirectoryEntry(DirectoryEntry entry)
 {
  if (entry == null)
  {
   return null;
  }

  Dictionary<string, string[]> properties = new Dictionary<string, string[]>(propertiesToLoad.Count);
  foreach (string property in propertiesToLoad)
  {
   if (entry.Properties.Contains(property))
   {
    var propertyValueCollection = entry.Properties[property];
    var strArray = new string[propertyValueCollection.Count];
    for (int index = 0; index < propertyValueCollection.Count; ++index)
     strArray[index] = propertyValueCollection[index].ToString();
    properties.Add(property, strArray);
   }
  }

  return new DirectoryData(DistinguishedName(properties), entry.SchemaClassName, properties);
 }

 protected new DirectoryData CreateDirectoryDataFromSearchResult(SearchResult result)
 {
  if (result == null)
  {
   return null;
  }

  Dictionary<string, string[]> properties = new Dictionary<string, string[]>(propertiesToLoad.Count);
  foreach (string property in propertiesToLoad)
  {
   if (result.Properties.Contains(property))
   {
    var propertyValueCollection = result.Properties[property];
    var strArray = new string[propertyValueCollection.Count];
    for (int index = 0; index < propertyValueCollection.Count; ++index)
     strArray[index] = propertyValueCollection[index].ToString();
    properties.Add(property, strArray);
   }
  }

  return new DirectoryData(DistinguishedName(properties), SchemaClassName(properties), properties);
 }

 private void Initialize()
 {
  propertiesToLoad = new List<string>(5) { DistingushedNameAttribute, ObjectClassAttribute };
 }
}

Thursday, August 30, 2012

Integrating Autofac to IIS hosted WCF service application

In this post I'm going to describe the way I've integrated Autofac into my WCF IIS hosted service. While the process is thought to be quite common I've found few interesting aspects there.

First steps could be found at Autofac wikiAt the global application startup one should register the service and set the AutofacHostFactory.Container property. But where is the global application startup in case of WCF service hosted on IIS? Basically we have 3 ways to goI think the most natural for WCF is to create custom ServiceHostFactory where we can register our services.

The next step of integration process is to specify Autofac.Integration.Wcf.AutofacServiceHostFactory as  a factory in our service's svc file. But we've already decided to use our own factory. So we have to dig into autofac sources to try to combine these two factories. Here is the code I've ended up with:

namespace VX.Service
{
    public class ServiceHostFactory : AutofacHostFactory
    {
        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<VocabExtService>();
            Container = builder.Build();

            return base.CreateServiceHost(constructorString, baseAddresses);
        }

        protected override ServiceHost CreateSingletonServiceHost(object singletonInstance, Uri[] baseAddresses)
        {
            if (singletonInstance == null)
            {
                throw new ArgumentNullException("singletonInstance");
            }
            if (baseAddresses == null)
            {
                throw new ArgumentNullException("baseAddresses");
            }
            return new ServiceHost(singletonInstance, baseAddresses);
        }
    }
}


As you can see I've inherited my factory from AutofacHostFactory and overrided a couple of methods. In CreateServiceHost method I register my service, the second method is left untouched.

The last thing you need to do is to specify the factory at the *.svc file markup. For example:

<%@ ServiceHost Language="C#" Debug="true" Service="VX.Service.VocabExtService, VX.Service" CodeBehind="VocabExtService.svc.cs" Factory="VX.Service.ServiceHostFactory, VX.Service"%>
Pay attention to Service attribute here. I have to change the default one to full name to make AutofacHostFactory work. That's it, you can access the service and use dependency injections in your code.