Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在appdomains之间共享数据,在两端运行linq查询,甚至更远_C#_.net_Linq_Appdomain_Dynamic Compilation - Fatal编程技术网

C# 在appdomains之间共享数据,在两端运行linq查询,甚至更远

C# 在appdomains之间共享数据,在两端运行linq查询,甚至更远,c#,.net,linq,appdomain,dynamic-compilation,C#,.net,Linq,Appdomain,Dynamic Compilation,从一开始,我就在开发一个实用程序来解析和查看来自第三方应用程序的日志,并明确要求具有极高的灵活性 为了实现这一点,我将LINQ压缩到它的全部潜能(可能超过这个:/) 因为用户应该通过实用程序的UI构建自己的查询,所以我需要能够动态地加载和卸载它们。为此,我使用一个方法(字面上称为“DoStuff”)构建虚拟类,该方法返回查询结果(通常基于LINQ,但可能存在异常),将它们编译(通过CSharpCodeProvider)到临时程序集中,将它们加载到“一次性”AppDomain中,运行查询,然后去掉

从一开始,我就在开发一个实用程序来解析和查看来自第三方应用程序的日志,并明确要求具有极高的灵活性

为了实现这一点,我将LINQ压缩到它的全部潜能(可能超过这个:/)

因为用户应该通过实用程序的UI构建自己的查询,所以我需要能够动态地加载和卸载它们。为此,我使用一个方法(字面上称为“DoStuff”)构建虚拟类,该方法返回查询结果(通常基于LINQ,但可能存在异常),将它们编译(通过CSharpCodeProvider)到临时程序集中,将它们加载到“一次性”AppDomain中,运行查询,然后去掉AppDomain和程序集

这里是警报触发的地方:LINQ+AppDomains=出现问题的机会很多

更糟糕的是,要确定每个域上运行的是什么,以及将跨越(或试图跨越)域边界的是什么,可能会变得非常棘手。我希望stackoverflow中的知识库能够帮助我们在这方面有所启发

因此,深入到细节中,以下是我所拥有的内容的简化版本:

在主程序集(实用程序的实际.exe文件)上,只有执行它的用户才能以“正常方式”加载它,我有一些类似的东西:

public abstract class LogEntry { // Should this inherit from MarshalByRefObject?
    protected internal abstract void Parse(StreamReader input);
    ...
    /* There are literally dozens of classes that inherit from LogEntry, dealing with different kinds of entries, and
     *   defining an appropriate implementation of Parse().
     * The logic to choose the appropriate sub-class would be within LogData's constructor (see below).
     */
}
internal class LogData: IEnumerable<LogEntry> { // Should this inherit from MarshalByRefObject?
    private List<LogEntry> loadedData = new List<LogEntry>();
    public LogData(IEnumerable<string> LogFiles) { ... }
    /* The enumerator for this class does some heavy-dutty work to find the log files and load them "as needed":
     * Each time a new entry is retrieved from the logs, it's saved on loadedData just before returning it;
     * When the enumerator is reset and used again, it goes through loadedData until exhausting it, and only then
     * goes back into actually loading (and saving) new data.
     * I'm not including the code here because it is ugly, verbose, and probably irrelevant to the question.
     */
}
公共抽象类日志项{//是否应该从MarshalByRefObject继承?
受保护的内部抽象空解析(StreamReader输入);
...
/*实际上有几十个类继承自LogEntry,处理不同类型的条目,以及
*定义Parse()的适当实现。
*选择适当子类的逻辑将在LogData的构造函数中(见下文)。
*/
}
内部类LogData:IEnumerable{//是否应该从MarshalByRefObject继承?
私有列表loadedData=新列表();
公共日志数据(IEnumerable日志文件){…}
/*此类的枚举器执行一些繁重的工作,以查找日志文件并“根据需要”加载它们:
*每次从日志中检索到新条目时,它都会在返回之前保存在loadedData上;
*当重新设置并再次使用枚举数时,它将遍历loadedData,直到耗尽它,然后
*返回到实际加载(和保存)新数据。
*我在这里不包括代码,因为它丑陋、冗长,而且可能与问题无关。
*/
}
(除了一些表格、锅炉板启动代码等)。该程序将要求用户提供日志文件的集合(使用FileOpenDialogs、支持带有经典“*”和“?”通配符的文件名模式的文本字段等),并使用它们初始化日志数据的单个实例

之后,用户将看到一个shinny GUI来构建和微调查询,包括运行查询的末日按钮。这就是乐趣的开始。其思想是生成表示用户查询的代码,然后将其包装成如下内容(我省略了“using”语句和其他附带内容):

public类{//我正在考虑使这个静态类和保存构造函数调用工作,但这是一个次要主题。
公共IEnumerable DoStuff(IEnumerable输入){
//这将由根据用户输入动态创建的代码填充
...
}
}
然后将其馈送到CSharpCodeProvider,以将其编译(使用预定义的、可配置的引用列表)到临时程序集中。然后,我将程序集加载到一个“扔掉”的AppDomain中,创建一个“whatever”的实例,调用它的DoStuff方法(将它传递给我的LogData实例),检索结果,卸载域,并除去assmebly

现在,幕下会发生什么?这才是真正的问题。跟踪每个域上运行的代码以及在它们之间来回跳转的数据是相当棘手的。经过一些研究,我正在做出一些有根据的猜测,所以如果有人能告诉我这些猜测是否正确就足够了:

1,日志数据必须从MarshalByRefObject继承,因此当它被传递到用户的查询(DoStuff)时,枚举器仍然在“默认”appdomain上执行,并且工作正常

2,)上面的意思是没有任何LogData实例会跨越AppDomain边界(对其成员的调用是另一回事),但是当从DoStuff迭代日志时,它的条目(LogEntry实例)会跨越AppDomain边界。那么,这些应该是可序列化的吗?对它们进行反序列化一点也不难,尽管我觉得这会使解析工作加倍。将它们改为MarshallByRefObject子体是否有效

3、无论DoStuff返回什么,它都需要被复制,而不是被引用,因此在卸载“丢弃”的AppDomain之后,它仍然可以从主AppDomain中使用。因此,查询结果中可能包含的任何类型都需要是可序列化的,并且从不封送ByRefObject

4,即使DoStuff返回一个LINQ查询(类似于“从输入中的条目返回…”),查询的枚举器也将只在“丢弃”域上运行,并且只有单个项(在上面的一点上,我已经说过这些项无论如何都应该是可序列化的)将实际跨越域边界

这些结论正确吗?我错过什么了吗

提前感谢您的回答

注:根据丹尼尔在下面的评论,我想我必须对该项目的某些方面进行更深入的探讨:

  • 虽然“查询生成器”旨在成为定义查询的主要机制,但该程序还允许用户在生成器不够时引入原始代码(也适用于那些对编程足够熟悉的用户,他们更喜欢键入一些代码而不是
    public class whatever { // I'm thinking of making this static and saving on the constructor invokation work, but that's a side topic.
        public IEnumerable DoStuff(IEnumerable<LogEntry> Input) {
            // This will be filled with code created on-the-fly based on user input
            ...
        }
    }