System.InvalidOperationException абсолютно всегда при попытке вытащить вложенные значения ИЛИ как сокрыть поля
ASP NET Core 6/EF Core 6. Есть эндпоинт, который выдает некоторые данные:
[HttpGet]
[Authorize]
public async Task<ActionResult<Contact[]>> Get()
{
return Ok(await _context.Contacts.Where((x) => x.Owner == _userService.GetUser())
.Include((x) => x.Owner).Include((x) => x.Target).ToListAsync());
}
Но я хотел бы избавиться от всех значений в блоках owner и target, кроме firstName, lastName и grade.
Теги [JsonIgnore] не подходят, так как они уже используются для сокрытия значений для другого эндпоинта.
Попробовал реализовать это с помощью ThenInclude, включив только нужные мне поля:
[HttpGet]
[Authorize]
public async Task<ActionResult<Contact[]>> Get()
{
return Ok(await _context.Contacts.Where((x) => x.Owner == _userService.GetUser())
.Include((x) => x.Owner).ThenInclude((owner) => owner.FirstName)
.Include((x) => x.Owner).ThenInclude((owner) => owner.LastName)
.Include((x) => x.Owner).ThenInclude((owner) => owner.Grade)
.Include((x) => x.Target).ThenInclude((target) => target.FirstName)
.Include((x) => x.Target).ThenInclude((target) => target.LastName)
.Include((x) => x.Target).ThenInclude((target) => target.Grade).ToListAsync());
}
Но такая реализация всегда выдаёт исключение:
System.InvalidOperationException: The expression 'owner.FirstName' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
Модель Contact:
public class Contact : Auditable
{
public int Id { get; set; }
public User Owner { get; set; } = new User();
public User Target { get; set; } = new User();
}
Модель User:
public class User : Auditable
{
[JsonIgnore]
public int Id { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public School School { get; set; } = new School();
public string Grade { get; set; } = string.Empty;
[JsonIgnore]
public byte[] PasswordHash { get; set; } = new byte[] { };
[JsonIgnore]
public byte[] PasswordSalt { get; set; } = new byte[] { };
[JsonIgnore]
public bool Verified { get; set; }
[JsonIgnore]
public string Role { get; set; } = string.Empty;
}
Ответы (1 шт):
Include(x => x.Owner) загрузит все свойства User, являющиеся примитивными типами (вернее, загрузит все данные, хранящиеся в этой же таблице БД). Чтобы избежать этого, можно сделать проекцию с помощью Select, явно указав нужные свойства.
Должно работать примерно следующее:
await _context.Contacts.Where(x => x.Owner == user)
.Include(x => x.Owner)
.Include(x => x.Target)
.Select(x => new //Contact
{
//Id = x.Id,
Owner = new //User
{
//Id = x.Owner.Id,
FirstName = x.Owner.FirstName,
LastName = x.Owner.LastName,
Grade = x.Owner.Grade,
},
Target = new //User
{
//Id = x.Target.Id,
FirstName = x.Target.FirstName,
LastName = x.Target.LastName,
Grade = x.Target.Grade
}
})
.ToListAsync();
Вместо анонимных типов можете сделать Contact и User, если необходимо.
