With Data Annotations it's now easy to localize error messages using Resource.resx files like this for example:
public class Student
{
. . .
[Required(ErrorMessageResourceName ="Required",
ErrorMessageResourceType = typeof(StudentResources))]
[StringLength(16)]
[Display(Name = "FirstName", ResourceType = typeof(StudentResources))]
public string FirstName { get; set; }
. . .
}
Now, let's say I want to check if a Student has already made a Payment for a given month and year:
public bool CheckIfAlreadyPaid(Payment payment)
{
return repository.GetPayments().Any(p => p.StudentId == payment.StudentId &&
p.Month == payment.Month &&
p.Year == payment.Year);
}
If he has already made the Payment, I'm doing the following in my Services layer:
if (CheckIfAlreadyPaid(payment))
{
modelState.AddModelError("AlreadyPaid",
Resources.Views.Payment.PaymentCreateResources.AlreadyPaid);
}
It works, but I don't fell confident about referencing the Resource file inside the Services layer.
Is there a standard or better way of localizing error messages that are not tied to model properties (Data Annotation) - errors that come from business logic rules? Should I still add these errors to the ModelStateDictionary?
I agree, I don't think that should be in the service layer. It looks like it could either be put into a custom data validation attribute, or handled with some other approach at that level (using Fluent Validation might be a good option). Either way, I think as long as the validation stays in the MVC app itself, you can be comfortable using resource files to store the messages.
I did it in a different way. The Service
layer is used to check if the Payment was already made. In my Controller
I add a validation error message to the ModelState object passing to it a localized string resource. Now I feel more comfortable with this approach.
Here's the code:
/// <summary>
/// Performs validation of business logic...
/// </summary>
/// <param name="payment"></param>
/// <returns></returns>
private bool ValidatePayment(Payment payment)
{
if (paymentService.IsPaymentMade(payment))
{
ModelState.AddModelError("AlreadyPaid", Localization.AlreadyPaid);
}
return ModelState.IsValid;
}
EDIT:
To complement my answer, I just found today that ValidationSummary @Html.ValidationSummary(true)
does exactly what I want:
Html.ValidationSummary
returns an unordered list (ul element) of validation messages that are in theModelStateDictionary
object and optionally displays only model model-level errors.
I'm passing true
and it will only display model-level errors (non data annotation errors) in the summary just at the top of the page. This is great but only if it would work... :)
I was facing a problem where my custom error messages not tied to model properties weren't appearing when I set ValidationSummary(true)
. I then searched using Google and found this post. I tried his solution but it didn't work. I then searched a little bit more and found this link in Google Books (Pro ASP.NET MVC 2 Framework by Steven Sanderson).
I tried what is described there passing an empty string as key (string.Empty
) and it did the job.
if(paymentService.IsPaymentMade(payment))
{
ModelState.AddModelError(string.Empty, Localization.PaymentAlreadyCreated);
}
来源:https://stackoverflow.com/questions/5697487/whats-the-best-way-to-localize-non-data-annotation-errors-with-asp-net-mvc-3