c# - 尝试在 MailKit 中获取文件夹时出现异常,但在首先枚举文件夹时却没有
问题描述
我正在使用 MaikKit,并试图弄清楚如何在Jim
我在 Gmail 帐户中设置的文件夹中获取邮件。我尝试按如下方式枚举文件夹...
private void GetFolders() {
using ImapClient client = new();
EmailAccount emailAccount = EmailAccountOptions.Value;
client.Connect(emailAccount.Server, emailAccount.Port, SecureSocketOptions.SslOnConnect);
client.Authenticate(emailAccount.UserName, emailAccount.Password);
foreach (FolderNamespace ns in client.PersonalNamespaces) {
IMailFolder folder = client.GetFolder(ns);
_msg += $"<br/>Ns: {ns.Path} / {folder.FullName}";
foreach (IMailFolder subfolder in folder.GetSubfolders()) {
_msg += $"<br/> {subfolder.FullName}";
}
}
try {
IMailFolder jim = client.GetFolder(new FolderNamespace('/', "Jim"));
jim.Open(FolderAccess.ReadOnly);
_msg += $"<br/>Got Jim, has {jim.Count} email(s)";
}
catch (Exception ex) {
_msg += $"<br/>Ex ({ex.GetType()}): {ex.Message}";
}
}
这显示了以下...
Ns: /
INBOX
Jim
[Gmail]
✔
✔✔
Got Jim, has 1 email(s)
所以,看来我可以Jim
毫无问题地访问。由于这样做的目的只是为了访问Jim
,因此不需要枚举。但是,当我删除它时,我得到了一个例外......
Ns: /
Ex (MailKit.FolderNotFoundException): The requested folder could not be found
经过一些试验和错误,我发现以下工作......
private void GetFolders() {
using ImapClient client = new();
EmailAccount emailAccount = EmailAccountOptions.Value;
client.Connect(emailAccount.Server, emailAccount.Port, SecureSocketOptions.SslOnConnect);
client.Authenticate(emailAccount.UserName, emailAccount.Password);
foreach (FolderNamespace ns in client.PersonalNamespaces) {
IMailFolder folder = client.GetFolder(ns);
var subs = folder.GetSubfolders();
}
try {
IMailFolder jim = client.GetFolder(new FolderNamespace('/', "Jim"));
jim.Open(FolderAccess.ReadOnly);
_msg += $"<br/>Got Jim, has {jim.Count} email(s)";
}
catch (Exception ex) {
_msg += $"<br/>Ex ({ex.GetType()}): {ex.Message}";
}
}
...但是如果我删除对它的调用,folder.GetSubfolders()
则会引发异常。
如果我更改代码以查找INBOX
...
IMailFolder jim = client.GetFolder(new FolderNamespace('/', "INBOX"));
...然后即使没有调用它也能正常工作folder.GetSubfolders()
。
有人知道为什么会这样吗?据我所知,foreach
循环没有做任何应该影响Jim
.
解决方案
很明显,使用ImapClient.GetFolder (FolderNamespace)
您使用它的方式并不是使用该 API 的正确方式。
使用 MailKit 时要考虑的一些通用 API 规则:
- 所有需要网络 I/O 的 API 都需要一个
CancellationToken
参数。因此,如果 *Client 或 *Folder API 不使用CancellationToken
,则意味着它是缓存查找或其他不需要访问网络的东西。 - MailKit API 不需要您调用构造函数来将字符串传递给方法;-)
对于ImapClient.GetFolder(FolderNamespace)
API,有一个等效的ImapFolder.GetFolder(string path, CancellationToken)
API 可用于此类任务。
(注意:FolderNamespace
.ctor 是公开的唯一原因是因为有人可能想要实现 MailKitIMailStore
或IImapClient
接口,并且需要能够创建FolderNamespace
实例)。
也就是说,我不愿意推荐这个 API,因为 MailKit 的设计目的是通过和/或APIImapFolder
一次获取 1 级文件夹,因此API 是一个巨大的 hack,必须递归地获取文件夹路径并将它们串在一起。ImapFolder.GetSubfolders()
ImapFolder.GetSubfolder()
ImapClient.GetFolder()
请记住,ImapFolder
实例具有ParentFolder
MailKit API 保证将始终设置的属性。
这使得很难保证开发人员是否可以ImapClient
使用完整路径在树中的任何位置请求随机文件夹。
无论如何......是的,Imapclient.GetFolder (string path, CancellationToken)
API 存在并且它可以工作,如果你愿意,你可以使用它,但要知道我讨厌那个 API。
好的,现在client.GetFolder(new FolderNamespace(...))
谈谈为什么如果您删除对GetSubfolders()
正如您所发现的,IMailFolder jim = client.GetFolder(new FolderNamespace('/', "Jim"));
只有在调用var subs = folder.GetSubfolders();
之前存在的调用恰好将/Jim
文件夹作为子文件夹之一返回时才有效。
你的问题是:为什么?
(或者你的问题是:为什么没有那条线它不起作用?)
无论哪种方式,答案都是一样的。
MailKit 的 ImapClient 保留了 IMAP 文件夹树中存在的已知文件夹的缓存,以便它可以快速轻松地找到每个文件夹的父节点,并且不必显式调用LIST
路径中的每个父节点来构建ImapFolder
链(记住:每个ImapFolder
都有一个ParentFolder
属性指向一个ImapFolder
实例,该实例代表其直接父级一直到根)。
所以...该GetSubfolders()
调用正在将/Jim
文件夹添加到缓存中。
由于GetFolder (new FolderNamesdpace ('/', "Jim"))
呼叫无法进入网络,它依赖于内部缓存。连接后缓存中唯一存在的文件夹是NAMESPACE
命令中返回的文件夹和INBOX
. LIST
由于响应(作为GetSubfolders()
和GetSubfolder()
调用的结果)而其他所有内容都被添加到缓存中。
推荐阅读
- java - 每次点击时在 javafx 中更改电影院座位颜色
- go - 为什么我不能使用 net.go 的 conn.ok()?
- jenkins - 詹金斯停止所有正在运行/待定的作业构建
- sql - 如何随机显示具有特定值的表中的数据
- c# - 如何根据动态参数数量过滤查询
- apache-spark - 排除 kubernetes 删除的 pod
- sql - 有重复组时选择最后一组的第一条记录
- javascript - 无法解析 Firebase 网址。请使用 https://
.firebaseio.com - python - 使用 cv2.putText() 将文本放在循环之外
- python - Django:如何将字节对象保存到models.FileField?