Search code examples
c#asp.net-core.net-5action-filter

How to pass parameters to a generic Action filter


I'm working on an Asp.net core 5 project targeted .Net 5, I try to create an Action filter that will receive a property name, and he will recieve a TEntity generic type (represent the table to select from) and catch the submitted model and get the property value from it (model), this Action Filter will look in database if a record already has the same value in the past property inside the table passed in TEntity.

My Action filter:

public class RecordShouldNotExistFilter<TEntity>:IActionFilter where  TEntity : class
{

    private readonly AppDbContext _dbContext;
    public           string     PropertyToBeChecked { get; set; }

    public RecordShouldNotExistFilter( AppDbContext dbContext )
    {
        _dbContext = dbContext;
    }

    public void OnActionExecuting( ActionExecutingContext context )
    {
      // Some logic here that uses the PropertyToBeChecked's value
    }


    }

    public void OnActionExecuted( ActionExecutedContext   context )
    {
        
    }

}

The problem: When I try to apply the filter on my action I don't know how to pass the PropertyToBeChecked value.

I paused here :

[TypeFilter(typeof(RecordShouldNotExistFilter<PedagogicalSequence>))]
public async Task<IActionResult> Create( PedagogicalSequenceModel model )
{
}

The question : How can I pass the PropertyToBeChecked value ? or how to achieve my object with another way ? except using Action parameters


Solution

  • You can get the property to be checked in the constructor of your filter, like so:

    public RecordShouldNotExistFilter(AppDbContext dbContext, string propertyToBeChecked)
    {
        _dbContext = dbContext;
        PropertyToBeChecked = propertyToBeChecked;
    }
    

    And then pass that value into the filter attribute:

    [TypeFilter(typeof(RecordShouldNotExistFilter<PedagogicalSequence>), Arguments = new object[] { "PropertyName" })]
    public async Task<IActionResult> Create(PedagogicalSequenceModel model)
    {
    }
    

    Generic attributes aren't supported so your other option is to make a non-generic attribute following this answer and get the entity type through a Type parameter. You would then use reflection to get the generic implementation:

    public class RecordShouldNotExistFilterAttribute : TypeFilterAttribute
    {
        public RecordShouldNotExistFilterAttribute(Type entityType, string propertyToBeChecked) 
            : base(typeof(RecordShouldNotExistFilter<>).MakeGenericType(entityType))
        {
            Arguments = new object[] { propertyToBeChecked };
        }
    }
    
    public class RecordShouldNotExistFilter<TEntity> : IActionFilter where TEntity : class
    {
        readonly AppDbContext _dbContext;
        public string PropertyToBeChecked { get; set; }
    
        public RecordShouldNotExistFilter(AppDbContext dbContext, string propertyToBeChecked)
        {
            _dbContext = dbContext;
            PropertyToBeChecked = propertyToBeChecked;
        }
    }
    

    This would allow you to do this instead:

    [RecordShouldNotExistFilter(typeof(PedagogicalSequenceModel), "PropertyName")]
    public async Task<IActionResult> Create(PedagogicalSequenceModel model)