C# 在ASP.NET MVC中发送多种电子邮件类型的最佳方法
你好,这里是苏的好朋友们 这更多的是一个设计问题,所以我将进入一个详细的示例 让我解释一下我们发送电子邮件的方式。 在应用程序的各个部分中,我们在C# 在ASP.NET MVC中发送多种电子邮件类型的最佳方法,c#,asp.net,asp.net-mvc,email,architecture,C#,Asp.net,Asp.net Mvc,Email,Architecture,你好,这里是苏的好朋友们 这更多的是一个设计问题,所以我将进入一个详细的示例 让我解释一下我们发送电子邮件的方式。 在应用程序的各个部分中,我们在通知表中为可能需要发送的不同类型的电子邮件创建条目。 例如,NotificationQueue表如下所示: NotificationQueueID OrderID EmailType Notes SentDatetime 1 461196 OrderU
通知
表中为可能需要发送的不同类型的电子邮件创建条目。
例如,NotificationQueue
表如下所示:
NotificationQueueID OrderID EmailType Notes SentDatetime
1 461196 OrderUpdate SomeNote1 2020-09-01 14:45:13.153
2 461194 OrderCancellation SomeNote2 2020-09-01 14:45:13.153
使用DbContext中的属性访问它,如下所示:
public DbSet<NotificationQueue> NotificationQueues { get; set; }
我们有一个EmailModel
类,它有一个TicketsInNotificationQueue
属性,该属性包含我们拥有的任何电子邮件类型的列表。例如:在任何给定的时间,它都可以有UpdatedTickets
或canceledtickets
的列表。电子邮件类型说明TicketsInNotificationQueue
属性中的票证类型
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, TicketsInNotificationQueue ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public TicketsInNotificationQueue TicketsInNotificationQueue { get; set; }
}
public class TicketsInNotificationQueue
{
public List<OrderCancellation> CancelledTickets { get; set; }
public List<OrderUpdate> UpdatedTickets { get; set; }
}
public class OrderCancellation : CommonOrderInformation
{
public string SomeOrderId { get; set; }
}
public class OrderUpdate: CommonOrderInformation
{
public string SomeUpdateRelatedProperty { get; set; }
}
public class CommonOrderInformation
{
public int NotificationQueueId { get; set; }
public string ReferenceNumber { get; set; }
}
现在,我只需要获取这个列表,过滤掉我刚刚收到的票据类型的通知ID
,然后继续处理它们。(我需要那些notificationid
来设置通知发送后的SentDatetime
)
var ticketsReceived=false;
notificationIds=新列表();
if(ticketsNotificationQueue.CancelledTickets!=null&&ticketsNotificationQueue.CancelledTickets.Any())
{
ticketsReceived=真;
NotificationId=ticketsNotificationQueue.Canceledtickets.Select(x=>x.NotificationQueueId.ToList();
}
else if(ticketsInNotificationQueue.UpdatedTickets!=null&&ticketsInNotificationQueue.UpdatedTickets.Any())
{
ticketsReceived=真;
NotificationId=ticketsNotificationQueue.UpdatedTickets.Select(x=>x.NotificationQueueId.ToList();
}
如果(收到票)
{
//继续发送电子邮件的过程,并设置“SentDateTime”`
}
我在这里看到的问题是,随着电子邮件的类型越来越大,比如说10-20
,检索票据并在以后过滤它们的方法需要变得越来越大,以至于在可读性和代码可管理性方面会失控,而我一点都不喜欢这些。我需要检查在获取中请求了什么emailType
,以及收到了什么emailType
(以获取SentDateTime
更新的相应notificationid
)。
那么,有没有其他方法来设计这个工作流(我甚至愿意使用反射之类的方法)以使它更易于管理和简洁呢
任何帮助都将不胜感激 您可以对现有系统和现有代码进行重大改进。为了得到一个更完整的答案,我将推荐一个不太昂贵的系统大修,然后继续你的确切答案
不同的行业标准方法
您已经有了正确的数据结构,这对于分布式持久队列来说是一个完美的工作,您不需要担心查询数据库的问题;相反,您只需将消息排队,并由一个处理器处理它们。既然您使用的是C#和.net,我强烈建议您检查一下。这实际上是一个大队列,您可以在其中发送消息(在您的情况下发送电子邮件请求),并且您可以根据消息类型将消息排队到服务总线中的不同通道
您还可以考虑创建一个现成的队列处理器。一旦你的电子邮件被发送,然后你可以写信给你的数据库,我们已经发送了这封电子邮件
所以,好的设计看起来像
- 拥有分布式持久队列,直接向其发送电子邮件请求
- 如果您想以一种节奏处理它们,请使用cron运行处理器,大多数行业解决方案都支持cron
- 如果您想在它们进入队列时处理它们,请使用触发器
您可以根据您的场景丰富处理器,它看起来与订单有关,因此您可能需要处理取消订单后不发送已排队的电子邮件等情况
改进现有的
由于某些情况,您可能无法使用上面的解决方案,因此让我们开始吧
查看如何重构switch语句(因为您有一个带有if
/else if
s的语句)
您可以通过多态性实现这一点,只需创建一个基本邮件类型并重写子类中的行为。这样,您就可以将正确的队列与正确的电子邮件类型关联起来
例如:
var results=wait getsomeemail(OrderMail);
//返回从基本处理器继承的单独处理器,以不同方式实现。
var processor=ProcessorFactory.Create(OrderMail);
等待处理器。发送(结果);
还有一些改进
在这个循环中,您不必要地反复检查电子邮件类型,您应该将这些语句移到for上方,并检查传入的参数,因为您已经知道要查询的类型。谢谢您的回答@Mavi-Domates
但这就是我最后所做的:
我修改了EmailModel
的TicketsInNotificationQueue
属性,这样就不用为不同类型的电子邮件提供不同类型的类,而只需要一种类型的公共类。这将避免我们按照原始问题中的说明,将这些检查放在获取逻辑中,以检查请求了哪种类型的电子邮件,同时也可以检索下一行的通知ID
(在发送电子邮件后更新SentDateTime
)
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, IEnumerable<CommonEmailModel> ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public IEnumerable<CommonEmailModel> TicketsInNotificationQueue { get; set; }
}
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
我只是通过以下方式获取记录:
var emailRecords = await CommonEmailModel.GetEmailBodyRecordsAsync(emailType);
只需将其作为ticketsInNotificationQueue
参数传递给EmailModel
构造函数即可。不需要做所有额外的检查来确定某些电子邮件类型的记录是否正确<
var ticketsReceived = false;
notificationIds = new List<int>();
if (ticketsInNotificationQueue.CancelledTickets != null && ticketsInNotificationQueue.CancelledTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.CancelledTickets.Select(x => x.NotificationQueueId).ToList();
}
else if (ticketsInNotificationQueue.UpdatedTickets != null && ticketsInNotificationQueue.UpdatedTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.UpdatedTickets.Select(x => x.NotificationQueueId).ToList();
}
if (ticketsReceived)
{
// Proceed with the process of sending the email, and setting the `SentDateTime`
}
foreach (var ntf in notifications)
{
if (ntf.EmailType == TypeOfEmail.OrderCancellation.ToString())
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, IEnumerable<CommonEmailModel> ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public IEnumerable<CommonEmailModel> TicketsInNotificationQueue { get; set; }
}
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
public class CommonEmailModel
{
// Common to all email types. A lot of email types only need these first 4 properties
public string EmailType { get; set; }
public int NotificationQueueId { get; set; }
public string OrderId { get; set; }
public string Notes { get; set; }
// Cancellation related
public string SomeOrderId { get; set; }
// Update related
public string SomeUpdateRelatedProperty { get; set; }
public static async Task<IEnumerable<CommonEmailModel>> GetEmailBodyRecordsAsync(TypeOfEmail emailType)
{
var emailModels = new List<CommonEmailModel>();
var emailEntries = await EmailNotificationQueue.GetEmailEntriesAsync(emailType);
var relevantOrdIds = emailEntries.Select(x => x.OrderID).Distinct().ToList();
using (var dbCon = GetSomeDbContext())
{
orders = dbCon.Orders.Where(x => relevantOrdIds.Contains(x.OrdNumber)).ToList();
}
foreach (var record in emailEntries)
{
var emailModel = new CommonEmailModel
{
EmailType = emailType,
NotificationQueueId = record.NotificationQueueID,
OrderId = record.OrderID,
Notes = record.Notes,
SomeOrderId = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.SomeOrderIdINeed,
SomeUpdateRelatedProperty = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.UpdateRelatedPropertyINeed
};
emailModels.Add(emailModel);
}
return emailModels;
}
}
var emailRecords = await CommonEmailModel.GetEmailBodyRecordsAsync(emailType);
if (emailRecords.Any())
{
var emailModel = new EmailModel(emailType, emailRecords);
}
if (emailWasSent)
{
await UpdateNotificationSentTimeAsync(emailRecords.Select(t => t.NotificationQueueId));
}