Bound Dynamic properties not retained after save - Using Vihang Dalal's Designer Rehosting
- I am using Designer Rehosting code posted by Vihang Dalal.
I am also using DynamicPropertiesActivity from Ghenadie's blog.
I create a workflow with DynamicPropertyActivity as one of the activities.
Added 'strTest' to DynamicProperties collection.
In order to be able to use this attribute in the rest of the workflow to bing to other activities, I believe I have to bind 'strTest' to a new member. So created a new Property member say dynProp_strTest. I can now use dynProp_strTest to bind to other activities. So far so good.
The issue comes up when I try to save this workflow and then reopen it again in Designer.
I can no longer see the new member 'dynProp_strTest' that I created. I understand it is getting lost during Save, but do not exactly know how and where.
Here is the Save code (As far as I remember I am using the same code posted by Vihang and have not made any modifications to this method).
Highly appreciate any pointers to help resolve the issue.
public String Save(string filePath)
{
if (this.loader != null)
{
if (filePath != null && filePath != string.Empty)
this.loader.Xoml = filePath;
this.loader.Flush();
}
// Referesh the code compile unit every time the file is saved
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
typeProvider.RemoveCodeCompileUnit(this.loader.XamlCodeCompileUnit);
this.loader.XamlCodeCompileUnit = new CodeCompileUnit();
this.loader.XamlCodeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(filePath != null ? filePath : this.loader.Xoml), this, ref this.nameSpace, ref this.typeName));
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
// Returning the file name
if (filePath != null && filePath != string.Empty)
return filePath;
else
return this.loader.Xoml;
}
Answers
The underlying problem has nothing to do with Ghenadie's DynamicPropertiesActivity. It is because the Designer Rehosting sample does not use the previously saved .cs file when the workflow is reloaded. This is because there are no .NET library functions to parse the .cs file and turning it into a CodeCompileUnit, and it would be way to much work for a sample. The last four lines for LoadWorkflow create the default empty class when any workflow is loaded:
// Add the code compile unit for the xaml file
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
this.loader.XamlCodeCompileUnit = new CodeCompileUnit();
this.loader.XamlCodeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
Two options you could try are writing a CSharp parser that will read the .cs file in and give back the CodeCompileUnit or have logic that would parse the xoml file to figure out which variable / properties had been added and re-add them. There is no easy solution for this. You should be able to do the following:
// Add the code compile unit for the xaml file
string codeFilePath = Path.ChangeExtension(xoml, "cs");
CodeCompileUnit codeCompileUnit = null;
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
if (File.Exists(codeFilePath))
{
CSharpCodeProvider csharpProvider = new CSharpCodeProvider();
codeCompileUnit = csharpProvider.Parse(File.OpenText(codeFilePath));
}
else
{
codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
}
this.loader.XamlCodeCompileUnit = codeCompileUnit;
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
But the Parse method is not implemented in the CSharpCodeProvider.
- After looking at the code it looks like the parser you are using is not doing its job correctly. It seems to be parsing variables correctly, but not properties.
All Replies
- I have been looking more into this and I now think the issue is with the way GenerateCodeFromCompileUnit in PerformFlush method of Loader.cs is implemented.
But I am still unable to figure out the exact issue.
When the workflow is open and I re-bind the dynamic properties to new members, the .cs file corresponding to the workflow gets correctly updated with the created new dependency property members. But when I close the file and open it again, the PerformFlush method basically wipes out the newly added dependency properties corresponding to the dynamic property.
Has anyone got the dynamic properties and the designer to work together successfully.
Really appreciate any pointers in the direction of resolving this issue.
Please let me know if there is any other information that you might require if I was not clear in description of the problem.
Thanks a lot in advance. - Any clues please!!
- I have not recieved any replies on this so far.
Has no-one faced this problem yet? OR Is the problem description unclear?
Highly appreciate if I can recieve any responses or further queries.
Thanks a lot in advance. The underlying problem has nothing to do with Ghenadie's DynamicPropertiesActivity. It is because the Designer Rehosting sample does not use the previously saved .cs file when the workflow is reloaded. This is because there are no .NET library functions to parse the .cs file and turning it into a CodeCompileUnit, and it would be way to much work for a sample. The last four lines for LoadWorkflow create the default empty class when any workflow is loaded:
// Add the code compile unit for the xaml file
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
this.loader.XamlCodeCompileUnit = new CodeCompileUnit();
this.loader.XamlCodeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
This is slightly alarming - don't you think?
It sounds like the only way to load your .cs file back in to a program hosting the workflow design component would be to build your own c sharp parser!
I'm just wondering how useful the workflow designer component is going to be if it has a fundamental flaw like this??
- Is there anyway to circumvent this problem in Designer Hosting app?
Will be grateful for any suggestions to overcome this problem.
Thanks in advance. Two options you could try are writing a CSharp parser that will read the .cs file in and give back the CodeCompileUnit or have logic that would parse the xoml file to figure out which variable / properties had been added and re-add them. There is no easy solution for this. You should be able to do the following:
// Add the code compile unit for the xaml file
string codeFilePath = Path.ChangeExtension(xoml, "cs");
CodeCompileUnit codeCompileUnit = null;
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
if (File.Exists(codeFilePath))
{
CSharpCodeProvider csharpProvider = new CSharpCodeProvider();
codeCompileUnit = csharpProvider.Parse(File.OpenText(codeFilePath));
}
else
{
codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
}
this.loader.XamlCodeCompileUnit = codeCompileUnit;
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
But the Parse method is not implemented in the CSharpCodeProvider.
- Based on your suggestion above, I have implemented the Parse method which will return the codeCompileUnit and replaced the last four lines of code in LoadWorkflow with the lines suggested by you.
But I wonder if this is sufficient.
In WorkflowLoader constructor the codeBesideccu variable is being built with hardcoded values and this is what is used in PerformFlush method in the line
generator.GenerateCodeFromCompileUnit(this.CodeBesideCCU, writer, options);
Now this will flush out whatever was there in .cs file and fill it in with only the codeBesideccu content which is hardcoded and populated in the workflowLoader constructor.
This I suspect removes all contents of the .cs file and retains just the skeleton. So I am back to square one.
So the code snippet that you suggested with Parse implemented alone may not be a solution to the problem I am facing. Could you please help me proceed further on this.
Thanks a ton in advance. - Please post or send me the code your are using. this.CodeBesideCCU should be the code compile unit that you put into the loader when you opened the workflow.
- Thanks for the quick response.
I have just mailed you the relevant files.
Please let me know if you would need any other information or have any more questions regarding my approach.
Thanks a ton. - After looking at the code it looks like the parser you are using is not doing its job correctly. It seems to be parsing variables correctly, but not properties.
- I faced the same problem.. pls when you reach to a solution can you post it step-by-step to solve this problem... thanks
Regarding CodeCompileUnit issue:
I guess there is one more option besides the two suggested above on how to create CodeCompileUnit from *.cs (CSharp parser) or *.xoml file. What if you modify Designer Rehosting sample to save not only *.cs file, but also save binary serialized CodeCompileUnit as well? In that case when loading *.cs file, you would also be able to use appropriate deserialized CodeCompileUnit.
Edmundas
- SolveIt!! I am facing the same problem. I hope you would have good intuition into the problem now.Please tell me how did you change the sample to keep the existing code intact. Please help.
- Sounds like a good idea to serialize CodeCompileUnit itself!! Let me try it out.
SolveIt,
Can you please post as to what has come from your testing and any code that solves this as well?
Anyone else solve this issue?
Thanks,
Dave
- Bump.
I'm facing a similar problem. Has there been any developments?
The main issue I'm having with the Designer is it ignores the workflows .cs file. In fact, it will auto generates a basic .cs file when loading the workflow.
thanks for your time and help. Dear sir,
-firstof all sory my english.-
I have same stiuation in rehosting example.I try to find a solution in this way that you can see below;
target idea : we may not parse a code (or maybe just me) but we can compile it and get assembly and type.
1) in
public void LoadWorkflow(string xoml) method implementation replace
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));
this.loader.XamlCodeCompileUnit = new CodeCompileUnit();
this.loader.XamlCodeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
with this code
bool fileExist = false; ;
string destinationFilePath = Path.GetDirectoryName(xoml) + "\\temp.cs";
string codeFilePath = Path.ChangeExtension(xoml, "cs");
if (File.Exists(codeFilePath))
{
fileExist = true;
File.Copy(codeFilePath, destinationFilePath, true);
}
TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider));this.loader.XamlCodeCompileUnit = new CodeCompileUnit();
this.loader.XamlCodeCompileUnit.Namespaces.Add(Helpers.GenerateCodeFromXomlDocument(Helpers.GetRootActivity(xoml), this, ref this.nameSpace, ref this.typeName));
typeProvider.AddCodeCompileUnit(this.loader.XamlCodeCompileUnit);
if (fileExist)
{
ReGenerateWorkflowCode(destinationFilePath);
}
2)Add this method in workflowdesigner.cs file (
"MyActivityLibrary.dll" just for me please remove it.
)
private void ReGenerateWorkflowCode(string codeFilePath)
{
IMemberCreationService memberCreationService = GetService(typeof(IMemberCreationService)) as IMemberCreationService;
if (memberCreationService == null)
{
throw new InvalidOperationException("IMemberCreationService is null.");
}
CSharpCodeProvider csharpProvider = new CSharpCodeProvider();
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
// Compile the assembly with the appropriate references
string[] assemblyReferences = new string[] { "System.dll" ,
"MyActivityLibrary.dll",
@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Workflow.Activities.dll",
@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Workflow.ComponentModel.dll",
@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll",
@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Workflow.Runtime.dll"
};
CompilerParameters param = new CompilerParameters(assemblyReferences);
param.MainClass = this.className;
param.GenerateExecutable = false;
param.GenerateInMemory = false;
param.TreatWarningsAsErrors = false;
param.WarningLevel = 4;
TempFileCollection tempFileCollection = new TempFileCollection(".", true);
CompilerResults results = new CompilerResults(tempFileCollection);
results = csharpProvider.CompileAssemblyFromFile(param, new string[] { codeFilePath });
foreach (CompilerError error in results.Errors)
{
Trace.WriteLine(error.ToString());
}
Assembly assembly = results.CompiledAssembly;
string className = "foo." + this.className;
Type workflowType = assembly.GetType(className);
PropertyInfo[] propertyInfo = workflowType.GetProperties();
foreach (PropertyInfo pi in propertyInfo)
{
if (pi.DeclaringType != workflowType)
{
continue;
}
List<CodeAttributeDeclaration> arrayList = new List<CodeAttributeDeclaration>();object[] attList = pi.GetCustomAttributes(false);
CodeAttributeDeclaration cad = null;
foreach (Attribute attr in attList)
{
if (attr is CategoryAttribute)
{
cad = new CodeAttributeDeclaration(new CodeTypeReference(typeof(CategoryAttribute)));
cad.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(((CategoryAttribute)attr).Category)));
arrayList.Add(cad);
cad = new CodeAttributeDeclaration(new CodeTypeReference(typeof(BrowsableAttribute)));
cad.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(true)));
arrayList.Add(cad);
cad = new CodeAttributeDeclaration(new CodeTypeReference(typeof(DesignerSerializationVisibilityAttribute)));
cad.Arguments.Add(new CodeAttributeArgument(new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(DesignerSerializationVisibility)), "Visible")));
arrayList.Add(cad);
break;
}
}
if (arrayList.Count == 0)
{
continue;
}
string propertyName = pi.Name;
Type propertyType = pi.PropertyType;
bool emitDependencyProperty = true;
bool isMetaProperty = false;
bool isAttached = false;
Type ownerType = null;
bool isReadOnly = false;
((MemberCreationService)memberCreationService).CreateProperty(className, propertyName, propertyType, arrayList, emitDependencyProperty, isMetaProperty, isAttached, ownerType, isReadOnly);
}
}
3)
MemberCreationService.cs file add this overloading methods.
public void CreateProperty(string className, string propertyName, Type propertyType, List<CodeAttributeDeclaration> codeAttributeDeclarationList, bool emitDependencyProperty, bool isMetaProperty, bool isAttached, Type ownerType, bool isReadOnly)
{
if (string.IsNullOrEmpty(className))
throw new ArgumentNullException("className");
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentNullException("propertyName");
if (propertyType == null)
throw new ArgumentNullException("propertyType");
if (!this.CodeProvider.IsValidIdentifier(propertyName))
throw new ArgumentException("Invalid Identifier");
if (!this.DoesPropertyExist(className, propertyName, propertyType))
{
// create property
CodeMemberProperty property = new CodeMemberProperty();
property.Name = propertyName;
property.Type = GetCodeTypeReference(className, propertyType);
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
// add property attributes
if (codeAttributeDeclarationList != null)
{
foreach (CodeAttributeDeclaration codeAttributeDeclaration in codeAttributeDeclarationList)
{
property.CustomAttributes.Add(codeAttributeDeclaration);
}
}
// Create private field to hold the property's data
IDesignerHost host = this.ServiceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host == null)
throw new InvalidOperationException(typeof(IDesignerHost).FullName);
ITypeProvider typeProvider = this.ServiceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
if (typeProvider == null)
throw new InvalidOperationException(typeof(ITypeProvider).FullName);
string fieldName = null;
if (emitDependencyProperty)
{
fieldName = propertyName + "Property";
property.UserData["_vsDependencyPropertyFieldKey"] = fieldName;
if (!isAttached)
CreateStaticFieldForDependencyProperty(className, propertyName, propertyType, fieldName, isMetaProperty, false);
}
else
{
bool existingField = false;
//We recreate the field everytime, this is done for the dynamic properties
fieldName = GeneratePropertyAssociatedFieldName(className, propertyName, propertyType, out existingField);
if (!existingField)
CreateField(className, fieldName, propertyType, null, MemberAttributes.Private, null, true);
}
// Add getter and setter logic to retrieve and assign the value to the new private field
if (emitDependencyProperty)
{
CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(isAttached ? ownerType.FullName : className), fieldName);
property.HasGet = true;
CodeTypeReference typeRef = new CodeTypeReference(propertyType);
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(typeRef, new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "GetValue", fieldRef))));
if (!isReadOnly)
{
property.HasSet = true;
property.SetStatements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "SetValue", fieldRef, new CodeSnippetExpression("value")));
}
}
else
{
property.HasGet = true;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
if (!isReadOnly)
{
property.HasSet = true;
CodeExpression ifNOTDesignModeExpression = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DesignMode"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
property.SetStatements.Add(new CodeConditionStatement(ifNOTDesignModeExpression, new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(InvalidOperationException), new CodeExpression[] { }))));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeSnippetExpression("value")));
}
}
string nsName = null;
Helpers.GetNamespaceAndClassName(className, out nsName, out className);
CodeTypeDeclaration typeDeclaration = GetCodeTypeDeclFromCodeCompileUnit(nsName, className);
int index = 0;
foreach (CodeTypeMember member in typeDeclaration.Members)
{
if (member is CodeMemberProperty)
index++;
else
break;
}
typeDeclaration.Members.Insert(index, property);
((TypeProvider)typeProvider).RefreshCodeCompileUnit(this.loader.CodeBesideCCU, new EventHandler(RefreshCCU));
}
}
public void CreateProperty(string className, string propertyName, Type propertyType, List<CodeAttributeDeclaration> codeAttributeDeclarationList, bool emitDependencyProperty, bool isMetaProperty, bool isAttached, Type ownerType, bool isReadOnly)
{
if (string.IsNullOrEmpty(className))
throw new ArgumentNullException("className");
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentNullException("propertyName");
if (propertyType == null)
throw new ArgumentNullException("propertyType");
if (!this.CodeProvider.IsValidIdentifier(propertyName))
throw new ArgumentException("Invalid Identifier");
if (!this.DoesPropertyExist(className, propertyName, propertyType))
{
// create property
CodeMemberProperty property = new CodeMemberProperty();
property.Name = propertyName;
property.Type = GetCodeTypeReference(className, propertyType);
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
// add property attributes
if (codeAttributeDeclarationList != null)
{
foreach (CodeAttributeDeclaration codeAttributeDeclaration in codeAttributeDeclarationList)
{
property.CustomAttributes.Add(codeAttributeDeclaration);
}
}
// Create private field to hold the property's data
IDesignerHost host = this.ServiceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host == null)
throw new InvalidOperationException(typeof(IDesignerHost).FullName);
ITypeProvider typeProvider = this.ServiceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
if (typeProvider == null)
throw new InvalidOperationException(typeof(ITypeProvider).FullName);
string fieldName = null;
if (emitDependencyProperty)
{
fieldName = propertyName + "Property";
property.UserData["_vsDependencyPropertyFieldKey"] = fieldName;
if (!isAttached)
CreateStaticFieldForDependencyProperty(className, propertyName, propertyType, fieldName, isMetaProperty, false);
}
else
{
bool existingField = false;
//We recreate the field everytime, this is done for the dynamic properties
fieldName = GeneratePropertyAssociatedFieldName(className, propertyName, propertyType, out existingField);
if (!existingField)
CreateField(className, fieldName, propertyType, null, MemberAttributes.Private, null, true);
}
// Add getter and setter logic to retrieve and assign the value to the new private field
if (emitDependencyProperty)
{
CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(isAttached ? ownerType.FullName : className), fieldName);
property.HasGet = true;
CodeTypeReference typeRef = new CodeTypeReference(propertyType);
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(typeRef, new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "GetValue", fieldRef))));
if (!isReadOnly)
{
property.HasSet = true;
property.SetStatements.Add(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "SetValue", fieldRef, new CodeSnippetExpression("value")));
}
}
else
{
property.HasGet = true;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
if (!isReadOnly)
{
property.HasSet = true;
CodeExpression ifNOTDesignModeExpression = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DesignMode"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
property.SetStatements.Add(new CodeConditionStatement(ifNOTDesignModeExpression, new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(InvalidOperationException), new CodeExpression[] { }))));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodeSnippetExpression("value")));
}
}
string nsName = null;
Helpers.GetNamespaceAndClassName(className, out nsName, out className);
CodeTypeDeclaration typeDeclaration = GetCodeTypeDeclFromCodeCompileUnit(nsName, className);
int index = 0;
foreach (CodeTypeMember member in typeDeclaration.Members)
{
if (member is CodeMemberProperty)
index++;
else
break;
}
typeDeclaration.Members.Insert(index, property);
((TypeProvider)typeProvider).RefreshCodeCompileUnit(this.loader.CodeBesideCCU, new EventHandler(RefreshCCU));
}
}
After that you can push the save button.code beside file will not be emty.
This could be not vaild solution but i developed and i want to share with you.
murat pekcan
turkey
Did anyone come up with an example of how to load in properties, fields and events generated dynamically? I'm a bit confused why we need to have two code compile units, and when to use them. Appreciate any help.
Craig
did you guys solve any of your issues here ?
regards Allan
- did you guys solve any of your issues here ? I have the same problem!!
Hello, I am facing the same problem. I bound property and it works fine until i save and re open it. when i reopen the same workflow, that property get losts. i am saving the workflow files(cs, rules, xoml, layout) in the database.
Please provide me the solution if any one have it.
Thanks in Advance.
Himal
HimalHello, Himal!
I want look on solution.
Can you send me it?
SmD- Hi SolveIt
I'm getting this problem - really annoying !! did you ever find a good solution? If so please can you post your parser,
Thanks a lot - Hi everyone. I know this post is quite old, but since it seems no solution has been posted yet and this post is one of the first post you will find when you search for the problem I thought that I could give it a try to contribute something:)I managed to get the properties to load after save -> restart -> load.My solution is very basic and works as follows (inspired by many post around this subject):
- When the workflow is saved, serialize the CodeBesideCCU as a binary file.
- When workflow is loaded, deserialize the binary representation of the CodeBesideCCU and replace the old one with it.
The serializiation code (credits to unknown programmer):How to load the serialized CCU (in the LoadWorkflow(String xoml) method in WorkflowDesignerControl.cs):private byte[] SerializeCCU(CodeCompileUnit codeCompileUnit1) { MemoryStream ms = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, codeCompileUnit1); ms.Seek(0, SeekOrigin.Begin); return ms.ToArray(); } private CodeCompileUnit DeserializeCCU(byte[] bytes) { MemoryStream ms = new MemoryStream(); ms.Write(bytes, 0, bytes.Length); ms.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(ms) as CodeCompileUnit; }
To serialize the CCU (in the Save(String path) method in WorkflowDesignerControl.cs):// Add the code compile unit for the code beside file string codeFilePath = Path.ChangeExtension(xoml, "bin"); TypeProvider typeProvider = (TypeProvider)GetService(typeof(ITypeProvider)); if (File.Exists(codeFilePath)) { using (FileStream fs = File.Open(codeFilePath, FileMode.Open)) { long length = fs.Length; BinaryReader br = new BinaryReader(fs); byte[] result = br.ReadBytes((int)length); CodeCompileUnit codeBesideCompileUnit = this.DeserializeCCU(result); //Remove old CodeBesideCCU typeProvider.RemoveCodeCompileUnit(this.loader.CodeBesideCCU); this.loader.CodeBesideCCU = codeBesideCompileUnit; //Add the new deserialized one. typeProvider.AddCodeCompileUnit(this.loader.CodeBesideCCU); } }
// Referesh the code compile unit every time the file is saved byte[] compileUnitAsBytes = this.SerializeCCU(this.loader.CodeBesideCCU); File.WriteAllBytes(Path.ChangeExtension(this.loader.Xoml, "bin"), compileUnitAsBytes);
As usual, this code come without any warranty:)Hope it helps!- Proposed As Answer byVSFustrated Thursday, October 15, 2009 5:34 PM
- I ran into problems myself with my "solution" regarding serializing the CodeBesideCCU. It exist a much more elegant solution and that is to use a .cs parser. I managed to found one called NRefactor, and is available for free. It is part of the IDE SharpDevelop.The Parser is not available as a seperate download but you can get it from the SharpDevelop source:
You need to add the dll ICSharpCode.NRefactory.dll to your project.I used the following code to parse the .cs file into a CodeCompileUnit:Hope it helps..It made things alot easier for me:)WorkflowLoader loader = new WorkflowLoader(); loader.Xoml = xoml; string codeFilePath = Path.ChangeExtension(xoml, "cs"); CodeCompileUnit codeCompileUnit = null; if (File.Exists(codeFilePath)) { IParser parser; parser = ParserFactory.CreateParser(codeFilePath); if (parser != null) { parser.Parse(); ICSharpCode.NRefactory.Visitors.CodeDomVisitor visit = new ICSharpCode.NRefactory.Visitors.CodeDomVisitor(); CodeNamespace globalNamespace = (CodeNamespace)visit.VisitCompilationUnit(parser.CompilationUnit, null); visit.codeCompileUnit.Namespaces.Remove(globalNamespace); visit.codeCompileUnit.Namespaces[0].Types[0].IsPartial = true; codeCompileUnit = visit.codeCompileUnit; parser.Dispose(); loader.CodeBesideCCU = codeCompileUnit; } }


