加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

实体框架 – 如何改进这个F#代码以使其更加惯用?

发布时间:2020-12-14 03:52:58 所属栏目:Windows 来源:网络整理
导读:我有一个 Windows服务,使用Entity Framework 5从数据库中的队列中读取电子邮件,然后使用System.Net.Mail.SmtpClient将它们发送到SMTP服务器 这是我第一次尝试编写F#应用程序,我来自C#背景.如何更好地改进功能和/或充分利用F#的功能?你能解释一下这些改变的
我有一个 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"

解决方法

Work方法创建一个最终需要停止的长时间运行的进程.在F#中表示这一点的更好方法是使用异步工作流 – 可以暂停异步工作流而不阻塞线程(因此您不需要Thread.Sleep)并且可以使用CancellationToken轻松取消它.

否则,您的代码对我来说很好.我做了一些小的改动(比如使用F#查询语法,这是一个不错的小功能).

我也不太明白为什么你的代码有两个嵌套的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()

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读