Event log with server set properties

I have an issue with the crud event log. It assumes that all the entity properties will be passed via the public request but in reality a lot of the time properties need to be defined by the system and not the user.

I like autocrud/event log, I have used it everywhere in my API, but I am not sure how to make this pattern where I want to define properties without doing a straight passthrough.

For instance lets say we have:


    [AutoApply(Behavior.AuditCreate)]
    public class CreateWidget : ICreateDb<Widget>, IReturn<IdResponse>
    {
        public required string Name { get; set; }
    }
	
    public class Widget 
    {
		[AutoIncrement]
		public int Id {get; set; }
        public required string Name { get; set; }
        public string Analysis { get; set; }
    }

We don’t want users to be able to submit analysis data, we want the system to add that.

So we can do something like:

var response = (IdResponse) await autoQuery.CreateAsync(request, base.Request);
var record = Db.LoadSingleById<Widget>(int.Parse(response.Id));
record.Analysis = "It's a good name";
await Db.SaveAsync(record);

But now there is no record of this change in the event log. If the system set property is a required field on the entity it also wont work at all because autoquery.create will have error due to missing field.

What is the recommended way to be setting properties without making them public and to have the data recorded in the event log? I basically just want all db changes to be visible in the audit log and events to be replayable.

I feel like the event log needs an extra column for the final entity that is saved separate from request body then I could manually write an entry so all information is contained and events are still replayable.

You can use the AutoPopulate attribute to populate Data Model properties that are not on the Request DTO, e.g:

[AutoApply(Behavior.AuditCreate)]
[AutoPopulate(nameof(Widget.Analysis), Value = "It's a good name")]
public class CreateWidget : ICreateDb<Widget>, IReturn<IdResponse>
{
    public required string Name { get; set; }
}

You could also register a custom populator to modify the data model values, e.g:

AutoMapping.RegisterPopulator((Dictionary<string,object> target, Widget source) => 
{
    target[nameof(source.Analysis)] = "It's a good name";
});

Thanks for the reply Mythz. I didn’t realize I could autopopulate fields not on the DTO, that’s helpful.

In the example I am assigning a string but what if to get the value I need a large code block that uses multiple dependencies in order to get the value?

The AutoPopulate attribute can use Eval to evaluate any #Script, look at the Advanced CRUD Example in the docs.

Thanks, I’ll have another pass at trying to understand #script