Reputation: 11221
Model:
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; }
}
Controller:
[HttpGet]
public IEnumerable<Word> Get()
{
return _db.Words.Include(x=>x.Author).ToList();
}
My Controller now returns entire ApplicationUser
class which is one of properties of Word
. I want to send only one property of ApplicationUser
: UserName
. How can I do that?
I've added AuthorName
, which would return only data that I want from ApplicationUser
. Unfortunately I still have to .Include(x=>x.Author)
to make this property work. Can I somehow omit including Author
in process of data serialization (to hide it when sending data to user)?
I know I can use .Select()
method, but it requires me to type all properties I will need. If I modify my Model in the future, I will need to update all those .Select()
which will would be inconvenient and waste of time.
How would you solve that?
Upvotes: 8
Views: 4957
Reputation: 12253
Create a View model and use AutoMapper to populate. Look at using AutoMapper and ProjectTo extension https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
That way if you add properties to View model they will be automatically mapped if they exist on your EF model
So create a VM with required properties named appropriately (see AutoMapper docs on naming conventions):
public class WordVM
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorUserName { get; set; }
}
Then use AutoMapper to project (it will do any required includes so if you changed the VM later then it would handle that)
_db.Words.ProjectTo<WordVM>().ToList();
You don't need the NotMapped
property AutoMapper would map the navigation property Author
and the Author Property UserName
to AuthorUserName
Upvotes: 2
Reputation: 201
My workaround was to get all the related entities with .include(), then loop over them and omit the property values I did not want to return. It would require some maintenance in case your model changed, but surprisingly, it did not impact the response time dramatically.
Upvotes: 0
Reputation: 65870
You can try it as shown below.
Note : You don't need to use Include
here.
[HttpGet]
public async Task<IEnumerable<Word>> Get()
{
return _db.Words.Select(x => new
{
Word = x,
AuthorName = x.Author.UserName
}
).ToList();
}
Upvotes: 2
Reputation: 64150
You need to create a Dto object and assign the values to it and return the Dto instead.
Dto
public class WordDto
{
public string Title { get; set; }
public DateTime? WhenCreated { get; set; }
public string AuthorName { get; set; }
}
Then in your action
[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();
}
Upvotes: 5