I have multiple input models, and the majority of them include 'OwnerId' and 'CreatedBy' properties. I am seeking a mechanism to automatically populate these properties with default values. I attempted to use middleware, but I encountered difficulties in locating the mentioned properties to populate them.
[HttpPost]
public async Task<bool> ExportPackingListProductBulkInsert(PackingListProductFromSalePermitInput input)
{
// input.OwnerId = 10;
// input.CreatedBy = get current date
// Inserting process
return await true;
}
I have a substantial number of Actions
where their inputs need to be populated before the Action is executed. Is there a mechanism to automatically populate these inputs?
You could inherit from ActionFilterAttribute
and tag your API method. Then, override OnActionExecuting
method on this attribute.
OnActionExecuting
is called right before entering the tagged API method.
You can access method arguments via ActionExecutingContext.ActionArguments
which is a Dictionary<string, object>
object. Its key is the argument name, here "input". You will need to cast the value to the right type, ie PackingListProductFromSalePermitInput
or one of its base classes.
Something like this:
[AttributeUsage(AttributeTargets.Method)]
public class FillAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext pContext)
{
var found = pContext.ActionArguments.TryGetValue("input", out var argument);
if (found && argument is PackingListProductFromSalePermitInput input)
{
input.OwnerId = 10;
input.CreatedBy = DateTime.UtcNow;//get current date
}
base.OnActionExecuting(pContext);
}
}
For your API method:
[HttpPost]
[Fill]
public async Task<bool> ExportPackingListProductBulkInsert(PackingListProductFromSalePermitInput input)
{
// Inserting process
return await true;
}
You can also inherit from IObjectModelValidator
and add it as middleware in ConfigureServices
method
.ConfigureServices(services =>
{
services.AddSingleton<IObjectModelValidator, FillValidator>();
}
You would not have to tag every API method in this case, but it will be called for every endpoint.
public class FillValidator : IObjectModelValidator
{
public void Validate(ActionContext pActionContext, ValidationStateDictionary? pValidationState, string pPrefix, object? pModel)
{
if(pModel is PackingListProductFromSalePermitInput input)
{
input.OwnerId = 10;
input.CreatedBy = DateTime.UtcNow;//get current date
}
}
}
The third option, and certainly the easiest, is to fill in the properties in the object's constructor. But I'm sure you already are aware of this :)
EDIT
In order to use IObjectModelValidator efficiently with many models, you could use polymorphism this way
public class BaseModel
{
public virtual void Fill()
{
}
}
public class PackingListProductFromSalePermitInput : BaseModel
{
public override void Fill()
{
this.OwnerId = 10;
this.CreatedBy = DateTime.UtcNow;//get current date
}
}
public class OtherModel : BaseModel
{
public override void Fill()
{
//TODO fill in what you want
}
}
public class FillValidator : IObjectModelValidator
{
public void Validate(ActionContext pActionContext, ValidationStateDictionary? pValidationState, string pPrefix, object? pModel)
{
if(pModel is BaseModel model)
{
model.Fill();
}
}
}