Skip to content

EF Core 7: The LINQ expression 'DbSet could not be translated when using Value Object #518

Open

Description

@pantonis

EF COre The LINQ expression 'DbSet 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'.

I have the following Name ValueObject:

 public class Name : ValueObject
 {
     public string Value { get; private set; }

     protected Name()
     {
     }

     private Name(string value)
     {
         Value = value;
     }

     public static Result<Name> Create(string name)
     {
         name = (name ?? string.Empty).Trim();

         if (string.IsNullOrEmpty(name))
             return Result.Failure<Name>("Name should not be empty");

         if (name.Length > 128)
             return Result.Failure<Name>("Name is too long");

         return Result.Success(new Name(name));
     }

     protected override IEnumerable<IComparable> GetEqualityComponents()
     {
         yield return Value;
     }
 }

and the following table

public class Team
{
    public int Id { get; private set; }
    public Name Name { get; private set; }
}

and in my dbcontext

 modelBuilder.Entity<Team>(x =>
 {
     x.Property(p => p.Name)
         .IsRequired()
         .HasConversion(p => p.Value, p => Name.Create(p).Value)
         .HasMaxLength(128);
 }

When I try to run the following

 var teamQuery = await (from team in dbContext.Team.AsNoTracking()
                        where team.Name.Value.Contains(input.SearchText)
                        select new 
                        {
                            Id = team.Id,
                            Name = team.Name.Value,
                        }).ToListAsync();

I get the following error:

 - The LINQ expression 'DbSet<Team>()
    .Where(t => t.Name.Value.Contains(__input_SearchText_0))' 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

Activity

  1. vkhorikov commented on Sep 27, 2023

    @vkhorikov
    Owner

    It most likely doesn't like the Name.Value part.

    LINQ queries don't work well, both in EF and NH, so you'd need to come up with some workaround, unfortunately.

  2. PNZeml commented on Nov 24, 2023

    @PNZeml
    Contributor

    It is working with the ComplexProperty mapping feature from EF Core 8.

    var hostBuilder = Host.CreateEmptyApplicationBuilder(new() { Args = args });
    
    hostBuilder.Logging.AddConsole().SetMinimumLevel(LogLevel.Debug);
    
    hostBuilder.Services.AddDbContext<SampleDbContext>(b =>  b.UseNpgsql(connectionString);
    
    var host = hostBuilder.Build();
    
    var sampleDbContext = host.Services.GetRequiredService<SampleDbContext>();
    
    var teams = sampleDbContext.Set<Team>();
    
    teams.AddRange(new Team {Name = new("some1")}, new Team {Name = new("anothersome")}, new Team {Name = new("newone")});
    
    sampleDbContext.SaveChanges();
    
    var teamDtos = await teams.AsNoTracking().Where(x => x.Name.Value.Contains("some"))
        .Select(x => new {x.Id, Name = x.Name.ToString()}).ToListAsync();
    
    host.Services.GetRequiredService<ILogger<DbContext>>()
        .LogDebug("Fetched teams: {@Teams}", teamDtos);
    
    public class SampleDbContext(DbContextOptions options) : DbContext(options) {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { }
    
        protected override void OnModelCreating(ModelBuilder b) {
            b.UseIdentityByDefaultColumns();
    
            b.Entity<Team>(static e => {
                e.ToTable("teams");
    
                e.HasKey(x => x.Id)
                    .HasName("teams_pk");
    
                e.Property(x => x.Id).HasColumnName("id");
    
                // This one            
                var name = e.ComplexProperty(x => x.Name).IsRequired();
                name.Property(x => x.Value).HasColumnName("name").IsRequired();
            });
        }
    }
    
    public class Team {
        public long Id { get; init; }
    
        public required Name Name { get; init; } 
    }
    
    public class Name(string value) : SimpleValueObject<string>(value);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions