Recently I was working with a CMS application from a reputed commercial vendor and it looked like they were doing dynamic code gen as the CMS features and components were added. It was intriguing and I kept that at the back of my head.
Recently while exploring Web API features, I came across an extension point I had missed earlier – CustomAssemblyResolvers. This extension point is very interesting as in, it is invoked whenever WebAPI has to resolve a Url to a corresponding controller and it can’t find it in its ‘cache’. This gives us a nice opportunity to ‘inject’ Web API controller libraries into a system dynamically.
This article has been co-authored by Sumit Maitra and me
Consider the scenario where you need a System that can dynamically spin up CRUD operation based on an Entity defined at Run time. For example, users creating an Invoice template via an ‘Invoice Designer’ UI where they can Add/Remove fields to the Invoice as desired. Once the Form has been designed, they will save it in the system and upon execution, a UI will be generated containing the fields defined earlier. These fields need to be submitted to a service when the Invoice is being saved. This sounds like a good case of doing a Code Generation of the Service whenever the Invoice design is changed in the ‘Invoice Designer’. If there are no changes, the previous designer continues to work.
Today we will see how we can generate Code that are essentially Web API controllers and then compile them into a dll, which is then invoked at runtime using the Custom Assembly Resolver extension point. Our Code Gen will however be limited to generating a very simple “Hello World” Web API today, but you’ll get the gist.
Creating a Web API Controller Dynamically and packaging it into a Dll
To package our ‘on the fly’ Web API Controller, we’ll need a Code Gen module, so we’ll start with a simple Console Application called CustomAssemblyResolverDemo.
Step 1: Start Visual Studio (Desktop Editor for Express users) and create a new Console Application called CustomAssemblyResolverDemo.
We will add a Web API Self Host server to this later, so we’ll keep this Dll empty for now and add another Class Library project to the solution where we’ll do the Code Gen. We’ll call this library WebApiCodeGenLib

Step 2: We add a class library to the solution called WebApiCodeGenLib.
Step 3: Next we rename Class1.cs into WebApiGenerator.cs and add a field and a Constructor.
public class WebApiGenerator
{
string _controllerName;
public WebApiGenerator(string controllerName)
{
_controllerName = controllerName.Replace(" ", string.Empty);
}
---
}
The constructor simply takes a name that we’ll use later in the Controller. Let’s go through the dicrf
Step 4: Next we have the crux of the dll, the code to generate dll at runtime. The CreateDll method is in the WebApiGenerator.dll itself.
- We initialize the Compiler Version to v.4
- Next we initialize the C# CodDomProvider.
- We use the name passed to use, to use it as the name of the dll
- We create a CompilerParameters instance and set them up to
- Generate a dll (as opposed to an Exe)
- Provide the name of the output dll
- Add Assembly references. These need to be present in the folder where the dynamic dll is being compiled. So be sure to add reference to the Dlls below. You can use the Package Manager Console as follows
PM> install-package Microsoft.Net.Http
PM> install-package Microsoft.AspNet.WebApi.Core
- Finally we add the source code to codeProvider. Note we have used the _controllerName passed into the WebApiGenerator as the class name and also reflected it in the Message.
- The CompileAssemblyFromSource call tries to compile the code into a Dll and if it fails all the Build errors are listed out.
- On success, a Build Succeeded message is shown.
- This completes our Code generator.
public void CreateDll()
{
IDictionary<string, string> compParams =
new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } };
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp", compParams);
string outputDll = _controllerName + ".dll";
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = outputDll;
parameters.ReferencedAssemblies.Add(@"System.Net.Http.dll");
parameters.ReferencedAssemblies.Add(@"System.Net.Http.WebRequest.dll");
parameters.ReferencedAssemblies.Add(@"System.Net.Http.Formatting.dll");
parameters.ReferencedAssemblies.Add(@"System.Web.Http.dll");
string code = new StringBuilder()
.AppendLine("using System.Web.Http;")
.AppendLine("namespace ControllerLibrary")
.AppendLine("{")
.AppendLine(string.Format("public class {0} : ApiController", _controllerName))
.AppendLine(" {")
.AppendLine(" public string Get()")
.AppendLine(" {")
.AppendLine(string.Format("return \"Hi from a Dynamic controller library- {0} !\";", _controllerName))
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}")
.ToString();
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
Console.WriteLine("Build Failed");
foreach (CompilerError CompErr in results.Errors)
{
Console.WriteLine(
"Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";" +
Environment.NewLine + Environment.NewLine);
}
}
else
{
Console.WriteLine("Build Succeeded");
return Assembly.LoadFrom(outputDll);
}
Console.ReadLine();
return null;
}
Step 5: To test the CodeGen, we go back to the CustomAssemblyResolverDemo.
- First we a add reference to the WebApiCodeGen project.
- Next we add the following code to the main method
static void Main(string[] args)
{
WebApiGenerator gen = new WebApiGenerator("DynamicWebApi");
gen.CreateDll();
}
Step 6: Run the Application and make sure you are getting a Build Succeeded message on the Console.

Once you get the above message it implies your new dll is working fine. Now to load the Dll and invoke it.
Implementing the AssemblyResolver
The Assembly Resolver used by Web API derives from DefaultAssembyResolver. In CustomAssemblyResolverDemo, we add a new Class called DynamicAssemblyResolver. The class is implemented as follows:
- We override the GetAssemblies method and retrieve the available list of assemblies from base.GetAssemblies.
- To this list, we add an Assembly created on the fly by WebApiGenerator class that we wrote above.
- If a dll is generated successfully, it is returned and added to the Assemblies collection.
public class DynamicAssemblyResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
ICollection<Assembly> baseAssemblies = base.GetAssemblies();
List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
try
{
WebApiGenerator gen = new WebApiGenerator("DynamicWebApi");
Assembly onTheFly = gen.CreateDll();
if (onTheFly != null)
{
assemblies.Add(onTheFly);
}
}
catch
{
// We ignore errors and just continue
}
return assemblies;
}
}
The Self-Host Web API Server and invoking Dynamic Dll from a Console Client
Now that we have the Dynamic Dll in place, let’s setup the server that will ‘serve’ up the controller. This could very well be an ASP.NET Web API server application but today we’ll take advantage of Web API self-host.
Setting up the Web API Client
The Web API Client is nothing but an HttpClient instance that’s going to ping the Dynamically created Web API’s URL to check if it gets a response. If it does get a response, it prints it to the command line.
static async void RunDynamicClientAsync()
{
HttpClient client = new HttpClient();
Uri address = new Uri(_baseAddress, "/api/DynamicWebApi");
HttpResponseMessage response = await client.GetAsync(address);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
content = await response.Content.ReadAsStringAsync();
Console.WriteLine("On The Fly Controller says {0}", content);
}
Setting up the Self Host Server
With all dependencies set, we finally setup the Self host server. You can refer to our previous article on Web API Self hosting if you are not familiar with it. Before we get started, we make sure references to the following package are set
PM> install-package Microsoft.AspNet.WebApi.SelfHost
PM> install-package Microsoft.Net.Http
PM> install-package Microsoft.AspNet.WebApi.Core
We need an additional dependency of System.ServiceModel that we can add from the Add-Reference dialog

Once we are set with the dependencies, we the Code is as follows –
1. Select a port at which the server will run and assign the url to the _baseAddress field.
2. Setup a Route in the configuration
3. Initialize an instance of the DynamicAssemblyResolver and replace the default assembly resolver in the routing config, with it.
4. Initialize HttpSelfHostServer with the above _baseAddress and routing Configuration.
5. Start the Server and wait for clients to connect
6. We run an infinite While loop as our dirty message pump. After initializing the server, we immediately invoke the client and get a response back from them. Thereafter we wait for the user to respond. If the User hits “Enter”, we ping the dynamic controller again via our client and print the response. This loop helps as verify that the ‘Dynamic Creation’ of dll only happens the first time when it is unavailable.
7. To shut down the server user simply needs to click Ctrl+C.
static readonly Uri _baseAddress = new Uri("http://localhost:60064");
static void Main(string[] args)
{
HttpSelfHostServer server = null;
try
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(_baseAddress);
config.HostNameComparisonMode = HostNameComparisonMode.Exact;
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
DynamicAssemblyResolver assemblyResolver = new DynamicAssemblyResolver ();
config.Services.Replace(typeof(IAssembliesResolver), assemblyResolver);
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Listening on " + _baseAddress);
while (true)
{
RunClient();
Console.WriteLine("Press Ctrl+C to exit...");
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine("Could not start server: {0}", e.GetBaseException().Message);
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
finally
{
if (server != null)
{
server.CloseAsync().Wait();
}
}
}
Web API Demo
Now that we have all the moving pieces set, let’s do a Demo. To verify our DLL is created only once, put a breakpoint in the WebApiGenerator.CreateDll method and run the application.
1. The system will break at the above breakpoint.
2. Continue here and the Console will show the following messages
Note the ‘Build Succeeded’ message for the dynamic Dll creation the first time.
3. As the system waits for input, hit Enter to ping the Dynamic Web Api Controller again. Now you will note that the above breakpoint is not hit and neither is there a ‘Build Succeeded’ message in the next set of console output.
Conclusion
We saw how we could code a Web API controller at run time and access it as it is generated. This is not something you’ll require. It is feature geared more towards applications like CMS system and Enterprise Dynamic Forms over data apps. Though the Code Gen technique we saw is nothing new, it blended well to showcase Web API’s CustomAssemblyResolver extension point.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of
DotNetCurry,
DNC Magazine for Developers,
SQLServerCurry and
DevCurry. He has also authored a couple of books
51 Recipes using jQuery with ASP.NET Controls and
The Absolutely Awesome jQuery CookBook.
Suprotim has received the prestigious Microsoft MVP award for Fifteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.
Get in touch with him on Twitter @suprotimagarwal or at LinkedIn