Piotrek
Piotrek

Reputation: 11221

Include only one property, not entire database row

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

Answers (4)

GraemeMiller
GraemeMiller

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

Floyd1256
Floyd1256

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

Sampath
Sampath

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

Tseng
Tseng

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

Related Questions