In FromSqlRaw und gespeicherte Prozedur in EF Core 3.1 einbinden

8

Hier ist der Deal: Ich verwende derzeit EF Core 3.1 und nehme an, ich habe eine Entität:

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

Wenn ich auf DbSet<Entity> Entitiesnormale Weise zugreife , füge ich AnotherEntity wie folgt hinzu:

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

und das funktioniert. Warum sollte es nicht richtig sein? Dann gehe ich mit:

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

und das funktioniert auch. Beide geben mir dieselbe Sammlung von Objekten zurück, die mit AnotherEntity verbunden sind. Dann verwende ich eine gespeicherte Prozedur, die aus derselben Abfrage SELECT * FROM Entitiesnamens spGetEntities besteht:

_context.Entities.FromSqlRaw("spGetEntities")

erraten Sie, was? Das funktioniert auch. Es gibt mir die gleiche Ausgabe, aber natürlich ohne Joined AnotherEntity. Wenn ich jedoch versuche, das Include wie folgt hinzuzufügen:

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

Ich bekomme:

FromSqlRaw oder FromSqlInterpolated wurde mit nicht zusammensetzbarem SQL und einer darüber erstellten Abfrage aufgerufen. AsEnumerable Ziehen Sie in Betracht, nach der FromSqlRaw- oder FromSqlInterpolated-Methode aufzurufen, um die Komposition auf der Clientseite durchzuführen.

Obwohl die Ausgabe von _context.Entities.FromSqlRaw("SELECT * FROM Entities")und _context.Entities.FromSqlRaw("spGetEntities") identisch ist.

Ich konnte keinen Beweis dafür finden, dass ich dies mit EF Core 3.1 kann oder nicht, aber wenn mir jemand einen Hinweis auf die Möglichkeit dieses Ansatzes geben könnte, wäre es schön.

Auch wenn es eine andere Möglichkeit gibt, verbundene Entitäten mithilfe einer gespeicherten Prozedur zu erhalten, würde ich dies wahrscheinlich als Lösung für mein Problem akzeptieren.

Gleb
quelle
2
Es ist nicht EF, die das nicht kann. Es ist SQL selbst ("nicht zusammensetzbares SQL"), geschweige denn EF.
Gert Arnold
@GertArnold bitte als Antwort hinzufügen. Es wird auch anderen Benutzern helfen.
Lutti Coelho
_context.Something.FromSqlRaw ("EXECUTE dbo.spCreateSomething @Id, @Year", sqlParameters) .IgnoreQueryFilters (). AsNoTracking (). AsEnumerable (). FirstOrDefault (); Dies funktioniert bei mir. Sie können ToList () auch über .AsEnumerable (). FirstOrDefault () verwenden, um viele zu erhalten.
Chris Go

Antworten:

6

In Kürze können Sie das nicht tun (zumindest für SqlServer). Die Erklärung ist in der EF Core-Dokumentation - Raw SQL Queries - Composing with LINQ enthalten :

Für das Erstellen mit LINQ muss Ihre unformatierte SQL-Abfrage zusammensetzbar sein, da EF Core das bereitgestellte SQL als Unterabfrage behandelt. SQL-Abfragen, die erstellt werden können, beginnen mit dem SELECTSchlüsselwort. Darüber hinaus sollte die übergebene SQL keine Zeichen oder Optionen enthalten, die für eine Unterabfrage nicht gültig sind, z.

  • Ein nachfolgendes Semikolon
  • Unter SQL Server ein nachfolgender Hinweis auf Abfrageebene (z. B. OPTION (HASH JOIN))
  • Unter SQL Server eine ORDER BYKlausel, die OFFSET 0 OR TOP 100 PERCENTin der SELECTKlausel nicht verwendet wird

SQL Server erlaubt nicht das Erstellen von Aufrufen gespeicherter Prozeduren. Daher führt jeder Versuch, zusätzliche Abfrageoperatoren auf einen solchen Aufruf anzuwenden, zu ungültigem SQL. Verwenden Sie AsEnumerableoder AsAsyncEnumerablemethod direkt nach FromSqlRawoder FromSqlInterpolatedMethoden, um sicherzustellen, dass EF Core nicht versucht, über eine gespeicherte Prozedur zu komponieren.

Da Include/ ThenIncludeEF EF Core erforderlich ist IQueryable<>, ist AsEnumerable/ AsAsyncEnumerableetc. keine Option. Sie benötigen wirklich zusammensetzbares SQL, daher sind gespeicherte Prozeduren keine Option.

Anstelle von gespeicherten Prozeduren können Sie jedoch TVF- (Table-Valued Functions) oder Datenbankansichten verwenden, da diese zusammensetzbar sind ( select * from TVF(params)oder select * from db_view).

Ivan Stoev
quelle
Dies funktioniert in meinem Fall nicht, da ich einen abgeleiteten Typ verwende. Wenn Sie einen abgeleiteten Typ aus dem im Modell verwendeten Typ verwenden, wird die Abfrage auch dann erstellt, wenn Sie AsEnumerable direkt nach FromSqlRaw aufrufen. Ich sehe keine andere Lösung, als diesen Typ nicht abzuleiten, mit allen Eigenschaften vom Basistyp zu trennen, was nicht bequem ist.
Hrvoje Batrnek
1
@HrvojeBatrnek Ich denke, Sie haben im Sinn stackoverflow.com/questions/61070935/…
Ivan Stoev
2

In meinem Fall habe ich funktionierende EF FromSql()mit einem Code für gespeicherte Prozeduren 2.1 in 3.1 konvertiert . Wie so:

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

Wo AccountSumsist ein SP.

Das einzige, was ich tun musste, war zu verwenden FromSqlRaw()und hinzuzufügen IgnoreQueryFilters(), damit es wieder funktioniert. Wie so:

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

Dies wird in den Kommentaren erwähnt, aber das habe ich zunächst verpasst, also hier aufgenommen.

Flores
quelle