0

I have an ASP.NET Core 9.0 MVC project where I use DTOs for input binding and FluentValidation 12.x for validation.

Entities exist separately and contain data annotations attributes like [Required] and [MaxLength], but those are only for EF Core and are not intended for MVC validation.

DTOs do not have any data annotations at all (intentionally).

Validators are placed in a Validators folder, each inheriting from AbstractValidator<T>.

I register validators in Program.cs like this:

builder.Services.AddValidatorsFromAssemblyContaining<CreateCustomerDtoValidator>();

builder.Services.AddControllersWithViews()
    .AddViewLocalization()
    .AddDataAnnotationsLocalization();

One example validator looks like this:

public class CreateCustomerDtoValidator : AbstractValidator<CreateCustomerDto>
{
    public CreateCustomerDtoValidator(IStringLocalizer<ValidationResource> localizer)
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Test message")
            .MinimumLength(2).WithMessage("Test message 2");
    }
}

My CreateCustomerDto.cs file:

namespace ProjectCustora.Dtos.CustomerDto
{
    public class CreateCustomerDto
    {
        
        public string Name { get; set; }
        public string Surname { get; set; }
        public string Tc { get; set; }
        public string? Phone { get; set; }
        public string Username { get; set; }
        public string? Notes { get; set; }
        public string? VatId { get; set; }
        public DateTime LastContactDate { get; set; } = DateTime.Now;

        public CreateAddressDto Address { get; set; } = new CreateAddressDto();
    }
}

My CustomerController.cs file:

        #region Add

    [HttpGet]
    public IActionResult AddCustomer()
    {
        var model = new CreateCustomerDto();
        return View(model);
        
    }

    [HttpPost]
    public async Task<IActionResult> AddCustomer(CreateCustomerDto createCustomerDto)
    {
        
        if (ModelState.IsValid)
        {
           OperationResult<CreateCustomerDto> result = await _customerService.CreateCustomerAsync(createCustomerDto);            
            if(result.IsSuccess)
            {
                  TempData["SuccessMessage"] = result.Message;
                  return RedirectToAction("Index", "Customer");
            }
            else
            {
                  TempData["ErrorMessage"] = result.Message;
                  return View(createCustomerDto);
            }
        }
        
        return View(createCustomerDto);
    }

    #endregion

My CustomerService.cs file:

public async Task<OperationResult<CreateCustomerDto>> CreateCustomerAsync(CreateCustomerDto createCustomer)
 {
     try
     {
         
         var customer = _mapper.Map<Customer>(createCustomer);
         await _unitOfWork.Customers.CreateAsync(customer);
         await _unitOfWork.CompleteAsync();
         return new OperationResult<CreateCustomerDto>
         {
             Data = createCustomer,
             Message="Customer created successfully",
             IsSuccess= true
         };
     }
     catch(Exception ex)
     {
         _logger.LogError(ex, "An error occurred while creating a customer.");
         return new OperationResult<CreateCustomerDto>
         {
             Data = createCustomer,
             Message = "An error occurred while creating the customer",
             IsSuccess = false
         };
     }
 }

When I run the application and submit an empty form, I expect to see only the localized FluentValidation message "Name is required".

  • I confirmed that DTOs do not contain [Required], so no data annotations should be involved
  • Entities do contain data annotations, but I am binding only to DTOs in my controller, not entities
  • FluentValidation works correctly when debugging — ValidateAsync returns the expected messages
  • I'm using .NET 8.0 and Fluent Validation v12.0 (latest)

Expected behavior:

only the FluentValidation messages (e.g., "Test message") should appear in the view under asp-validation-for.

Actual behavior:

the view shows the default ASP.NET Core data annotations message:

The Name field is required

It seems ASP.NET Core is still triggering some data annotations validation, even though I only want FluentValidation to handle validation for DTOs.

5
  • 1
    To confirm that your view model (from controller to view) is using DTO but not entity right? And are you validating the model via FluentValidation manually? Also would be great to attach your controller action Commented Sep 27 at 23:55
  • 1
    Can you please add the CreateCustomerDto model? Commented Sep 28 at 5:32
  • 1
    Hello, I added CreateCustomerDto.cs controller and my service files. Commented Sep 28 at 17:55
  • @enesdurmuş "Yes, I tried. Unfortunately, it didn't work." - you get exactly the same error? Can you please post a full solution (ideally in a minimal reproducible example format) somewhere to play with? Commented Sep 29 at 13:02
  • I created a small sample project that reproduces the exact same error I’m facing. You can see it here: Github Link The FluentValidation package versions, .NET version, and all other settings are the same as in my main project, so the error can be reproduced exactly in this repo. Commented Sep 29 at 18:10

2 Answers 2

2

Without seeing your model and controller setup it's just a guess but let's still try.

If you have nullable reference types enabled in your ASP.NET Core project MVC will treat non-nullable reference types as implicitly required, so you try either making your Name property nullable. For example:

public class CreateCustomerDto
{
    public string? Name { get; set; }
    // ...
}

Or you can disable the check:

builder.Services.AddControllers(
    opts => opts.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Please see the Non-nullable reference types and [Required] attribute docs.

Sign up to request clarification or add additional context in comments.

3 Comments

I shared the CreateCustomerDto
Have you tried changes suggested in the answer?
Yes, I tried. Unfortunately, it didn't work.
0

I have cloned your repo and found the solution:

You are trying to replace/hook into built in validation mechanism in ASP.NET Core MVC. As can be read here FluentValidation: Automatic Validation:

We no longer recommend using this approach for new projects but it is still available for legacy implementations.

So you should try look into more modern ways, that do not depend on deprecated APIs.

But, there we can also find instructions for your exact case (it's detailed here: FluentValidation.AspNetCore).

What we need is to add package FluentValidation.AspNetCore (which is deprecated already) and adjust your Program.cs to:

builder.Services.AddValidatorsFromAssemblyContaining<CreateCustomerValidator>()
    .AddControllersWithViews()
    // This is what you need
    .AddFluentValidation(c =>
    {
        c.RegisterValidatorsFromAssemblyContaining<CreateCustomerValidator>();
        c.DisableDataAnnotationsValidation = true;
    })
    .AddViewLocalization();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.