#c# #asp.net #entity-framework #asp.net-core #entity-framework-core
#c# #asp.net #entity-framework #asp.net-core #entity-framework-core
Вопрос:
Модель:
public class Word
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public ApplicationUser Author { get; set; }
[NotMapped]
public string AuthorName
{
get
{
if (Author != null)
{
return Author.UserName;
}
else {
return "";
}
}
}
public List<Definition> Definitions { get; set; }
}
Контроллер:
[HttpGet]
public IEnumerable<Word> Get()
{
return _db.Words.Include(x=>x.Author).ToList();
}
Мой контроллер теперь возвращает весь ApplicationUser
класс, который является одним из свойств Word
. Я хочу отправить только одно свойство ApplicationUser
: UserName
. Как я могу это сделать?
Я добавил AuthorName
, которое возвращало бы только те данные, из которых я хочу получить ApplicationUser
. К сожалению, мне все еще нужно .Include(x=>x.Author)
заставить это свойство работать. Могу ли я как-то исключить включение Author
в процессе сериализации данных (чтобы скрыть это при отправке данных пользователю)?
Я знаю, что могу использовать .Select()
метод, но это требует от меня ввода всех свойств, которые мне понадобятся. Если я изменю свою модель в будущем, мне нужно будет обновить все те, .Select()
которые будут неудобными и пустой тратой времени.
Как бы вы это решили?
Ответ №1:
Вам нужно создать объект Dto и присвоить ему значения, а вместо этого вернуть Dto.
Dto
public class WordDto
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorName { get; set; }
}
Затем в вашем действии
[HttpGet]
public async Task<IEnumerable<WordDto>> Get()
{
return _db.Words
.Include(x=>x.Author)
.Select(x =>
new WordDto
{
Title = x.Title,
DateTime = x.WhenCreated,
AuthorName = x.Author?.UserName ?? string.Empty
}
)
.ToListAsync();
}
Комментарии:
1. Я бы удалил
.ToList()
, это кажется более правильным, пусть контроллер материализует запрос, когда это требуется2. Извините, но я не до конца понимаю это. Использование Select таким образом вернет только некоторую
UserName
переменную (я не уверен, что это такое, ее нет вWord
классе). Я хочу вернуть все свойстваWord
класса и одно из свойствAuthor
3. Затем вам нужно создать объект Dto и выполнить присвоение / сглаживание для него
4. Это то, чего я не хотел делать, потому что каждое небольшое изменение в основной модели потребует изменений во всех местах, с которыми я ее использую
.Select()
. Это, конечно, решает эту проблему, но я рассматриваю это как временное решение.5. Вы никогда не должны возвращать модели сохраняемости из служб REST, последствия изменения базы данных намного хуже, чем необходимость обновлять модели: все приложения, которые используют этот RestAPI, должны будут обновляться при каждом изменении сохраняемости. Это намного хуже. Вот почему вы абстрагируете уровень доступа к данным в репозитории или обработчики запросов
Ответ №2:
Вы можете попробовать это, как показано ниже.
Примечание: Вам не нужно использовать Include
здесь.
[HttpGet]
public async Task<IEnumerable<Word>> Get()
{
return _db.Words.Select(x => new
{
Word = x,
AuthorName = x.Author.UserName
}
).ToList();
}
Комментарии:
1. К сожалению, отображается ошибка: «Недопустимый аноним-член типа declarator. Члены анонимного типа должны быть объявлены с назначением члена, простым именем или доступом к члену»
2. Как вы можете это сделать,
x.Author.Select(y => new { y.UserName })
когдаAuthor
свойство не являетсяIEnumerable
?3. О .. Исправлено. Спасибо @Brad
4. В EF Core вы обнаружите, что вам действительно нужно выполнить,
Include(x => x.Author)
иначеAuthor
свойство будет равно null.5. Это никак не компилируется, вы не можете неявно преобразовать анонимный тип в класс
Ответ №3:
Создайте модель представления и используйте AutoMapper для заполнения. Посмотрите на использование AutoMapper и расширения ProjectTo https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
Таким образом, если вы добавите свойства в View model, они будут автоматически сопоставлены, если они существуют в вашей модели EF
Итак, создайте виртуальную машину с требуемыми свойствами, названными соответствующим образом (см. Документы AutoMapper по соглашениям об именовании):
public class WordVM
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorUserName { get; set; }
}
Затем используйте AutoMapper для проекта (он выполнит любые требуемые включения, поэтому, если вы измените виртуальную машину позже, он справится с этим)
_db.Words.ProjectTo<WordVM>().ToList();
Вам не нужно, чтобы NotMapped
автоматическое отображение свойств отображало свойство навигации Author
и свойство автора UserName
на AuthorUserName
Ответ №4:
Мой обходной путь состоял в том, чтобы получить все связанные объекты с помощью .include(), затем перебрать их и опустить значения свойств, которые я не хотел возвращать. Это потребовало бы некоторого обслуживания в случае изменения вашей модели, но, что удивительно, это не сильно повлияло на время отклика.