我有使用XPath过滤的Xml(类似于此的查询):
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace TestCancellation
{
class Program
{
static void Main(string[] args)
{
ItWorksWellAndThrowsException();
//ItShouldThrowAExceptionButStallsInstead();
}
private static void ItShouldThrowAExceptionButStallsInstead()
{
Task.Run(async () =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() =>
{
waitHandle.WaitOne();
//this works well though - it throws
//coordinator.ThrowIfCancellationRequested();
}, coordinator.Token);
await coordinator.CancelAsync();
//waitHandle.Set(); -- with or without this it will throw
task.Wait();
}).Wait();
}
private static void ItWorksWellAndThrowsException()
{
Task.Run(() =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() => { waitHandle.WaitOne(); }, coordinator.Token);
coordinator.Cancel();
task.Wait();
}).Wait();
}
}
public class CancellationHelper
{
private CancellationTokenSource cancellationTokenSource;
private readonly List<Task> tasksToAwait;
public CancellationHelper()
{
cancellationTokenSource = new CancellationTokenSource();
tasksToAwait = new List<Task>();
}
public CancellationToken Token
{
get { return cancellationTokenSource.Token; }
}
public void AwaitOnCancellation(Task task)
{
if (task == null) return;
tasksToAwait.Add(task);
}
public void Reset()
{
tasksToAwait.Clear();
cancellationTokenSource = new CancellationTokenSource();
}
public void ThrowIfCancellationRequested()
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
public void Cancel()
{
cancellationTokenSource.Cancel();
Task.WaitAll(tasksToAwait.ToArray());
}
public async Task CancelAsync()
{
cancellationTokenSource.Cancel();
try
{
await Task.WhenAll(tasksToAwait.ToArray());
}
catch (AggregateException ex)
{
ex.Handle(p => p is OperationCanceledException);
}
}
}
}
这会过滤掉原始Persons Xml中的所有重复项。我想从上面生成的XmlNodeList创建一个新的XmlDocument实例。在那一刻,我能看到的唯一方法是循环遍历XmlNode列表并构建一个Xml字符串(如此):
XmlNodeList allItems =
xDoc.SelectNodes("//Person[not(PersonID = following::Person/PersonID)]");
必须有一种更有效的方法吗?
答案 0 :(得分:1)
如果您乐意将其转换为LINQ to XML,那么它非常简单:
XDocument original = ...; // However you load the original document
// Separated out for clarity - could be inlined, of course
string xpath = "//Person[not(PersonID = following::Person/PersonID)]"
XDocument people = new XDocument(
new XElement("Persons",
original.XPathSelectElements(xpath)
)
);
您绝对不需要将每个节点转换为字符串并返回。您也不需要使用XmlDocument
,但它不会像使用LINQ to XML那样简单:)
答案 1 :(得分:1)
使用XmlDocument
,您可以使用
XmlDocument doc2 = new XmlDocument();
doc2.AppendChild(doc2.CreateElement("Persons"));
foreach (XmlElement person in allItems)
{
doc2.DocumentElement.AppendChild(doc2.ImportNode(person, true));
}