I have an example application that has a number of endpoints (c# classes) which use an interface which defines some methods. These endpoints are in their own class libraries.
In an assembly called "EndPoints"
namespace EndPoints
{
public interface IEndPoint
{
void Initialize(XmlDocument message);
bool Validate();
void Execute();
}
}
In an assembly called "EndPoints.EndPoint1"
namespace EndPoints
{
public class EndPoint1 : IEndPoint
{
private XmlDocument _message;
public void Initialize(XmlDocument message)
{
_message = message;
Console.WriteLine("Initialize EndPoint1");
}
public bool Validate()
{
Console.WriteLine("Validate EndPoint1");
return true;
}
public void Execute()
{
Console.WriteLine("Execute EndPoint1");
}
}
}
The application will "choose" an endpoint to use and then find the appropriate class, create an instance of it and then call the methods in turn.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Generate "random" endpoint name
string endPointName = GetEndPointName();
// Get the class name from the namespaced class
string className = GetClassName(endPointName);
// Dummy xmldocument that used to pass into the end point
XmlDocument dummyXmlDocument = new XmlDocument();
// Load appropriate endpoint assembly because the application has no reference to it so the assembly would not have been loaded yet
LoadEndPointAssembly(endPointName);
// search currently loaded assemblies for that class
var classTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p.FullName == endPointName)
.ToList();
// cycle through any found types (should be 1 only)
for (int i = 0; i < classTypes.Count; i++)
{
var classType = classTypes[i];
IEndPoint classInstance = Activator.CreateInstance(classType) as IEndPoint;
classInstance.Initialize(dummyXmlDocument);
if (classInstance.Validate())
{
classInstance.Execute();
}
}
}
private static void LoadEndPointAssembly(string endPointName)
{
using (StreamReader reader = new StreamReader(endPointName + ".dll", System.Text.Encoding.GetEncoding(1252), false))
{
byte[] b = new byte[reader.BaseStream.Length];
reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length));
reader.Close();
AppDomain.CurrentDomain.Load(b);
}
}
private static string GetEndPointName()
{
// Code to create "random" endpoint class name
Random rand = new Random();
int randomEndPoint = rand.Next(1, 4);
return string.Format("EndPoints.EndPoint{0}", randomEndPoint);
}
private static string GetClassName(string namespacedClassName)
{
string className = null;
string[] components = namespacedClassName.Split('.');
if (components.Length > 0)
{
className = components[components.Length - 1];
}
return className;
}
}
}
I want to change the application to achieve the following;
- each endpoint assembly (and any config files and/or other assemblies that it uses) are contained in a subfolder below the application folder. the subfolder name would be the name of the endpoint class e.g. "EndPoint1"
- each endpoint runs in its own appdomain.
However, so far I've been unable to achieve this. I keep getting an exception stating its failed to load the appropriate assembly even though when I create the appdomain I specify the subfolder to be used by setting the ApplicationBase and PrivateBinPath properties of the AppDomainSetup; e.g.
AppDomain appDomain = null;
AppDomain root = AppDomain.CurrentDomain;
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = root.SetupInformation.ApplicationBase + className + @"\";
setup.PrivateBinPath = root.SetupInformation.ApplicationBase + className + @"\";
appDomain = AppDomain.CreateDomain(className, null, setup);
I've then been trying to use the Load method on the newly created appDomain to load the assembly. That's when I get the error.
Please does anyone have any thoughts about how I can load the appropriate assembly and call the methods defined in the interface? Many thanks.