Bao gồm với FromSqlRaw và thủ tục được lưu trữ trong EF Core 3.1


8

Vì vậy, đây là thỏa thuận - Tôi hiện đang sử dụng EF Core 3.1 và giả sử tôi có một thực thể:

public class Entity
{
    public int Id { get; set; }
    public int AnotherEntityId { get; set; }
    public virtual AnotherEntity AnotherEntity { get; set; }
}

Khi tôi truy cập DbSet<Entity> Entitiestheo cách thông thường, tôi bao gồm AnotherEntity như:

_context.Entities.Include(e => e.AnotherEntity)

và điều này hoạt động. Tại sao không, phải không? Sau đó tôi đi với:

_context.Entities.FromSqlRaw("SELECT * FROM Entities").Include(e => e.AnotherEntity)

và điều này cũng hoạt động. Cả hai trả lại cho tôi cùng một bộ sưu tập các đối tượng được tham gia với AnotherEntity. Sau đó, tôi sử dụng một thủ tục được lưu trữ bao gồm cùng một truy vấn SELECT * FROM Entitiescó tên spGetEntities:

_context.Entities.FromSqlRaw("spGetEntities")

đoán xem cái gì Điều này cũng hoạt động. Nó mang lại cho tôi cùng một đầu ra nhưng rõ ràng không tham gia AnotherEntity. Tuy nhiên nếu tôi cố gắng thêm Bao gồm như thế này:

_context.Entities.FromSqlRaw("spGetEntities").Include(e => e.AnotherEntity)

Tôi đang nhận được:

FromSqlRaw hoặc FromSqlInterpiated đã được gọi với SQL không thể tổng hợp và với một truy vấn sáng tác trên nó. Xem xét việc gọi AsEnumerable sau phương thức FromSqlRaw hoặc FromSqlInterpiated để thực hiện thành phần ở phía máy khách.

Mặc dù đầu ra của _context.Entities.FromSqlRaw("SELECT * FROM Entities")_context.Entities.FromSqlRaw("spGetEntities") là giống hệt nhau.

Tôi không thể tìm thấy bằng chứng rằng tôi có thể hoặc tôi không thể làm điều này với EF Core 3.1 nhưng nếu ai đó có thể cho tôi bất kỳ gợi ý nào về khả năng của phương pháp này thì sẽ rất tuyệt.

Ngoài ra nếu có một cách khác để có được các thực thể tham gia bằng cách sử dụng thủ tục được lưu trữ, tôi có thể chấp nhận nó như là giải pháp cho vấn đề của mình.


2
Không phải là EF không thể làm điều này. Đó là SQL ("SQL không thể tổng hợp"), vì vậy hãy để một mình EF có thể.
Gert Arnold

@GertArnold vui lòng thêm nó dưới dạng câu trả lời. Nó sẽ giúp người dùng khác quá.
Lutti Coelho

_context.S Something.FromSqlRaw ("EXECUTE dbo.spCreateS Something @Id, @Year", sqlParameter) .IgnoreQueryFilters (). AsNoTracking (). Điều này làm việc cho tôi, bạn có thể sử dụng ToList () cũng như .AsEnumerable (). FirstOrDefault () để nhận được nhiều.
Chris Go

Câu trả lời:


6

Một thời gian ngắn, bạn không thể làm điều đó (ít nhất là đối với SqlServer). Lời giải thích có trong tài liệu của EF Core - Truy vấn SQL thô - Soạn thảo với LINQ :

Việc soạn thảo với LINQ yêu cầu truy vấn SQL thô của bạn phải có thể kết hợp được vì EF Core sẽ coi SQL được cung cấp như một truy vấn con. Các truy vấn SQL có thể được soạn thảo bắt đầu bằng SELECTtừ khóa. Hơn nữa, SQL thông qua không nên chứa bất kỳ ký tự hoặc tùy chọn nào không hợp lệ trên truy vấn con, chẳng hạn như:

  • Dấu chấm phẩy
  • Trên SQL Server, một gợi ý cấp truy vấn (ví dụ OPTION (HASH JOIN):)
  • Trên SQL Server, một ORDER BYmệnh đề không được sử dụng OFFSET 0 OR TOP 100 PERCENTtrong SELECTmệnh đề

SQL Server không cho phép soạn thảo các cuộc gọi thủ tục được lưu trữ, do đó, bất kỳ nỗ lực nào để áp dụng các toán tử truy vấn bổ sung cho cuộc gọi như vậy sẽ dẫn đến SQL không hợp lệ. Sử dụng AsEnumerablehoặc AsAsyncEnumerablephương pháp ngay sau FromSqlRawhoặc FromSqlInterpolatedphương thức để đảm bảo rằng EF Core không cố gắng soạn thảo một quy trình được lưu trữ.

Ngoài ra, vì Include/ ThenIncludeyêu cầu EF Core IQueryable<>, AsEnumerable/ AsAsyncEnumerablev.v. không phải là một tùy chọn. Bạn thực sự cần SQL có thể tổng hợp, do đó các thủ tục được lưu trữ không có tùy chọn.

Thay vì các thủ tục được lưu trữ, bạn có thể sử dụng Hàm giá trị bảng (TVF) hoặc chế độ xem cơ sở dữ liệu vì chúng có thể ghép được ( select * from TVF(params)hoặc select * from db_view).


Điều này không hoạt động trong trường hợp của tôi bởi vì tôi đang sử dụng một loại dẫn xuất. Khi sử dụng loại dẫn xuất từ ​​loại được sử dụng trong mô hình, truy vấn sẽ được soạn thảo ngay cả khi bạn gọi AsEnumerable ngay sau FromSqlRaw. Tôi không thấy giải pháp nào khác ngoài việc làm cho loại đó không xuất phát, tách biệt với tất cả các thuộc tính từ loại cơ sở, không thuận tiện.
Hrvoje Batrnek

1
@HrvojeBatrnek Tôi đoán bạn có trong tâm trí stackoverflow.com/questions/61070935/
Ivan Stoev

2

Trong trường hợp của tôi, tôi đã chuyển đổi EF làm việc FromSql()với mã 2.1 được lưu trữ thành 3.1. Thích như vậy:

ctx.Ledger_Accounts.FromSql("AccountSums @from, @until, @administrationId",
                                                            new SqlParameter("from", from),
                                                            new SqlParameter("until", until),
                                                            new SqlParameter("administrationId", administrationId));

Trong trường hợp AccountSumslà một SP.

Điều duy nhất tôi phải làm là sử dụng FromSqlRaw()và thêm IgnoreQueryFilters()vào để nó hoạt động trở lại. Thích như vậy:

ctx.Ledger_Accounts.FromSqlRaw("AccountSums @from, @until, @administrationId",
               new SqlParameter("from", from),
               new SqlParameter("until", until),
               new SqlParameter("administrationId", administrationId)).IgnoreQueryFilters();

Điều này được đề cập trong các ý kiến, nhưng tôi đã bỏ lỡ điều đó lúc đầu nên bao gồm cả điều này ở đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.