我有一个Windows服务,它使用Entity Framework 5从数据库中的队列中读取电子邮件,然后使用System.Net.Mail.SmtpClient将它们发送到SMTP服务器
这是我第一次尝试编写F#应用程序,我来自C#背景。如何更好地改进功能和/或充分利用F#的功能?你能解释一下这些改变的优势是什么吗?
worker由服务主机构建,在服务启动时调用它的工作函数,在服务停止时将ContinueWorking设置为false。
namespace EmailService
open log4net
open System
open System.Linq
open System.Net.Mail
open EmailService.Context
type Worker(contextFactory: EmailContextFactory, mailClient: ISmtpClient, logger: ILog) =
let MapToMessage(email : Email) =
let message = new MailMessage()
message.Sender <- new MailAddress(email.From)
message.From <- new MailAddress(email.From)
message.Subject <- email.Subject
message.Body <- email.Body
message.IsBodyHtml <- email.IsBodyHtml
message.To.Add(email.To)
(email, message)
member val ContinueWorking = true with get, set
member this.Work() =
logger.Info "Starting work"
let mutable unsentEmails = Array.empty<Email>
while this.ContinueWorking do
use context = contextFactory.GetEntities()
while this.ContinueWorking && Array.isEmpty unsentEmails do
System.Threading.Thread.Sleep(1000)
unsentEmails <- query { for q in context.QueueItems do
where (q.Error = null)
select q.Email }
|> query.Take(10)
|> query.toArray
Array.map MapToMessage unsentEmails
|> Array.iter (fun (email, message) ->
try
mailClient.SendMail(message)
email.DateSent <- new Nullable<DateTime>(DateTime.UtcNow)
context.QueueItems.Remove(email.QueueItem) |> ignore
with
| ex ->
logger.Error(ex)
email.QueueItem.Error <- ex.ToString())
context.SaveChanges() |> ignore
logger.Info (sprintf "Sent %d emails" unsentEmails.Length)
logger.Info "Work complete"
答案 0 :(得分:5)
Work
方法创建一个最终需要停止的长时间运行的进程。在F#中表示这一点的更好方法是使用异步工作流 - 可以暂停异步工作流而不阻塞线程(因此您不需要Thread.Sleep
),并且可以使用CancellationToken
轻松取消它。 / p>
否则,您的代码对我来说很好。我做了一些小的更改(比如在F#查询语法中使用take
,这是一个不错的小功能)。
我也不太明白为什么你的代码有两个嵌套的while
循环。这有必要吗?如果不是,我想你可以这样写:
member this.Work() = async {
logger.Info "Starting work"
while true do
do! Async.Sleep(1000)
use context = contextFactory.GetEntities()
let unsentEmails =
query { for q in context.QueueItems do
where (q.Error = null)
select q.Email
take 10 }
unsentEmails
|> Array.map MapToMessage
|> Array.iter (fun (email, message) ->
try
mailClient.SendMail(message)
email.DateSent <- new Nullable<DateTime>(DateTime.UtcNow)
context.QueueItems.Remove(email.QueueItem) |> ignore
with ex ->
logger.Error(ex)
email.QueueItem.Error <- ex.ToString())
context.SaveChanges() |> ignore
logger.Info (sprintf "Sent %d emails" unsentEmails.Length)
logger.Info "Work complete" }
要开始这个过程(并在以后停止它),你会写一些类似的东西:
// Start the work
let cts = new CancellationTokenSource()
Async.Start(worker.Work(), cts.Token)
// Stop the worker
cts.Cancel()