Un besoin très courant, vous en conviendrez (au moins pour me faire plaisir) :)

Le contexte

Pour être plus précis, je travaille en ce moment sur une application à destination de fonctionnels qui doit leur permettre de mettre au point des processus métier avec un outil haut niveau. Et de les tester bien sûr...

Je suis donc parti sur l'intégration du Wrokflow Designer dans une application Windows Form (Visual Studio étant un peu trop hardcore pour cette population). Et pour le versioning, je me suis dit : facile, on utilise l'attribut AssemblyVersion et pour bien faire, on signe le résultat.

Et là, je me suis pris le mur pour faire cela coté code : j'ai seché lamenteblement avant de trouver une solution un peu compliquée mais que je trouve très interressante. Bien entendu, il y a beaucoup plus simple et je vais commencer par la seconde solution. 

 La bonne solution

Les développeurs de la librairie CodeDom avaient du voir venir la question car ils avaient prévu une option fourre-tout au niveau de l'objet CompilerParameters : il dispose d'une propriété CompilerOptions de type string qui prend en fait tous les paramètres disponibles par la ligne de commande. Ici, on a au choix : /keyfile ou /keycontainer (merci à Aditya Bhandarkar pour l'information). Et voila...

L'autre solution

Oui, mais si on veut positionner d'autres éléments non disponibles par la ligne de commande ?

C'est là tout l'intérêt de ma solution "un peu compliquée" :)

Tout d'abord, je compile le Workflow en mémoire et conserve l'objet généré CompiledUnit :

WorkflowCompiler compiler = new WorkflowCompiler();
WorkflowCompilerParameters parameters = new WorkflowCompilerParameters(assemblyNames);
parameters.LibraryPaths.Add(Path.GetDirectoryName(typeof(ActivityLibrary.SimpleWebServiceActivity).Assembly.Location));
parameters.OutputAssembly = string.Format("{0}.netmodule", this.WorkflowName);
parameters.GenerateCodeCompileUnitOnly = true;
_compilerResults = compiler.Compile(parameters, fileName);

Puis je récupère l'objet voulu en mémoire et je le compile je modifie ses attributs:

CodeCompileUnit ccu = _compilerResults.CompiledUnit;
ccu.AssemblyCustomAttributes.Add(new CodeAttributeDeclaration("System.Reflection.AssemblyKeyFile",

   new CodeAttributeArgument (new CodePrimitiveExpression(@"c:\key.snk"))));

Enfin, je compile le résultat :

CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();

string libPath = typeof(ActivityLibrary.SimpleWebServiceActivity).Assembly.Location;
ICodeCompiler gen = provider.CreateCompiler();
CompilerParameters cp = new CompilerParameters(new string[] { libPath,
                    @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll",
                    @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll",
                    @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll",
                @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll",
                @"C:\Program Files\Reference Assemblies\Microsoft\WinFX\v3.0\System.Workflow.Activities.dll",
                @"C:\Program Files\Reference Assemblies\Microsoft\WinFX\v3.0\System.Workflow.ComponentModel.dll",
                @"C:\Program Files\Reference Assemblies\Microsoft\WinFX\v3.0\System.Workflow.Runtime.dll"});

                cp.GenerateExecutable = false;

                cp.OutputAssembly = exeName;

                cp.GenerateInMemory = false;

                cp.TreatWarningsAsErrors = false;

                CompilerResults _compilerResults2 = gen.CompileAssemblyFromDom(cp, ccu);

Et voila une jolie assembly correspondant à mes besoins :)

A noter que l'objet CompilerResults expose une propriété Output qui contient le résultat de l'affichage du compilateur en mode ligne de commande : très pratique pour trouver les erreurs voir les afficher...