4

I am exactly at the situation where this person (Controller ModelState with ModelStateWrappper) is except that I am using Castle Windsor DI. I am trying to validate my model's business logic side in my service before I save the model pass it to the data store. if the validation fails I want to get the model state errors and display in my view. (I am following this article as a guide to implement validation from service: http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs)

Below is my sample code,

//My controller code
        public class TimeEntryController : TempoBaseController
        {
            public TimeEntryController(TimeService service, UserService userService)
            {
                _service = service;
                _userService = userService;
            }
            [Authorize(Roles = "Worker")]
            public ActionResult Index() 
            {
            return View();
            }
            [AcceptVerbs(HttpVerbs.Post)]
            public ActionResult Index(EntryLogDto entry)
            {
              if(_service.AddEntryLog(entry))
              {
                return ViewSuccess();
              }else
              {
                return ViewFailue();
              }
            }

        }

//My Service class
    public class TimeService : GenericService
    {
        public TimeService(IRepository repository, IValidationDictionary validationDictionary, UserManager<ApplicationUser>     userManager)
            : base(repository, validationDictionary, userManager)
        {
        }

        public bool AddEntryLog(EntryLogDto log)
        {
            if (!ValidateEntryLog(log))
              {
                //return false;

            }
            else
            {
                //insert entry into database and return true
            }
            }
        protected bool ValidateEntryLog(EntryLogDto log)
        {
            //Check if the entry overlaps with any other entries
            bool res = _repository.Find<EntryLogDto>(//my linq logic);
            if (res)
            {
              _validationDictionary.IsValid = true;
            }else
            {
            _validatonDictionary.AddError("Entry", "Entry Overlaps.");
              _validationDictionary.IsValid = false;
            }
            return _validationDictionary.IsValid;
        }
    }

//Validation class

    public class TempoValidation : IValidationDictionary
    {
        private ModelStateDictionary _modelState;
        public TempoValidation(ModelStateDictionary modelState) // Issue is how am I gona give it this as the ModelStateDictiona         ry is controller specific
        {
            _modelState = modelState;
        }

        public void AddError(string key, string error)
        {
            _modelState.AddModelError(key, error);

        }

        public bool IsValid
        {
            get { return _modelState.IsValid; }
        }
    }

//Global.asax castle compnonent registration method
                container
                .Register(Component
                            .For<Tempo.Model.Configuration.TempoDbContext>()
                            .LifestylePerWebRequest()
                            .DependsOn(new { connectionString }))
                .Register(Component
                            .For<DbRepositories.ITempoDataContextFactory>()
                            .AsFactory())
                .Register(Component
                            .For<IRepository>()
                            .ImplementedBy<Tempo.Repositories.EntityFrameworkRepository.Repository>())
                .Register(Component
                            .For<TimeService>().LifestyleTransient())

I am injecting IValidationDictionary in my service class where I set the model state depending on the validation result. Is there a way I can pass in the model state of the controller when I use it? I don't know how to approach this, I have many controllers and I don't know how I will/when will I pass the respective controller's model state (I would like to do that by DI if its possible )... I don't know if castle can create a separate instance of TempoValidation class for each controller??

Community
  • 1
  • 1
Ramesh Sivaraman
  • 1,295
  • 3
  • 19
  • 32
  • https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/models-data/validating-with-a-service-layer-cs – juFo Mar 11 '19 at 13:37

1 Answers1

1

I know that this is impossible to do this by default, but you can use Fluent Validation to achieve this.

Example:

ViewModel

 [Validator(typeof(VmSysTestValidator))]
    public class VmSysTestModel
    {
        public int Id { get; set; }
        [Required]
        public string FirstName { get; set; }
        [Required]
        public string LastName { get; set; }
    }

Fluent validation implementation :

 public class VmSysTestValidator : AbstractValidator<VmSysTestModel>
    {
        public VmSysTestValidator()
        {
            RuleFor(x => x.FirstName).NotNull().WithMessage("First name is required");
            RuleFor(x => x.LastName).NotNull().WithMessage("Last Name is required");
        }
    }

Controller or business logic side :

[HttpPost]
        public ActionResult TestPost(VmSysTestModel obj)
        {

            //Start Validation     From start to end you can call this code everywhere you want  , this will work like server side valdiatin
            //Instead of ModelState.IsValid you will call your fluent validator
            var testValidator = new VmSysTestValidator();
            var validationResult = testValidator.Validate(obj);
            if (validationResult.IsValid)
            {

            }
            else
            {

            }
            //End valdiation 
        }
Nic
  • 1,088
  • 3
  • 19
  • 43
  • You could perhaps include some example of Fluent Validation to add some meat to your answer, a link-only answer may lose relevance if links rot – samy Jul 07 '14 at 08:27
  • Thanks for the comment, I have checked the link I don't know if the scenario is clear. I am intending to use the service level validation for my domain objects based on my business rules like a time entry should not overlap another one in the data base etc.. Could you explain how Fluent Validation can help me with this scenario? I am still using my default MVC validations for basic input validations. – Ramesh Sivaraman Jul 07 '14 at 10:24
  • today I will post full example. – Nic Jul 07 '14 at 11:36
  • please take a look to example...fill free to ask if my post is not clear – Nic Jul 07 '14 at 11:53
  • Thanks for the example friend.This looks good to me but I am also stumbled upon this http://stackoverflow.com/questions/3873530/inject-asp-net-mvc-controller-property-into-service-layer-dependency?rq=1 ,need to figure out which approach best suits for my needs.. Thanks for your answer.. – Ramesh Sivaraman Jul 08 '14 at 21:43
  • I will take a look to this post today . But can you please explain why you need ModelState in service? – Nic Jul 09 '14 at 08:18
  • yes, I wanted to separate the error handling + validation out so I was creating a class which handles this and at the same time I need my controllers been clean and neat. Now with the other link I posted they have more cleaner approach (even your suggestion was good but I am bit cautious when using 3rd party dll's and would more like to create own solution if that is possible, in this scenario I worked out something which is very clean and scalable as per our requirements.. ) – Ramesh Sivaraman Jul 10 '14 at 11:17