是否可以将 RequiredAttribute 绑定到函数?

Is it possible to bind A RequiredAttribute to a function?

public partial class TaskScheduling
{
    public Guid EvaluationTemplateId { get; set; }

    //[Required(ErrorMessage = "TaskScheduling.OwnerUser")]
    [Required(ErrorMessage = ResolveErrorMessage())]
    public string SenderName { get; set; }

    [Required]
    public string SenderMail { get; set; }

    [Required]
    public string Subject { get; set; }

    [Required]
    public string BodyMessage { get; set; }

    private string ResolveErrorMessage()
    {
        //Code to customize the error message here.
        return "test";
    }
}

我正在尝试将错误消息绑定到一个函数,这样我就可以自定义要显示的消息类型。 但是当我尝试将错误消息设置为函数 ResolveErrorMessage 时,编译器说:需要对象引用才能访问非静态字段、方法或 属性 'TaskScheduling.ResolveErrorMessage()'.

因此我尝试将 ResolveErrorMessage 设置为静态函数。

    private static string ResolveErrorMessage()
    {
        //Code to customize the error message here.
        return "test";
    }

现在编译器抱怨如下: 属性参数必须是常量表达式 typeof 表达式或数组创建表达式。

那么,我尝试做的事情是否可行?

如评论中所述,这是不可能的。该值需要在编译时知道。此外,运行时如何知道何时调用此方法,以及如何在没有任何可用数据的情况下自定义它,例如无效值。

还有其他解决方法,但这取决于您要自定义错误消息的程度。

1。简单案例 - 内置消息格式

在最简单的情况下,您可以简单地在消息文本中添加一个 {0} 占位符,用于放置 属性 名称的位置:

[Required(ErrorMessage = "Please enter {0}!")]
public string SenderName { get; set; }

或者,如果您想稍后更新错误消息而不重新编译代码,您可以引用资源字符串而不是硬编码的消息文本,使用 ErrorMessageResourceTypeErrorMessageResourceName 属性:

[Required(ErrorMessageResourceType = typeof(MyMessageResources), ErrorMessageResourceName = "SenderNameRequiredMessage")]
public string SenderName { get; set; }

2。自定义逻辑 - 不调用经过验证的实例

如果您 需要逻辑来自定义错误消息,您最好创建 RequiredAttribute 的子 class 并覆盖属性或方法根据需要在那里添加逻辑。

例如,如果出于某些不明确的原因您想将星期几添加到错误消息中,您可以覆盖 FormatErrorMessage() 来执行此操作:

public class DayOfWeekRequiredAttribute : RequiredAttribute
{
    public override string FormatErrorMessage(string propertyName)
    {
        string day = DateTime.Today.DayOfWeek.ToString();
        return string.Format(this.ErrorMessage, propertyName, day);
    }
}

public partial class TaskScheduling
{
    // {0} is property name, {1} is day of week
    [DayOfWeekRequired(ErrorMessage = "Hey, it's {1} and {0} is a required field!")]
    public string SenderName { get; set; }
}

你可以让它更通用一点,并传递一个负责解析错误消息的类型,这样你就不需要很多 XXXXRequireAttribute classes:

// interface representing a class for resolving the error message
public interface IErrorMessageResolver
{
    // The attribute will call this method
    string ResolveErrorMessage(string propertyName, object value);
}

public class MessageResolverRequiredAttribute : RequiredAttribute
{
    // Stores the type responsible for resolving the error message
    public Type MessageResolverType { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // call the built-in RequiredAttribute validator
        ValidationResult result = base.IsValid(value, validationContext);

        // if not valid, attempt to get the custom error message
        if (!result.Equals(ValidationResult.Success))
        {
            if (this.MessageResolverType != null)
            {
                // create the resolver and check it actually is a resolver
                IErrorMessageResolver handler = Activator.CreateInstance(this.MessageResolverType) as IErrorMessageResolver;
                if (handler == null)
                {
                    throw new Exception("MessageResolverType is not an IErrorMessageResolver");
                }

                // get the custom error message, if returns null, don't override the built-in error message
                string customError = handler.ResolveErrorMessage(validationContext.MemberName, value);
                if (customError != null)
                {
                    result.ErrorMessage = customError;
                }
            }
        }

        return result;
    }
}

public partial class TaskScheduling
{
    [MessageResolverRequired(MessageResolverType = typeof(MyMessageResolver))]
    public string SenderName { get; set; }
}

3。自定义逻辑 - 使用经过验证的实例

要向正在验证自身的实例询问错误消息,您可以使用与上述通用自定义逻辑相同的主体,但它将是需要实现的实例 IErrorMessageResolver。您可以使用 validationContext.ObjectInstance 属性 来获取它。我会说这真的只有在您需要在实例中使用 private 数据来创建错误消息时才有用。否则它有点奇怪,非 SOLID 模式。

// interface representing a class for resolving the error message
public interface IErrorMessageResolver
{
    // The attribute will call this method
    string ResolveErrorMessage(string propertyName, object value);
}

public class MessageResolverRequiredAttribute : RequiredAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        ValidationResult result = base.IsValid(value, validationContext);

        if (!result.Equals(ValidationResult.Success))
        {
            IErrorMessageResolver handler = validationContext.ObjectInstance as IErrorMessageResolver;
            if (handler == null)
            {
                throw new Exception("Not validating an IErrorMessageResolver");
            }

            string customError = handler.ResolveErrorMessage(validationContext.MemberName, value);
            if (customError != null)
            {
                result.ErrorMessage = customError;
            }
        }

        return result;
    }
}

public partial class TaskScheduling
    : IErrorMessageResolver
{
    [MessageResolverRequired(MessageResolverType = typeof(MyMessageResolver))]
    public string SenderName { get; set; }

    public string ResolveErrorMessage(string propertyName, object value)
    {
        if (propertyName == "SenderName")
        {
            return "You need to set a Sender!";
        }

        return null;
    }
}