Custom DataAnnotation and Dependency Injection

Sagar Tube

I have 2 model classes Batch and Coach as follows

Batch.cs

public class Batch
  {
    public int Id { get; set; }

    public string Name { get; set; }

     [NoBatchAfter9PM]
     public TimeSpan StartTime { get; set; }

     [NoBatchAfter9PM]
     public TimeSpan EndTime { get; set; }

     public string? Description { get; set; }

     [Display(Name="Is it a Weekend batch?")]
     public bool IsWeekendBatch { get; set; }

     [Display(Name = "Is it a Womens batch?")]
     public bool IsWomenBatch { get; set; }

     public ICollection<Learner>? Learners { get; set; }

     public Coach? Coach { get; set; }
}

Coach.cs

public class Coach
{
   public int Id { get; set; }

   public string Name { get; set; }

   [Display(Name="Gender")]
   public GenderId GenderId { get; set; }

   [Display(Name = "Batch")]
   public int BatchId { get; set; }

   [ForeignKey("BatchId")]
   public Batch Batch { get; set; }

   [ForeignKey("GenderId")]
   public Gender Gender { get; set; }
}

They both have one to many relationship.

In the CoachForm I have a dropdown for the batch field. When the form is submitted I have to check the gender of the coach and if gender is female and the batch is womens batch then only ModelValidation is to made successful for which I've created a custom DataAnnotation as in the below.

public class CheckForGenderAndBatch:ValidationAttribute
    {
        IBatchRepository batchRepository;
        static List<Batch> WomensBatch;

        public CheckForGenderAndBatch(IBatchRepository batchRepository)
        {
            this.batchRepository = batchRepository;
            WomensBatch = batchRepository.GetBatches().Where(x => x.IsWomenBatch).ToList();
        }
        protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
        {
            Coach coach = (Coach)validationContext.ObjectInstance;

            if (coach.GenderId == GenderId.Male && WomensBatch.Any(x => x.Id == coach.BatchId))
           {
            return new ValidationResult("Only Female Coach is allowed in womens batch");
           }
        return ValidationResult.Success;
         }
    }

For the IBatchRepository interface I've registered a concrete class as in the below in Program.cs

builder.Services.AddTransient<IBatchRepository, BatchProjectDbRepository>();

While I'm adding the attribute on top of the property BatchId in Coach.cs as in the below I'm getting the following error.

enter image description here

I know the there is no default constructor in the custom data annotation so I tried to pass the reference of the Repository class as in the below.

[CheckForGenderAndBatch(new BatchProjectDbRepository())]
[Display(Name = "Batch")]
public int BatchId { get; set; }

But the problem here is even the BatchProjectDbRepository class also does not have the default constructor.The consturctor of the BatchProjectDbRepository class is as in the below.

ProjectDbContext context;
 public BatchProjectDbRepository(ProjectDbContext context)
 {
    this.context = context;
 }  

So thought of passing the ProjectDbContext object to it as in the below way.

[CheckForGenderAndBatch(new BatchProjectDbRepository(new ProjectDbContext()))]
[Display(Name = "Batch")]
public int BatchId { get; set; }

But since ProjectDbContext class constructor also expects a parameter I'm getting the following error enter image description here

So my question is that is there anyways in which we can inject the object of concrete class into the class of the custom Data Annotation?

(Sorry for defining the problem in such a lengthy way)

IbraHim M. Nada

I Get what you are trying to do. you don't need to inject the service using the constructor.

you have to get the implementation at run time using the ValidationContext object and GetService method

-The Steps

1-make your validator class CheckForGenderAndBatch's contractor parameterless.

2-inside of IsValid use this line

var batchRepository = (IBatchRepository ) validationContext.GetService(typeof(IBatchRepository ));

now you can use your repo by using the batchRepository object.

one more thing always declarer your repository as scoped inside of the DI container.

Side Note The constructor in such situations is used to get more data related to the validation domain like say you are trying to validate a balance threshold then in the constructor you send the value of the threshold needed yo make the validator as usable as possible.

another example is the length validation you send in the constructor the value of the length that a string should surpass like 150 char for example.

Good luck

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

MVC 6 Custom Model Binder with Dependency Injection

Custom DataAnnotation IsValid Not Called

Custom NLog LayoutRenderer with constructor using Dependency Injection

Dependency Injection in EventFlow custom output

How to integrate dependency injection with custom decorators

Class not found for custom plugin block when trying to do dependency injection

Dependency Injection for custom validation attributes

ASP.Net MVC 4 Custom ValidationAttribute Dependency Injection

Angularjs custom filter and dependency injection

how to implement dependency injection container and dependency injection

Public properties or custom initializers for Dependency Injection?

Symfony2 Dependency Injection to custom/business classes

Creating a custom logger using JClouds with Guice dependency injection

Unity vs custom dependency injection?

Load custom configuration in a console command using dependency-injection

MVC Custom dataannotation

ScalaWS in play framework 2.5 and dependency injection of WSClient in custom class

How Can a Custom Controller Factory Relate to Dependency Injection?

Custom Route Constraint with Dependency Injection in .Net Core

Avoid Dependency cycles in Dependency Injection

Dependency Injection, constructor with custom and simple object

How to Implement ViewController custom init using dependency injection and factory patterns?

NestJS create a new instance with custom parameters staying in the dependency injection layer

Dependency injection in custom attribute

How validate request with custom FormRequest without dependency injection

Custom consumer implementation factory with Microsoft Dependency Injection

Custom constraint validator doesn't allow dependency injection

Dependency Injection

How to register custom Spring validator and have automatic dependency injection?