public override int GetHashCode()
 {
     unchecked
     {
         var hashCode = SubjectExpression.GetHashCode();
         hashCode = (hashCode * 397) ^ FromExpression.GetHashCode();
         hashCode = (hashCode * 397) ^ ForExpression.GetHashCode();
         return(hashCode);
     }
 }
        public async Task <ComposeMailResult> ComposeAndSend(IProgress <SendMailProgress> progress,
                                                             CancellationToken stopRequestedToken)
        {
            var compiledAddressExpression     = ToAddressExpression.Compile();
            var compiledNameExpression        = ToNameExpression.Compile();
            var compiledSubjectExpression     = SubjectExpression.Compile();
            var compiledFromAddressExpression = FromAddressExpression.Compile();
            var compiledFromNameExpression    = FromNameExpression.Compile();

            var parsingOptions = new ParserOptions(Template);

            parsingOptions.Timeout = TimeSpan.FromMinutes(1);
            parsingOptions.Formatters.AddFromType(typeof(LinqFormatter));
            parsingOptions.Formatters.AddFromType(typeof(DynamicLinq));
            var parsedTemplate = await Parser.ParseWithOptionsAsync(parsingOptions);

            var sendData            = 0;
            var sendSuccess         = 0;
            var buffered            = 0;
            var currentlyProcessing = 0;

            var sendFailed  = new ConcurrentDictionary <MailData, SendMailStatus>();
            var maxSendData = await MailDataStrategy.Count();

            var mailDatas = await MailDataStrategy.GetMails();

#if DEBUG
            var rand = new Random(1337);
#endif

            void Progress(SendMailTaskProgress taskProgress, MailData mailData)
            {
                Interlocked.Add(ref sendData, 1);
                if (!taskProgress.Success)
                {
                    sendFailed.TryAdd(mailData, new SendMailStatus()
                    {
                        ErrorText = taskProgress.Error
                    });
                }
                else
                {
                    Interlocked.Add(ref sendSuccess, 1);
                }

                progress.Report(new SendMailProgress(taskProgress.To, sendData, maxSendData, true, sendSuccess, sendFailed.Count, buffered, currentlyProcessing));
            }

            if (SendInParallel)
            {
                var consumerItems = new BlockingCollection <MailData>(new ConcurrentBag <MailData>());
                IMailDistributorState globalState = null;

                if (MailDistributor.ParallelSupport == ParallelSupport.Full)
                {
                    globalState = await MailDistributor.BeginSendMail();
                }

                var threads = new Thread[ParallelNoOfParallism];
                for (int i = 0; i < threads.Length; i++)
                {
                    void Start()
                    {
                        try
                        {
                            IMailDistributorState threadState = globalState;
                            if (MailDistributor.ParallelSupport == ParallelSupport.MultiInstance)
                            {
                                threadState = MailDistributor.BeginSendMail().ConfigureAwait(true).GetAwaiter()
                                              .GetResult();
                            }

                            foreach (var mailData in consumerItems.GetConsumingEnumerable())
                            {
                                Interlocked.Add(ref currentlyProcessing, 1);
                                Interlocked.Exchange(ref buffered, consumerItems.Count);
                                if (stopRequestedToken.IsCancellationRequested)
                                {
                                    break;
                                }
//#if DEBUG
//								Task.Delay(rand.Next(1050, 2000), stopRequestedToken)
//									.ConfigureAwait(true).GetAwaiter().GetResult();
//#endif
                                SendSingleItem(taskProgress =>
                                {
#if DEBUG
                                    if (rand.Next(0, 2) == 1)
                                    {
                                        taskProgress = new SendMailTaskProgress(taskProgress.To, "RAND");
                                    }
#endif

                                    Progress(taskProgress, mailData);
                                },
                                               mailData,
                                               parsedTemplate,
                                               compiledAddressExpression,
                                               compiledSubjectExpression,
                                               compiledNameExpression,
                                               compiledFromAddressExpression,
                                               compiledFromNameExpression,
                                               threadState).ConfigureAwait(true).GetAwaiter().GetResult();
                                Interlocked.Add(ref currentlyProcessing, -1);
                            }

                            if (MailDistributor.ParallelSupport == ParallelSupport.MultiInstance)
                            {
                                threadState = MailDistributor.EndSendMail(threadState).ConfigureAwait(true).GetAwaiter()
                                              .GetResult();
                            }
                        }
                        catch (Exception e)
                        {
                        }
                        finally
                        {
                        }
                    }

                    var thread = new Thread(Start);
                    thread.IsBackground = true;
                    thread.Name         = "MailComposerConsumer_" + i;
                    thread.Priority     = ThreadPriority.Normal;
                    thread.TrySetApartmentState(ApartmentState.MTA);
                    threads[i] = thread;
                }

                foreach (var thread in threads)
                {
                    thread.Start();
                }

                var readAheadCount = ParallelReadAheadCount;
                await foreach (var item in mailDatas.WithCancellation(stopRequestedToken))
                {
                    consumerItems.Add(item, stopRequestedToken);
                    while (consumerItems.Count >= readAheadCount)
                    {
                        await Task.Delay(10, stopRequestedToken);
                    }
                }

                consumerItems.CompleteAdding();
                try
                {
                    foreach (var thread in threads)
                    {
                        thread.Join();
                    }
                }
                catch (Exception e)
                {
                }

                if (MailDistributor.ParallelSupport == ParallelSupport.Full)
                {
                    globalState = await MailDistributor.EndSendMail(globalState);
                }
            }
            else
            {
                var beginSend = await MailDistributor.BeginSendMail();

                Interlocked.Add(ref currentlyProcessing, 1);
                await foreach (var mailData in mailDatas.WithCancellation(stopRequestedToken))
                {
                    buffered = 1;
                    if (stopRequestedToken.IsCancellationRequested)
                    {
                        break;
                    }

//#if DEBUG
//					await Task.Delay(rand.Next(150, 200), stopRequestedToken);
//#endif
                    await SendSingleItem(taskProgress =>
                    {
#if DEBUG
                        if (rand.Next(0, 2) == 1)
                        {
                            taskProgress = new SendMailTaskProgress(taskProgress.To, "RAND");
                        }
#endif
                        Progress(taskProgress, mailData);
                    },
                                         mailData,
                                         parsedTemplate,
                                         compiledAddressExpression,
                                         compiledSubjectExpression,
                                         compiledNameExpression,
                                         compiledFromAddressExpression,
                                         compiledFromNameExpression,
                                         beginSend);
                }
                buffered = 0;
                Interlocked.Add(ref currentlyProcessing, -1);
                await MailDistributor.EndSendMail(beginSend);
            }

            var resultData = new ComposeMailResult();
            resultData.SendSuccessfully = sendData;
            resultData.SendFailed       = sendFailed.ToDictionary(e => e.Key, e => e.Value);

            progress.Report(new SendMailProgress("", sendData, maxSendData, true, sendSuccess, sendFailed.Count, buffered, currentlyProcessing));
            return(resultData);
        }