Home > Mobile >  C# & Entity Framework linq query how to return the mapper even when object is null
C# & Entity Framework linq query how to return the mapper even when object is null

Time:05-18

I have the following query. When there is no matching record in the database, the method returns null. But I want the response to return object with null properties. How to achieve this?

    public async Task<UserResponse> GetUserByEmployeeId(string employeeNumber)
    {
        var userRecord = await _context.User
            .AsNoTracking()
            .Include(u => u.Manager)
            .Where(x => x.PersonNumber == employeeNumber)
            .Select(user => UserReponseMapper.ToUserResponse(user))
            .FirstOrDefaultAsync();

        // The following returns expected value: 
        // But, looking for a better solution
        
        /*
        if(userRecord == null)
        {
            userRecord = new UserResponse();
        }
        */

        return userRecord;
    }
    
    public static UserResponse ToUserResponse(User user)
    {
        return new UserResponse
        {
            EmployeeNumber = user.PersonNumber,
            ManagerNumber = user.Manager?.PersonNumber
        };
    }       

Expected result when no matching record

{
  EmployeeNumber: null,
  ManagerNumber: null
}

Currently getting result as NULL;

CodePudding user response:

If there are no matching records, ToUserResponse is not called and FirstOrDefaultAsync returns null. You can use the null-coalescing operator to return the alternative:

public async Task<UserResponse> GetUserByEmployeeId(string employeeNumber)
{
    var userRecord = (await _context.User
        .AsNoTracking()
        .Include(u => u.Manager)
        .Where(x => x.PersonNumber == employeeNumber)
        .Select(user => UserReponseMapper.ToUserResponse(user))
        .FirstOrDefaultAsync()) 
        ?? new UserResponse() { EmployeeNumber: null, ManagerNumber: null };
    // ...

This operator checks the result of the first parameter against null; if the result is not null, it is returned, otherwise the second parameter is returned. It is basically a short form of the if-statement in your sample.

CodePudding user response:

If you are looking to condense the lines.

userRecord = (userRecord == null) ? new UserResponse() : userRecord;

Or if you want to add it to the original you can add the Elvis operator. "??"

var userRecord = (await _context.User
    .AsNoTracking()
    .Include(u => u.Manager)
    .Where(x => x.PersonNumber == employeeNumber)
    .Select(user => UserReponseMapper.ToUserResponse(user))
    .FirstOrDefaultAsync()) 
    ?? new UserResponse();
 // ^ Checks for null

You need to change your construct if user is null as well.

return new UserResponse
    {
        EmployeeNumber = user?.PersonNumber,
        ManagerNumber = user?.Manager?.PersonNumber
    };

CodePudding user response:

I was wrong in my comment (and deleted it), but it got me thinking; since you might want to add more logic to the "create this new object when the result is null" you could do something like the "pseudocode" below (it compiles, but it's just testcode)

    public async Task<ResultObject> GetResultObjectAsync()
    {
        return ProcessResult(
             (await GetResult())
            .Where(x => x?.ID == new Guid())
            .FirstOrDefault());
    }

    // this is your DB
    async Task<List<ResultObject>> GetResult()
    {
        return new List<ResultObject>();
    }

    // this is the null-coalesce replacement
    public static ResultObject ProcessResult(ResultObject? value)
    {
        if (value == null) return new ResultObject(); // or add more info like setting default values
        return value;
    }

    // this is just a simple POCO for testing
    public class ResultObject
    {
        public Guid ID { get; set; }
    }
  • Related