The LINQ expression cannot be translated when using null check

232 Views Asked by At

I'm using projection in EF Core with Linqkit to reuse expressions with Invoke(). I want to apply filters to projected models:

public class TransportModel
{
        public Guid Id { get; set; }
        public InmateModel Inmate  { get; set; }
}

public class InmateModel
{
        public Guid Id { get; set; }
        public List<Guid> InmateIds  { get; set; }
}

    public static Expression<Func<Transport, Inmate>> GetInmateProjection()
    {
        return transport => transport.Inmate;
    }

    public static Expression<Func<Inmate, InmateModel>> InmateModelProjection()
    {
        return inmate => new InmateModel()
        {
            Id = inmate.Id,
            InmateIds = inmate.Collusions.Select(x => x.InmateId).ToList()
        };
    }

    public static Expression<Func<Transport, TransportModel>> GetTransportModelProjection()
    {
        return transport => new TransportModel()
        {
            Inmate = InmateModelProjection().Invoke(GetInmateProjection().Invoke(transport)),
        };
    }

// Actual query
IQueryable<TransportModel> query = dbContext.Transports
                    .Select(GetTransportModelProjection());

This code works unless there is an Inmate which is null. Then I get the following exception:

System.InvalidOperationException: Nullable object must have a value.
at lambda_method1483(Closure , QueryContext , DbDataReader , ResultContext , SingleQueryResultCoordinator ) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)

So I add a null check to prevent this error:

public static Expression<Func<Transport, TransportModel>> GetTransportModelProjection()
{
    return transport => transport != null ? new TransportModel()
    {
        Inmate = InmateModelProjection().Invoke(GetInmateProjection().Invoke(transport)),
    } : null;
}

The exception does not appear anymore, however I cannot filter all data anymore:

// This works
query = query.Where(transport => transport.Inmate.Id == someId);

// This does not work anymore due to null check
query = query.Where(transport => transport.Inmate.InmateIds.Contains(someId));

This is the exception I get for the second filter:

The LINQ expression could not be translated. 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'.

Is there a way to translate these null checks correctly?

Edit: The only workaround I can use so far is to flatten the models:

public static Expression<Func<Transport, TransportModel>> GetTransportModelProjection()
    {
        return transport => new TransportModel()
        {
            Inmate = InmateModelProjection().Invoke(GetInmateProjection().Invoke(transport)),
InmateIds = GetInmateProjection().Invoke(transport).Collusions.Select(x => x.Id).ToList()
        };
    }

and query like this

query = query.Where(transport => transport.InmateIds.Contains(someId));

or build the filter directly on the unprojected query:

query = dbContext.Transports.Where(transport => GetInmateProjection().Invoke(transport).InmateIds.Contains(someId));

But it would be nice to do this with nested projections.

0

There are 0 best solutions below