Author: Romiko Derbynew

Lucene Full Text Indexing with Neo4j

Hi Guys,

I spent some time working on full text search for Neo4j. The basic goals were as follows.

    • Control the pointers of the index
    • Full Text Search
    • All operations are done via Rest
    • Can create an index when creating a node
    • Can update and index
    • Can check if an index exists
    • When bootstrapping Neo4j in the cloud run Index checks
    • Query Index using full text search lucene query language.
Download:
This is based on Neo4jClient:
Source Code at:

Introduction

So with the above objectives, I decided to go with Manual Indexing. The main reason here is that I can put an index pointing to node A based on values in node B.

Imagine the following.

You have Node A with a list:

Surname, FirstName and MiddleName. However Node A also has a relationship to Node B which has other names, perhaps Display Names, Avatar Names and AKA’s.

So with manual indexing, you can have all the above entries for names in Node A and Node B point to Node A only.

So, in a Rest call to the Neo4j server, it would look something like this in Fiddler.

image

Notice the following:

Url: http://localhost:7474/db/data/index/node/{IndexName}/{Key}/{Value}

So, if we were adding 3 names for the SAME client from 2 different nodes. You would have the same IndexName and Key then with different values in the Url. The node pointer (In the request body) will then be the address to the Node.

Neo4jClient Nuget Package

I have updated the Neo4jClient which is on Nuget, to now support:

  • Creating Exact or FullText Indexes on it’s own, so that it just exists
  • Creating Exact or FullTest indexes when creating a node, the node reference will automatically be calculated.
  • Updating an Index
  • Deleting entries from an index.
    Class diagram for the indexing solution in Neo4jClient.

image

RestSharp

The Neo4jClient package uses RestSharp, thus making all the index call operations a trivial task for us, so lets have a look at some of the code inside the client to see how to consume manual index api from .Net, and then in the next section well look how we consume this code from another application.

 public Dictionary<string, IndexMetaData> GetIndexes(IndexFor indexFor)
        {
            CheckRoot();

            string indexResource;
            switch (indexFor)
            {
                case IndexFor.Node:
                    indexResource = RootApiResponse.NodeIndex;
                    break;
                case IndexFor.Relationship:
                    indexResource = RootApiResponse.RelationshipIndex;
                    break;
                default:
                    throw new NotSupportedException(string.Format("GetIndexes does not support indexfor {0}", indexFor));
            }

            var request = new RestRequest(indexResource, Method.GET)
            {
                RequestFormat = DataFormat.Json,
                JsonSerializer = new CustomJsonSerializer { NullHandling = JsonSerializerNullValueHandling }
            };

            var response =  client.Execute<Dictionary<string, IndexMetaData>>(request);

            if (response.StatusCode != HttpStatusCode.OK)
                throw new NotSupportedException(string.Format(
                    "Received an unexpected HTTP status when executing the request.\r\n\r\n\r\nThe response status was: {0} {1}",
                    (int)response.StatusCode,
                    response.StatusDescription));

            return response.Data;
        }

        public bool CheckIndexExists(string indexName, IndexFor indexFor)
        {
            CheckRoot();

            string indexResource;
            switch (indexFor)
            {
                case IndexFor.Node:
                    indexResource = RootApiResponse.NodeIndex;
                    break;
                case IndexFor.Relationship:
                    indexResource = RootApiResponse.RelationshipIndex;
                    break;
                default:
                    throw new NotSupportedException(string.Format("IndexExists does not support indexfor {0}", indexFor));
            }

            var request = new RestRequest(string.Format("{0}/{1}",indexResource, indexName), Method.GET)
            {
                RequestFormat = DataFormat.Json,
                JsonSerializer = new CustomJsonSerializer { NullHandling = JsonSerializerNullValueHandling }
            };

            var response = client.Execute<Dictionary<string, IndexMetaData>>(request);

            return response.StatusCode == HttpStatusCode.OK;
        }

        void CheckRoot()
        {
            if (RootApiResponse == null)
                throw new InvalidOperationException(
                    "The graph client is not connected to the server. Call the Connect method first.");
        }

        public void CreateIndex(string indexName, IndexConfiguration config, IndexFor indexFor)
        {
            CheckRoot();

            string nodeResource;
            switch (indexFor)
            {
                case IndexFor.Node:
                    nodeResource = RootApiResponse.NodeIndex;
                    break;
                case IndexFor.Relationship:
                    nodeResource = RootApiResponse.RelationshipIndex;
                    break;
                default:
                    throw new NotSupportedException(string.Format("CreateIndex does not support indexfor {0}", indexFor));
            }

            var createIndexApiRequest = new
                {
                    name = indexName.ToLower(),
                    config
                };

            var request = new RestRequest(nodeResource, Method.POST)
                {
                    RequestFormat = DataFormat.Json,
                    JsonSerializer = new CustomJsonSerializer {NullHandling = JsonSerializerNullValueHandling}
                };
            request.AddBody(createIndexApiRequest);

            var response = client.Execute(request);

            if (response.StatusCode != HttpStatusCode.Created)
                throw new NotSupportedException(string.Format(
                    "Received an unexpected HTTP status when executing the request..\r\n\r\nThe index name was: {0}\r\n\r\nThe response status was: {1} {2}",
                    indexName,
                    (int) response.StatusCode,
                    response.StatusDescription));
        }

        public void ReIndex(NodeReference node, IEnumerable<IndexEntry> indexEntries)
        {
            CheckRoot();

            var nodeAddress = string.Join("/", new[] {RootApiResponse.Node, node.Id.ToString()});

            var updates = indexEntries
                .SelectMany(
                    i => i.KeyValues,
                    (i, kv) => new {IndexName = i.Name, kv.Key, kv.Value});

            foreach (var update in updates)
            {
                if (update.Value == null)
                    break;

                string indexValue;
                if(update.Value is DateTimeOffset)
                {
                    indexValue = ((DateTimeOffset) update.Value).UtcTicks.ToString();
                }
                else if (update.Value is DateTime)
                {
                    indexValue = ((DateTime)update.Value).Ticks.ToString();
                }
                else
                {
                    indexValue = update.Value.ToString();
                }

                AddNodeToIndex(update.IndexName, update.Key, indexValue, nodeAddress);
            }
        }

        public void DeleteIndex(string indexName, IndexFor indexFor)
        {
            CheckRoot();

            string indexResource;
            switch (indexFor)
            {
                case IndexFor.Node:
                    indexResource = RootApiResponse.NodeIndex;
                    break;
                case IndexFor.Relationship:
                    indexResource = RootApiResponse.RelationshipIndex;
                    break;
                default:
                    throw new NotSupportedException(string.Format("DeleteIndex does not support indexfor {0}", indexFor));
            }

            var request = new RestRequest(string.Format("{0}/{1}", indexResource, indexName), Method.DELETE)
            {
                RequestFormat = DataFormat.Json,
                JsonSerializer = new CustomJsonSerializer { NullHandling = JsonSerializerNullValueHandling }
            };

            var response = client.Execute(request);

            if (response.StatusCode != HttpStatusCode.NoContent)
                throw new NotSupportedException(string.Format(
                    "Received an unexpected HTTP status when executing the request.\r\n\r\nThe index name was: {0}\r\n\r\nThe response status was: {1} {2}",
                    indexName,
                    (int)response.StatusCode,
                    response.StatusDescription));
        }

        void AddNodeToIndex(string indexName, string indexKey, string indexValue, string nodeAddress)
        {
            var nodeIndexAddress = string.Join("/", new[] { RootApiResponse.NodeIndex, indexName, indexKey, indexValue });
            var request = new RestRequest(nodeIndexAddress, Method.POST)
            {
                RequestFormat = DataFormat.Json,
                JsonSerializer = new CustomJsonSerializer { NullHandling = JsonSerializerNullValueHandling }
            };
            request.AddBody(string.Join("", client.BaseUrl, nodeAddress));

            var response = client.Execute(request);

            if (response.StatusCode != HttpStatusCode.Created)
                throw new NotSupportedException(string.Format(
                    "Received an unexpected HTTP status when executing the request.\r\n\r\nThe index name was: {0}\r\n\r\nThe response status was: {1} {2}",
                    indexName,
                    (int)response.StatusCode,
                    response.StatusDescription));
        }

        public IEnumerable<Node<TNode>> QueryIndex<TNode>(string indexName, IndexFor indexFor, string query)
        {
            CheckRoot();

            string indexResource;

            switch (indexFor)
            {
                case IndexFor.Node:
                    indexResource = RootApiResponse.NodeIndex;
                    break;
                case IndexFor.Relationship:
                    indexResource = RootApiResponse.RelationshipIndex;
                    break;
                default:
                    throw new NotSupportedException(string.Format("QueryIndex does not support indexfor {0}", indexFor));
            }

            var request = new RestRequest(indexResource + "/" + indexName, Method.GET)
                {
                    RequestFormat = DataFormat.Json,
                    JsonSerializer = new CustomJsonSerializer {NullHandling = JsonSerializerNullValueHandling}
                };

            request.AddParameter("query", query);

            var response = client.Execute<List<NodeApiResponse<TNode>>>(request);

            if (response.StatusCode != HttpStatusCode.OK)
                throw new NotSupportedException(string.Format(
                    "Received an unexpected HTTP status when executing the request.\r\n\r\nThe index name was: {0}\r\n\r\nThe response status was: {1} {2}",
                    indexName,
                    (int) response.StatusCode,
                    response.StatusDescription));

            return response.Data == null
           ? Enumerable.Empty<Node<TNode>>()
           : response.Data.Select(r => r.ToNode(this));
        }
		

Using the Neo4jClient from within an application

Create an Index and check if it exists

This is useful when bootstrapping Neo4j, to see if there are any indexes that SHOULD be there and are not, so that you can enumerate all the nodes for that index and add entries.

public void CreateIndexesForAgencyClients()
        {
            var agencies = graphClient
                .RootNode
                .Out<Agency>(Hosts.TypeKey)
                .ToList();

            foreach (var agency in agencies)
            {
                var indexName = IndexNames.Clients(agency.Data);
                var indexConfiguration = new IndexConfiguration
                    {
                        Provider = IndexProvider.lucene,
                        Type = IndexType.fulltext
                    };

                if (!graphClient.CheckIndexExists(indexName, IndexFor.Node))
                {
                    Trace.TraceInformation("CreateIndexIfNotExists {0} for Agency Key {0}", indexName, agency.Data.Key);
                    graphClient.CreateIndex(indexName, indexConfiguration, IndexFor.Node);
                    PopulateAgencyClientIndex(agency.Data);
                }
            }
        }

Create an Index Node Entry when creating a node

 var indexEntries = GetIndexEntries(agency.Data, client, clientViewModel.AlsoKnownAses);

var clientNodeReference = graphClient.Create(
                client,
                new[] {new ClientBelongsTo(agencyNode.Reference)}, indexEntries);

public IEnumerable<IndexEntry> GetIndexEntries(Agency agency, Client client, IEnumerable<AlsoKnownAs> alsoKnownAses)
        {
            var indexKeyValues = new List<KeyValuePair<string, object>>
            {
                new KeyValuePair<string, object>(AgencyClientIndexKeys.Gender.ToString(), client.Gender)
            };

            if (client.DateOfBirth.HasValue)
            {
                var dateOfBirthUtcTicks = client.DateOfBirth.Value.UtcTicks;
                indexKeyValues.Add(new KeyValuePair<string, object>(AgencyClientIndexKeys.DateOfBirth.ToString(), dateOfBirthUtcTicks));
            }

            var names = new List<string>
            {
                client.GivenName,
                client.FamilyName,
                client.PreferredName,
            };

            if (alsoKnownAses != null)
            {
                names.AddRange(alsoKnownAses.Where(a => !string.IsNullOrEmpty(a.Name)).Select(aka => aka.Name));
            }

            indexKeyValues.AddRange(names.Select(name => new KeyValuePair<string, object>(AgencyClientIndexKeys.Name.ToString(), name)));

            return new[]
            {
                new IndexEntry
                {
                    Name = IndexNames.Clients(agency),
                    KeyValues = indexKeyValues.Where(v => v.Value != null)
                }
            };
        }
		

Reindex a node

Notice there was a call to PopulateAgencyClientIndexin in the code, this is done in our bootstrap to ensure indexes are always there as expected, and if for some reason they are not, then they created and populated by using reindex feature.

void PopulateAgencyClientIndex(Agency agency)
        {
            var clients = graphClient
                .RootNode
                .Out<Agency>(Hosts.TypeKey, a => a.Key == agency.Key)
                .In<Client>(ClientBelongsTo.TypeKey);

            foreach (var client in clients)
            {
                var clientService = clientServiceCallback();
                var akas = client.Out<AlsoKnownAs>(IsAlsoKnownAs.TypeKey).Select(a => a.Data);
                var indexEntries = clientService.GetIndexEntries(agency, client.Data, akas);
                graphClient.ReIndex(client.Reference, indexEntries);
            }
        }
		

Querying a full text search index using Lucene

Below is sample code to query full text search. Basically your index entries for a person with

Name: Bob, Surname:Van de Builder, Aka1: Bobby, Aka2: Bobs, PrefferedName: Bob The Builder

The index entries will need to look like the

Key:Value
Name: Bob
Name:Van
Name:de
Name: Builder
Name: Bobby
Name: Bobs

Remember, Lucene has a white space analyser, so any names with spaces MUST become a new index entry, so what we do is split out names based on whitespaces and this becomes our collection of IndexEntries. The above is related to full text search context.

Note: If using EXACT Index match, then composite entries are needed for multiple words, since you no longer using lucene full text search capabilities. e.g.

Name: Bob The Builder

This is good to know, because things like postal code searches or Gender where exact matches are required do not need full text indexes.

Lets check out an example of querying an index.

        [Test]
        public void VerifyWhenANewClientIsCreateThatPartialNameCanBeFuzzySearchedInTheFullTextSearchIndex()
        {
            using (var agency = Data.NewTestAgency())
            using (var client = Data.NewTestClient(agency, c =>
            {
                c.Gender = Gender.Male;
                c.GivenName = "Joseph";
                c.MiddleNames = "Mark";
                c.FamilyName = "Kitson";
                c.PreferredName = "Joey";

                c.AlsoKnownAses = new List<AlsoKnownAs>
                    {
                       new AlsoKnownAs {Name = "J-Man"},
                       new AlsoKnownAs {Name = "J-Town"}
                    };
            }
                ))
            {
                var indexName = IndexNames.Clients(agency.Agency.Data);
                const string partialName = "+Name:Joe~+Name:Kitson~";
                var result = GraphClient.QueryIndex<Client>(indexName, IndexFor.Node, partialName);
                Assert.AreEqual(client.Client.Data.UniqueId, result.First().Data.UniqueId);
            }
        }
		

Dates

Notice that in some of the code, you may have noticed that when I store date entries in the index, I store them as Ticks, so this will be as long numbers, this is awesome, as it gives raw power to searching dates via longs Smile

 [Test]
        public void VerifyWhenANewClientIsCreateThatTheDateOfBirthCanBeRangeSearchedInTheFullTextSearchIndex()
        {
            // Arrange
            const long dateOfBirthTicks = 634493518171556320;
            using (var agency = Data.NewTestAgency())
            using (var client = Data.NewTestClient(agency, c =>
            {
                c.Gender = Gender.Male;
                c.GivenName = "Joseph";
                c.MiddleNames = "Mark";
                c.FamilyName = "Kitson";
                c.PreferredName = "Joey";
                c.DateOfBirth = new DateTimeOffset(dateOfBirthTicks, new TimeSpan());
                c.CurrentAge = null;
                c.AlsoKnownAses = new List<AlsoKnownAs>
                    {
                       new AlsoKnownAs {Name = "J-Man"},
                       new AlsoKnownAs {Name = "J-Town"}
                    };
            }
                ))
            {
                // Act
                var indexName = IndexNames.Clients(agency.Agency.Data);
                var partialName = string.Format("DateOfBirth:[{0} TO {1}]", dateOfBirthTicks - 5, dateOfBirthTicks + 5);
                var result = GraphClient.QueryIndex<Client>(indexName, IndexFor.Node, partialName);
                // Assert
                Assert.AreEqual(client.Client.Data.UniqueId, result.First().Data.UniqueId);
            }
        }
		

Summary

Well, I hope you found this post useful. Neo4jClientis on nuget, so have a bash using it and would love to know your feedback.

Download

NuGetPackage:
Source Code at:

Cheers

Express yourself

Everyday when you wake up and do the things you do, take a moment to ask yourself:

“What is it within me that has remained unexpressed?”

Then go about your day and find a way to express it in it’s most natural state. The light inside you is like a natural spring, the more you drink from it, the more will seep through.

Automating Windows Azure Deployments leveraging TeamCity and PowerShell

Hi Guys,

Introduction

We will cover:

  • Overview to configure multiple build projects on TeamCity
  • Configure one of the build projects to deploy to the Azure Cloud
  • Automated deployments to the Azure Cloud for Web and Worker Roles
  • Leverage the msbuild target templates to automate generation of azure package files
  • Alternative solution of generating the azure package files
  • Using configuration transformations to manage settings e.g. UAT, Dev, Production

A colleague of mine Tatham Oddie and I are currently use TeamCity to automatically deploy our Azure/MVC3/Neo4j based project to the Azure cloud. Lets see how this can be done with relative ease and is fully automated. The focus here will be based on Powershell scripts which are using the Cerebrata Command scriplets, which can be found here: http://www.cerebrata.com/Products/AzureManagementCmdlets/Default.aspx

The PowerShell script included here will automatically undeploy and redploy you azure service and will even wait until all the services are in the ready state.

I will leave you to checking those commandlets out, and they worth every penny spent.

Now lets check how we get the deployment working.

The basic idea is that you have a continuous integration build configured on the Build Server in TeamCity, then what you do is configure the CI build to generate artifacts, which are basically the output from the build that can be used by another build project e.g. You can take the artifacts for the CI build and then run Functional Tests or Integration tests builds that run totally separate from the CI build. The idea here is, your functional and integration will NEVER interfere with the CI build and the Unit tests. Thus keeping CI builds fast and efficient.

Prerequisites on Build Server

  • TeamCity Professional Version 6.5.1
  • Cloud Subscription Certificate with Private key is imported into the User Certificate Store for the Team City service account
  • Cerebrata CMDLETS

TeamCity -Continuous Integration Build Project

Ok, so, lets do a quick check at my CI build that spits out the Azure Packages.

image

As we can see above, the CI build creates an Artifact called AzurePackage.

image

The way we generate these artifacts is very easy. In the settings for the CI Build Project we setup the artifacts path.

image

e.g. MyProjectMyProject.Azurebin%BuildConfiguration%Publish => AzurePackage

So, we will look at the build steps to configure.

image

As we can see below, we just say where the MSBuild is run from and then where the unit tests dll’s are.

image

Cool, now we need to setup the artifacts and configuration.

We just mention we want a release build.

image

Ok, now we need to tell our Azure Deployment project to have a dependency on the CI project we configured above.

Team City – UAT Deployment Build Project

So lets now go check out the UAT Deployment project.

image

This project will have dependencies on the CI build and then we will configure all the build parameters so it can connect to your Azure Storage and Service for automatic deployments. Once we done here, we will have a look at the powershell script that we use to automatically deploy to the cloud, the script supports un-deploying existing deployment slots before deploying a new one with retry attempts.

Ok, lets check the following for the UAT deployment project.

image

image

The above screenshot is the command that executes the powershell script, the parameters (%whatever%) will resolve from Build parameters in Step 6 of the screen shot above.

Here is the command for copy/paste friendless. Of course if you using some other Database then you do not need the Neo4j stuff.

-AzureAccountName “%AzureAccountName%” -AzureServiceName “%AzureServiceName%” -AzureDeploymentSlot “%AzureDeploymentSlot%” -AzureAccountKey “%AzureAccountKey%” -AzureSubscriptionId “%AzureSubscriptionId%” -AzureCertificateThumbprint “%AzureCertificateThumbprint%” -PackageSource “%AzurePackageDependencyPath%MyProject.Azure.cspkg” -ConfigSource “%AzurePackageDependencyPath%%ConfigFileName%” -DeploymentName “%build.number%-%build.vcs.number%” -Neo4jBlobName “%Neo4jBlobName%” -Neo4jZippedBinaryFileHttpSource “%Neo4jZippedBinaryFileHttpSource%”

This is the input for a deploy-package.cmd file, which is in our source repository.

image

Now, we also need to tell the Deployment project to use the Artifact from our CI Project. So we setup an Artifact Dependencies as show below in the dependencies section. Also, notice how we use a wildcard, so get all files from AzurePackage (AzurePackage/**). This will be the cspackage files.

image

Notice above, that I have a SnapShot Dependency, this is forcing the UAT deployment to USE the SAME source code that the CI build project is using.

So, the parameters are as follows.

image

PowerShell Deployment Scripts

The Deployment scripts consist of three files and remember I assumed you installed the Cerebrata Management Command Scriptlets.

Ok, so lets look at the Deploy-Package.cmd file, I would like to pay my gratitude to Jason Stangroome(http://blog.codeassassin.com) for this,

Jason wrote: “This tiny proxy script just writes a temporary PowerShell script containing all the arguments you’re trying to pass to let PowerShell interpret them and avoid getting them messed up by the Win32 native command line parser.”

@echo off
setlocal
set tempscript=%temp%\%~n0.%random%.ps1
echo $ErrorActionPreference="Stop" >"%tempscript%"
echo ^& "%~dpn0.ps1" %* >>"%tempscript%"
powershell.exe -command "& \"%tempscript%\""
set errlvl=%ERRORLEVEL%
del "%tempscript%"
exit /b %errlvl%

Ok, and now here is the PowerShell code, Deployment-Package.ps1. I will leave you to read what it does. In Summary.

It demonstrates.

  • Un-Deploying a service deployment slot
  • Deploying a service deployment slot
  • Using the certificate store to retrieve the certificate for service connections via a cert thumbprint – users cert store under the service account that TeamCity runs on.
  • Uploading Blobs
  • Downloading Blobs
  • Waiting until the new deployment is in a ready state
#requires -version 2.0
param (
	[parameter(Mandatory=$true)] [string]$AzureAccountName,
	[parameter(Mandatory=$true)] [string]$AzureServiceName,
	[parameter(Mandatory=$true)] [string]$AzureDeploymentSlot,
	[parameter(Mandatory=$true)] [string]$AzureAccountKey,
	[parameter(Mandatory=$true)] [string]$AzureSubscriptionId,
	[parameter(Mandatory=$true)] [string]$AzureCertificateThumbprint,
	[parameter(Mandatory=$true)] [string]$PackageSource,
	[parameter(Mandatory=$true)] [string]$ConfigSource,
	[parameter(Mandatory=$true)] [string]$DeploymentName,
	[parameter(Mandatory=$true)] [string]$Neo4jZippedBinaryFileHttpSource,
	[parameter(Mandatory=$true)] [string]$Neo4jBlobName
)

$ErrorActionPreference = "Stop"

if ((Get-PSSnapin -Registered -Name AzureManagementCmdletsSnapIn -ErrorAction SilentlyContinue) -eq $null)
{
	throw "AzureManagementCmdletsSnapIn missing. Install them from Https://www.cerebrata.com/Products/AzureManagementCmdlets/Download.aspx"
}

Add-PSSnapin AzureManagementCmdletsSnapIn -ErrorAction SilentlyContinue

function AddBlobContainerIfNotExists ($blobContainerName)
{
	Write-Verbose "Finding blob container $blobContainerName"
	$containers = Get-BlobContainer -AccountName $AzureAccountName -AccountKey $AzureAccountKey
	$deploymentsContainer = $containers | Where-Object { $_.BlobContainerName -eq $blobContainerName }

	if ($deploymentsContainer -eq $null)
	{
		Write-Verbose  "Container $blobContainerName doesn't exist, creating it"
		New-BlobContainer $blobContainerName -AccountName $AzureAccountName -AccountKey $AzureAccountKey
	}
	else
	{
		Write-Verbose  "Found blob container $blobContainerName"
	}
}

function UploadBlobIfNotExists{param ([string]$container, [string]$blobName, [string]$fileSource)

	Write-Verbose "Finding blob $container\$blobName"
	$blob = Get-Blob -BlobContainerName $container -BlobPrefix $blobName -AccountName $AzureAccountName -AccountKey $AzureAccountKey

	if ($blob -eq $null)
	{
		Write-Verbose "Uploading blob $blobName to $container/$blobName"
		Import-File -File $fileSource -BlobName $blobName -BlobContainerName $container -AccountName $AzureAccountName -AccountKey $AzureAccountKey
	}
	else
	{
		Write-Verbose "Found blob $container\$blobName"
	}
}

function CheckIfDeploymentIsDeleted
{
	$triesElapsed = 0
	$maximumRetries = 10
	$waitInterval = [System.TimeSpan]::FromSeconds(30)
	Do
	{
		$triesElapsed+=1
		[System.Threading.Thread]::Sleep($waitInterval)
		Write-Verbose "Checking if deployment is deleted, current retry is $triesElapsed/$maximumRetries"
		$deploymentInstance = Get-Deployment `
			-ServiceName $AzureServiceName `
			-Slot $AzureDeploymentSlot `
			-SubscriptionId $AzureSubscriptionId `
			-Certificate $certificate `
			-ErrorAction SilentlyContinue

		if($deploymentInstance -eq $null)
		{
			Write-Verbose "Deployment is now deleted"
			break
		}

		if($triesElapsed -ge $maximumRetries)
		{
			throw "Checking if deployment deleted has been running longer than 5 minutes, it seems the delployment is not deleting, giving up this step."
		}
	}
	While($triesElapsed -le $maximumRetries)
}

function WaitUntilAllRoleInstancesAreReady
{
	$triesElapsed = 0
	$maximumRetries = 60
	$waitInterval = [System.TimeSpan]::FromSeconds(60)
	Do
	{
		$triesElapsed+=1
		[System.Threading.Thread]::Sleep($waitInterval)
		Write-Verbose "Checking if all role instances are ready, current retry is $triesElapsed/$maximumRetries"
		$roleInstances = Get-RoleInstanceStatus `
			-ServiceName $AzureServiceName `
			-Slot $AzureDeploymentSlot `
			-SubscriptionId $AzureSubscriptionId `
			-Certificate $certificate `
			-ErrorAction SilentlyContinue
		$roleInstancesThatAreNotReady = $roleInstances | Where-Object { $_.InstanceStatus -ne "Ready" }

		if ($roleInstances -ne $null -and
			$roleInstancesThatAreNotReady -eq $null)
		{
			Write-Verbose "All role instances are now ready"
			break
		}

		if ($triesElapsed -ge $maximumRetries)
		{
			throw "Checking if all roles instances are ready for more than one hour, giving up..."
		}
	}
	While($triesElapsed -le $maximumRetries)
}

function DownloadNeo4jBinaryZipFileAndUploadToBlobStorageIfNotExists{param ([string]$blobContainerName, [string]$blobName, [string]$HttpSourceFile)
	Write-Verbose "Finding blob $blobContainerName\$blobName"
	$blobs = Get-Blob -BlobContainerName $blobContainerName -ListAll -AccountName $AzureAccountName -AccountKey $AzureAccountKey
	$blob = $blobs | findstr $blobName

	if ($blob -eq $null)
	{
	    Write-Verbose "Neo4j binary does not exist in blob storage. "
	    Write-Verbose "Downloading file $HttpSourceFile..."
		$temporaryneo4jFile = [System.IO.Path]::GetTempFileName()
		$WebClient = New-Object -TypeName System.Net.WebClient
		$WebClient.DownloadFile($HttpSourceFile, $temporaryneo4jFile)
		UploadBlobIfNotExists $blobContainerName $blobName $temporaryneo4jFile
	}
}

Write-Verbose "Retrieving management certificate"
$certificate = Get-ChildItem -Path "cert:\CurrentUser\My\$AzureCertificateThumbprint" -ErrorAction SilentlyContinue
if ($certificate -eq $null)
{
	throw "Couldn't find the Azure management certificate in the store"
}
if (-not $certificate.HasPrivateKey)
{
	throw "The private key for the Azure management certificate is not available in the certificate store"
}

Write-Verbose "Deleting Deployment"
Remove-Deployment `
	-ServiceName $AzureServiceName `
	-Slot $AzureDeploymentSlot `
	-SubscriptionId $AzureSubscriptionId `
	-Certificate $certificate `
	-ErrorAction SilentlyContinue
Write-Verbose "Sent Delete Deployment Async, will check back later to see if it is deleted"

$deploymentsContainerName = "deployments"
$neo4jContainerName = "neo4j"

AddBlobContainerIfNotExists $deploymentsContainerName
AddBlobContainerIfNotExists $neo4jContainerName

$deploymentBlobName = "$DeploymentName.cspkg"

DownloadNeo4jBinaryZipFileAndUploadToBlobStorageIfNotExists $neo4jContainerName $Neo4jBlobName $Neo4jZippedBinaryFileHttpSource

Write-Verbose "Azure Service Information:"
Write-Verbose "Service Name: $AzureServiceName"
Write-Verbose "Slot: $AzureDeploymentSlot"
Write-Verbose "Package Location: $PackageSource"
Write-Verbose "Config File Location: $ConfigSource"
Write-Verbose "Label: $DeploymentName"
Write-Verbose "DeploymentName: $DeploymentName"
Write-Verbose "SubscriptionId: $AzureSubscriptionId"
Write-Verbose "Certificate: $certificate"

CheckIfDeploymentIsDeleted

Write-Verbose "Starting Deployment"
New-Deployment `
	-ServiceName $AzureServiceName `
	-Slot $AzureDeploymentSlot `
	-PackageLocation $PackageSource `
	-ConfigFileLocation $ConfigSource `
	-Label $DeploymentName `
	-DeploymentName $DeploymentName `
	-SubscriptionId $AzureSubscriptionId `
	-Certificate $certificate

WaitUntilAllRoleInstancesAreReady

Write-Verbose "Completed Deployment"

Automating Cloud Package File without using CSPack and CSRun explicitly

We will need to edit the Cloud Project file so that Visual Studio can create the cloud package files , as it will then automatically run the cspackage for you which can be consumed by the artifacts and hence other build projects. This allows us to bake functionality into the MSBuild process to generate the package files without the need for explicitly using cspack.exe and csrun.exe. Resulting in less scripts, else you would need a separate PowerShell script just to package the cloud project files.

Below are the changes for the .ccproj file of the Cloud Project. Notice the condition is that we generate these package files ONLY if the build is outside of visual studio, so this is nice to keep it from not always creating the packages to keep our development experience build process short. So for the condition below to work, you will need to build the project from the command line using MSBuild.

Here is the config entries for the project file.

  <PropertyGroup>
    <CloudExtensionsDir Condition=" '$(CloudExtensionsDir)' == '' ">$(MSBuildExtensionsPath)\Microsoft\Cloud Service\1.0\Visual Studio 10.0\</CloudExtensionsDir>
  </PropertyGroup>
  <Import Project="$(CloudExtensionsDir)Microsoft.CloudService.targets" />
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" />
  <Target Name="AzureDeploy" AfterTargets="Build" DependsOnTargets="CorePublish" Condition="'$(BuildingInsideVisualStudio)'!='True'">
  </Target>

e.g.

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe MyProject.sln /p:Configuration=Release

image

Configuration Transformations

You can also leverage configuration transformations so that you can have configurations for each environment. This is discussed here:

http://blog.alexlambert.com/2010/05/using-visual-studio-configuration.html

However, in a nutshell, you can have something like this in place, this means you can then have separate deployment config files, e.g.

ServiceConfiguration.cscfg

ServiceConfiguration.uat.cscfg

ServiceConfiguration.prod..cscfg

Just use the following config in the .ccproj file.

 <Target Name="ValidateServiceFiles"
          Inputs="@(EnvironmentConfiguration);@(EnvironmentConfiguration->'%(BaseConfiguration)')"
          Outputs="@(EnvironmentConfiguration->'%(Identity).transformed.cscfg')">
    <Message Text="ValidateServiceFiles: Transforming %(EnvironmentConfiguration.BaseConfiguration) to %(EnvironmentConfiguration.Identity).tmp via %(EnvironmentConfiguration.Identity)" />
    <TransformXml Source="%(EnvironmentConfiguration.BaseConfiguration)" Transform="%(EnvironmentConfiguration.Identity)"
     Destination="%(EnvironmentConfiguration.Identity).tmp" />

    <Message Text="ValidateServiceFiles: Transformation complete; starting validation" />
    <ValidateServiceFiles ServiceDefinitionFile="@(ServiceDefinition)" ServiceConfigurationFile="%(EnvironmentConfiguration.Identity).tmp" />

    <Message Text="ValidateServiceFiles: Validation complete; renaming temporary file" />
    <Move SourceFiles="%(EnvironmentConfiguration.Identity).tmp" DestinationFiles="%(EnvironmentConfiguration.Identity).transformed.cscfg" />
  </Target>
  <Target Name="MoveTransformedEnvironmentConfigurationXml" AfterTargets="AfterPackageComputeService"
          Inputs="@(EnvironmentConfiguration->'%(Identity).transformed.cscfg')"
          Outputs="@(EnvironmentConfiguration->'$(OutDir)Publish\%(filename).cscfg')">
    <Move SourceFiles="@(EnvironmentConfiguration->'%(Identity).transformed.cscfg')" DestinationFiles="@(EnvironmentConfiguration->'$(OutDir)Publish\%(filename).cscfg')" />
  </Target>

Here is a sample ServiceConfiguration.uat.config that will then leverage the transformations. Note the transformation for the web and worker roles sections. Our worker role is Neo4jServerHost and the Web is just called Web.

<?xml version="1.0"?>
<sc:ServiceConfiguration
    xmlns:sc="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"
    xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <sc:Role name="Neo4jServerHost" xdt:Locator="Match(name)">
    <sc:ConfigurationSettings>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=myprojectname;AccountKey=myaccountkey"/>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="Storage connection string" value="DefaultEndpointsProtocol=https;AccountName=myprojectname;AccountKey=myaccountkey"/>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="Drive connection string" value="DefaultEndpointsProtocol=http;AccountName=myprojectname;AccountKey=myaccountkey"/>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="Neo4j DBDrive override Path" value=""/>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="UniqueIdSynchronizationStoreConnectionString" value="DefaultEndpointsProtocol=https;AccountName=myprojectname;AccountKey=myaccountkey"/>
    </sc:ConfigurationSettings>
  </sc:Role>
  <sc:Role name="Web" xdt:Locator="Match(name)">
    <sc:ConfigurationSettings>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=myprojectname;AccountKey=myaccountkey"/>
      <sc:Setting xdt:Transform="Replace" xdt:Locator="Match(name)" name="UniqueIdSynchronizationStoreConnectionString" value="DefaultEndpointsProtocol=https;AccountName=myprojectname;AccountKey=myaccountkey"/>
    </sc:ConfigurationSettings>
  </sc:Role>
</sc:ServiceConfiguration>

Manually executing the script for testing

Prerequisites:

  • You will need to install the Cerebrata Azure Management CMDLETS from: https://www.cerebrata.com/Products/AzureManagementCmdlets/Download.aspx
  • If you are running 64 bit version, you will need to follow the readme file instructions contained with the AzureManagementCmdlets, as it requires manual copying of files. If you followed the default install, this readme will be in C:\Program Files\Cerebrata\Azure Management Cmdlets\readme.pdf
  • You will need to install the Certificate and Private Key (Which must be marked as exportable) to your User Certificate Store. This file will have an extension of .pfx. Use the Certificate Management Snap-In, for User Account Store. The certificate should be installed in the personal folder.
  • Once the certificate is installed, you should note the certificate thumbprint, as this is used as one of the parameters when executing the PowerShell script. Ensure you remove all the spaces from the thumbprint when using it in the script!

1) First up, you’ll need to make your own ”MyProject.Azure.cspkg” file. To do this, run this:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe MyProject.sln /p:Configuration=Release

(Adjust paths as required.)

You’ll now find a package waiting for you at ”C:\Code\MyProject\MyProject\MyProject.Azure\bin\Release\Publish\MyProject.Azure.cspkg”.

2) Make sure you have the required management certificate installed on your machine (including the private key).

3) Now you’re ready to run the deployment script.

It requires quite a lot of parameters. The easiest way to find them is just to copy them from the last output log on TeamCity.

You will need to manually execute is Deploy-Package.ps1.

The Deploy-Package.ps1 file has input parameters that need to be supplied. Below is the list of parameters and description.

Note: These values can change in the future, so ensure you do not rely on this example below.

AzureAccountName: The Windows Azure Account Name e.g. MyProjectUAT

AzureServiceName: The Windows Azure Service Name e.g. MyProjectUAT

AzureDeploymentSlot: Production or Staging e.g. Production

AzureAccountKey: The Azure Account Key: e.g. youraccountkey==

AzureSubscriptionId:*The Azure Subscription Id e.g. yourazuresubscriptionId

AzureCertificateThumbprint: The certificate thumbprint you note down when importing the pfx file e.g. YourCertificateThumbprintWithNoWhiteSpaces

PackageSource: Location of the .cspkg file e.g. C:\Code\MyProject\MyProject.Azure\bin\Release\Publish\MyProject.Azure.cspkg

ConfigSource: Location of the Azure configuration files .cscfg e.g. C:\Code\MyProject\MyProject\MyProject.Azure\bin\Release\Publish\ServiceConfiguration.uat.cscfg

DeploymentName: This can be a friendly name of the deployment e.g. local-uat-deploy-test

Neo4jBlobName: The name of the blob file containing the Neo4j binaries in zip format e.g. neo4j-community-1.4.M04-windows.zip

Neo4jZippedBinaryFileHttpSource: The http location of the Neo4j zipped binary files e.g. https://mydownloads.com/mydownloads/neo4j-community-1.4.M04-windows.zip?dl=1

-Verbose: You can use an additional parameter to get Verbose output which is useful when developing and testing the script, just append -Verbose to the end of the command.

Below is an example executed on my machine, this will be different on your machine, so use it as a guideline only:

<code title=Sample Deployment Execution>

.\Deploy-Package.ps1 -AzureAccountName MyProjectUAT `

-AzureServiceName MyProjectUAT `

-AzureDeploymentSlot Production `

-AzureAccountKey youraccountkey== `

-AzureSubscriptionId yoursubscriptionid `

-AzureCertificateThumbprint yourcertificatethumbprint `

-PackageSource “c:\Code\MyProject\MyProject\MyProject.Azure\bin\Release\Publish\MyProject.Azure.cspkg” `

-ConfigSource “c:\Code\MyProject\MyProject\MyProject.Azure\bin\Release\Publish\ServiceConfiguration.uat.cscfg” `

-DeploymentName local-uat-deploy-test -Neo4jBlobName neo4j-community-1.4.1-windows.zip `

-Neo4jZippedBinaryFileHttpSource https://mydownloads.com/mydownloads/neo4j-community-1.4.1-windows.zip?dl=1 -Verbose

</code>

Note: When running the PowerShell command and the 64bit version of the scripts, ensure you running the PowerShell version that you fixed in the readme file from Cerebrata, do not rely on the default shortcut links in the start menu!

Summary

Well, I hope this will help you automating Azure Deployments to the cloud, this a great way to keep UAT happy with Agile deployments to meet the goals of every sprint.

If you do not like the way we generate the package files above, you can choose to use CSRun and CSPack explicitly, I have prepared this script already, below is the code for you to use.

#requires -version 2.0
param (
	[parameter(Mandatory=$false)] [string]$ArtifactDownloadLocation
)

$ErrorActionPreference = "Stop"

$installPath= Join-Path $ArtifactDownloadLocation "..\AzurePackage"
$azureToolsPackageSDKPath="c:\Program Files\Windows Azure SDK\v1.4\bin\cspack.exe"
$azureToolsDeploySDKPath="c:\Program Files\Windows Azure SDK\v1.4\bin\csrun.exe"

$csDefinitionFile="..\..\Neo4j.Azure.Server\ServiceDefinition.csdef"
$csConfigurationFile="..\..\Neo4j.Azure.Server\ServiceConfiguration.cscfg"

$webRolePropertiesFile = ".\WebRoleProperties.txt"
$workerRolePropertiesFile=".\WorkerRoleProperties.txt"

$csOutputPackage="$installPath\Neo4j.Azure.Server.csx"
$serviceConfigurationFile = "$installPath\ServiceConfiguration.cscfg"

$webRoleName="Web"
$webRoleBinaryFolder="..\..\Web"

$workerRoleName="Neo4jServerHost"
$workerRoleBinaryFolder="..\..\Neo4jServerHost\bin\Debug"
$workerRoleEntryPointDLL="Neo4j.Azure.Server.dll"

function StartAzure{
	"Starting Azure development fabric"
	& $azureToolsDeploySDKPath /devFabric:start
	& $azureToolsDeploySDKPath /devStore:start
}

function StopAzure{
	"Shutting down development fabric"
	& $azureToolsDeploySDKPath /devFabric:shutdown
	& $azureToolsDeploySDKPath /devStore:shutdown
}

#Example: cspack Neo4j.Azure.Server\ServiceDefinition.csdef /out:.\Neo4j.Azure.Server.csx /role:$webRoleName;$webRoleName /sites:$webRoleName;$webRoleName;.\$webRoleName /role:Neo4jServerHost;Neo4jServerHost\bin\Debug;Neo4j.Azure.Server.dll /copyOnly /rolePropertiesFile:$webRoleName;WebRoleProperties.txt /rolePropertiesFile:$workerRoleName;WorkerRoleProperties.txt
function PackageAzure()
{
	"Packaging the azure Web and Worker role."
	& $azureToolsPackageSDKPath $csDefinitionFile /out:$csOutputPackage /role:$webRoleName";"$webRoleBinaryFolder /sites:$webRoleName";"$webRoleName";"$webRoleBinaryFolder /role:$workerRoleName";"$workerRoleBinaryFolder";"$workerRoleEntryPointDLL /copyOnly /rolePropertiesFile:$webRoleName";"$webRolePropertiesFile /rolePropertiesFile:$workerRoleName";"$workerRolePropertiesFile
	if (-not $?)
	{
		throw "The packaging process returned an error code."
	}
}

function CopyServiceConfigurationFile()
{
	"Copying service configuration file."
	copy $csConfigurationFile $serviceConfigurationFile
}

#Example: csrun /run:.\Neo4j.Azure.Server.csx;.\Neo4j.Azure.Server\ServiceConfiguration.cscfg /launchbrowser
function DeployAzure{param ([string] $azureCsxPath, [string] $azureConfigPath)
	"Deploying the package"
    & $azureToolsDeploySDKPath $csOutputPackage $serviceConfigurationFile
	if (-not $?)
	{
		throw "The deployment process returned an error code."
	}
}

Write-Host "Beginning deploy and configuration at" (Get-Date)

PackageAzure
StopAzure
StartAzure
CopyServiceConfigurationFile
DeployAzure '$csOutputPackage' '$serviceConfigurationFile'

# Give it 60s to boot up neo4j
[System.Threading.Thread]::Sleep(60000)

# Hit the homepage to make sure it's warmed up
(New-Object System.Net.WebClient).DownloadString("http://localhost:8080") | Out-Null

Write-Host "Completed deploy and configuration at" (Get-Date)

note, if using .Net 4.0 which I am sure you all are, you will need to provide the text files for web role and worker role with these entries.

WorkerRoleProperties.txt
TargetFrameWorkVersion=v4.0
EntryPoint=Neo4j.Azure.Server.dll

WebRoleProperties.txt
TargetFrameWorkVersion=v4.0

Thanks to Tatham Oddie for contributing and coming up with such great ideas for our builds.
Cheers

Romiko

Neo4jClient Primer

Introduction

Neo4jClient is a .NET client for the Neo4j Rest Server built by a colleague of mine Tatham Oddie. Currently we have a Neo4jClient, which is the latest source code build. This can be found on NuGet with package name Neo4jClient- Neo4jClient is a .NET client for the Neo4j Rest Server.

NuGetPackage:
Source Code at:

It leverages RestSharp and Newtonsoft.json for Rest and Json serialization/deserialization respectively.

The client also supports executing Gremlin queries in the following ways

  • Send raw gremlin statements that return scalar results
  • Send raw gremlin statements that return enumerable collection of nodes
  • Type safe lambda expressions

The raw version is provided to provide flexibility if you find certain lambda expressions are not supported, over time, more expressions will be added. We will be extending the client over time to certain more expressions as we find ourselves needing new expressions.

Lets have a look at some samples.

First you will need to install the package, so at the package console manager type:

install-package neo4jclient

Once this is installed, you can then of course load the IGraphClient in the entry point of your application, manually or using an IoC. The concept of the GraphClient is to provide basic CRUD operations.

The other powerful aspect is that the IGremlinQuery interface provide cool extensions into Gremlin so that a series of extensions methods will support out and in vertices and edges querying.

Below is a diagram illustrating the core concept.Basically we have a GraphClient, GremlinQuery and NodeReference. Notice that you can also query directly off a NodeReference. A NodeReference will represent a reference to a Vertice in the database. A Node will store the actual data, which is cast to a specific type with generics.

image

Entry Point

Here is sample code in loading the GraphClient using an IoC. It is this easy to get it started.

 builder.Register<IGraphClient>(context =>
            {
                var resolver = context.Resolve<IRoleEndpointResolver>();
                var baseUri = resolver.GetNeo4JBaseUri();
                var graphClient = new GraphClient(baseUri);
                graphClient.Connect();
                return graphClient;
            })
            .SingleInstance();

Type Safe Lambda Expressions

Lets look at the cool features we can do. Below is a sample query we can run.

public Node<User> GetUser(User identifier)
        {
            if (identifier.IsSysAdmin)
            {
                return graphClient
                    .RootNode
                    .In<User>(Administers.TypeKey, u=> u.Username == identifier.Username)
                    .SingleOrDefault();
            }

            return graphClient
                .RootNode
                .Out<CustomerSite>(Hosts.TypeKey, a => a.Key == identifier.CustomerSiteKey)
                .In<User>(UserBelongsTo.TypeKey, u => u.Username == identifier.Username)
                .SingleOrDefault();
        }

You can even then run queries off a NodeReference, lets look at an example.

public int CreateUser(User user, NodeReference<Company> companyReference)
{
    return companyReference
                .InE(UserBelongsTo.TypeKey)
                .OutE<User>(u=>u.Username == user.Username)
}

You have the flexibility.

Creating Nodes and Relationships

You notice that in the above, we had a TypeKey representing the relationship, this is important, you can enforce very strict rules on your nodes, we can define a class that represents a relationship and enforce which source and target nodes or data models it is allowed to have e.g.

    public class UserBelongsTo :
        Relationship,
        IRelationshipAllowingSourceNode<User>,
        IRelationshipAllowingTargetNode<CustomerSite>
    {
        public UserBelongsTo(NodeReference targetNode)
            : base(targetNode)
        {
        }

        public const string TypeKey = "USER_BELONGS_TO";
        public override string RelationshipTypeKey
        {
            get { return TypeKey; }
        }
    }

This means, that you get compile time safety, if you try and create a node in the database with a relationship. Lets look at a Create statement.

        public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();

            var node = graphClient.Create(
                user,
                new UserBelongsTo(customerSite));

            return node;
        }

Above, if you tried to swap the create, it would not work e.g.

        public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();
            var node = graphClient.Create(
                customerSite,
                new UserBelongsTo(user));

            return node;
        }

Updates

You can also update nodes, this is done by passing in a NodeReference and a delegate e.g.

public void UpdateUser(User user, Action<User> updateCallback)
{
            graphClient.Update(userNode.Reference, u =>
                {
                    updateCallback(u);
                });
}

Notice, you also get type safety here as well. The reference to the delegate/method with then get executed when neo4jClient persists the data.

Here is the sample updateCallback call to the above method. Notice I am in fact using the MVC updateModel facility to do the mappings for me, any how, you can update the object using your logic of course, there is no restrictions here. Here “this” refers to the MVC Controller class. It is just a nice way to get MVC to compare the user with the user from the DB and then merge the changes, no need to write logic to merge changes if using in the context of MVC, since it has a nice UpdateModel method that we can use. Othewise you would be using AutoMapper or something even nicer like ValueInjecter(http://valueinjecter.codeplex.com/).

userService.UpdateUser(user, u => this.UpdateModel(u,
                UpdateModel,
                tu => tu.GivenName,
                tu => tu.FamilyName,
                tu => tu.Username,
                tu => tu.EmailAddress,
                tu => tu.BusinessPhone,
                tu => tu.MobilePhone
            ));

The update above looks a bit tricky at first as we see a method pointer to a method pointer i.e. two action calls, in fact there are 4, one from the graph client, one from the service, one from the extended version of UpdateModel and then the updateModel itself.

The reason why I have a custom extension method for UpdateModel, is so we can Explicitly set which columns to update. Remember UpdateModel takes a callback and a list of properties to explicitly update, you can of course just call the default version if ALL fields need to be updated.

userService.UpdateUser(user, UpdateModel);

Below is the code for the extended UpdateModel.

 public static class ControllerExtensions
    {
        public static void UpdateModel<TModel>(this Controller controller, TModel instanceToUpdate, Action<TModel, string[]> updateModelCallback, params Expression<Func<TModel, object>>[] includeProperties)
        {
            var propertyNames = GetPropertyNames(includeProperties);
            updateModelCallback(instanceToUpdate, propertyNames.ToArray());
        }

        public static IEnumerable<string> GetPropertyNames<T>(params Expression<Func<T, object>>[] action)
        {
            return action
                .Select(property =>
                {
                    var body = property.Body;

                    var unaryExpression = body as UnaryExpression;
                    if (unaryExpression != null) body = unaryExpression.Operand;

                    return (MemberExpression) body;
                })
                .Select(expression => expression.Member.Name);
        }
    }

The above extension method will now allow you to call the UpdateModel with type safety on the model fields to explicitly update. As i mentioned, if you need a simpler update to default to all fields then this call will work:

Deletes

You can also delete data. Notice the pattern here, get a node reference then run an operation.

graphClient.Delete(node.Reference,DeleteMode.NodeAndRelationships);

Relationships

You can also create relationships between existing nodes.

graphClient.CreateRelationship(customerNodeReference, new Speaks(languageNode.Reference));

Scalar Query Result – Raw

You might find that a complex lambda is not support, in which case you can execute a raw statement directly an still get type safety e.g.

var count = g.v(0).out('IsCustomer').Count()

IEnumerable Query Result – Raw

We can also do raw queries that return an enumerable of a node.

var users = graphClient.ExecuteGetAllNodesGremlin<IsCustomer>("g.v(0).out('IsCustomer'){it.'Name' == 'BobTheBuilder'}");

Rest under the hood

The Client is smart enough to query the rest endpoint on the server and utilize the correct rest points. So the baseUri would be something like http://locahost:7474/db/data

The graphclient will do a Http get request with application/json to the above endpoint and will retrieve the following response.

{
  "index" : "http://localhost:7474/db/data/index",
  "node" : "http://localhost:7474/db/data/node",
  "reference_node" : "http://localhost:7474/db/data/node/0"
}

The above endpoints is the fundamental way to run rest queries, of course the graphclient does all the work for you, however it is always nice to know how it works.

Summary

So this is my little primer on the project we working with and it is fantastic working with someone like Tatham Oddie who built this brilliant tool for us to use.

We will be extending this tool as we go along and build new functionality as we need it. Not all Lambda expressions are supported yet and it is limited in tis regard but it is easy to extend going forward.

Identifying our inner urges

We all want the ability to do whatever we want, I mean in context of what our conscious will allow. In our lives we must have quick reflexes, accurate time and many skills.

The years grow shorter as we get older, and so the days get shorter. So what really matters in our lives is not the car you drive, the house you going to buy or the fantastic job you got. The ONLY thing that matters is accomplishing YOUR visions of what you want to do with the greatest priority.

For most, there is no end to desire, however let us have a look at it in another view, we must feel satisfaction in our lives. It may be the job you do, caring for the kids, surfing a magnificent wave on a cold morning or free climbing a mountain.

Todays meditation brings the realization  that we should never undercut ourselves or our ambitions, we should follow our inner desires.

We have been brought up to become “Adults”, which means what, what have schools and education been telling us? To feed the mind and not the body? To supress playfulness. A simple comparison will be a bus full of adults on the way to work with a bus full of children on their way to school, adulthood should not be boring and supressed, it should be as playful and innocent as when we were children, even if it is only 5 minutes of a day, this is the time where we can connect.

Therefore, we must identify our inner longings and dispatch them with the accuracy of an Archer.

The system that says:

Child –> School –> University –> Job –> Partner –> Mortgage –> Marriage –> Kids –>….

is out of date, our lives have endless paths to take, we should remember that nobody can penetrate the deepest parts of you, and so there is nothing to fear and nobody to impress or prove. We all have our skills and a true Team will never try change a member of the team, a true team is made whole by its parts, each being their own person, contributing their part.

So, if you feel you need to make someone proud, then you are perhaps on the wrong path, the only thing you need to impress or prove, is following that which lies dormant inside and is longing to come out and do, so if it is painting, then paint, if it is selling everything and going backpacking, then it must be done.

Neo4j and gremlin plugin install guide

29/08/2011: OBSOLETE – Now baked into the Core of Neo4j.

Hi,

I was having some difficulties getting the Gremlin query plugin working correctly with the Neo4j server which we will host on a Windows Azure VM.

Below is some steps to get this working nicely.

Firstly you will of course need to have Neo4j running. Then all we need to do is install the following:

Java JDK – Here is my version
java version “1.6.0_26”
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

JDK is needed to compile the plugin.

Maven 2.2.1 (There is compilation errors with SnapShot compiles with 3.0.3 at time of writing)
http://www.apache.org/dyn/closer.cgi/maven/binaries/apache-maven-2.2.1-bin.zip

Also, we need MVN, this is used to compile the Gremln Plugin. I was have problems with

Neo4j gremlin plugin
https://github.com/neo4j/gremlin-plugin

I like to setup environment variables to Neo4j server folder, java_home and also the maven location.

image

Once done we can then compile the plugin and copy it into the Neo4j plugins folder.

mvn clean package 
copy target\neo4j-gremlin-plugin-0.1-SNAPSHOT.jar $NEO4J_HOME\plugins 
cd $NEO4J_HOME\bin\neo4j.bat restart

Compiled version of the plugin.

image

Here we can see the plugin in the folder.

image

Now, to ensure Neo4j has the plugin, we can execute a curl command to check the extension is installed:

C:\Users\Romiko>curl localhost:7474/db/data/
{
  "relationship_index" : "http://localhost:7474/db/data/index/relationship",
  "node" : "http://localhost:7474/db/data/node",
  "relationship_types" : "http://localhost:7474/db/data/relationship/types",
  "extensions_info" : "http://localhost:7474/db/data/ext",
  "node_index" : "http://localhost:7474/db/data/index/node",
  "reference_node" : "http://localhost:7474/db/data/node/0",
  "extensions" : {
    "GremlinPlugin" : {
      "execute_script" : "http://localhost:7474/db/data/ext/GremlinPlugin/graphd
b/execute_script"
    }
  }
}

As we can see above, the rest result from the server has the GremlinPlugin Extension. In fact we can now do an HTTP Post Gremlin query to get the nodes from the object graph in the database.

e.g.

I want to see if I have a Node at the second level that has a relationship of type Related To with the Out Direction.

g.v(1).outE(‘RELATED_TO’)

Now we need to URL encode this.

+g.v(1).outE(%27RELATED_TO%27)

curl -d “script=+g.v(1).outE(%27RELATED_TO%27)” http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

as we can see the output is:

C:\Users\Romiko>curl -d “script=+g.v(1).outE(%27RELATED_TO%27)” http://localhost

:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

[ {

“start” : “http://localhost:7474/db/data/node/1″,

“data” : {

},

“self” : “http://localhost:7474/db/data/relationship/23″,

“property” : “http://localhost:7474/db/data/relationship/23/properties/{key}”,

“properties” : “http://localhost:7474/db/data/relationship/23/properties”,

“type” : “RELATED_TO”,

“extensions” : {

},

“end” : “http://localhost:7474/db/data/node/2″

} ]

We have Node 23 being related to Node 2. (Remember Node 0, then Node 1).

the above result can be confirmed in the gremlin console (now baked into Neo4j) as of June 2011.

Note: The gremlin extension that is now part of theneo4j server\lib extensions is not for rest API queries, you still need this plugin!

  • gremlin> g.v(1).outE(‘RELATED_TO’)
  • ==> e[23][1-RELATED_TO->2]
  • gremlin>

Here is a screenshot of the gremlin console now baked into Neo4j.

image

image

Hope this gets you started with Neo4j and the gremlin plugin query language Smile

I might be looking at building a custom IQuerable expression translation, so we can then use Linq to query a gremlin based API. Might be fun to do, but first need to learn more about gremlin and Neo4j.

There is a fluent API for gremlin queries you can leverage as a .Net client:

NuGetPackage:
Source Code at:

Cheers

WCF Architecture/Extensibility Overview

Hi Guys,

This post is going to discuss some basic high level aspects of WCF. Below is a diagram of the architecture for it.

Some high level facts.

  • Message Contract is the structure of an actual WCF Message, it describes the nature of the message
  • A Data Contract is the PAYLOAD or actual data, which is embedded into the Message.
  • The Service runtime is primarily concerned with processing the content of message bodies
    • The message layer is concerned with “channels” and channel stacks (More than one channel)
      There are two types of channels
      Protocol Channel – Message Header Management – WS-Security/WS-Reliability
      Transport Channel – How data is communicated/translated/encoded/decoded on the wire. Http, Netmsmq
    • Hosting – WCF can be hosted in a Windows Service, Executable, IIS WAS or IIS. You can even run it inside a NServiceBus host if you wanted.

We all know the ABC’s of WCF.

A service will need an Address, Binding and a Contract. But there is allot more to WCF than meat the eye.

Behaviors
Control various run-time aspects of a service, an endpoint, a particular operation, or a client. You have common behaviors affect all endpoints globally,
Service behaviors affect only service-related aspects,
Endpoint behaviors affect only endpoint-related properties, and
Operation-level behaviors affect particular operations.

e.g. One service behavior is throttling, which specifies how a service reacts when an excess of messages threaten to overwhelm the system. An endpoint behavior, such as where to find a security credential.

In regards to service behaviours, one aspect that is overlook is Instance and Concurrency modes. Read more about it further down in this article.

Instances and Concurrency

This is often overlook, always be aware of how you write your WCF service and ensure the code is thread safe and can handle multiple instances and concurrency aspects, else you might find your WCF services not scalable! These are things you should always think about BEFORE your write the service. You should read this article to get a better understanding of it.

http://msdn.microsoft.com/en-us/library/ms731193.aspx

Have a read, and ensure you classes etc are thread safe so they can scale, no shared variables etc in your WCF code that maintain a state at the service layer, you will find yourself in deep water. You can use sessions, instances or concurrency mode combinations to control these aspects.

Here is an interesting  example of customizing this option, which should get you thinking about how you combine these sort of behavioural modes!

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
    public class ProductsService : IProductsService
    {
      //Your Service Logic
     }

Per-call services are the Windows Communication Foundation default instantiation mode. When the service type is configured for per-call activation, a service instance, a common language runtime (CLR) object, exists only while a client call is in progress. Every client request gets a new dedicated service instance. This keeps the lifetime of objects short as possible.

  1. The client calls the proxy and the proxy forwards the call to the service.
  2. Windows Communication Foundation creates a service instance and calls the method on it.
  3. After the method call returns, if the object implements IDisposable, then Windows Communication Foundation calls IDisposable.Dispose on it.

Single: Each instance context is allowed to have a maximum of one thread processing messages in the instance context at a time. Other threads wishing to use the same instance context must block until the original thread exits the instance context.

Can you see something here, this combination is irrelevant? Since PerCall is here,the proxy will never allow multiple threads, since the proxy will know there is an instance already, so some combinations will never need to be explicit, such as the redundant attributes in the above code.

Dispatcher Runtime And Client Runtime

What if you want to customize Wcf? How about introducing a custom Encoding/Decoding Algorithm or custom compression/validation system or a custom error handler for legacy systems?

Lets get even more fancy, how about a custom instance provider that can hydrate and dehydrate WCF instances to and from a database for  long running transactions, similar to the idea of Saga’s in NServiceBus…

This can all be done on the client runtime or dispatcher on the service.

There is allot going on in WCF and there is several posts on making a Hello World WCF service, lets skip all that and get down to extensibility of WCF. We will focus on the dispatcher and message inspectors. Lets check what we can do firstly on the client side and then on the server side.

Here is an overview of the architecture.

image

From the above diagram you can see that WCF is very extensible, there are hooks in the architecture where you can extend the functionality of WCF.

The  client runtime is responsible for translating method invocations into outbound messages, pushing them to the underlying channels, and translating results back into return values and out parameters.

This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, operation selection, message encoding and other extensibility functionality.

In the service, the dispatcher runtime is responsible for pulling incoming messages out of the underlying channels, translating them into method invocations in application code, and sending the results back to the caller. This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, message filtering, encoding and other extensibility functionality.

There is numerous examples here:

http://msdn.microsoft.com/en-us/library/ff183867.aspx

Here is an example of a WCF Service using a Behaviour for JSON Serialization. Notice the different level of behaviours from EndPoint Behaviours/Service Behavoirs etc, also notice we have a JSON which is done in the EndPoint Behaviour. Also notice the bindings, we have different types for different clients, .Net can use the webHttpBinding and Java clients can use the BasicHttpBinding.

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="BaseBehaviors">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" httpGetUrl="Products/GetList" httpsGetUrl="Products/GetList" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="BaseHttpEndpointBehavior">
        </behavior>
        <behavior name="jsonBehavior">
          <enableWebScript  />
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" />

    <services>
      <service behaviorConfiguration="BaseBehaviors" name="Romiko.MyService">
        <endpoint name="ProductService" address="Products" behaviorConfiguration="BaseHttpEndpointBehavior"
          binding="basicHttpBinding" bindingConfiguration=""
          contract="Romiko.IProductsService" />
        <endpoint name="ProductServiceSSL" address="ProductsSSL" behaviorConfiguration="BaseHttpEndpointBehavior"
          binding="basicHttpBinding" bindingConfiguration="SecureSSL"
          contract="Romiko.IProductsService">
        </endpoint>
        <endpoint name="ProductsServiceJSON" address="ProductsJSON" behaviorConfiguration="jsonBehavior"
                  binding="webHttpBinding" bindingConfiguration=""
                  contract="Romiko.IProductsProductsService" />
        <endpoint name="ProductsServiceJSONSSL" address="ProductsJSONSSL" behaviorConfiguration="jsonBehavior"
                  binding="webHttpBinding" bindingConfiguration="SecureSSLWeb"
                  contract="Romiko.IProductsService">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost/Products" />
            <add baseAddress="https://localhost:443/Products" />
          </baseAddresses>
        </host>
      </service>
    </services>
    
    
    <bindings>
      <basicHttpBinding>
        <binding name="SecureSSL">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding name="SecureSSLWeb">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
  </system.serviceModel>

 

Well, I hope this helps you get your toes a little deeper into WCF, so the next time you write a WCF service you can nut out all the architectural principles BEFORE writing the code.

References:

http://msdn.microsoft.com/en-us/library/ff183867.aspx

http://msdn.microsoft.com/en-us/library/ms733128.aspx

PayPal Payment Standard IPN/PDT–Asynchronous Processing

Hi Guys,

I am currently working on a personnel project to interface with PayPal Payment Standard on an MVC3 and Windows Azure based application. I needed to find a nice way to convert request/response objects from PayPal to their corresponding IPN/PDT objects.

I want a payment solution that can be easily scaled on high load. So we will leverage NServiceBus as the front end –> back end service bus infrastructure. This allows decoupling of payments from the functionality of the site, so under high load the payments will not restrict the usability of the site, This is done with MSMQ and NServicebus. I will show a basic example of how.

Below is a DTO you can use for IPN or PDT data. Of course you will need logic to convert http request/response data to this DTO and you have many ways of doing so.

More information about PDT/IPN variables can be found here:

https://www.paypal.com/IntegrationCenter/ic_ipn-pdt-variable-reference.html
 https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables#id091EB0901HT

You can then take and IPN and convert the request form/query string values  to a DTO e.g.

  _myIpnNotification.Invoice = _myRequest["invoice"];

The same applies for the PDT which you will receive out of band on a separate listener.

 

Here are the different type of transaction types and status

 [Serializable]
    public enum PdtStatus
    {
        Unknown = 0,

        [Description("SUCCESS")] Success,
        [Description("FAIL")] Fail
    }

  [Serializable]
    public enum IpnStatus
    {
        Unknown,
        [Description("verified")] Verified,
        [Description("invalid")] Invalid
    }

    [Serializable]
    public enum TransactionType
    {
        //[Description(null)]
        Unknown = 0,

        [Description("cart")] Cart,

        [Description("express_checkout")] ExpressCheckout,

        [Description("merch_pmt")] MerchantPayment,

        [Description("send_money")] SendMoney,

        [Description("virtual_terminal")] VirtualTerminal,

        [Description("web_accept")] WebAccept,

        [Description("masspay")] MassPayment,

        [Description("subscr_signup")] SubscriptionSignUp,

        [Description("subscr_cancel")] SubscriptionCancellation,

        [Description("subscr_failed")] SubscriptionPaymentFailed,

        [Description("subscr_payment")] SubscriptionPayment,

        [Description("subscr_eot")] SubscriptionEndOfTime,

        [Description("subscr_modify")] SubscriptionModification
    }

Security, if you want the best security, never trust anything from Paypal, always double check the data, so when an IPN comes in, take that data and do a check with Payal and then verify the results match, and the same with PDT data.

This means checking the IPN data – currency, amount, date, subscription type etc is the same as what is in your transaction log, this way, any spoofing is protected, here is an example of a rule.

 public interface IRulesCommon
    {
        List<Error> ApplyRulesStandardPayments( decimal resultAmount, Currency resultCurrency, string receiver, TransactionLog transaction);
    }
 public interface IRulesPaypal
    {
        List<Error> ComparePaymentFrequencyTransactionStatus(TransactionLog transaction, TransactionType transactionType);

        //Used by IPN Only for SignUp
        List<Error> CheckPaymentFrequencyOnSignupIpn(TransactionLog transaction, TimePeriod subscriptionPeriod);

        List<Error> CheckSubscriptionPayment(decimal mcAmount3, Currency currency, string receiver,
                                             TransactionLog transaction);



        List<Error> CheckSubscriptionSignUpCancel(decimal mcAmount3, string recurring, Currency currency, string receiver,
                                     TransactionLog transaction);
    }

 

Unfortunately, I cannot show you implementation logic for this, as it may breach the security of my site if something is found which can be compromised, however this interface is a good start.

As you can see above, I do allot of checks, I ensure the amount, currency, receiver all match the original transaction object.

So, now we have an IPN or PDT listener, so say I have an MVC3 listener controller like so:

 public class IpnController : Controller
    {
        public readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        readonly IBus _bus; //NServiceBus Object
        readonly IIpnDataProvider _ipnDataProvider;
        readonly ISettingsDataProvider _settings;

        
        //Constructor Inject with Autofac
        public IpnController(IBus bus, IIpnDataProvider ipnDataProvider, ISettingsDataProvider settings)
        {
            _bus = bus;
            _settings = settings;
            _ipnDataProvider = ipnDataProvider;
        }

        /// <summary>
        /// Expects a post with variables like: ?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User&notify_version=2.6&custom=1||1||myredirecturl&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.0
        /// </summary>
        /// <returns></returns>
        public ActionResult Ipn()
        {
            string rawData = Extract.GetHttpRawData(Request);

            if (_settings.AuditHttpEnabled)
                Audit.AuditHttp(rawData, _bus, Request.RawUrl); //Asynchronous auditing of the IPN message for audit tracking

            if (_ipnDataProvider != null && _ipnDataProvider.MandatoryDataSpecified)
            {
                try
                {
                    var message = new PaypalIpnMessage
                        {
                            OriginalHttpRequest =
                                rawData,
                            MessageId = Guid.NewGuid(),
                            InvoiceId = _ipnDataProvider.TransactionId,
                            Notification =
                                new ResponseToIpnNotification(Request.Form).GetIpnNotification()
                        };
                    _bus.Send(message); //Asynchronous processing of the payment message to the backend systems
                }
                catch (Exception e)
                {
                    Log.Info(e);
                    Log.Info(rawData);
                    return new HttpStatusCodeResult(400);
                }
                return View();
            }

            Log.Info("Request data does not contain mandatory fields e.g. MerchantId and MerchantTransactionId");
            Log.Info(rawData);
            return new HttpStatusCodeResult(“_codeResult”);
        }
    }

 

As you can see above, any payment processing is now totally decoupled, the Front End and Back End work independent of each other with no synchronous calls. Yes this is all wrapped within a MSDTC transaction. I prefer this than using WCF, due to no issue in dealing with latency and I have guaranteed delivery of my crucial message.,You will write a similar controller for the PDT out of band response as well. We also leveraging an IoC container to automatically inject the service bus object into the controller as well as other dependencies. My favourite is Autofac.

 public ActionResult Process()
        {
            string rawData = Extract.GetHttpRawData(Request);
            if (_settings.AuditHttpEnabled)
                Audit.AuditHttp(rawData, _bus, Request.RawUrl);

            if (_successDataProvider != null && _successDataProvider.MandatoryDataSpecified)
            {
                try
                {
                    var message = new PaypalPdtMessage
                        {
                            OriginalHttpRequest =rawData,
                            MessageId = Guid.NewGuid(),
                            TransactionIdForPdtToken = _successDataProvider.TransactionIdForPdtToken,
                            AmountPaidRecordedByPdt = _successDataProvider.AmountPaidRecordedByPdt,
                            InvoiceId = _successDataProvider.TransactionId,
                            
                        };

                    _bus.Send(message);
                   
                }
                catch (Exception e)
                {
                    Log.Info(e);
                    Log.Info(rawData);
                }

                return Redirect(_successDataProvider.MerchantRedirectURL);
            }


            Log.Info("Request data does not contain mandatory fields e.g. MerchantId");
            Log.Info(rawData);
            return new HttpStatusCodeResult(“_codeResult”);
        }
    }

I hope this gives you some ideas for developing robust payment options for your site and provide a nice user experience for users Smile

Remember to also deal with your dates in UTC format, and ensure the date kind property is set to UTC as well for extra safety when storing the transaction date.

So in a nutshell, you can have top notch validation that protects from spoofing attacks if you ALWAYS take a IPN and PDT and compare it with the original transaction object! If you keep your transaction id’s unpredictable, users can never guess someone else  transactionid and hijack it for payment. So you can have a nice invoice number e.g. 90124, but the transactionid is not easy to predict, it could be a Guid etc.

 

Imagine if the above was not the case a smart user could then create two transactions, one for a cheap item and one for an expensive one, here can then hijack the ipn/pdt, or send a FAKE pdt/ipn to your system, and then swap the item information around, and then later cancel the expensive item transaction, however, he hijacked the cheap item and changed the item list and transactionid, hence why I say, check AMOUNT, CURRENCY, Items, TranactionID etc. There are allot of sites out there that are easy to hack due to them not doing these sort of double checks on Ipn and Pdt messages from Paypal. If I know you IPN or PDT listener, I can send fake messages in and try guess weaknesses by using methods such as generating two transactions on the site and trying to swap items around, currencies, etc In fact, because they cannot guess my transaction number, I do not need to check items in the list, as this is not processed in my listener, so if they changed a flower pot to a BMW, who cares, I ignore this sort of data in an IPN/PDT, as long as the fundamentals are the same we in good shape.

Matthew Will and I spent allot of time think these sort of scenarios through and nutted out a nice secure solution. The above samples should get you started in the right direction for Paypal Standard integration. Allot of implementation logic is left out, and this is done on purpose.

Cheers

SysTrans Enterprise 7–Translation Server / Web Translations

Hi,

The SysTrans (http://www.systransoft.com/) documentation discusses the Ajax API and SOAP API, however most of the time for web sites, we want a way to translate all web pages.

 

The best thing to do is to create a control and add it to the master page. Below is a sample test page used that can be incorporated into any Master Page or Layout.cshtml page for MVC.

I have refrained from using JQuery for the Base64 encoding/decoding as I wanted a solution that does not rely on any external assemblies, of course you can decided to use JQuery and reduce the amount of scripting on the page.

SysTrans will basically send a query to their server when the page loads and it will take your url e.g.

http://mywebsite.com.pages and will then modify it to:

http-mywebsite.com.pages

and then it will Base64 encode the url into a url paramter for paramter name: &systranuid

The best way to see this is to use Fiddler, and see how translation works.

Below is the test page you can use to play with and get a feed. The Encode/Decode code comes directly from the systrans web translation javascript, and is exactly the same as the JQuery library as far as how the algorithm works.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
    <div class="translate">
        Translate this page
        <div class="selectBox">
            <span class="arrow"></span>Change Language
            <ul>
                <li><a id="languageLinks0" href="en_fr">French</a> </li>
                <li><a id="languageLinks1" href="en_de">German</a> </li>
            </ul>
        </div>
    </div>

    <script type="text/javascript">
        window.onload = assignLinksToLanguage;
        var translationServer = "http://mySysTransServer/turl/?systranprofile=0&systranpopup=0&systranpopupmode=0&systranuid=";

        function assignLinksToLanguage() {

            var varLink;
            var counter = 0;
            var linkKey = "languageLinks";
            varLink = document.getElementById(linkKey + counter);

            while (varLink != null) {
                var language = varLink.href.substring(varLink.href.lastIndexOf("/") + 1)
                var encodedUrl = encodeTranslationSite(language);
                varLink.href = translationServer + encodedUrl;
                counter++;
                varLink = document.getElementById(linkKey + counter);
            }
        }


        function encodeTranslationSite(language) {
            return encode64(document.URL.replace("://", "-") + "/" + language);
        }

        var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

        function encode64(input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            do {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
            } while (i < input.length);

            return output;
        }

        function decode64(input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            do {
                enc1 = keyStr.indexOf(input.charAt(i++));
                enc2 = keyStr.indexOf(input.charAt(i++));
                enc3 = keyStr.indexOf(input.charAt(i++));
                enc4 = keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }
            } while (i < input.length);

            return output;
        }    
    </script>
</body>
</html>

Hope this gets you started using the Enterprise Version of Systrans.

Linq to SQL Anti-Patterns–Dealing with nullable types

Hi Guys,

This blog will demonstrate some bad habits in LinqToSql and how to deal with nullable types that provide clean code without null checks all over and improved performance on the projections.

In one of my posts I recommended and demonstrated value of using NHProf, well the same goes for Linq to SQL. You can download a trial here:

http://l2sprof.com/

It is really easy to use, the the case of ASP.NET, just add a global.asax or edit an existing one’s code behind like so:

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            try
            {

                var profileOn = false;
                bool.TryParse(ConfigurationManager.AppSettings["EnableLinqToSQLProfile"], out profileOn);
                if (profileOn)
                    HibernatingRhinos.Profiler.Appender.LinqToSql.LinqToSqlProfiler.Initialize();
            }
            catch (Exception)
            {
               Debug.WriteLine("Could not initialize LinqToSql Profiling");
            }
        }

Excellent, then add a reference to the HibernatingRhinos.Profiler.Appender.dll which I guess can be in your lib folder.

Right, now lets have a look at some customer code that is causing 2 hits to the database, which we can reduce it to one hit.

Old Code causing 2 hits:

 public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(i => i.PageID == pageID);

            if (result.Any())
            {
                return result.First().CaseStudyId;
            }

            return -1;
        }

So from above, if we attach the L2SQL profiler, we will see 2 exact same queries going to the DB, one for the .Any() and then the other for the c.First.

image

Useful Query: (Note the extra columns we do not need, we come back to this, as I do not like the projection here, too many columns, I just need the PageID!!)

SELECT TOP ( 1 ) [t0].[CaseStudyId],
                 [t0].[PageID],
                 [t0].[Title],
                 [t0].[ShortDescription],
                 [t0].[LongDescription],
                 [t0].[Challenge],
                 [t0].[Solution],
                 [t0].[Results],
                 [t0].[ImageURL],
                 [t0].[Rank],
                 [t0].[Visible],
                 [t0].[ModifiedById],
                 [t0].[DateCreated],
                 [t0].[DateModified]
FROM   [dbo].[CaseStudy] AS [t0]
WHERE  [t0].[PageID] = 128 /* @p0 */

So the extra query is:

SELECT (CASE 
          WHEN EXISTS (SELECT NULL AS [EMPTY]
                       FROM   [dbo].[CaseStudy] AS [t0]
                       WHERE  [t0].[PageID] = 128 /* @p0 */) THEN 1
          ELSE 0
        END) AS [value]

Lets Improve it:

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID);
            return result == null ? -1 : result.CaseStudyId;
        }

Now in the profiler, we will see only 1 statement being executed, as we removed the Any() extension method.

image

The same goes for counts

Old code – 2 queries to the DB:

public int GetCaseStudyIDByPageID(int pageID)
        {
            var r = from I in DB.CaseStudies
                    where I.PageID == pageID
                    select I.CaseStudyId;

            return r.Count() == 0 ? -1 : r.First();
        }

 

New optimised code:

      public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID);
            return result == null ? -1 : result.CaseStudyId;
        }

 

Let’s improve it further by using projections to reduce the number of columns coming back, if you look at the result, we get all the columns back, which is extra data over the wire:

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => (int?)z.CaseStudyId).FirstOrDefault();
            return result ?? -1;

        }
SELECT TOP ( 1 ) [t1].[value]
FROM   (SELECT [t0].[CaseStudyId] AS [value],
               [t0].[PageID]
        FROM   [dbo].[CaseStudy] AS [t0]) AS [t1]
WHERE  [t1].[PageID] = 128 /* @p0 */

This is much less columns being returned.

image

However, we can improve this further by creating a method to handle this for us, how about something along the lines of NullableFirstOrDefault….

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = NullableFirstOrDefault(_db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId));
            return result ?? -1;

        }


        public Nullable<T> NullableFirstOrDefault<T>(IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

Now, we get the same result, with limited projection over the wire, but we can then use this as an extension method to optimise all nullable  first or defaults.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ROMIKO.NET.LINQ.Extensions
{
    public static class LinqToSqlExtension
    {


        public static Nullable<T> NullableFirstOrDefault<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

        public static IQueryable<Nullable<T>> SelectNullable<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z);
        }
    }
}

and now, we can use it like this, without any null checks

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId).NullableFirstOrDefault();
            return result ?? -1;

        }

 

The result is the same, but easier to read code and faster queries Smile

Lets take it a step further and make it even easier to read the code by introducing extension methods that allows you to provide default values for nullable types (Matthew, you a geek!)

        public static T FirstOrDefault<T>(this IQueryable<T> input, T defaultValue) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault() ?? defaultValue;
        }

So our LINQ extension class looks like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MATTHEWWILLS.NET.LINQ.Extensions
{
    public static class LinqToSqlExtension
    {


        public static Nullable<T> NullableFirstOrDefault<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

        public static T FirstOrDefault<T>(this IQueryable<T> input, T defaultValue) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault() ?? defaultValue;
        }


        public static IQueryable<Nullable<T>> SelectNullable<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z);
        }
    }
}

Nice, now look how easy the code is to read and it is optimised on projects.

 

      public int GetCaseStudyIDByPageID(int pageID)
        {
            return _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId).FirstOrDefault(-1);
        }

The above produces the same result in the profiler:

SELECT TOP ( 1 ) [t1].[value]
FROM   (SELECT [t0].[CaseStudyId] AS [value],
               [t0].[PageID]
        FROM   [dbo].[CaseStudy] AS [t0]) AS [t1]
WHERE  [t1].[PageID] = 128 /* @p0 */

This anti pattern was used allot throughout their code, and caused double/triple calls to the DB for every page load. Therefore, using a profiling tool like NHProf, L2SProf or EHProf will save you and your customer/employee allot of money in the long term and perhaps save developers from picking up bad habits where IQuerable is being abused and treated like lists when in fact they execute on the backend.

We have solved this, and also provided a neat way of dealing with nullable types with  clean extension methods.

So we have solved scalar issues with FirstOrDefault and the code below which is easy to write to the untrained eye will not need to be thought of when invalid checks occur on nullable types by using the custom extension methods provided above.

var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID).Select(x=>x.PageId);

return result ?? -1; //Invalid Check

Also, see the repository pattern being used and people ended up with code with these stats:

clip_image002

Above, we have +- 700 SQL Statements being called, and each on average uses a data context. Try to have a minimum amount of data contexts. One Data Context can server all sorts of requests, so when choosing a repository pattern or strategy profile the number of data contexts created and try reduce them.

image

Above, is for one page load, not a good design pattern, so room to improve with some dependency injection and a singleton on the datacontext Smile

You can read other tips on profiling here:

https://romikoderbynew.wordpress.com/tag/nhprof/

Thanks to Matthew Wills again for some awesome tips on profiling Smile

Cheers