Background

The MIT License (MIT)

Copyleft (ↄ) 2016 Delegate A/S

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyleft notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYLEFT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  

As mentioned in a previous post, and because Daxif is now Open Source, I will be blogposting on a weekly series of How to Daxif: …. The first topic will be how to setup Daxif as part of a Visual Studio project.

Our XrmFramework is (was) based on the Developer Toolkit for Microsoft Dynamics CRM but since it’s not been updated lately, we have decided that we will follow our own path.

Note: The only thing we needed to do was to remove the following lines from our .SLN files

  GlobalSection(CRMSolutionProperties) = preSolution
    SolutionIsBoundToCRM = True
  EndGlobalSection

The reason we followed Microsoft on this matter was that we see a lot of other consultancies creating monolithic frameworks which are almost impossible to get away from. We would like to think that our approach is a bit more fair in the way that if our customers would like to replace us, the consultancy taking over, just need to have the basic understanding on how the Developer Toolkit for Microsoft Dynamics CRM works.

As you can see it’s more or less the same: Plug-in and Workflow projects are the same and the CrmPackage is just replaced by Daxif. We have added the BusinessDomain, generated code from the MS CRM datamodel, and the BusinessLogic, reusability of code projects; as well as the Blueprint project to store the MS CRM solution in our source control system.

Daxif has helped us out where the Developer Toolkit for Microsoft Dynamics CRM wasn’t good enough:

  • Easily generate strongly typed proxy classes without having to run CrmSvcUtil.exe

  • Edit and register plug-ins without using the Plug-in registration tool

    • This component has never worked properly and to solve this issue, we have added the syncSolution to Daxifs Plugins module. There will be a blogpost on this topic very soon.
  • Create new web resources or extract existing web resources, add them to your solution, edit them, and deploy changes all within Visual Studio.

    • When adding files to this componenet, it actually moves web ressources to a specific container (HTML Page, JScript File, Style Sheet, … folders) which most certanly would break any HTML5 app that was built for MS CRM, no relative path will work. We have taken the approach that what you see locally in your source control, should be mapped 1:1 to your MS CRM solution (code should always be master). We handle this matter in Daxif with the syncSolution in the WebResources modules. There will also be a blogpost on this topic soon.

Install Daxif from Nuget and keep updated

Back to the setup of Daxif, the only thing that is needed is to add the Daxif NuGet package to the Daxif project in Visual Studio:

and ensure regularly that is updated:

Attempting to gather dependencies information for package 'Delegate.Daxif.2.2.0.5' with respect to project 'Daxif', targeting '.NETFramework,Version=v4.5'
Attempting to resolve dependencies for package 'Delegate.Daxif.2.2.0.5' with DependencyBehavior 'Lowest'
Resolving actions to install package 'Delegate.Daxif.2.2.0.5'
Resolved actions to install package 'Delegate.Daxif.2.2.0.5'
Executing script file 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages\Delegate.Daxif.2.2.0.0\tools\uninstall.ps1'...
Cleaning out .dll and .xml files from script folder: Daxif\Daxif
Removed package 'Delegate.Daxif.2.2.0' from 'packages.config'
Successfully uninstalled 'Delegate.Daxif.2.2.0' from Daxif
Adding package 'Delegate.Daxif.2.2.0.5' to folder 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages'
Added package 'Delegate.Daxif.2.2.0.5' to folder 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages'
Added package 'Delegate.Daxif.2.2.0.5' to 'packages.config'
Executing script file 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages\Delegate.Daxif.2.2.0.5\tools\install.ps1'...
Copying required .dll and .xml files to scripts folder: Daxif\Daxif\
Successfully installed 'Delegate.Daxif 2.2.0.5' to Daxif
Removing package 'Delegate.Daxif.2.2.0' from folder 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages'
Removed package 'Delegate.Daxif.2.2.0' from folder 'D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\packages'
========== Finished ==========

Nuget limitations

The final thing that needs to be done is to execute from inside Visual Studio the DG.EnsureAssemblies.fsx. This is needed due to some limitations in Nuget:

  1. Open the script file

  2. Mark all the text (CTRL+A) and send to F# Interactive (ALT+ENTER)

And you should see the following output:

Microsoft (R) F# Interactive version 14.0.23413.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> 
val root : string =
  "D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\Daxif"
val pkgs : string =
  "D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToD"+[22 chars]
val blacklist : string list = ["net20"; "portable-"]
val exclude : path:string -> bool
val helper : paths:seq<string> -> unit
val it : unit = ()

> 

Note: The reason we use F# scripts is:

  • Intellisense and autocompletion when updating scripts in Visual Studio.

  • Executing scripts from Visual Studio, no need to leave the IDE.

  • Re-usability of code made in Daxif.

  • Typesafe scripts: Scripts will be checked for inconsistency before executed by the F# interpreter. Neither Cmd or PowerShell can provide this.

Update Auth and Config information

Now all Daxif scripts can be executed but you will still need to update these two scripts files so their match your current setup:

DG.Delegate.HowToDaxif.AuthInfo.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
[<Literal>]
let usr = @"admin@mydev.onmicrosoft.com"
[<Literal>]
let domain = @""
[<Literal>]
let pwd = @"pass@word1"

[<Literal>]
let usr' = @"usr"
[<Literal>]
let domain' = @"domain"
[<Literal>]
let pwd' = @"pwd"

DG.Delegate.HowToDaxif.Config.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#r @"Microsoft.Xrm.Sdk.dll"
#r @"Delegate.Daxif.dll"

(** Open loaded libraries for use *)
open Microsoft.Xrm.Sdk.Client
open DG.Daxif

let log = LogLevel.Verbose

(** Serialize Type *)
let xml = SerializeType.XML

(** Auth information *)
#load @"DG.Delegate.HowToDaxif.AuthInfo.fsx"

[<Literal>]
let authType = AuthenticationProviderType.OnlineFederation

(** Dev auth & environment information *)
[<Literal>]
let usrDev = DG.Delegate.HowToDaxif.AuthInfo.usr
[<Literal>]
let pwdDev =  DG.Delegate.HowToDaxif.AuthInfo.pwd
[<Literal>]
let domainDev = DG.Delegate.HowToDaxif.AuthInfo.domain
[<Literal>]
let wsdlDev  = @"https://mydev.crm4.dynamics.com/XRMServices/2011/Organization.svc"
let wsdlDev' = Uri(wsdlDev)

(** Test auth & environment information *)
[<Literal>]
let usrTest = DG.Delegate.HowToDaxif.AuthInfo.usr
[<Literal>]
let pwdTest =  DG.Delegate.HowToDaxif.AuthInfo.pwd
[<Literal>]
let domainTest = DG.Delegate.HowToDaxif.AuthInfo.domain
[<Literal>]
let wsdlTest  = @"https://mytest.crm4.dynamics.com/XRMServices/2011/Organization.svc"
let wsdlTest' = Uri(wsdlTest)

(** Prod auth & environment information *)
[<Literal>]
let usrProd = DG.Delegate.HowToDaxif.AuthInfo.usr
[<Literal>]
let pwdProd =  DG.Delegate.HowToDaxif.AuthInfo.pwd
[<Literal>]
let domainProd = DG.Delegate.HowToDaxif.AuthInfo.domain
[<Literal>]
let wsdlProd  = @"https://myprod.crm4.dynamics.com/XRMServices/2011/Organization.svc"
let wsdlProd' = Uri(wsdlProd)

(** Source auth & environment information for data migration *)
[<Literal>]
let usrSource = DG.Delegate.HowToDaxif.AuthInfo.usr
[<Literal>]
let pwdSource =  DG.Delegate.HowToDaxif.AuthInfo.pwd
[<Literal>]
let domainSource = DG.Delegate.HowToDaxif.AuthInfo.domain
[<Literal>]
let wsdlSource  = @"https://SOURCE.api.crm4.dynamics.com/XRMServices/2011/Organization.svc"
let wsdlSource' = Uri(wsdlSource)

(** Target auth & environment information for data migration *)
[<Literal>]
let usrTarget = DG.Delegate.HowToDaxif.AuthInfo.usr'
[<Literal>]
let pwdTarget =  DG.Delegate.HowToDaxif.AuthInfo.pwd'
[<Literal>]
let domainTarget = DG.Delegate.HowToDaxif.AuthInfo.domain'
[<Literal>]
let wsdlTarget  = @"https://TARGET.api.crm4.dynamics.com/XRMServices/2011/Organization.svc"
let wsdlTarget' = Uri(wsdlTarget)

(** Shared environment information *)
let rootFolder = __SOURCE_DIRECTORY__

let solutions = rootFolder + @"\solutions\"

let translations = rootFolder + @"\translations\"

let metadata = rootFolder + @"\metadata\"
let data = rootFolder + @"\data\"
let state = rootFolder + @"\state\"
let associations = rootFolder + @"\associations\"
let mapping = rootFolder + @"\mapping\"
let imported = rootFolder + @"\imported\"

let webresources = rootFolder + @"\..\..\WebResources\src\"
let tools = rootFolder + @"\..\..\..\Tools\"

let pubPrefix = @"dg"
let pubName = @"delegateas"
let pubDisplay = @"Delegate A/S"
let solution = @"HowToDaxif"
let solDisplay = @"HowToDaxif"

Create your publisher and solution

When this is done, you should be able to create your Publisher and Solution by executing:

DG.Delegate.HowToDaxif.SolutionCreateDev.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

Solution.createPublisher 
  cfg.wsdlDev'
    cfg.pubName cfg.pubDisplay cfg.pubPrefix 
      cfg.authType cfg.usrDev cfg.pwdDev cfg.domainDev 
        cfg.log;;

Solution.create
  cfg.wsdlDev'
    cfg.solution cfg.solDisplay cfg.pubPrefix 
      cfg.authType cfg.usrDev cfg.pwdDev cfg.domainDev 
        cfg.log
Microsoft (R) F# Interactive version 14.0.23413.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> 
[Loading D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\Daxif\DG.Delegate.HowToDaxif.AuthInfo.fsx
 Loading D:\tmp\howToDaxif\XrmFramework\DG.Delegate\DG.Delegate.HowToDaxif\Daxif\DG.Delegate.HowToDaxif.Config.fsx]

namespace FSI_0002.DG.Delegate.HowToDaxif
  val rk : unit -> System.ConsoleKeyInfo
  val cw : s:string -> unit
  val cwl : s:string -> unit
  val mask : key:(unit -> System.ConsoleKeyInfo) -> string
  val usr : string
  val domain : string
  val pwd : string
  val usr' : string
  val domain' : string
  val pwd' : string


namespace FSI_0002.DG.Delegate.HowToDaxif
  val ( +/ ) : a:string -> b:string -> string
  val ( +. ) : a:string -> b:string -> string
  val getArg : argv:string [] -> key:string -> string
  val ensureFolder : path:string -> unit
  val log : DG.Daxif.LogLevel
  val xml : arg0:string -> DG.Daxif.SerializeType
  val authType : Xrm.Sdk.Client.AuthenticationProviderType
  val usrDev : string
  val pwdDev : string
  val domainDev : string
  val wsdlDev : string
  val wsdlDev' : System.Uri
  val usrTest : string
  val pwdTest : string
  val domainTest : string
  val wsdlTest : string
  val wsdlTest' : System.Uri
  val usrProd : string
  val pwdProd : string
  val domainProd : string
  val wsdlProd : string
  val wsdlProd' : System.Uri
  val usrSource : string
  val pwdSource : string
  val domainSource : string
  val wsdlSource : string
  val wsdlSource' : System.Uri
  val usrTarget : string
  val pwdTarget : string
  val domainTarget : string
  val wsdlTarget : string
  val wsdlTarget' : System.Uri
  val rootFolder : string
  val solutions : string
  val translations : string
  val metadata : string
  val data : string
  val state : string
  val associations : string
  val mapping : string
  val imported : string
  val webresources : string
  val tools : string
  val pubPrefix : string
  val pubName : string
  val pubDisplay : string
  val solution : string
  val solDisplay : string

2016-02-14T08:34:28.0886803+01:00 - Info: Create publisher: Delegate A/S
2016-02-14T08:34:28.0908973+01:00 - Verbose: Organization: https://OMITTED.crm4.dynamics.com/XRMServices/2011/Organization.svc
2016-02-14T08:34:28.0908973+01:00 - Verbose: Name: delegateas
2016-02-14T08:34:28.0918859+01:00 - Verbose: Display name: Delegate A/S
2016-02-14T08:34:28.0918859+01:00 - Verbose: Prefix: dg
2016-02-14T08:34:28.0918859+01:00 - Verbose: Authentication Provider: OnlineFederation
2016-02-14T08:34:28.0918859+01:00 - Verbose: User: admin@OMITTED.onmicrosoft.com
2016-02-14T08:34:28.0918859+01:00 - Verbose: Password: ***********
2016-02-14T08:34:28.0918859+01:00 - Verbose: Domain: 
2016-02-14T08:34:30.6865130+01:00 - Verbose: Service Manager instantiated
2016-02-14T08:34:30.6865130+01:00 - Verbose: Service Proxy instantiated
2016-02-14T08:34:33.8585329+01:00 - Error: Cannot insert duplicate key.

val it : unit = ()

> 2016-02-14T08:34:33.8695263+01:00 - Info: Create solution: HowToDaxif
2016-02-14T08:34:33.8695263+01:00 - Verbose: Organization: https://OMITTED.crm4.dynamics.com/XRMServices/2011/Organization.svc
2016-02-14T08:34:33.8695263+01:00 - Verbose: Name: HowToDaxif
2016-02-14T08:34:33.8695263+01:00 - Verbose: Display name: HowToDaxif
2016-02-14T08:34:33.8695263+01:00 - Verbose: Publisher prefix: dg
2016-02-14T08:34:33.8695263+01:00 - Verbose: Authentication Provider: OnlineFederation
2016-02-14T08:34:33.8695263+01:00 - Verbose: User: admin@OMITTED.onmicrosoft.com
2016-02-14T08:34:33.8695263+01:00 - Verbose: Password: ***********
2016-02-14T08:34:33.8695263+01:00 - Verbose: Domain: 
2016-02-14T08:34:34.8031142+01:00 - Verbose: Service Manager instantiated
2016-02-14T08:34:34.8031142+01:00 - Verbose: Service Proxy instantiated
2016-02-14T08:34:35.5162006+01:00 - Verbose: Solution was created successfully (Solution ID: e1c27e63-edd2-e511-80dc-d89d67645050)
2016-02-14T08:34:35.5181803+01:00 - Info: The solution was created successfully.
val it : unit = ()
> 

Note: As I created the publisher before, Daxif will give an error as duplicate publishers are not allowed

We don’t make MS CRM solutions, but software solutions

We tend to say that we don’t create MS CRM solution but software solutions. As you can see, by combining Daxif with our XrmFramework (MS Developer Toolkit) We are able to spend less time with the technical stuff that just should work everytime, automation by removing the human part will always ensure a more robust approach, and by spending more time implementing business logic, we think that we are giving our customers more value for their money.

More info:

References: