Fork me on GitHub

Argue for robustness

So many of us working on a daily basis with F# always claim that we are able to make more robust and bulletproof applications with fewer lines of code than we would need if we used the C’s (C,C++,C#, …). So how do we achieve this?

I will try to explain this in a less theoretical way so people don’t get lost in translation. Besides I will provide the usual foo/bar examples as well as a basic real world example.

Let’s start by defining a couple of functions:

let log a b = System.Console.WriteLine(sprintf "-Log: %A (%A)" a b)

let foo x = try 2*x |> Some with ex -> log x ex; None
let bar x = try 2+x |> Some with ex -> log x ex; None
let foobar x = try 2/x |> Some with ex -> log x ex; None
val log : a:'a -> b:'b -> unit
val foo : x:int -> int option
val bar : x:int -> int option
val foobar : x:int -> int option

We can all agree that the function look pretty robust right? The main operation is performed inside a try/with statement, for the C’s think of it as a try/catch statement. Now if the operation fails, 2/0 is possible in foobar, the log function will be called with the input parameter x and the exception ex. What seems a bit strange in the functions is that both operations, try/with, finishes in Some/None. This is one of the powerful features of F#, Some/None is a union type between the type and no-value. In other words, either you have a value of the given type Some of 'a or you don’t any value at all None. If you are familiar to ML-like languages, you will have seen this as datatype 'a option = NONE | SOME of 'a, in a identical form for OCaml as type 'a option = None | Some of 'a (you might be able to argue that F# is the OCaml .NET version) and finally as data Maybe a = Just a | Nothing in Haskell.

Remark: Just for correctness, the log function is implemented with the Console.WriteLine method, which is threadsafe and in combination with sprintf/"%A", to make it generic.

Robustness but verbosity

Now that we have the robust functions. lets combine a couple of them together as we do when we write code:

2 |> foo |> bar |> foobar
error FS0001: Type mismatch. Expecting a
int option -> 'a
but given a
int -> int option
The type 'int option' does not match the type 'int'

We can see that we get a type error as the function bar takes an int as input and not an int option type. Let’s re-write the code in a correct way:

2
|> foo
|> function | Some v -> bar v | None -> None
|> function | Some v -> foobar v | None -> None
val it : int option = Some 0

I think it’s easy to argument for robustness and correctness but you might think: “Less code you say?”. And you are right, this kind of implementation would be really annoying to write for every single function you would have to pipe the result to.

Monads to the rescue

The more theoretical approach to simplify the code but still maintaining correctness, would be to implement the Maybe Monad (monads are called Computation expressions in F#):

type MaybeMonad() =
member t.Bind(m,f) =
match m with
| Some v -> f v
| None -> None
member t.Return v = Some v
let maybe = MaybeMonad()
type MaybeMonad =
class
new : unit -> MaybeMonad
member Bind : m:'b option * f:('b -> 'c option) -> 'c option
member Return : v:'a -> 'a option
end
val maybe : MaybeMonad

Where we can use the monad to write the previous code as:

maybe{ let! x = foo 2
let! y = bar x
let! z = foobar y
return z }
val it : int option = Some 0

By using the monad we don’t have to write function | Some v -> some_function v | None -> None for each time we pipe the value but, it’s still some kind of annoying having to write all the temporary variables x,y,z in order to get the final result. The ideal scenario would be to write the following code:

maybe{ return 2 |> foo |> bar |> foobar }
error FS0001: Type mismatch. Expecting a
int option -> 'a
but given a
int -> int option
The type 'int option' does not match the type 'int'

But this is not possible as we need to bind the functions together. Actually that is what let! does. The let! operator is just syntactic sugar for calling the Bind method.

Remark: The Maybe Monad can be implemented in less verbose code by using the built-in Option.bind function:

type MaybeMonad() =
member t.Bind(m,f) = Option.bind f m
member t.Return v = Some v
let maybe = MaybeMonad()

Infix operator to the rescue (»=)

So how do we get as close to 2 |> foo |> bar |> foobar but without compromising on correctness and robustness? Well the answer is quite simple

What we need to do is to introduce the following infix operator:

let (>>=) m f = Option.bind f m
val ( >>= ) : m:'a option -> f:('a -> 'b option) -> 'b option

Now we can combine functions together in the following manner:

2 |> Some >>= foo >>= bar >>= foobar
> val it : int option = Some 0

Which is pretty close to what we wanted to achieve, 2 |> foo |> bar |> foobar, right?

Another thing to have in mind when using binded functions is to think of the bind as how Short-circuit evaluation works. SCE denotes the semantics of some Boolean operators in some programming languages in which the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression. For example: when the first argument of the AND function evaluates to false , the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true. Binding functions is more or less the same, where the output from the first function is bounded to the input of the second. If the first function returns None, then the second is never called and None is returned for the whole expression. Let’s see this in an example using foobar and 0 as input:

0 |> Some >>= foobar >>= foobar >>= foobar 
> -Log: 0 (System.DivideByZeroException: Division by zero 
at FSI_0045.foobar (Int32 x) [0x00000] in <filename unknown>:0 )
val it : int option = None

After foobar throws an exception and return None, none of the other following foobar functions are evaluated. Cool right?

Another infix operator to the rescue (|=)

As in real life you might want to get the value of the type and use it in other frameworks that doesn’t have support for Some/None . What you can do is to do something like:

42 |> Some |> function | Some v -> printfn "%A" v | None -> ()
> 42
val it : unit = ()

or

42 |> Some |> function | Some v -> v | None -> failwith "Some error"
val it : int = 42

This will limit your code to unit = () or to throw and exception. which would be OK if it’s encapsulated in a try/with statement. But sometimes you will just want be able to assign a value that means no change in the final result of the computation. For example: 0 in a sum of integers, 1 in a product of integers, an empty list in a concatenation, and so on. To achieve this I usually implement the following infix operator:

let (|=) a b = match a with | Some v -> v | None -> b
val ( |= ) : a:'a option -> b:'a -> 'a

This will now allow us to use the value as the given type and if there is no value then use the specified default value:

42 |> Some >>= foo >>= bar >>= foobar |= 0
val it : int = 0

Remark: As with the Maybe Monad, this infix operator can also be implemented in less verbose code by using the built-in Option.fold function:

let (|=) a b = a |> Option.fold(fun _ x -> x) b

So let’s use the infix operators on a basic real world example

Now that we have the receipt to create correct and robust one-liner functions, let’s define two functions for this example. The first will return Some array of even numbers from an arbitrary array. And the second will return Some array of the top 10 biggest numbers from an arbitrary array.

let even a =
try a |> Array.filter(fun x -> x % 2 = 0) |> Some
with ex -> log a ex; None

let top10 a =
try Array.sub (a |> Array.sortBy(~-)) 0 10 |> Some
with ex -> log a ex; None
val even : a:int [] -> int [] option
val top10 : a:int [] -> int [] option

For the first function it’s easy to argument for it to never break. If the array doesn’t contain any even numbers, Some empty array will be returned. But for the second function we can see that there will always be returned a Some sub-array of size 10. What will happen when the input array is of a smaller size? Let’s execute the code:

[|0 .. 2000|] |> Some >>= even >>= top10 |= Array.empty

[|0 .. 12|] |> Some >>= even >>= top10 |= Array.empty
> val it : int [] =
[|2000; 1998; 1996; 1994; 1992; 1990; 1988; 1986; 1984; 1982|]
> -Log: [|0; 2; 4; 6; 8; 10; 12|] (System.ArgumentException:
The index is outside the legal range.
Parameter name: count
at Microsoft.FSharp.Collections.ArrayModule.GetSubArray[Int32]
(System.Int32[] array, Int32 startIndex, Int32 count) [0x00000]
in <filename unknown>:0
at FSI_0015.top10 (System.Int32[] a) [0x00000] in <filename unknown>:0 )
val it : int [] = [||]

We can see that the first evaluation returns an array of ten even numbers from 2000 to 1982 while the second returns an empty array and logs the out of boundary exception to the console.

Remark: Please never write code like this, it’s always more desirable to check for the size of the array than to get an out of boundary exception. This was just to make a point of bulletproof functions and hereby applications by using F#.

Conclusion

Well now that I gave you the receipt for creating small robust and bulletproof functions, or Lego blocks as I call them, that can easily be tested for correctness and robustness, now it’s your turn to create your blocks, combine them to create bigger blocks and make robust applications. Happy coding and remember to have fun.

Where to go from here

Finally if you want to get a deeper understanding of what is happening here, please spend an of your life watching this amazing video:

I’ve been employed @ Delegate A/S for about a year. In this short period I have created some tools for our CRM developers/consultants in order to make working with Microsoft Dynamics CRM more smoothly. One of these tools is DAXIF# which is defined as A set of tools that in combination with other MS tools make it easier to work with CRM/xRM on a daily basis (also for developers who are not familiar with the platform)

The interface is through F# script files that can be executed from a command prompt or directly from Visual Studio (the best IDE for F# scripts):

All

The main reason to use F# to create this set of tools is as usual the same sales speech we use to give again and again and again: Error free projects with smaller code base, where there is a need to use one programming language (no. Bat files or PowerShell, …). Where big data, external data sources, parallelism, concurrency, asynchronous processor are trivial to use.:

All

One of the things I learned from this project was that I actually could make F# scripted and self documented Unit Test that can be executed without having to build the final .DLL:

All

All

All

All

All

DAXIF# is proprietary so you will need a license to use it. We don’t provide licenses to other CRM Partner/competitors

Keep updated for the upcoming website and NuGet package.

For more information on DAXIF# and the presentations, please look into these slides:

  • Link to slides from MF#K (English): Slides

  • Link to slides from CRM Partner Community (Danish): Slides

  • Geek alert: A few references to Dota 2 might appear in the code:

All

or in the project structure: All

I tried to implement the bitonicsorter I wrote about in my masters thesis. The result is the following code:

// BitonicSort
//
// http://www.diku.dk/forskning/performance-engineering/Ramon/thesis.pdf

let inline isPow2 x =
match x with
| 0 -> false
| _ -> x &&& (x - 1) = 0

let comparator x y =
match x with
| _ when x < y -> (x,y)
| _ -> (y,x)

let halfCleaner bs =
let n = bs |> Array.length
let m = n/2

match isPow2(n) with
| true -> ()
| false -> failwith "Input array %A, must be n=2^k" bs

Array.mapi(fun i x ->
match i with
| _ when i < m -> fst (comparator x bs.[m+i])
| _ -> snd (comparator x bs.[i-m])) bs

let rec bitonicSorter bs =
let n = bs |> Array.length
let m = n/2

match isPow2(n) with
| true -> ()
| false -> failwith "Input array %A, must be n=2^k" bs

let bs' = halfCleaner bs
let bs1 = bs'.[0 .. (m - 1)]
let bs2 = bs'.[m .. (n - 1)]

match n with
| _ when 2 < n ->
Array.append (bitonicSorter bs1) (bitonicSorter bs2)
| _ -> bs'

let merger ss1 ss2 =
let m1 = ss1 |> Array.length
let m2 = ss2 |> Array.length
let n = m1 + m2
let m = n/2

match (m1 = m2) with
| true -> ()
| false -> failwith "Input arrays (%A,%A), must have the same length" ss1 ss2

match isPow2(n) with
| true -> ()
| false -> failwith "Comibnation of (%A,%A) arrays, must be n=2^k" ss1 ss2

let ss2' = ss2 |> Array.rev

let ss1'' = Array.map2(fun x y -> fst (comparator x y)) ss1 ss2'
let ss2'' = Array.map2(fun x y -> snd (comparator x y)) ss1 ss2'

match n with
| _ when 2 < n -> Array.append (bitonicSorter ss1'') (bitonicSorter ss2'')
| _ -> Array.append ss1'' ss2''

let rec sorter array =
let n = array |> Array.length
let m = n/2

match isPow2(n) with
| true -> ()
| false -> failwith "Input array %A, must be n=2^k" array

let as1 = array.[0 .. (m - 1)]
let as2 = array.[m .. (n - 1)]

match n with
| _ when 2 < n -> merger (sorter as1) (sorter as2)
| _ -> merger as1 as2

let n = 1 <<< 16
let a = Array.init n (fun i -> i % 2)

sorter a

It still lacks of speed, even with the use of the included libraries Array.Parallel or Async.Parallel / Async.RunSynchronously (fork/join) but it was fun to write as usual.

REMARK: It’s much more readable than the code I wrote back in the days …

Last June I was in Madrid for TechEd Conference. The main focus was The Cloud. Microsoft has actually done a really good job and the platform is very mature. I’m not going to lie by saying that I will prefer to host everything in the cloud than doing it on-premise. A few PowerShell scripts and voila you got yourself the desired environments. And with the instance slider, you got yourself the amount of instances that you could need for a specific period. Try to do something similar with your on-premises infrastructure. Another awesome feature is that from now on you will only pay for the environments if they are running. This means that DEV and TEST can be shut down while they are not being used:

All

Lucian Wischik gave three talks with regards to async arriving to C# 5.0 (no callbacks needed). Hmmmm, I wonder were we have seen this before, who said F#?

All

Another really interesting talk was David Starr regarding Brownfield Development. We all have seen this huge amount of spaghetti code right?

All

But how do we actually ensure that we don’t get to this point? And how do we avoid that methods grow to become huge? I think the main problem is because we use a toolbox that actually allows this to happen, mostly cos it’s part of it’s verbosity

All

… well the answer isn’t that difficult. Even though Dustin Campbell gave a good talk, Microsoft really needs to understand that they are not going to catch the businessmen attention by showing a how F# is really good to solve Project Euler problems. What Microsoft needs to do, is to show on one of their platforms how using F# provides a more clean and robust way to make quality software, and we might able to help out on this one, stay tuned:

All

Finally, not everything in Madrid had to be hard work, there were also time to some pleasure:

All

As it has been a while since I went to TechEd and because I have to give a small talk for the rest of Delegate A/S employees, I needed to get the PowerPoints and some videos. I was a bit bored and cos I love F# I decided to make a small file crawler. Things I noticed while creating the app is how simple it is to convert from a sequential to parallel app. Just convert the sequence to an array and then apply parallelism, as simple as that. The only issue I found while converting the app to run in parallel is that printfn is not thread-safe so a few changes to Console.WriteLine and sprintf and voila, it’s 100% parallel. This is one of the strong sides of F#, like any other .NET language, it has access to the whole Frameworks API.

namespace Stermon.Tools.TechEdFileCrawler

open System
open System.IO
open System.Net
open System.Text.RegularExpressions
open FSharp.Net

type TechEdFileCrawlerLib() =
member private this.absoluteUrl url href =
WebUtility.HtmlDecode(Uri(url).GetLeftPart(UriPartial.Authority) + href)

member private this.ensureUrl url (match':string) (tags:string) =
match'
|> fun s -> s.Replace(@"href=""", String.Empty)
|> fun s ->
let rec cleanTags (s':string) tags' =
match tags' with
| x::xs -> cleanTags (s'.Replace(@""">" + x, String.Empty)) xs
| [] -> s'
cleanTags s (tags.Split('|') |> Array.toList)
|> fun s ->
match s.StartsWith("http") with
| false -> this.absoluteUrl url s
| true -> s

member this.Crawl url (tags:string) =
let tags' =
tags.Split('|')
|> Array.map(fun x -> @"href.*" + x)
|> Array.reduce(fun x y -> x + "|" + y)

let html = Http.Request(url)

let m = Regex.Match(html, tags')

let rec hrefs (m:Match) = seq{
match (m.Success) with
| true ->
yield! [for g in m.Groups -> this.ensureUrl url g.Value tags]
yield! hrefs (m.NextMatch())
| false -> ()
}

hrefs m

member this.PagesRec url tag =
let rec pages acc sq = seq{
match (sq |> Seq.toList) with
| x::xs -> yield! pages (Seq.append acc sq) (this.Crawl x tag)
| [] -> yield url; yield! acc
}

pages Seq.empty (this.Crawl url tag)

member this.Download href =
try
match Http.RequestDetailed(href).Body with
| HttpResponseBody.Binary bytes ->
let name (href':string) =
href'
|> fun s -> s.Replace(@"http://", String.Empty)
|> fun s -> s.Replace(@"/", "_")

match (Directory.Exists(@".\techEd")) with
| false -> Directory.CreateDirectory(@".\techEd") |> ignore
| true -> ()

File.WriteAllBytes(@".\techEd\" + (name href), bytes)

Console.WriteLine(sprintf "
%O" (@"-Saved: " + href))
| _ -> ()
with
| exn ->
let en = Environment.NewLine

Console.WriteLine(
sprintf @"
-An exception occurred:%s >>%s %s >>%s"
en exn.Message en href)

Remark: There is no need to actually change the algorithm, so it is still as readable as it was before. Like stated before, change three lines and voila, the app runs in parallel …

open System
open Stermon.Tools.TechEdFileCrawler

let getArg argv key =
let arg = Array.tryFind(fun (a:string) -> a.StartsWith(key)) argv
match arg with
| Some x -> x.Replace(key, String.Empty)
| None -> failwith ("Missing argument: " + key)

[<EntryPoint>]
let main argv =
try
let tec = TechEdFileCrawlerLib()
tec.PagesRec (getArg argv "url=") (getArg argv "next=")
// |> Seq.iter(fun x ->
// tec.Crawl x (getArg argv "tags=")
// |> Seq.iter(fun x -> tec.Download x))
|> Seq.toArray
|> Array.Parallel.iter(fun x ->
tec.Crawl x (getArg argv "tags=")
|> Seq.toArray
|> Array.Parallel.iter(fun x -> tec.Download x))
with
| exn ->
let iexn =
match (exn.InnerException) with
| null -> "No InnerException."
| iexn' -> iexn'.Message
printfn "An exception occurred:\n -%s\n -%s" exn.Message iexn
0

The crawler is called from a terminal like this:

TechEdFileCrawler.exe ^
"url=http://channel9.msdn.com/Events/TechEd/Europe/2013?sort=status" ^
"next=next" ^
"tags=Slides|Zip"

And will save the files and write the following output:

...
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/ATC-B210.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/OUC-B306.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/DEV-IL201.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/OUC-B302.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/WCA-B208.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/ATC-B204.pptx
-An exception occurred:
>>The remote server returned an error: (404) Not Found.
>>http://video.ch9.ms/sessions/teched/eu/2013/WPH-H201.zip
-An exception occurred:
>>The remote server returned an error: (404) Not Found.
>>http://video.ch9.ms/sessions/teched/eu/2013/WPH-H202.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/SES-H201.zip
-An exception occurred:
>>The remote server returned an error: (404) Not Found.
>>http://video.ch9.ms/sessions/teched/eu/2013/WPH-H203.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/DBI-H212.zip
-An exception occurred:
>>The remote server returned an error: (404) Not Found.
>>http://video.ch9.ms/sessions/teched/eu/2013/WPH-H206.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/ATC-B214.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/WAD-B291.pptx
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/SES-H204.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/DBI-H213.zip
-An exception occurred:
>>The remote server returned an error: (404) Not Found.
>>http://video.ch9.ms/sessions/teched/eu/2013/WPH-H204.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/SES-H202.zip
-Saved: http://video.ch9.ms/sessions/teched/eu/2013/OUC-B307.pptx
...

p.s.: It wouldn’t be that difficult to convert the code above to a generic website file crawler …

I recently participated at Microsoft’s Dynamics CRM Training Blitz Day, on the Technical Overview for Application consultants, Presales consultants and Developers Track. My first impression was …

All

… and it’s actually the way it should be. As we have it now, there a lot of code monkeys that really don’t understand the complexity of the systems they are developing, and I know cos I’ve been that monkey on several occasions. But it’s not always the monkeys fault, what you have to understand is that what people have been working on for several years, a developer has to learn and master in very short period in order to implement in code, normally the pre-analysis/design phase of a project. On other occasions it’s the experts in the subject that are not able to communicate what they want to the developers in an understandable language, like for example plain English.

Back in the days while I was studying at the Computer Science Department at Copenhagen University, myself and two fellow students, Joakim Ahnfelt-Rønne and Jørgen Thorlund Haahr, wrote a bachelor thesis on the subject: Klient/server-applikation til kvalitetskontrol af tandbehandlinger. We tried to create a generic application that would allow subject-matter experts to define some business processes from an administration interface without the use of any kind of code. This processes would be defined with a set of rules that would be enforced whenever his/hers peers/colleagues would execute the defined process on both the client side but as well on the server side. It’s been about 5 years since we made that initial prototype, a very limited piece of software but theoretical correct and usable in real-life, to what we have nowadays in form of CRM2013 and I can’t avoid to get a little smile on my face thinking that at least one of the big software companies are doing things the right way, or at least they are trying.

The agenda for the Training Blitz Day looked promising with buzzwords as: New UI, Process Agility, Mobile Client, Yammer Integration, Exchange Sync, Business Rules, Client Extensibility

All

… and it didn’t disappointed me, even though it was 4 hours with very short breaks combined with my previous 8 hours at work.

They started by given a simple introduction to the new UI. The left bar, which took about 20% of the screen is now placed on the top. This new bar is always visible and you can access all the different sections of the CRM system at any time.

All

Another major change in the Forms are that they now are a combination of several entities thanks to the Business Process flows. Given the many devices that now are able to connect to CRM, the Form visibility will adjust automatically based on the size of the screen. Also mention that Forms are now a single page, where the previous iFrames are replaced with div html tags that are loaded asynchronously.

All

Finally, as Microsoft already pointed out, the very heavy loading Ribbons are gone for good. They are replaced with the command bar, which is enabled for touch screens, which is always visible.

All

As mentioned in the beginning of this blog post, the Business Process Flows are pushed to a whole new level, where you are just not locked to a single process but while you are working on an opportunity for example, you can choose to run the cross-sale process if you already know the customer instead of having to go through the standard lead-to-opportunity process.

All

The processes are easily defined with the well known interface, there are some minor changes.

All

The best part is that these Business Processes will work for all your interfaces: CRM web interface, Mobile applications, Outlook, Custom apps

All

One of the awaited moments was the presentation of the Mobile Apps, only available for Microsoft and Apple phones and tablets at the moment. The application are build on HTML5 but with a native wrapper in order get access to the specific hardware features. By using HTML5 it’s easier to provide new functionality without having to deploy new applications to the different App Stores.

Note: There will be no offline client, what Microsoft tries to push is the always-online and when you aren’t, you will have cached previously downloaded data. As with the Xbox One, they would might have to rethink that one again, or at least we will need developers to do that part.

All

The XML used to save the visual representations for the Forms and Dashboard, will be reused on the Mobile Apps. This will make it easier to re-use already implemented functionality. There are introduced some limits in order to provide a fluent user experience.

All

There is also made a separate phone app that integrates with contacts in order to make phone-calls directly from the CRM app.

All

Another feature of the new CRM is that the old heavy-in-memory Outlook client

All

will be replaced by several processes that will operate separately avoiding the OS memory limit

All

Another awaited feature is the Server-side synchronization. No more E-mail routers and no more Outlook client must be running on the users PC in order to send a couple of messages.

All

From now on, these tasks will now be done between the Exchange and CRM servers.

All

A flow of the update of an item from a phone, is done without Outlook even been used.

All

So how difficult is it going to be to upgrade from CRM2011 to CRM2013? Well it’s going to be really easy, if your solution complies with the SDK. On the CRM online version, Microsoft will take care of everything. On-Premises there are to options:

  • Best-practice: Just take a backup of the current tenant and then import it into your new CRM2013 setup.

  • Alternatively: Upgrade the current CRM2011 with CRM2013 and choose to update all current tenants or wait to do it later from the Deploy Manager.

All

One of the performance improvements of the upgrade is that tables will be merged into a single table, it was separated into two tables because of previous SQL limitations for tables.

All

In order to implement Business Process Flows there will be almost no need to use code made by developers, that is usually difficult to maintain across different developers. Instead there will be used a declarative/visualized language/interface

All

Once a rule is defined, it will work everywhere.

All

The subject-matter experts will be able to define the processes from a very simple but powerful interface.

All

The process will be deployed with the solution packages and they now also support export/import of labels for use with several languages.

All

Only headache will be once again the unsupported solutions created by some “Partners”, even though Microsoft keeps saying time and time again, don’t do it.

All

What the final customer has to understand is that by allowing this to happen, it gets more difficult to upgrade smoothly and even install the latest roll-updates containing not only new functionality but also fixes to know bugs. Finally the amount of time/money used to correct these problems, that shouldn’t be there in the first place, are the final customer going to pay for and not the “Partner”. Just think about that for a moment …

All

Due to the new UI, the Client SDK is expanded to support the new functionality.

Remark: Some CRM2011 functionality will be @deprecated in the new release. You can see the differences between the two SDK visualizations here: Xrm.Page Object Model

All

Another must-have for any single business critical application out there is an built-in autosave functionality.

Remark: Plug-ins will trigger every time auto-saved is called, so please re-think the logic on how plug-ins are implemented. The best-practices recommended by Microsoft are always to limit the fields an update plug-in should be triggered on.

All

Sitemaps and customizations to that will still be done through the XML as we are used to.

All

Another AWESOME feature in combination with the Business Process flows are that now workflows can be run synchronously as plug-ins (both pre- and post). This will also limit the amount of code needed to implement plug-ins.

All

All

In order to support real-life scenarios where a set of events needs to be triggered and combine state between these events will now also be possible to be done without writing code. I wished they used a bit more time on this matter as it will be game changer compared to other CRM providers.

All

Also CRM2013 will provide OData access to Mobile/Custom Apps that uses the OData interface through the Windows Azure Active Authentication library.

There will also be support for phone/SMS mechanism to login for very high secured organizations.

All

As an CRM Architect is very easy to understand why so many, and more and more organizations are choosing the CRM Framework (xRM), as the backbone of their systems. Just look at the next picture. By adding a simple entity to your solution, you will automatically get all this. Just by doing a few mouse clicks and adding some text. Impressive right?

All

Finally I would like to say that I’m very exited and gratefully surprised with the outcome of the new CRM2013 release. I’m really looking forward to work with it and as I’m writing this blog post, we just received and e-mail from Microsoft noticing us that Delegate A/S CRM Online solution will be upgraded in December this year, without having to do anything from our side. The cloud is no longer the future, but the present.