Dynamic loading controllers in .NET Core
I needed to load dynamically some controllers to the Stankins application .Dynamic like in – written in some text file, then loading them, The best reference was https://www.strathweb.com/2018/04/generic-and-dynamically-generated-controllers-in-asp-net-core-mvc/ . But it was not enough : what I wanted is to write the controller in some text file and compile and load them.
First problem ; What about the dll’s to be loaded ?
I ended up with this code :
var refs=new List<MetadataReference>(); var ourRefs=Assembly.GetEntryAssembly().GetReferencedAssemblies(); foreach(var item in ourRefs) { var ass=Assembly.Load(item); refs.Add(MetadataReference.CreateFromFile(ass.Location)); } refs.Add(MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location)); //MetadataReference NetStandard = MetadataReference.CreateFromFile(Assembly.Load("netstandard, Version=2.0.0.0").Location); MetadataReference NetStandard = MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location); refs.Add(NetStandard); refs.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location) );
Second problem: where to put the errors ? The simplest option ( not the best one) is to put as comments to the end of the file – at reloading I will find them and correct
using (var ms = new MemoryStream()) { var res = compilation.Emit(ms); if (!res.Success) { string diag=string.Join(Environment.NewLine, res.Diagnostics.Select(it=>it.ToString())); File.AppendAllText(fileName,"/*"+diag+"*/"); return null; } }
( Better – to display the error inline…)
Third problem: The controlers are loaded at initial step of configuring .NET MVC Core 2.x , not later. The assembly loader /unloader will come as a feature at .NET Core 3 – so not yet. I figure up that the best solution is to re-load the application
static CancellationTokenSource cancel ; static bool InternalRequestRestart; public async static Task Main(string[] args) { do{ InternalRequestRestart=false; cancel= new CancellationTokenSource(); await CreateWebHostBuilder(args).Build().RunAsync(cancel.Token); await Task.Delay(10);//just waiting some time Console.WriteLine("restarting"); }while(InternalRequestRestart); } public static void Shutdown() { InternalRequestRestart=true; cancel.Cancel(); }
You can find the code source for loading the controllers at https://github.com/ignatandrei/stankins/blob/master/stankinsv2/solution/StankinsV2/StankinsData/GenericControllerFeatureProvider.cs
You can find the code source for restarting the application at https://github.com/ignatandrei/stankins/blob/master/stankinsv2/solution/StankinsV2/StankinsData/Program.cs
You can test the code at https://azurestankins.azurewebsites.net/receiveData ( add a new controller , hit save and then access the route – or swagger at https://azurestankins.azurewebsites.net/swagger/index.html
– or you can try with Docker with
( for linux containers)
docker run -p 5000:5000 ignatandrei/stankins_linux
( for windows containers)
docker run -p 5000:5000 ignatandrei/stankins_linux
( restart docker if any problems)
and access http://localhost:5000/receiveData
That’s all 😉