Абасс... обсудите рахитекурту
Ну а если всё ещё сложнее (что только не придумают бвл-щики, лишь бы не думать) то говорим - сорян, сам объект ничего не гарантирует, валидация только общая (всего объекта целиком), и не вызывается при каждом изменении.
Ещё вариант - при десериализации присваивать значения полям поддержки свойств, а не самим свойствам. Т.к. валидация запускается обычно через свойства, то она не будет вызываться, если в конструкторе не вызывать их сеттеры.
Сериализовать и десериализовать айди вместо всего объекта просто. Для коллекций там сложнее. У меня есть такая конструкция. Тут бизнес логика работает лишь со свойством Items (комментарий "for business logic"), а всё остальное - обвязка. Приватные члены не сериализуются (если не обозначить атрибутом JsonProperty). Сериализуется лишь список айдишников ItemIds, который просто проходится по списку предметов, выбирая их айдишники. Для бизнес логики обвязка в виде ItemIds не используется, хотя может. Вместо ленивой инициализации можно использовать инициализацию в конструкторе. Но репозиторий тогда тоже должен быть доступен в нём же - т.е. в процессе десериализации. А это иногда нетривиально сделать. Поэтому ленивая загрузка - один из выходов.
// for deserialization and lazy initialization of Items List<int> itemIds; // for serialization [JsonProperty] List<int> ItemIds => Items.Select(i => i.Id).ToList(); // for lazy initialization of Items and as a backing field for it List<Item> items; // for business logic [JsonIgnore] public List<Item> Items => items ??= itemIds ?.Select(/* obtain item from somewhere by its id */) .ToList() ?? new List<Item>(); [JsonConstructor] public MyClass(List<int> itemIds) { this.itemIds = itemIds; }