我正在编写一个控制台应用程序来进行Web服务调用并接收XML,并且该XML将是“文件夹”或“文档”的条目。我在C#中编写了以下do while循环来循环调用webservice,但我想知道是否有更多功能的方法来执行它:
...
var documentUrls = new Dictionary<string, string>();
// get the root folder's children
var rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
.Do(str => Console.WriteLine($"{str}:"))
.GetResponse(headerToken)
.GetXmlString()
.DeserializeFromXml<Library.Children.Feed>();
var folderUrls = rootFeed.Entries
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
.Select(entry => $"{entry.Link.Href}/children")
.ToList();
// go through each folder and pull out the documents - if a nested folder is found then check that too
do
{
var feeds = folderUrls
.Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Library.Children.Feed>())
.SelectMany(x => x.Entries);
var folders = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
.Select(x => $"{x.Link.Href}/children")
.ToList();
var documents = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
.ToDictionary(
keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);
// if there are no more folders or documents to check, break out of this while loop
if (!documents.Any() && !folders.Any())
break;
Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");
folderUrls.RemoveAll(x => true); // remove all the folder urls as they have already been checked!!!!
folderUrls.AddRange(folders);
documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));
} while (true);
Console.WriteLine($"There are {documentUrls.Count()} {nameof(documentUrls)}");
...
答案 0 :(得分:1)
没有无限循环的模式通常会在循环之前预先加载退出条件,
var folders = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
.Select(x => $"{x.Link.Href}/children")
.ToList();
var documents = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
.ToDictionary(
keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);
然后使用while循环条件作为退出测试:
while(documents.Any() || folders.Any()){
var feeds = folderUrls
.Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Library.Children.Feed>())
.SelectMany(x => x.Entries);
Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");
folderUrls.RemoveAll(x => true); // remove all the folder urls as they have already been checked!!!!
folderUrls.AddRange(folders);
documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));
最后在循环之前加载退出条件的变量:
folders = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
.Select(x => $"{x.Link.Href}/children")
.ToList();
documents = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
.ToDictionary(
keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);
}
...
这种方法重复代码,因此为每个查询创建方法,因此重复的代码只是对适当方法的几次调用。这将导致代码更易读,因为代码中的描述性方法名称比LINQ查询更容易理解(更不用说它不会有无限循环)。这种模式可能需要更多的努力来处理变量范围,但在这种情况下,这应该不是问题(在这种情况下feed
应该是方法的唯一参数)。
答案 1 :(得分:1)
使用for
,while
或递归,这不是更多功能。这主要是为了避免副作用。
E.g。如何使用folderUrls
跟踪已经检查和嵌套的文件夹不是很有用。相反,设计一个函数,它获取文件夹列表并返回文档。即定义明确的输入和输出。从您的代码示例中,很难说出您想要实现的目标。
这个函数可以像这样解释:
// I don't know what the type of "entry" is. Replace "TEntry" with the correct type.
public IEnumerable<TEntry> LoadDocuments(IEnumerable<TEntry> feedEntries)
{
var folderUrls = feedEntries
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
.Select(entry => $"{entry.Link.Href}/children");
if (!folderUrls.Any())
return Enumerable.Empty<TEntry>();
var feeds = folderUrls
.Select(url => url.GetResponse(headerToken)
.GetXmlString()
.DeserializeFromXml<Library.Children.Feed>())
.SelectMany(x => x.Entries)
.ToList();
// Recursive call to load nested documents.
var nestedDocuments = LoadDocuments(feeds);
var documents = feeds
// What's the type of "entry" here? Use that type for the return type of this
// function.
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"));
return documents.Concat(nestedDocuments);
}
调用它并像以前一样将此函数的输出转换为字典:
var rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
.Do(str => Console.WriteLine($"{str}:"))
.GetResponse(headerToken)
.GetXmlString()
.DeserializeFromXml<Library.Children.Feed>();
var documentUrls = LoadDocuments(rootFeed.Entries)
.ToDictionary(
x => x.Content.Attrs.Attr
.Where(y => y.Name.ToLower() == "id")
.First().Text,
x => x.Content.Attrs.Attr
.Where(y => y.Name.ToLower() == "name")
.First().Text);
答案 2 :(得分:0)
关心自己的典型化
static void Main(string[] args)
{
var documentUrls = new Dictionary<string, string>();
Feed rootFeed = $"{baseUrl}/biprws/infostore/{23}/children"
.Do(str => Console.WriteLine($"{str}:"))
.GetResponse(headerToken)
.GetXmlString()
.DeserializeFromXml<Library.Children.Feed>();
IEnumerable<object> folderUrls = rootFeed.Entries
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "folder"))
.Select(entry => $"{entry.Link.Href}/children")
.ToList();
object feeds;
object folders;
object documents;
Init(folderUrls, out feeds, out folders, out documents);
while (documents.Any() || folders.Any())
{
Console.WriteLine($"Got {folders.Count} folders and {documents.Count} documents");
folderUrls.RemoveAll(x => true);
folderUrls.AddRange(folders);
documents.ForEachDo(x => documentUrls.Add(x.Key, x.Value));
Init(folderUrls, out feeds, out folders, out documents);
}
Console.WriteLine($"There are {documentUrls.Count()} {nameof(documentUrls)}");
}
private static void Init(object foldersUrl, out object feeds, out object folders, out object documents)
{
feeds = InitFeeds(foldersUrl);
folders = InitFolders(feeds);
documents = InitDocs(feeds);
}
private static object InitDocs(object feeds)
{
var documents = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "webi"))
.ToDictionary(
keySelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "id").First().Text,
elementSelector: x => x.Content.Attrs.Attr.Where(y => y.Name.ToLower() == "name").First().Text);
return documents;
}
private static object InitFolders(object feeds)
{
var folders = feeds
.Where(entry => entry.Content.ContentContainsFolderUrl(type: "Folder"))
.Select(x => $"{x.Link.Href}/children")
.ToList();
return folders;
}
private static object InitFeeds(object folderUrls)
{
var feeds = folderUrls
.Select(url => url.GetResponse(headerToken).GetXmlString().DeserializeFromXml<Feed>())
.SelectMany(x => x.Entries);
return feeds;
}