Link to home
Start Free TrialLog in
Avatar of MarkMyburgh
MarkMyburghFlag for South Africa

asked on

Strange error compiling a class at runtime

Hi all

I am trying to compile a class at runtime and I get this strange error ("System.RuntimeType is inaccessible due to its protection level. Only public types can be processed.")
when I try to serialise my xml input into the newly compiled assembly.

I have attached a rather large code snippet that I use to test my compiler and I keep getting the error

Every method in the customer.cs class is public so Im not sure why Im get this error.

Basically I get a class object from a file stored somewhere on the server

Input is provided via XML from the UI, for this example I simply save the xml to file and loaded it into a Xml document that is then serialized into customer.cs.

Help would most definitely be appreciated
//
//TEST CLASS
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.CodeDom.Compiler;
using System.IO;
 
namespace MarksCompilerTest
{
    public class Tester
    { 
        public static void Main()
        {
            //declare the name of the new class to create
            string nameSpace = "Customer";
            //get the class object from file
            string Classcs = System.IO.File.ReadAllText(@"C:\Customer.cs");
            //compile the class 
            object Class = Compiler.CompileClass(Classcs, nameSpace);
 
            //get input xml
            XmlDocument doc = new XmlDocument();
            doc.Load(@"C:\customer_1.xml");
            
            //merge input xml into newly compiled class, BANG!! ERROR :'(
            object mergedClass = XML.SaveXMLtoClass(doc, Class);
        }
    }
 
    internal class Compiler
    {        
        static public object CompileClass(string classobj, string nameSpace)
        {
            CompilerResults results = CompileCode(classobj, nameSpace);
            if (results.Errors.Count != 0)
            {
                Exception ex = new Exception("Cannot compile class[" + nameSpace + "]", new Exception(XML.ConvertToXMLString(results.Errors)));
                ex.Source = "Compiler.CompileClass()";
                throw ex;
                return null;
            }
            else
            {
                Type[] type = results.CompiledAssembly.GetTypes();
                return type[0];
            }
        }
 
        private static CompilerResults CompileCode(string source, string nameSpace)
        {
            var providerOptions = new Dictionary<string, string>();
            providerOptions.Add("CompilerVersion", "v3.5");
 
            var provider = new Microsoft.CSharp.CSharpCodeProvider(providerOptions);
 
            CompilerParameters parameters = new CompilerParameters();
 
            // Generate an a class library instead of a executeable
            parameters.GenerateExecutable = false;
 
            // Generate debug information.
            parameters.IncludeDebugInformation = true;
 
            parameters.OutputAssembly = nameSpace;
 
            // Add an assembly reference.
            MarksCompilerTest.Properties.Settings settings = new MarksCompilerTest.Properties.Settings();
            foreach (string assembly in settings.Assemblies)
            {
                parameters.ReferencedAssemblies.Add(assembly);
            }
 
            //Add using statements to source code
            var sourceBuilder = new StringBuilder();
            foreach (string usingStatement in settings.Usings)
            {
                sourceBuilder.AppendLine("using " + usingStatement + ";");
            }
            sourceBuilder.AppendLine(source);
            source = sourceBuilder.ToString();
 
            // Save the assembly as to memory.
            parameters.GenerateInMemory = true;
 
            parameters.OutputAssembly = nameSpace;
 
            // Set the level at which the compiler 
            // should start displaying warnings.
            parameters.WarningLevel = 3;
 
            // Set whether to treat all warnings as errors.
            parameters.TreatWarningsAsErrors = false;
 
            // Set compiler argument to optimize output.
            parameters.CompilerOptions = "/optimize";
 
            
            // Invoke compilation.
            return provider.CompileAssemblyFromSource(parameters, source);
 
        }
    }
 
        internal class XML
        {
            public static string ConvertToXMLString(object obj)
            {
                var memoryStream = new MemoryStream();
                var doc = new XmlDocument();
 
                try
                {
                    XmlSerializer serializer = new XmlSerializer(obj.GetType());
                    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
                    serializer.Serialize(xmlTextWriter, obj);
                    memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
                    return UTF8ByteArrayToString(memoryStream.ToArray());
                }
                catch (Exception e)
                {
                    return "";
                }
                finally
                {
                    memoryStream.Flush();
                    memoryStream.Close();
                    memoryStream.Dispose();
                }
            }
 
            static private string UTF8ByteArrayToString(Byte[] characters)
            {
 
                UTF8Encoding encoding = new UTF8Encoding();
                String constructedString = encoding.GetString(characters);
                return (constructedString);
            }
 
            public static object SaveXMLtoClass(XmlDocument doc, object Class)
            {
                StringReader stream = null;
                XmlTextReader reader = null;
                try
                {
                    var serializer = new XmlSerializer(Class.GetType());                    
                    stream = new StringReader(doc.InnerXml);
                    reader = new XmlTextReader(stream);
                    return (object)serializer.Deserialize(reader);
                }
                catch (Exception ex)
                {
                    throw;
                    return null;
                }
                finally
                {
                    if (stream != null) stream.Close();
                    if (reader != null) reader.Close();
                }
            }
        }
}
 
 
//
// App config
//
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="MarksCompilerTest.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <applicationSettings>
        <MarksCompilerTest.Properties.Settings>
            <setting name="Usings" serializeAs="Xml">
                <value>
                    <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                        <string>System</string>
                        <string>System.Text</string>
                        <string>System.Xml</string>
                    </ArrayOfString>
                </value>
            </setting>
            <setting name="Assemblies" serializeAs="Xml">
                <value>
                    <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                        <string>System.dll</string>
                        <string>System.XML.dll</string>
                    </ArrayOfString>
                </value>
            </setting>
        </MarksCompilerTest.Properties.Settings>
    </applicationSettings>
</configuration>
 
 
 
//
// Customer_1.xml  (INPUT) 
//
<?xml version="1.0" encoding="UTF-8" ?>
<Customer>  
  <FirstName>Mark</FirstName>
  <LastName>Myburgh</LastName>
  <IDNumber>99999999999</IDNumber>
  <DateOfBirth>2000-07-29T02:50:43.2324</DateOfBirth>
  <Email>m@m.co.za</Email>
  <Address>
    <Line1>CODE HELL</Line1>
    <Line2>Crash Street</Line2>
    <Line3></Line3>
    <City>Broken</City>
    <Code>666</Code>
  </Address>
</Customer>
 
 
//
//  Customer.cs (FILE TO BE COMPILED)
//
public class Customer
{
 
  public string FirstName { get; set; }
  public string LastName { get; set; }  
  public string IDNumber { get; set; } 
  public DateTime DateOfBirth { get; set; } 
  public string Email { get; set; }      
  public Address Address { get; set; }
}
 
public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Line3 { get; set; }
    public string City { get; set; }
    public int Code { get; set; }
    public Address() { }
}

Open in new window

SOLUTION
Avatar of Anurag Thakur
Anurag Thakur
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of MarkMyburgh

ASKER

Hi Rag
yip it is a class.  In 2008 you don't have to declare private variables for properties anymore :
            public string FirstName { get; set; }      <-- this is how you declare a property in 2008 :D  sweet hey.  I LIKE it a lot :D
Also I'm sure you noticed that there is no references in the class, at runtime I add all the Using referencies before compiling the source code.
            var sourceBuilder = new StringBuilder();
            foreach (string usingStatement in settings.Usings)
            {
                sourceBuilder.AppendLine("using " + usingStatement + ";");
            }
            sourceBuilder.AppendLine(source);
            source = sourceBuilder.ToString();

What i am worred about is that the CompilerResults.Error is not populated, how would one attach onto the compiler stream.  that way i can have a look at the output and see if there is any compile errors.
thought i found the error but no i still get the issue.
below find the update customer class

//
//  Customer.cs (FILE TO BE COMPILED)
//
public class Customer
{
 
  public string FirstName { get; set; }
  public string LastName { get; set; }  
  public string IDNumber { get; set; } 
  public DateTime DateOfBirth { get; set; } 
  public string Email { get; set; }      
  public Address Address { get; set; }
  publlic Customer (){}
}
 
public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Line3 { get; set; }
    public string City { get; set; }
    public int Code { get; set; }
    public Address() { }
}

Open in new window

Avatar of Bob Learned
Do it matter that you have a typo?

    publlic Customer (){}
lol yeah that just on here i simple copied and pasted the example from above.  my bad.  The code is clean;
 

public class Customer
{
  public string FirstName { get; set; }
  public string LastName { get; set; }  
  public string IDNumber { get; set; } 
  public DateTime DateOfBirth { get; set; } 
  public string Email { get; set; }      
  public Address Address { get; set; }
  public Customer () {}
}
 
public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Line3 { get; set; }
    public string City { get; set; }
    public int Code { get; set; }
    public Address() { }
}

Open in new window

Hmmm...it looks like you are using auto-implemented properties, which means that the fields are generated at run-time (I believe).  What happens if you use explicitly declared fields for the property (just a test)?
:'''(   and there goes that bubble ....
same error
 
but i found a few errors inside the compiled assembly, even though the compiler did not through an error compiling the code:
error 1:
DeclaringMethod = '((System.RuntimeType)(type)).DeclaringMethod' threw an exception of type 'System.InvalidOperationException'

base {System.SystemException} = {"Method may only be called on a Type for which Type.IsGenericParameter is true."}

error 2:
GenericParameterAttributes = '((System.RuntimeType)(type)).GenericParameterAttributes' threw an exception of type 'System.InvalidOperationException'

base {System.SystemException} = {"Method may only be called on a Type for which Type.IsGenericParameter is true."}

error 3:
GenericParameterPosition = '((System.RuntimeType)(type)).GenericParameterPosition' threw an exception of type 'System.InvalidOperationException'

base {System.SystemException} = {"Method may only be called on a Type for which Type.IsGenericParameter is true."}


public class Customer
{
    private string _fn;
    private string _ln;
    private string _ID;
    private DateTime _dob;
    private string _em;
    private Address _add;
 
    public string FirstName 
    {
        get { return _fn; }
        set { _fn = value; }
    }
 
    public string LastName
    {
        get { return _ln; }
        set { _ln = value; }
    }
 
    public string IDNumber
    {
        get { return _ID; }
        set { _ID = value; }
    }
 
    public DateTime DateOfBirth
    {
        get { return _dob; }
        set { _dob = value; }
    }
 
    public string Email
    {
        get { return _em; }
        set { _em = value; }
    }
 
    public Address Address
    {
        get { return _add; }
        set { _add = value; }
    }
 
    public Customer () {}
}
 
public class Address
{
    private string _l1;
    private string _l2;
    private string _l3;
    private string _ct;
    private int _cd;
 
    public string Line1
    {
        get { return _l1; }
        set { _l1 = value; }
    }
 
    public string Line2
    {
        get { return _l2; }
        set { _l2 = value; }
    }
 
    public string Line3
    {
        get { return _l3; }
        set { _l3 = value; }
    }
 
    public string City
    {
        get { return _ct; }
        set { _ct = value; }
    }
 
    public int Code
    {
        get { return _cd; }
        set { _cd = value; }
    }
 
    public Address() { }
}

Open in new window

I believe that I have too narrow of a view to understand your problem (from where I am sitting).
hmmm
you could try compile the code on yourside and see what you get.
open a new project
copy and past the class into a new class file
add a config file to the app and copy the config settings from above
create a new file at c:\  call it customer.cs and past the customer class info from above

that should take care of all that needs to be created.
That code compiles just fine, but I am using 2005, and not 2008 (which I don't have at work).
do you still get the same error?
I'm just loading up 2005 to determine if i get the same error.
Question tho after compiling the object did the xml seriliasation step work?
 //get input xml
            XmlDocument doc = new XmlDocument();
            doc.Load(@"C:\customer_1.xml");
           
            //merge input xml into newly compiled class, BANG!! ERROR :'(
            object mergedClass = XML.SaveXMLtoClass(doc, Class);

this is the section thats giving me so much grief
nope same error on 2005 as well.  cannot serialise the xml into the newly compiled class
What is this code testing?  Are you trying to compare the compiled class against the serialized XML file?
2 things;
1 :
  compile a class object
2:
    serialize a xml file into the new compiled class object
 
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
strange thing is;  if i add the class to the projec
and do the following:
{
            //declare the name of the new class to create
            string nameSpace = "Customer";
            //get the class object from file
            //string Classcs = System.IO.File.ReadAllText(@"C:\Customer.cs");
            //compile the class
            //object Class = Compiler.CompileClass(Classcs, nameSpace);
           customer Class = new Customer();

            //get input xml
            XmlDocument doc = new XmlDocument();
            doc.Load(@"C:\customer_1.xml");
           
            //merge input xml into newly compiled class, BANG!! ERROR :'(
            object mergedClass = XML.SaveXMLtoClass(doc, Class);
        }
 
I can serialiase the object no error.  Only when i use the compiled version of the same class do i get the error :(
this is the method i'm using to serialize
 public static object SaveXMLtoClass(XmlDocument doc, object Class)
            {
                StringReader stream = null;
                XmlTextReader reader = null;
                try
                {
                    var serializer = new XmlSerializer(Class.GetType());                    
                    stream = new StringReader(doc.InnerXml);
                    reader = new XmlTextReader(stream);
                    return (object)serializer.Deserialize(reader);
                }
                catch (Exception ex)
                {
                    throw;
                    return null;
                }
                finally
                {
                    if (stream != null) stream.Close();
                    if (reader != null) reader.Close();
                }
            }
found this might help you out
http://forums.asp.net/t/1323181.aspx 
ignore my previous post:  I pasted it into the wrong window
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Managed to resolve the error.  Thanks all for your assistance.
save coding
Managed to resolve error.  Thanks all for your assistance and input
save coding
Avatar of Shahnaseem
Shahnaseem

I have seen your comments to solve the problem, but if you could specifically tell us what you have done to solve the problem it will be great.