У меня есть цикл Parallel.ForEach, выполняющий следующие действия:
ConcurrentBag<Participant> participantsList = new ConcurrentBag<Participant>() {
new Participant() { EMail = "[email protected]", FirstName = "First1", LastName = "Last1"},
new Participant() { EMail = "[email protected]", FirstName = "First2", LastName = "Last2"},
new Participant() { EMail = "[email protected]", FirstName = "First3", LastName = "Last3"},
};
Parallel.ForEach(participantsList, (p) =>
{
var mail = new Email("REGISTERMAIL", p.FirstName, p.LastName);
mail.AddRecipient(p.EMail);
mail.Send();
});
Что происходит, так это то, что ForEach-Loop успешно обрабатывает только одного участника. Два других выдают «Ссылка на объект не указывает на экземпляр объекта».
Сначала я пришел к выводу, что это может быть связано с тем, что процесс не является потокобезопасным, поэтому я заменил список участников (первый из которых имел тип List‹>) на ConcurrentBag. Но ошибка все равно возникает.
Я не вижу другого места в моем коде, где поток мог бы совместно использовать одну коллекцию, поскольку каждый поток получает свой собственный экземпляр, если Email.
Что может быть источником этой ошибки? Может быть, статические свойства внутри электронной почты? Поскольку они не копируются для каждого экземпляра...
Все работает нормально с обычным циклом foreach.
РЕДАКТИРОВАТЬ: внутри класса EMail был один статический словарь. Я заменил его на ConcurrentDictionary, и это еще не решило проблему.
Решение. Благодаря Люку Мерретту я обнаружил проблему и смог ее решить:
я ссылался на HttpContext.Current в нескольких потоках. Проблема в том, что HttpContext.Current становится нулевым при переключении потока. Поэтому мне пришлось передать текущий HttpContext.Current в каждый поток, который я запускаю:
HttpContext ctx = HttpContext.Current;
Parallel.ForEach(participantsList, (p) =>
{
HttpContext.Current = ctx;
var mail = new Email("REGISTERMAIL", p.FirstName, p.LastName);
mail.AddRecipient(p.EMail);
mail.Send();
});
Дополнительная информация
Класс Email является оболочкой для System.Net.Mail. Конструктор принимает параметр создания, который запускает стандартную инициализацию электронной почты, предопределенную пользователем и сохраненную в статическом словаре. Он также принимает массив объектов для применения к нему String.Format:
public Email(string creationFlag, params object[] formatObjects)
{
Mail = new MailMessage();
Smtp = new SmtpClient();
Action<Email, object[]> action;
if (OptionsToActionsMap.TryGetValue(creationFlag.ToString(), out action))
{
action(this, formatObjects);
}
}
Затем выполняется следующее действие:
private static Action<Email, object[]> registerMail = new Action<Email, object[]>((mail, formatParams) =>
{
mail.Smtp.Host = "smtp.sendgrid.net";
mail.SetCredentials(WebConfigurationManager.AppSettings["mailAccount"],
WebConfigurationManager.AppSettings["mailPassword"]);
mail.From = "[email protected]";
mail.Subject = "Deine Anmeldung für die TNTC.";
mail.AddAttachment(HttpContext.Current.Server.MapPath("~/img/TNTC-Logo.png"), "logo", new ContentType("image/png"));
mail.AddAttachment(HttpContext.Current.Server.MapPath("~/img/Anfahrt.png"), "anfahrt", new ContentType("image/png"));
mail.AddHtmlView(HttpContext.Current.Server.MapPath("~/EMail/MailBody.html"), formatParams);
});
Теперь здесь есть два метода, называемых именно AddAttachment:
public void AddAttachment(string path, string contentId, ContentType ct)
{
if (LinkedResources == null)
{
LinkedResources = new List<LinkedResource>();
}
var linkedResource = new LinkedResource(path, ct);
linkedResource.ContentId = contentId;
LinkedResources.Add(linkedResource);
}
и AddHtmlView, который вызывает AddView с типом содержимого "text/html":
public virtual void AddView(string path, string ctype, params object[] formatObjects)
{
if (Views == null)
{
Views = new List<AlternateView>();
}
if (new ContentType(ctype) != null)
{
var view = AlternateView.CreateAlternateViewFromString(String.Format(File.ReadAllText(path), formatObjects), null, ctype);
Mail.AlternateViews.Add(view);
}
}
Теперь mail.Send() просто добавляет связанные ресурсы в каждое представление и отправляет почту:
public virtual void Send()
{
if (EmailValid())
{
if (LinkedResources.Count() > 0)
{
foreach (var view in Mail.AlternateViews)
{
foreach (var linkedResource in LinkedResources)
{
view.LinkedResources.Add(linkedResource);
}
}
}
foreach (var view in Views)
{
Mail.AlternateViews.Add(view);
}
Smtp.Send(Mail);
}
}
Email
в этом контексте, является ли он оберткой вокругSmtpClient
/MailMessage
? Изменить: чтобы расширить мой комментарий; Я запускаю этот код локально с издевательскими классамиParticipant
иEmail
, и никаких исключений не возникает. Сможем ли мы увидеть внутренности этих классов? - person Luke Merrett   schedule 06.07.2015NullReferenceException
происходит. - person Peter Duniho   schedule 06.07.2015