尝试在 MailKit 中获取文件夹时出现异常,但在首先枚举文件夹时却没有
Exception trying to get folder in MailKit, but not when enumerating folders first
我正在使用 MaikKit,我正在尝试弄清楚如何在我在 Gmail 帐户中设置的 Jim
文件夹中获取邮件。我尝试按如下方式枚举文件夹...
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 规则:
- 所有 API需要网络I/O的都采用
CancellationToken
参数。因此,如果 *Client 或 *Folder API 不接受 CancellationToken
,这意味着它是缓存查找或其他不需要访问网络的东西。
- MailKit APIs 不需要您调用构造函数只是为了将字符串传递给方法;-)
对于 ImapClient.GetFolder(FolderNamespace)
API,有一个等效的 ImapFolder.GetFolder(string path, CancellationToken)
API 用于此类任务。
(注意:FolderNamespace
.ctor 是 public 的唯一原因是因为有人可能想要实现 MailKit IMailStore
或 IImapClient
接口和将需要能够创建 FolderNamespace
个实例)。
也就是说,我不愿意推荐这个 API,因为 MailKit 的 ImapFolder
旨在通过 ImapFolder.GetSubfolders()
and/or ImapFolder.GetSubfolder()
APIs 所以 ImapClient.GetFolder()
API 是一个巨大的 hack,它必须递归地获取文件夹路径并将它们串在一起。
请记住,ImapFolder
个实例具有 ParentFolder
属性,MailKit API 保证将始终设置。
如果开发人员可以使用完整路径在 ImapClient
级别的树中的任何位置请求 rando 文件夹,这就很难保证。
无论如何...是的,Imapclient.GetFolder (string path, CancellationToken)
API 存在并且有效,您可以根据需要使用它,但要让大家知道我讨厌 API .
好的,现在来谈谈为什么如果您删除对 GetSubfolders()
的调用,您的 client.GetFolder(new FolderNamespace(...))
hack 将不起作用
正如您所发现的那样,IMailFolder jim = client.GetFolder(new FolderNamespace('/', "Jim"));
只有 才有效,如果 var subs = folder.GetSubfolders();
之类的调用在 return 之前存在/Jim
文件夹作为子文件夹之一。
您的问题是:为什么?
(或者您的问题可能是:为什么没有该行它不能工作?)
无论哪种方式,答案都是一样的。
MailKit 的 ImapClient 缓存存在于 IMAP 文件夹树中的已知文件夹,以便它可以快速轻松地找到每个文件夹的父文件夹,而不必在每个父节点上显式调用 LIST
在构建 ImapFolder
链的路径中(记住:每个 ImapFolder
都有一个 ParentFolder
属性 指向代表其直接父级的 ImapFolder
实例一直回到根)。
所以... GetSubfolders()
调用正在将 /Jim
文件夹添加到缓存中。
由于 GetFolder (new FolderNamesdpace ('/', "Jim"))
调用无法传出网络,它依赖于内部缓存。连接后缓存中唯一存在的文件夹是在 NAMESPACE
命令和 INBOX
中 returned 的文件夹。由于 LIST
响应(作为 GetSubfolders()
和 GetSubfolder()
调用的结果),其他所有内容都被添加到缓存中。
我正在使用 MaikKit,我正在尝试弄清楚如何在我在 Gmail 帐户中设置的 Jim
文件夹中获取邮件。我尝试按如下方式枚举文件夹...
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 规则:
- 所有 API需要网络I/O的都采用
CancellationToken
参数。因此,如果 *Client 或 *Folder API 不接受CancellationToken
,这意味着它是缓存查找或其他不需要访问网络的东西。 - MailKit APIs 不需要您调用构造函数只是为了将字符串传递给方法;-)
对于 ImapClient.GetFolder(FolderNamespace)
API,有一个等效的 ImapFolder.GetFolder(string path, CancellationToken)
API 用于此类任务。
(注意:FolderNamespace
.ctor 是 public 的唯一原因是因为有人可能想要实现 MailKit IMailStore
或 IImapClient
接口和将需要能够创建 FolderNamespace
个实例)。
也就是说,我不愿意推荐这个 API,因为 MailKit 的 ImapFolder
旨在通过 ImapFolder.GetSubfolders()
and/or ImapFolder.GetSubfolder()
APIs 所以 ImapClient.GetFolder()
API 是一个巨大的 hack,它必须递归地获取文件夹路径并将它们串在一起。
请记住,ImapFolder
个实例具有 ParentFolder
属性,MailKit API 保证将始终设置。
如果开发人员可以使用完整路径在 ImapClient
级别的树中的任何位置请求 rando 文件夹,这就很难保证。
无论如何...是的,Imapclient.GetFolder (string path, CancellationToken)
API 存在并且有效,您可以根据需要使用它,但要让大家知道我讨厌 API .
好的,现在来谈谈为什么如果您删除对 GetSubfolders()
client.GetFolder(new FolderNamespace(...))
hack 将不起作用
正如您所发现的那样,IMailFolder jim = client.GetFolder(new FolderNamespace('/', "Jim"));
只有 才有效,如果 var subs = folder.GetSubfolders();
之类的调用在 return 之前存在/Jim
文件夹作为子文件夹之一。
您的问题是:为什么?
(或者您的问题可能是:为什么没有该行它不能工作?)
无论哪种方式,答案都是一样的。
MailKit 的 ImapClient 缓存存在于 IMAP 文件夹树中的已知文件夹,以便它可以快速轻松地找到每个文件夹的父文件夹,而不必在每个父节点上显式调用 LIST
在构建 ImapFolder
链的路径中(记住:每个 ImapFolder
都有一个 ParentFolder
属性 指向代表其直接父级的 ImapFolder
实例一直回到根)。
所以... GetSubfolders()
调用正在将 /Jim
文件夹添加到缓存中。
由于 GetFolder (new FolderNamesdpace ('/', "Jim"))
调用无法传出网络,它依赖于内部缓存。连接后缓存中唯一存在的文件夹是在 NAMESPACE
命令和 INBOX
中 returned 的文件夹。由于 LIST
响应(作为 GetSubfolders()
和 GetSubfolder()
调用的结果),其他所有内容都被添加到缓存中。