Reputation: 3881
I created a simple ASP.NET MVC core filter following the documentation, but adding an argument on the filter:
public class MyFilterAttribute : TypeFilterAttribute
{
public ApiAuthorizeAttribute(string arg1 = null) : base(typeof(MyFilterImpl))
{
this.Arguments = new object[] { arg1 };
}
private class MyFilterImpl : IAsyncActionFilter
{
private readonly string _arg1;
public MyFilterImpl(string arg1 /*, DI dependencies */)
{
this._arg1 = arg1;
}
async Task IAsyncActionFilter.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do stuff
await next();
}
}
}
This works well when applying the attribute like this:
[MyFilter(arg1: "foo")]
public async Task<IActionResult> Get()
{
}
But it fails when passing null in the attribute declaration:
[MyFilter] // equivalent to [MyFilter(arg1: null)]
public async Task<IActionResult> Get()
{
}
The runtime throws an NullReferenceException
on this line. I can work around this by passing ""
instead of null in my case, but is this an expected -or unavoidable- behavior?
Upvotes: 1
Views: 800
Reputation: 8642
You have instantiated this.Arguments
in your constructor so I would say it is expected behaviour.
You have a couple of options to fix this.
You can either add an if
clause to not initialize this.Arguments if the parameter is null
public MyFilterAttribute(string arg1 = null) : base(typeof(MyFilterImpl))
{
if(!string.IsNullOrEmpty(arg1))
{
this.Arguments = new object[] { arg1 };
}
}
Or add a second parameterless constructor which does not initialize this.Arguments
public MyFilterAttribute(string arg1) : base(typeof(MyFilterImpl))
{
this.Arguments = new object[] { arg1 };
}
public MyFilterAttribute() : base(typeof(MyFilterImpl))
{
}
Both will result in the MyFilterImpl
being instantiated with null
or the passed in value.
Personally I would favour the second option as it keeps the code cleaner and upholds the Single Responsibility Principle
Upvotes: 4
Reputation: 29966
As you have found the root cause,
if (_factory == null)
{
var argumentTypes = Arguments?.Select(a => a.GetType())?.ToArray();
_factory = ActivatorUtilities.CreateFactory(ImplementationType, argumentTypes ?? Type.EmptyTypes);
}
It will initialize argumentTypes with a.GetType()
, if you pass null or with optionl parameter null
, this line will throw error expected.
To avoid passing ""
in every action, you could try set string arg1
with default value ""
.
public MyFilterAttribute(string arg1 = "") : base(typeof(MyFilterImpl))
{
this.Arguments = new object[] { arg1 };
}
Upvotes: 1