C# 这是否正确执行单一责任原则

C# 这是否正确执行单一责任原则,c#,asp.net,asp.net-mvc,entity-framework,C#,Asp.net,Asp.net Mvc,Entity Framework,我想知道这是否是单一责任原则的最佳方法。 此类的职责是提醒由于不活动而导致帐户即将过期的用户 在不活动的30天内,用户会收到一封电子邮件。 在不活动的45天内,用户没有收到电子邮件,他们被禁用 public class AccountReminder { private readonly IUnitOfWork _uow; private readonly ISendExpiringEmail _sendExpiringEmail; public AccountRem

我想知道这是否是单一责任原则的最佳方法。 此类的职责是提醒由于不活动而导致帐户即将过期的用户

在不活动的30天内,用户会收到一封电子邮件。 在不活动的45天内,用户没有收到电子邮件,他们被禁用

public class AccountReminder
{

    private readonly IUnitOfWork _uow;
    private readonly ISendExpiringEmail _sendExpiringEmail;

    public AccountReminder(IUnitOfWork uow, ISendExpiringEmail sendExpiringEmail)
    {
        _uow = uow;
        _sendExpiringEmail = sendExpiringEmail;
    }

    public void NotifyUpcomingExpiringAccounts()
    {

        // Establish dates
        DateTime accountWarningDateTime = DateTime.Now.AddDays(-30).Date;
        DateTime expirationDateTime = DateTime.Now.AddDays(-45).Date;

        // Retrieve users whose accounts are either about to expire or have already expired
        var users = _uow.UserRepository
            .FindBy(element => (DbFunctions.TruncateTime(element.LastLoginDate) == accountWarningDateTime));

        // Send a reminder
        foreach (var user in users)
        {
            string htmlBody = "<html xmlns:v=\"urn:...xmlns=\"http://www.w3.org/TR/REC-html40\" >";
            htmlBody += "<head><meta http-equiv=...</head>";
            htmlBody += "<body>";

            //following line for only outlook to display image
            htmlBody += "<v:shape id=\>...</v:shape>";
            htmlBody += "<table width='100%'...</table>";
            htmlBody += "</body></html>";

            AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
            AlternateView plainView = AlternateView.CreateAlternateViewFromString(htmlBody, null, "text/plain");
            LinkedResource pic1 = new LinkedResource("images/Logo.jpg", MediaTypeNames.Image.Jpeg);
            pic1.ContentId = "Logo";
            htmlView.LinkedResources.Add(pic1);

            _sendExpiringEmail.SendTo(new[] { user.Email }, "EmailSubject", htmlView, plainView);

        }

        DisableExpiringAccounts(expirationDateTime);
    }

    private void DisableExpiringAccounts(DateTime expirationDateTime)
    {
        var expiredUsers = _uow.UserRepository.FindBy(element => element.Enabled && (DbFunctions.TruncateTime(element.LastLoginDate) <= expirationDateTime));

        foreach (var user in expiredUsers)
        {
            user.Enabled = false;
        }

        if (expiredUsers.Count > 0)
        {
            _uow.SaveChanges();
        }
    }

}
公共类帐户提醒
{
私人只读IUnitOfWork;
私人只读邮件发送过期电子邮件;
公共帐户提醒(IUnitOfWork uow、iSendeExpiregeMail发送过期电子邮件)
{
_uow=uow;
_sendExpiringEmail=sendExpiringEmail;
}
public void NotifyUpcomingExpiringAccounts()
{
//确定日期
DateTime accountWarningDateTime=DateTime.Now.AddDays(-30).Date;
DateTime expirationDateTime=DateTime.Now.AddDays(-45).Date;
//检索帐户即将过期或已过期的用户
var users=\u uow.UserRepository
.FindBy(element=>(DbFunctions.TruncateTime(element.lastloginandate)==accountWarningDateTime));
//发送提醒
foreach(用户中的var用户)
{
字符串htmlBody=“element.Enabled&&(DbFunctions.TruncateTime(element.LastLoginDate)0)
{
_uow.SaveChanges();
}
}
}
我认为你对责任的理解太高了。这是一个常见的问题。罗伯特·马丁建议对其进行解释:

单一责任原则(SRP)规定,每个软件模块都应该有一个且只有一个更改原因

以及(同一链接):

在编写软件模块时,您希望确保在请求更改时,这些更改只能来自单个人员,或者更确切地说,来自表示单个狭义定义的业务功能的单个紧密耦合的人员组

在您的示例中,业务需求的更改可能涉及:

  • 帐户过期机制背后的逻辑

  • 用于通知用户的通信通道

  • 消息的格式

因此,我可以在这里看到至少3个职责,至少3个层次结构(名称仅为示例):

  • IAccountExpirationAgent
    -验证帐户是否已过期并禁用它的部分,如果是,可能只有一个实现

  • IAccountExpirationNotifier
    -发送电子邮件(例如
    AccountExpirationEmailNotifier
    )或SMS消息等的类

  • IAccountExpirationMessageFormatter
    -格式化消息的类(例如,
    AccountExpirationMessageHtmlFormatter
    AccountExpirationMessagePlainTextFormatter


确实,这个问题应该发布在上,但我仍然想提供一些建议

这可能是错误的;但我对单一责任原则的理解是“一种方法应该有一份工作”。这使得我们无法编写一个600行的方法来完成所有工作,而且维护起来非常麻烦

更小、更专用(单一责任)的方法更容易维护,因为它们使更大的应用程序更抽象、更容易理解

如果我重写你的课程,我可能会这样做:

  • 拆分电子邮件发送功能

  • 拆分HTML正文生成;可能会将用户模型发送到Razor视图


公共类帐户提醒
{
私人只读IUnitOfWork;
私人只读邮件发送过期电子邮件;
公共帐户提醒(IUnitOfWork uow、iSendeExpiregeMail发送过期电子邮件)
{
_uow=uow;
_sendExpiringEmail=sendExpiringEmail;
}
public void NotifyUpcomingExpiringAccounts()
{
//确定日期
DateTime accountWarningDateTime=DateTime.Now.AddDays(-30).Date;
DateTime expirationDateTime=DateTime.Now.AddDays(-45).Date;
//检索帐户即将过期或已过期的用户
var users=\u uow.UserRepository
.FindBy(element=>(DbFunctions.TruncateTime(element.lastloginandate)==accountWarningDateTime));
foreach(用户中的var用户)
{
发送提醒(用户);
}
禁用ExpiringAccounts(expirationDateTime);
}
私有无效发送提醒(用户)
{
//将模型传递给视图以生成HTML
//razor实现还应该处理纯文本邮件和html邮件;您可以有两个单独的模板
字符串htmlBody=MyRazorImplementation.GenerateBody(用户);
_sendExpiringEmail.SendTo(新[]{user.Email},“EmailSubject”,htmlView,plainView);
}
私有void DisableExpiringAccounts(DateTime expirationDateTime)
{
var expiredUsers=\u uow.UserRepository.FindBy(element=>element.Enabled&&(DbFunctions.TruncateTime(element.lastloginandate)0)
{
_uow.SaveChanges();
}
}
}

您的问题是代码审查请求,因此更适合您。如果您有关于如何解决特定问题的具体问题,我们很乐意提供帮助。
public class AccountReminder
{

    private readonly IUnitOfWork _uow;
    private readonly ISendExpiringEmail _sendExpiringEmail;

    public AccountReminder(IUnitOfWork uow, ISendExpiringEmail sendExpiringEmail)
    {
        _uow = uow;
        _sendExpiringEmail = sendExpiringEmail;
    }

    public void NotifyUpcomingExpiringAccounts()
    {
        // Establish dates
        DateTime accountWarningDateTime = DateTime.Now.AddDays(-30).Date;
        DateTime expirationDateTime = DateTime.Now.AddDays(-45).Date;

        // Retrieve users whose accounts are either about to expire or have already expired
        var users = _uow.UserRepository
            .FindBy(element => (DbFunctions.TruncateTime(element.LastLoginDate) == accountWarningDateTime));

        foreach (var user in users)
        {
            SendReminder(user);
        }

        DisableExpiringAccounts(expirationDateTime);
    }

    private void SendReminder(User user)
    {
        // pass a model to a view to generate the HTML
        // the razor implementation should also handle plain-text vs. html mail; you can have two separate templates
        string htmlBody = MyRazorImplementation.GenerateBody(user); 

        _sendExpiringEmail.SendTo(new[] { user.Email }, "EmailSubject", htmlView, plainView);
    }

    private void DisableExpiringAccounts(DateTime expirationDateTime)
    {
        var expiredUsers = _uow.UserRepository.FindBy(element => element.Enabled && (DbFunctions.TruncateTime(element.LastLoginDate) <= expirationDateTime));

        foreach (var user in expiredUsers)
        {
            user.Enabled = false;
        }

        if (expiredUsers.Count > 0)
        {
            _uow.SaveChanges();
        }
    }

}