braces and curly brackets when generating interface with codeDom - c#

I am using codeDom to generate an interface and I am getting braces and curly brackets in places I don't want. I am decorating a method with [OperationContract()] but I don't want the braces there. Here is the code I have written
toStringMethod.CustomAttributes.Add(new CodeAttributeDeclaration("OperationContract"));
Also, the methods that are generated have curly brackets added. I don't want that. Since this is an interface, I want just a semicolon. Here is what it looks like now.
[OperationContract()]
System.Collections.Generic.List<Aristotle.P6.Model.KeyIssue.Issue> GetAllIssues()
{
}
Below lies the majority of the code I have written;
foreach (var dll in dlls)
{
Assembly assembly = Assembly.LoadFrom(dll);
foreach (var type in assembly.ExportedTypes)
{
var methodInfo = type.GetMethods();
CodeCompileUnit targetUnit;
CodeTypeDeclaration targetClass;
targetUnit = new CodeCompileUnit();
CodeNamespace samples = new CodeNamespace("CodeDOMSample");
samples.Imports.Add(new CodeNamespaceImport("System"));
targetClass = new CodeTypeDeclaration("CodeDOMCreatedClass");
targetClass.IsClass = true;
targetClass.TypeAttributes =
TypeAttributes.Public | TypeAttributes.Sealed;
samples.Types.Add(targetClass);
targetUnit.Namespaces.Add(samples);
foreach (var method in methodInfo)
{
CodeMemberMethod toStringMethod = new CodeMemberMethod();
toStringMethod.Attributes =
MemberAttributes.AccessMask;
toStringMethod.Name = method.Name;
toStringMethod.CustomAttributes.Add(new CodeAttributeDeclaration("OperationContract"));
foreach (var item in method.GetParameters())
{
toStringMethod.Parameters.Add(new CodeParameterDeclarationExpression(item.ParameterType, item.Name));
}
toStringMethod.ReturnType =
new CodeTypeReference(method.ReturnType);
targetClass.Members.Add(toStringMethod);
}
Program program = new Program();
program.GenerateCSharpCode(type.Name, targetUnit);
}
}
Update
This is what my GenerateCSharpCode method looks like:
public void GenerateCSharpCode(string fileName, CodeCompileUnit targetUnit)
{
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
using (StreamWriter sourceWriter = new StreamWriter(fileName))
{
provider.GenerateCodeFromCompileUnit(
targetUnit, sourceWriter, options);
}
}

When you use CodeDom, I understand you have some options on how code is created. You have to use the CodeGeneratorOptions and set the BracingStyle = "C"; if you want to change the default behavior.
Take a look on the Examples section on the following article.
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codegeneratoroptions(v=vs.110).aspx
Hope this helps.
EDIT
Are you generated code creating classes or interfaces? I understand you want interfaces, so you should replace the IsClass = true to IsInterface = true.
Take a look at this question: how to create an interface method with CodeDom

Related

Hosting Windows Forms Designer - Serialize designer at runtime and generate C# code

I am creating a designer surface and loading the controls to a runtime.
I am having issues when deserializing/loading the controls to the runtime.
All methods I have tried seem to have some type of issue.
Issued faced for example:
Controls are still bound of the design-time
Not all properties deserialize with all the properties, namely nested properties.
Control associations does seem to be followed, i.e. Button in a Panel, will not be in the panel anymore, even though the property is still the parent after loading.
I have created a sample Project on git here: Surface Designer Test
There are the main code snippets:
Serialization from the design-time
private void LoadRuntime(int type)
{
var controls = surface.ComponentContainer.Components;
SerializationStore data = (SerializationStore)surface.
_designerSerializationService.Serialize(controls);
MemoryStream ms = new MemoryStream();
data.Save(ms);
SaveData.Data = ms.ToArray();
SaveData.LoadType = type;
new RuntimeForm().Show();
}
public object Serialize(System.Collections.ICollection objects)
{
ComponentSerializationService componentSerializationService =
_serviceProvider.GetService(typeof(ComponentSerializationService)) as
ComponentSerializationService;
SerializationStore returnObject = null;
using (SerializationStore serializationStore =
componentSerializationService.CreateStore())
{
foreach (object obj in objects)
{
if (obj is Control control)
{
componentSerializationService.SerializeAbsolute(serializationStore, obj);
}
returnObject = serializationStore;
}
}
return returnObject;
}
Deserialization in runtime
Here is attempt with reflection:
MemoryStream ms = new MemoryStream(SaveData.Data);
Designer d = new Designer();
var controls = d._designerSerializationService.Deserialize(ms);
ms.Close();
if (SaveData.LoadType == 1)
{
foreach (Control cont in controls)
{
var ts = Assembly.Load(cont.GetType().Assembly.FullName);
var o = ts.GetType(cont.GetType().FullName);
Control controlform = (Control)Activator.CreateInstance(o);
PropertyInfo[] controlProperties = cont.GetType().GetProperties();
foreach (PropertyInfo propInfo in controlProperties)
{
if (propInfo.CanWrite)
{
if (propInfo.Name != "Site" && propInfo.Name != WindowTarget")
{
try
{
var obj = propInfo.GetValue(cont, null);
propInfo.SetValue(controlform, obj, null);
}
catch { }
}
else { }
}
}
Controls.Add(controlform);
}
}
Here is attempt with loading controls directly (still bound to the design-time):
MemoryStream ms = new MemoryStream(SaveData.Data);
Designer d = new Designer();
var controls = d._designerSerializationService.Deserialize(ms);
foreach (Control cont in controls)
Controls.Add(cont);
I feel like I am missing a concept from the System.ComponentModel.Design framework.
I also do not believe there is a need to write a custom serializer for each control, as surely the already have this has Visual Studio is able to serialize all their properties as they are changed in the PropertyGrid and load them back when you run the program.
I'd love to serialize the designer into a .cs file, but how? How do you serialize controls/form and changed properties to a file like the VS designer, I tried and looked only to find xml and binary serializer. My ideal solution would be build a designer.cs with the CodeDom.
What is the correct way do accomplish this serialization between design-time and run-time?
Assuming you have a DesignSurface to show a Form as root component of the designer and having some components created at run-time by using CreateComponent method of IDesignerHost, here is how I approach the problem:
Get an instance of IDesignerHost from DesignSurface
Create new DesignerSerializationManager
Get an instance of TypeCodeDomSerializer from serialization manager
Serialize the RootComponent of the IDesignerHost
Create an instance of CSharpCodeProvider
Generate code by calling GenerateCodeFromType and passing the serialized root component.
You can also extend the example a bit and use ISelectionService to get notified about selected components and change properties at run-time using a PropertyGrid:
Example - Generate C# code from DesignSurface at runtime
Here in this example, I'll show how you can host a windows forms designer at run-time and design a form containing some controls and components and generate C# code at run-time and run the generated code.
Please note: It's not a production code and it's just an example as a
proof of concept.
Create the DesignSurface and host the designer
You can create the design surface like this:
DesignSurface designSurface;
private void Form1_Load(object sender, EventArgs e)
{
designSurface = new DesignSurface(typeof(Form));
var host = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));
var root = (Form)host.RootComponent;
TypeDescriptor.GetProperties(root)["Name"].SetValue(root, "Form1");
root.Text = "Form1";
var button1 = (Button)host.CreateComponent(typeof(Button), "button1");
button1.Text = "button1";
button1.Location = new Point(8, 8);
root.Controls.Add(button1);
var timer1 = (Timer)host.CreateComponent(typeof(Timer), "timer1");
timer1.Interval = 2000;
var view = (Control)designSurface.View;
view.Dock = DockStyle.Fill;
view.BackColor = Color.White;
this.Controls.Add(view);
}
Generate C# code using TypeCodeDomSerializer and CSharpCodeProvider
This is how I generate code from design surface:
string GenerateCSFromDesigner(DesignSurface designSurface)
{
CodeTypeDeclaration type;
var host = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));
var root = host.RootComponent;
var manager = new DesignerSerializationManager(host);
using (manager.CreateSession())
{
var serializer = (TypeCodeDomSerializer)manager.GetSerializer(root.GetType(),
typeof(TypeCodeDomSerializer));
type = serializer.Serialize(manager, root, host.Container.Components);
type.IsPartial = true;
type.Members.OfType<CodeConstructor>()
.FirstOrDefault().Attributes = MemberAttributes.Public;
}
var builder = new StringBuilder();
CodeGeneratorOptions option = new CodeGeneratorOptions();
option.BracingStyle = "C";
option.BlankLinesBetweenMembers = false;
using (var writer = new StringWriter(builder, CultureInfo.InvariantCulture))
{
using (var codeDomProvider = new CSharpCodeProvider())
{
codeDomProvider.GenerateCodeFromType(type, writer, option);
}
return builder.ToString();
}
}
For example:
var code = GenerateCSFromDesigner(designSurface);
Run the code sing CSharpCodeProvider
Then to run it:
void Run(string code, string formName)
{
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] {
"mscorlib.dll",
"System.Windows.Forms.dll",
"System.dll",
"System.Drawing.dll",
"System.Core.dll",
"Microsoft.CSharp.dll"});
parameters.GenerateExecutable = true;
code = $#"
{code}
public class Program
{{
[System.STAThread]
static void Main()
{{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
System.Windows.Forms.Application.Run(new {formName}());
}}
}}";
var results = csc.CompileAssemblyFromSource(parameters, code);
if (!results.Errors.HasErrors)
{
System.Diagnostics.Process.Start(results.CompiledAssembly.CodeBase);
}
else
{
var errors = string.Join(Environment.NewLine,
results.Errors.Cast<CompilerError>().Select(x => x.ErrorText));
MessageBox.Show(errors);
}
}
For example:
Run(GenerateCSFromDesigner(designSurface), "Form1");

Fetching documentation of ISymbol from MetadataReference

I started learning about Roslyn Code Analysis recently. I went through provided sample codes. My question is following:
Is there a way how to get XML documentation comment of a symbol loaded from a referenced library?
Sample code I worked with is FAQ(7). The goal is to get documentation comment of, let us say, a Console.Write function.
public void GetWriteXmlComment()
{
var project1Id = ProjectId.CreateNewId();
var document1Id = DocumentId.CreateNewId(project1Id);
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var solution = new AdhocWorkspace().CurrentSolution
.AddProject(project1Id, "Project1", "Project1", LanguageNames.CSharp)
.AddMetadataReference(project1Id, mscorlib);
var declarations = SymbolFinder.FindDeclarationsAsync(solution.Projects.First(), "Write", true).Result;
var decFirst = declarations.First();
var commentXml = decFirst.GetDocumentationCommentXml();
}
The sample code works well for some methods - it gets the documentation text. But for methods, such as Console.Write, it uses NullDocumentationProvider and therefore returns empty string.
UPDATE
I have found I can load the MetadataReference with TestDocumentationProvider instance as following:
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location,
default(MetadataReferenceProperties), new TestDocumentationProvider());
where TestDocumentationProvider implements Microsoft.CodeAnalysis DocumentationProvider abstract class.
private class TestDocumentationProvider : DocumentationProvider
{
protected override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default(CancellationToken))
{
// To-Be-Done
}
}
Now the question narrows to how to read documentation using documentationMemberID?
Update: In Roslyn 2.0 you can use XmlDocumentationProvider.CreateFromFile.
The only way I can think of is using Reflection to create a FileBasedXmlDocumentationProvider (or otherwise copying its implementation from GitHub). You'll also need to search for the reference assemblies, since the load location of the framework assemblies does not contain documentation.
private static MetadataReference FromType(Type type)
{
var path = type.Assembly.Location;
return MetadataReference.CreateFromFile(path, documentation: GetDocumentationProvider(path));
}
private static string GetReferenceAssembliesPath()
{
var programFiles =
Environment.GetFolderPath(Environment.Is64BitOperatingSystem
? Environment.SpecialFolder.ProgramFilesX86
: Environment.SpecialFolder.ProgramFiles);
var path = Path.Combine(programFiles, #"Reference Assemblies\Microsoft\Framework\.NETFramework");
if (Directory.Exists(path))
{
var directories = Directory.EnumerateDirectories(path).OrderByDescending(Path.GetFileName);
return directories.FirstOrDefault();
}
return null;
}
private static DocumentationProvider GetDocumentationProvider(string location)
{
var referenceLocation = Path.ChangeExtension(location, "xml");
if (File.Exists(referenceLocation))
{
return GetXmlDocumentationProvider(referenceLocation);
}
var referenceAssembliesPath = GetReferenceAssembliesPath();
if (referenceAssembliesPath != null)
{
var fileName = Path.GetFileName(location);
referenceLocation = Path.ChangeExtension(Path.Combine(referenceAssembliesPath, fileName), "xml");
if (File.Exists(referenceLocation))
{
return GetXmlDocumentationProvider(referenceLocation);
}
}
return null;
}
private static DocumentationProvider GetXmlDocumentationProvider(string location)
{
return (DocumentationProvider)Activator.CreateInstance(Type.GetType(
"Microsoft.CodeAnalysis.FileBasedXmlDocumentationProvider, Microsoft.CodeAnalysis.Workspaces.Desktop"),
location);
}
I've used something similar in RoslynPad.

Generating semantic code with roslyn

We try to figure out how to generate code with Roslyn. I'm not speaking about something like CSharpSyntaxTree.ParseText that will take some strings and convert them into an AST. Instead, I would like to build my model somehow like this (pseudo code):
Create file as compilation unit
Add class MyClass to file
Add method DoSomething to MyClass
Set body of DoSomething in a similar fashion like System.Linq.Expressions
We recently discovered Microsoft.CodeAnalysis.CSharp.SyntaxFactory, and it seemed to be promising. However, obviously we have to add trivia ourselves.
After building a tree with SyntaxFactory.CompilationUnit() and adding some members back and forth, the output of ToFullString() is just a bunch of text, that is neither readable, nor compilable (e.g., missing braces). Do we miss something when generating the text from the model?
EDIT:
When using workspaces, you can set options affecting the whitespace behavior:
public string Generate (CompilationNode rootNode)
{
var cw = new CustomWorkspace();
cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
var formattedCode = Formatter.Format (CreateFile(rootNode), cw);
return formattedCode.ToFullString();
}
This already yields a better result. Can someone confirm this as a good solution or is it rather a hack?
One problem remains. We want to generate an auto-property, currently using SF.AccessorDeclaration but it misses the semicolon when converting to the full string.
You basically have to add block definitions, then Roslyn handles the trivia for you as long as you use the Formatter (as you have written)
Here is an example for a simple class that is generated correctly without myself having to specify any trivia
var consoleWriteLine = Syntax.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
Syntax.IdentifierName("Console"),
name: Syntax.IdentifierName("WriteLine"));
var arguments = Syntax.ArgumentList (
Syntax.SeparatedList (
new[]
{
Syntax.Argument (
Syntax.LiteralExpression (
SyntaxKind.StringLiteralExpression,
Syntax.Literal (#"""Goodbye everyone!""", "Goodbye everyone!")))
}));
var consoleWriteLineStatement = Syntax.ExpressionStatement (Syntax.InvocationExpression (consoleWriteLine, arguments));
var voidType = Syntax.ParseTypeName ("void");
var method = Syntax.MethodDeclaration (voidType, "Method").WithBody (Syntax.Block(consoleWriteLineStatement));
var intType = Syntax.ParseTypeName ("int");
var getterBody = Syntax.ReturnStatement (Syntax.DefaultExpression (intType));
var getter = Syntax.AccessorDeclaration (SyntaxKind.GetAccessorDeclaration, Syntax.Block (getterBody));
var property = Syntax.PropertyDeclaration (intType, "Property").WithAccessorList (Syntax.AccessorList (Syntax.SingletonList (getter)));
var #class = Syntax.ClassDeclaration ("MyClass").WithMembers (Syntax.List (new MemberDeclarationSyntax[] { method, property }));
var cw = new CustomWorkspace();
cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
var formattedCode = Formatter.Format (#class, cw);
Console.WriteLine (formattedCode.ToFullString());
Note: Syntax = Microsoft.CodeAnalysis.CSharp.SyntaxFactory
This generates the following class definiton:
class MyClass
{
void Method()
{
Console.WriteLine("Goodbye everyone!");
}
int Property
{
get
{
return default(int);
}
}
}
Seems fine.
I had this same problem and found CustomWorkspace is now called AdhocWorkspace.
var cw = new AdhocWorkspace();
cw.Options.WithChangedOption(CSharpFormattingOptions.IndentBraces, true);
var formatter = Formatter.Format(cu, cw);
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
formatter.WriteTo(writer);
}
var code = sb.ToString();

How to pass a generic list to a dynamically compiled C# code?

I want to let the user to write a block of code as text, compile it on the fly, execute it on a collection which I obtain from a data source and get the result.
I have been tying to pass a generic list as a parameter to the dynamically compiled code, but I could not figure out a way. Following is my code:
//User writes this code in a textbox and executes
var executeCode = #"//this line doesn't work because I don't know the type
MessageBox.Show(Parameters[0].Count());
//following works fine
var t = new List<string>{""asd"", ""xyz""};
var a = t.Select(x => x).First();
MessageBox.Show(a);
return (object) a;";
#region template Code
executeCode = #"
using System;
using System.IO;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;
namespace MyNamespace {
public class MyClass {
public object DynamicCode(params object[] Parameters) {
" + executeCode +
"} } }";
#endregion template Code
var references = new[] { "System.dll", "System.Core.dll", "System.Windows.Forms.dll" };
var compilerParams = new CompilerParameters
{
GenerateInMemory = true,
TreatWarningsAsErrors = false,
GenerateExecutable = false,
CompilerOptions = "/optimize"
};
compilerParams.ReferencedAssemblies.AddRange(references);
var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(compilerParams, executeCode);
if (compile.Errors.HasErrors)
{
var text = compile.Errors.Cast<CompilerError>()
.Aggregate("Compile Error: ", (current, ce) => current + ("rn" + ce.ToString()));
throw new Exception(text);
}
// execute the compiled code
var assembly = compile.CompiledAssembly;
var myObject = assembly.CreateInstance("MyNamespace.MyClass");
if (myObject == null)
{
MessageBox.Show("Couldn't load class.");
return;
}
var sampleList = new List<string> { "abcd", "bcd" };
var codeParams = new object[] { sampleList };
try
{
var loResult = myObject.GetType().InvokeMember("DynamicCode",BindingFlags.InvokeMethod, null, myObject, codeParams);
MessageBox.Show("Method Call Result:\r\n\r\n" + loResult, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception loError)
{
MessageBox.Show(loError.Message, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
In the above code I just pass a string list. But I will replace it with an object. User will write Linq queries to filter the collection, which I compile on the fly and return the result.
Any pointers on this will be really helpful. (I use C# 4.5)
There are several options.
Change the type of your Parameters object to List<string>[]. This is the way to go if you always know you are passing in a List<string>.
Cast in your dynamically generated code: ((List<string)Parameters[0]).Count; It is a little bit clunky, but will get rid of the error.
Change the type of your Parameters object to dynamic. Since your are compiling the code at runtime, compile time type checking may not be a priority for you.

MonoTouch: Can't create DbParameterCollection?

I'm a vb.net guy trying to figure out MonoTouch c#.
I made this data helper:
public static void ExecuteCommand (SqliteConnection cnn, string command, System.Data.Common.DbParameterCollection parameters)
{
using (var c = cnn.CreateCommand()) {
c.CommandText = command;
c.CommandType = CommandType.Text;
foreach (var p in parameters)
{
c.Parameters.Add (p);
}
c.ExecuteNonQuery ();
}
}
And now I want to call the ExecuteCommand...
var parameters = new System.Data.Common.DbParameterCollection();
parameters.Add("#1", DbType.String).Value = "test";
DataConnection.ExecuteCommand ("INSERT INTO mytest (name) VALUES (#)", parameters);
But MonoTouch says...
var parameters = new System.Data.Common.DbParameterCollection(); <-- "Cannot create an instance of the abstract class or interface 'System.Data.Common.DbParameterCollection'"
parameters.Add("#1", DbType.String).Value = "test"; <-- "A local variable 'parameters' cannot be used before it is declared."
I'm sure the answer is pretty easy, but comming from a VB.Net world, this is not obvious to me.
System.Data.Common.DbParameterCollection is abstract as such you cannot create it. You should be creating a (concrete( collection that inherits from it. In SQLite case it would be Mono.Data.Sqlite.SqliteParameterCollection.
Your second error is likely related to the first, since parameters could not be compiled correctly.

Categories

Resources