none
Can I use the CSharpCodeProvider to dynamically create an assembly which implements an interface defined in the caller? RRS feed

  • Question

  • I'm in way over my head here, but I hope my intention is clear enough.

    In some program P, I would like to dynamically create a class which implements an interface defined by P. In a super-simple-ideal-world, P would look something like this:

    public interface MyInterface { int MyMember { get; set; } }
    // ...
    string source = @"public class C : MyInterface { public int MyMember { get; set; } }";
    MyInterface someObject = System.Magic.CreateObjectFromSourceCode(source);
    int i = someObject.MyMember;

    OK, so without System.Magic available I tried to use the CodeDom. I was not surprised to learn that the following does not work:

    using System;
    using DOM = System.CodeDom;
    
    namespace MyNS {
    	public interface MyInterface {
    		int MyMember { get; }
    	}
    
    	class Program {
    		static void Main(string[] args) {
    
    			Microsoft.CSharp.CSharpCodeProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
    
    			DOM.Compiler.CompilerParameters cp = new DOM.Compiler.CompilerParameters {
    				GenerateExecutable = false,
    				GenerateInMemory = true,
    				IncludeDebugInformation = false
    			};
    
    			string s = @"namespace MyNS { public class C : MyInterface { public int MyMember { get; } } }";
    
    			DOM.Compiler.CompilerResults r = provider.CompileAssemblyFromSource(cp, s);
    			foreach(DOM.Compiler.CompilerError e in r.Errors) {
    				Console.WriteLine(e.ErrorText);
    			}
    		}
    	}
    }

    The error is what I expected - the code being compiled doesn't know about MyInterface: "The type or namespace name 'MyInterface' could not be found (are you missing a using directive or an assembly reference?)

    Why am I trying to do this? Well, ultimately I want a number of different objects to exist. They are all simple DTO's. A few fields will be shared between all of them (these I want to represent by the interface). The rest of the fields in each class might be different, but I don't need to refer to those in my program, they just need to be serialized and handed off to someone further up the chain.




    http://www.sqlservercentral.com/blogs/don_halloran/


    • Edited by allmhuran Wednesday, April 18, 2018 12:16 PM
    Wednesday, April 18, 2018 6:09 AM

Answers

  • In order to solve the mentioned error, try adding this line:

    cp.ReferencedAssemblies.Add( Assembly.GetExecutingAssembly().Location );

    • Marked as answer by allmhuran Wednesday, April 18, 2018 11:48 AM
    Wednesday, April 18, 2018 11:45 AM

All replies

  • In order to solve the mentioned error, try adding this line:

    cp.ReferencedAssemblies.Add( Assembly.GetExecutingAssembly().Location );

    • Marked as answer by allmhuran Wednesday, April 18, 2018 11:48 AM
    Wednesday, April 18, 2018 11:45 AM
  • Thanks Viorel, I'm not at my work PC right now but I believe that will solve the problem.

    After asking my question and doing some more research I did find this answer, but I did not think I could use it because of the nature of the executing assembly - I was hoping to do some tricky stuff with SQL CLR, in which case the concept of the executing assembly's location doesn't make sense.

    On further research I discovered that the code DOM cannot be used in SQL CLR code anyway. I will therefore make use of this solution in my service-side code and come up with an alternative approach for the CLR code.

    http://www.sqlservercentral.com/blogs/don_halloran/

    Wednesday, April 18, 2018 11:51 AM