FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it

See original GitHub issue

I’m trying to compose a raw query that should be composable with Linq operators, but I get this error:

FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it. Consider calling AsEnumerable after the FromSqlRaw or FromSqlInterpolated method to perform the composition on the client side.

Steps to reproduce

Run this code:

class Foo
{
    public int Id { get; set; }
    public int CategoryId { get; set; }
    public DateTime StartDate { get; set; }
    public ICollection<FooTranslation> Translations { get; set; } = new HashSet<FooTranslation>();
}

class FooTranslation
{
    public int FooId { get; set; }
    public string Lang { get; set; }
    public string Name { get; set; }
    public string Content { get; set; }
}

class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }
    
    public DbSet<Foo> Foos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        modelBuilder.Entity<Foo>(entity =>
        {
            entity.ToTable("Foos");
            entity.HasKey(f => f.Id);
            entity.OwnsMany(f => f.Translations, e =>
            {
                e.ToTable("FooTranslations");
                e.HasKey(t => new {t.FooId, t.Lang});
                e.Property(t => t.Lang).IsRequired().HasMaxLength(2);
                e.Property(t => t.Name).IsRequired().HasMaxLength(100);
                e.Property(t => t.Content).IsRequired().HasMaxLength(200);
            });
        });
    }
}

...

var sql = @"
WITH foosWithRownum as (
    SELECT Id, CategoryId, StartDate,
        ROW_NUMBER() OVER (PARTITION BY CategoryId ORDER BY StartDate DESC, Id DESC) rownum
    FROM Foos
    WHERE StartDate < CURRENT_TIMESTAMP
)
SELECT f.*, t.FooId, t.Lang, t.Name, t.Content
FROM foosWithRownum f
LEFT JOIN FooTranslations t ON f.Id = t.FooId
WHERE f.rownum = 1";

IQueryable<Foo> query = dbContext.Foos.FromSqlRaw(sql);

// Add sorts and projections to query with Linq, and execute it
query = query.OrderBy(...)
var result = query.Select(...).ToList();

This gives the error above.

As far as I can tell, my raw SQL should be composable, according to the rules here. There is an ORDER BY, but it’s in an OVER clause in a CTE, so it shouldn’t prevent the whole query from being composable. I tried adding OFFSET 0 as suggested in the docs, but I get a syntax error (it’s probably not supported in an OVER clause). I also tried adding TOP 100 PERCENT to the SELECT clause, but it didn’t change anything.

Is there any way I can run this query with EF Core ?

What is really strange is that I get the same error if I directly call ToList() on the result of FromSqlRaw(), even though I’m not composing with anything.

Further technical details

EF Core version: 3.1.8 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET Core 3.1 Operating system: Windows 10 IDE: irrelevant, I can easily reproduce without any IDE

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
bitxwisecommented, Mar 28, 2021

I see that this is a closed issue, but doesn’t the required to start with SELECT break support for querying via stored procedure?

Support for stored procedures should still be available according to documentation (albeit from October 2019), and multiple other popular blog posts from as recent as September 2020 - which is confusing…

As for potential workarounds…

Database.ExecuteSqlCommand is only intended for INSERT, UPDATE, and DELETE - with no support for SELECT.

Database.GetDbConnection can allow devs to leverage ADO.NET, but manually mapping values from a DbDataReader is cumbersome and doesn’t support tracking.

1reaction
paulresdatcommented, Mar 11, 2021

We’ve found this breaks when using AutoMapper when previously it didn’t in 3.1. The fix is to put this into a List first before casting to an IQueryable that AutoMapper can use.

EntityQuerable<SystemQueueReceive> rowsFromStoredProcedure = PrepareProcedure();
var queryable = rowsFromStoredProcedure.ToList()
return queryable.AsQueryable()
  .ProjectTo<T2>(mapperConfig)
  .ToList()
  .FirstOrDefault()

If there’s a better way to do it, I’m all ears.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Include with FromSqlRaw and stored procedure in EF Core ...
FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it. Consider calling AsEnumerable after ...
Read more >
FromSqlRaw or FromSqlInterpolated was called with non ...
[Solved]-'FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it' when using OwnsOne()-entityframework core.
Read more >
RelationalStrings.FromSqlNonComposable Property
'FromSql' or 'SqlQuery' was called with non-composable SQL and with a query composing over it. Consider calling 'AsEnumerable' after the method to perform ......
Read more >
Raw SQL Queries in EF-Core - Learn Entity Framework Core 7
Composing Over Raw SQL​​ In Entity Framework Core, it is possible to compose over raw SQL queries using the LINQ operators. Due to...
Read more >
Recursive Data With Entity Framework Core and SQL Server
InvalidOperationException : 'FromSqlRaw' or 'FromSqlInterpolated' was called with non-composable SQL and with a query composing over it.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found