Search code examples
c#.netasp.net-coreentity-framework-corelinq-to-entities

Why EF core tell me 'specified member is unmapped'?


I want to filter users by name and return a new DTO by using the projection query (by Select):

var result = context.Users
    .Where(user => user.FullName == search)
    .Select(u => new UserPagingViewModel
    {
        Id = u.Id,
        IsSearchable = u.IsSearchable,
        FirstName = u.FirstName,
        LastName = u.LastName,
        FullName = u.FullName,
        Photo200 = u.Photo200,
        City = u.City,
        About = u.About,
        Skills = u.UserSkills.Select(us => us.Skill.Name),
        IsTeamMember = u.UserTeams.Any(ut => ut.UserAction == UserActionEnum.JoinedTeam)
    })
    .ToList();

User class:

public class User : IHasId
{
    public long Id { get; set; }
    public string FirstName { get; set; }
    public string SecondName { get; set; }
    public string LastName { get; set; }
    public string City { get; set; }
    public string About { get; set; }
    public string Telegram { get; set; }
    public string Email { get; set; }
    public string Mobile { get; set; }
    public string FullName => FirstName + " " + SecondName + " " + LastName;
    public string Photo100 { get; set; }
    public string Photo200 { get; set; }
    public bool IsModerator { get; set; }
    public List<UserTeam> UserTeams { get; set; }
    public List<UserSkill> UserSkills { get; set; }

    [NotMapped]
    public List<Team> TeamsToRecruit { get; set; }
    [NotMapped]
    public bool AnyTeamOwner { get; set; }
}

Data Base is PostgreSql. Data provider PostgreSQL/Npgsql provider for Entity Framework Core

But when I try to execute this request, I get an exception with the following message:

The LINQ expression 'DbSet<User>() .Where(u => user.FullName == search)' could not be translated. Additional information: Translation of member 'FullName' on entity type 'User' failed. This commonly occurs when the specified member is unmapped. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

I don't want use client evaluation. If I compare for example the FirstName property everything works fine.

Please help me figure out how to solve this problem.


Solution

  • You have FullName in three places. Since you don't have a full name column (or computed column) you can't use that in dbcontext queries. It will generate sql like below.

    SELECT FULLNAME,... FROM USERS WHERE FULLNAME = N"some value".
    

    This is why you are getting an error.

    You need to remove all FullName from dbcontext query and do where filter like below.

    var result = context.Users
        .Where(x => (x.FirstName + " " + x.SecondName + " " + x.LastName) == search)
        .Select(u => new UserPagingViewModel
        {
            Id = u.Id,
            IsSearchable = u.IsSearchable,
            FirstName = u.FirstName,
            LastName = u.LastName,
            //FullName = u.FullName,
            Photo200 = u.Photo200,
            City = u.City,
            About = u.About,
            Skills = u.UserSkills.Select(us => us.Skill.Name),
            IsTeamMember = u.UserTeams.Any(ut => ut.UserAction == UserActionEnum.JoinedTeam)
        })
        .ToList();
    

    This will you something like below

    FROM [Users] AS [t]
    WHERE ([t].[FirstName] + N' ' + [t].[SecondName] + N' ' + [t].[LastName]) = N'some value' 
    

    Then remove FullName from User object.

    public class User : IHasId
    {
        public long Id { get; set; }
        public string FirstName { get; set; }
        public string SecondName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }
        public string About { get; set; }
        public string Telegram { get; set; }
        public string Email { get; set; }
        public string Mobile { get; set; }
        // public string FullName => FirstName + " " + SecondName + " " + LastName;
        public string Photo100 { get; set; }
        public string Photo200 { get; set; }
        public bool IsModerator { get; set; }
        public List<UserTeam> UserTeams { get; set; }
        public List<UserSkill> UserSkills { get; set; }
    
        [NotMapped]
        public List<Team> TeamsToRecruit { get; set; }
        [NotMapped]
        public bool AnyTeamOwner { get; set; }
    }
    

    Add FullName to UserPagingViewModel.

    public class UserPagingViewModel{
        ...
        public string FirstName { get; set; }
        public string SecondName { get; set; }
        public string LastName { get; set; }
        public string FullName => $"{FirstName} {SecondName} {LastName}";
    }
    

    If you want FullName as part of user object, then add as notmapped or use HasComputedColumnSql