【问题标题】:Creating an AppDomain and calling a method from an assembly in a subfolder创建 AppDomain 并从子文件夹中的程序集调用方法
【发布时间】:2013-12-17 11:36:51
【问题描述】:

我有一个示例应用程序,它有许多端点(c# 类),它们使用定义一些方法的接口。这些端点位于它们自己的类库中。

在名为“EndPoints”的程序集中

namespace EndPoints
{
    public interface IEndPoint
    {
        void Initialize(XmlDocument message);
        bool Validate();
        void Execute();
    }
}

在名为“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");
        }
    }
}

应用程序将“选择”要使用的端点,然后找到适当的类,创建它的实例,然后依次调用方法。

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;
        }
    }
}

我想改变应用实现如下;
- 每个端点程序集(以及它使用的任何配置文件和/或其他程序集)都包含在应用程序文件夹下的子文件夹中。子文件夹名称将是端点类的名称,例如“端点 1” - 每个端点都在自己的 appdomain 中运行。

但是,到目前为止,我一直无法做到这一点。我不断收到一个异常,指出它无法加载适当的程序集,即使当我创建 appdomain 时,我通过设置 AppDomainSetup 的 ApplicationBase 和 PrivateBinPath 属性来指定要使用的子文件夹;例如

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);

然后我一直尝试在新创建的 appDomain 上使用 Load 方法来加载程序集。那是我得到错误的时候。

请问有人对我如何加载适当的程序集并调用接口中定义的方法有任何想法吗?非常感谢。

【问题讨论】:

  • AppDomain.Load 方法正在将程序集加载到当前 AppDomain 中,这不是您要查找的内容。你需要一个 MarshalByRefDerived 加载器,就像 Michal 的回答中的那个。

标签: c# .net .net-assembly appdomain


【解决方案1】:

我会按照以下方式进行。首先,您需要一个派生自 MarshalByRef 的类。它将负责加载 EndPoints 并在单独的应用程序域中执行它们。在这里,我假设它是在 ConsoleApplication1 中定义的,但它可以移动到其他地方:

public class EndPointLoader : MarshalByRefObject
{
    public void Load(string path, string endPointName)
    {
        Assembly.LoadFrom(path);

        var classTypes = AppDomain.CurrentDomain.GetAssemblies()
                                  .SelectMany(s => s.GetTypes())
                                  .Where(p => p.FullName == endPointName)
                                  .ToList();

        for (int i = 0; i < classTypes.Count; i++)
        {
             ....
        }
    }
} 

这是使用此类的代码。您可以放入您的 LoadEndPointAssembly 方法。

var appDomain = AppDomain.CreateDomain(endPointName);
var loader = (EndPointLoader)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(EndPointLoader).FullName);
loader.Load(assemblyPath, endPointName);

【讨论】:

    猜你喜欢
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-02
    • 1970-01-01
    相关资源
    最近更新 更多