5
anrufen

In den letzten paar Tagen suche ich nach Tutorials, wie man einen Stored Procedure von innerhalb einer Web API Controller-Methode mit EntityFramework 7 aufrufen kann.Wie kann ich eine SQL Stored Procedure unter Verwendung von EntityFramework 7 und Asp.Net 5

Alle Tutorials, die ich durchkam, zeigen es umgekehrt, d. H. Code First Ansatz. Aber ich habe bereits eine Datenbank an Ort und Stelle und ich muss es verwenden, um eine Web API zu bauen. Verschiedene Geschäftslogiken sind bereits als Stored Procedures und Views geschrieben und ich muss diese von meiner Web API konsumieren.

Frage 1: Ist es überhaupt möglich, mit Database First Ansatz mit EF7 weiter zu machen und Datenbankobjekte wie oben zu verbrauchen?

Ich installierte EntityFramework 6.1.3 auf mein Paket durch den folgenden Befehl NuGet:

install-package EntityFramework die Version 6.1.3 zu meinem Projekt fügt aber sofort beginnt mir Fehlermeldung zeigt (bitte den Screenshot unten). Ich habe keine Ahnung, wie ich das lösen kann.

enter image description here

Ich habe ein anderes Testprojekt, bei dem in project.json ich zwei Einträge sehen wie folgt vor:

"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", "EntityFramework.MicrosoftSqlServer.Design": "7.0.0-rc1-final",

Allerdings, wenn ich in Nu-Get Paket-Manager bin auf der Suche, ich don t sehen diese Version! Nur 6.1.3 steht vor der Tür.

Mein Hauptziel ist es, bereits gespeicherte Stored Procedures und Views aus einer bestehenden Datenbank zu konsumieren.

1) Ich will nicht ADO.Net verwenden, eher würde Ich mag ORM verwenden mit EntityFramework

2) Wenn EntityFramework 6.1.3 die Fähigkeit Stored Procs und Views aus bereits vorhandenen Datenbank zu nennen hat, wie ich den Fehler beheben kann (Bildschirmfoto)?

Was ist die beste Vorgehensweise, um dies zu erreichen?

+0

Wie verwenden Sie derzeit Entity Framework? Könnten Sie ein kleines Beispiel wie Sie es derzeit verwenden? Ich persönlich benutze auch STORED PROCEDURES, die ich hauptsächlich in Controller Action anmelde und die Ergebnisse von Web Api rerringt. Es erlaubt mir, überhaupt keine Klassen (Entitäten) für Tabellen zu definieren.Mein JavaScript-Code hängt von den Namen von Spalten ab, die von STORED PROCEDURES zurückgegeben wurden, nicht jedoch von meinem C# -Code. Siehe [die Antwort] (http://stackoverflow.com/a/34162630/315935). Somit gibt es viele Implementierungsoptionen. Wenn Sie ein Beispiel Ihres aktuellen Codes veröffentlichen würden, wäre es besser zu zeigen, wie Sie helfen können. – Oleg

+0

Sie können die Version 7 von Entity Framework in NuGet wahrscheinlich nicht sehen, weil Sie das Kontrollkästchen "Nur stabil" aktiviert haben. Dies schließt Pre-Release-Versionen aus, und das "rc" in "EF7 rc1" steht für "Release candidate" (d. H. Pre-Prelease). –

+0

@Oleg: Ich habe noch nie Stored Procedure mit EntityFramework verwendet. Aber hier ist die Datenbank bereits erstellt, also muss ich das selbe verwenden. Mein Teil ist es, die Web-API für die laufende Entwicklung der mobilen Version zu entwickeln. Es wäre wirklich eine große Hilfe, wenn Sie mir bitte einen Beispielcode geben. –

Antwort

10

Ich hoffe, dass ich Ihr Problem richtig verstehe. Sie haben bereits gespeicherte Prozedur, z. B. dbo.spGetSomeData, in der Datenbank, die die Liste einiger Elemente mit einigen Feldern zurückgibt, und Sie müssen die Daten von Web-API-Methode bereitstellen.

Die Implementierung könnte über die folgenden sein.Sie können ein leerDbContext wie definieren:

public class MyDbContext : DbContext 
{ 
} 

und appsettings.json mit der Verbindungszeichenfolge zu definieren, um die Datenbank

{ 
    "Data": { 
    "DefaultConnection": { 
     "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyDb;Trusted_Connection=True;MultipleActiveResultSets=true" 
    } 
    } 
} 

Sie Microsoft.Extensions.DependencyInjection verwenden sollten MyDbContext auf die

public class Startup 
{ 
    // property for holding configuration 
    public IConfigurationRoot Configuration { get; set; } 

    public Startup(IHostingEnvironment env) 
    { 
     // Set up configuration sources. 
     var builder = new ConfigurationBuilder() 
      .AddJsonFile("appsettings.json") 
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); 
      .AddEnvironmentVariables(); 
     // save the configuration in Configuration property 
     Configuration = builder.Build(); 
    } 

    // This method gets called by the runtime. Use this method to add services to the container. 
    public void ConfigureServices(IServiceCollection services) 
    { 
     // Add framework services. 
     services.AddMvc() 
      .AddJsonOptions(options => { 
       options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 
      }); 

     services.AddEntityFramework() 
      .AddSqlServer() 
      .AddDbContext<MyDbContext>(options => { 
       options.UseSqlServer(Configuration["ConnectionString"]); 
      }); 
    } 
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
    { 
     ... 
    } 
} 
hinzufügen

Jetzt können Sie Ihre WebApi-Aktion wie folgt implementieren :

[Route("api/[controller]")] 
public class MyController : Controller 
{ 
    public MyDbContext _context { get; set; } 

    public MyController([FromServices] MyDbContext context) 
    { 
     _context = context; 
    } 

    [HttpGet] 
    public async IEnumerable<object> Get() 
    { 
     var returnObject = new List<dynamic>(); 

     using (var cmd = _context.Database.GetDbConnection().CreateCommand()) { 
      cmd.CommandText = "exec dbo.spGetSomeData"; 
      cmd.CommandType = CommandType.StoredProcedure; 
      // set some parameters of the stored procedure 
      cmd.Parameters.Add(new SqlParameter("@someParam", 
       SqlDbType.TinyInt) { Value = 1 }); 

      if (cmd.Connection.State != ConnectionState.Open) 
       cmd.Connection.Open(); 

      var retObject = new List<dynamic>(); 
      using (var dataReader = await cmd.ExecuteReaderAsync()) 
      { 
       while (await dataReader.ReadAsync()) 
       { 
        var dataRow = new ExpandoObject() as IDictionary<string, object>; 
        for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++) { 
         // one can modify the next line to 
         // if (dataReader.IsDBNull(iFiled)) 
         //  dataRow.Add(dataReader.GetName(iFiled), dataReader[iFiled]); 
         // if one want don't fill the property for NULL 
         // returned from the database 
         dataRow.Add(
          dataReader.GetName(iFiled), 
          dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {} 
         ); 
        } 

        retObject.Add((ExpandoObject)dataRow); 
       } 
      } 
      return retObject; 
     } 
    } 
} 

Der obige Code

ausführen nur exec dbo.spGetSomeData und verwenden dataRader unter Verwendung aller Ergebnisse zu lesen und es in dynamic Objekt speichern. Wenn Sie $.ajax Anruf von api/My machen würden Sie die Daten erhalten von dbo.spGetSomeData, die Sie direkt in JavaScript-Code verwenden können. Der obige Code ist sehr transparent. Die Namen der Felder aus dem von dbo.spGetSomeData zurückgegebenen Dataset sind die Namen der Eigenschaften im JavaScript-Code. Sie müssen keine Entitätsklassen in Ihrem C# -Code in irgendeiner Weise verwalten. Ihr C# -Code hat keine Namen von Feldern, die von der gespeicherten Prozedur zurückgegeben werden. Wenn Sie also den Code von dbo.spGetSomeData erweitern/ändern (einige Felder umbenennen, neue Felder hinzufügen), müssen Sie nur Ihren JavaScript-Code anpassen, aber keinen C# -Code.

+0

Hallo Oleg, vielen Dank für deine schnelle und ausführliche Antwort. Jetzt habe ich etwas Licht! Ich werde das versuchen und mit meiner Erfahrung zu dir zurückkommen. –

+0

@NiladriSarkar: Gern geschehen! Sie können sehen, dass der Code der Get-Aktion sehr häufig ist und man den Code nur unter Verwendung eines anderen Namens der gespeicherten Prozedur ('dbo.spGetSomeData') und der Liste der Parameter teilen kann. Sie können 'cmd.Parameters.AddRange' verwenden und ein Array von' SqlParameter' bereitstellen, das für die spezifische gespeicherte Prozedur gefüllt ist. Ich denke du verstehst was ich meine. Ich möchte nur den Code von Get Action klein und einfach machen, um die Hauptidee beim Schreiben von C# -Code zu zeigen, der gespeicherte Prozeduren aufruft und * transparent die Ergebnisse zurückgibt, die in JavaScript-Code verwendet werden können. – Oleg

+0

Großartig! Sie haben meinen Tag gemacht :) Ich bin jetzt in der Lage, eine sehr einfache Speicherprozedur aufzurufen, die eine Reihe von Datensätzen mit 'Inner Join' auflistet. Ich sollte jetzt in der Lage sein, mit komplexeren Teilen weiterzumachen. Im Zweifelsfall, rate mal was ... Ich werde dir ein bisschen mehr Ärger geben. –

4

DbContext hat eine Database Eigenschaft, die eine Verbindung zu der Datenbank enthält, die Sie tun können, was Sie wollen mit:

context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar); 

Anstatt jedoch diese Aktionen in Ihren Web-Api tun, würde ich vorschlagen, entweder das Hinzufügen eine Methode für Ihren Kontext oder für welchen Dienst/Repository, der mit Ihrem Kontext interagiert. Rufen Sie diese Methode dann einfach in Ihrer Aktion auf. Idealerweise möchten Sie all Ihre SQL-Dateien an einem Ort aufbewahren.

+0

Ich würde es gerne mehr EF-isch machen, indem ich einen SP auf ein EDM-Designer-Tool lege, seinen Typ (Scalar, Complex etc.) markiere und ihn schließlich wie ein Objekt von EntityFramework aufrufe. Und es wäre großartig, wenn ich all dies mit 'ASP.NET' und' EntityFramework7' machen könnte. Wenn 7 nicht die Lösung ist, kann ich zu früheren Versionen wie '6.1.3' zurückkehren, aber das gibt mir einen Fehler, von dem ich keine Ahnung habe, wie ich ihn lösen soll. Ich sehe auch keine "Entity Data Model" (EDM) Vorlage, die meinem Projekt in der VS 2015 Community Edition hinzugefügt werden kann! –

+0

Das ist * nicht * der "EF-ish" Weg. EF hat die Unterstützung für EDMX in EF7 eingestellt. Der EDM-Designer ist veraltet und eine gute Lösung. –

+0

Ja, EF7 hat EDM fallen gelassen. Hast du eine Ahnung, ob das dauerhaft gemacht wurde oder es sich um einen temporären Fang handelt? –

1

Wie oben erwähnt, können Sie einfach FromSQL() anstelle von SqlQuery <>() verwenden.

context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45); 
+0

Diese Lösung funktioniert mit der letzten ef-Core-Version? –

+2

Ich bekomme nur 'Die Typargumente für DbContext.Set () kann nicht aus der Verwendung abgeleitet werden ' –

+0

Können Sie bitte den Code, den Sie verwendet, auch, mit welchem ​​.NET Framework arbeiten Sie? – TGarrett

0

Für Database ersten Ansatz haben Sie Scaffold-DbContext Befehl

Nuget Installieren von Paketen benutzen Microsoft.EntityFrameworkCore.Tools und Microsoft.EntityFrameworkCore.SqlServer.Design

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models 

aber das wird bekomme deine gespeicherten Prozeduren nicht. Es ist noch in Arbeit, Tracking-Ausgabe #245

Aber, um die gespeicherten Prozeduren auszuführen, verwenden FromSql Methode, die RAW-SQL

Abfragen ausführt z.B.

var products= context.Products 
    .FromSql("EXECUTE dbo.GetProducts") 
    .ToList(); 

Um mit Parametern

var productCategory= "Electronics"; 

var product = context.Products 
    .FromSql("EXECUTE dbo.GetProductByCategory {0}", productCategory) 
    .ToList(); 

oder

var productCategory= new SqlParameter("productCategory", "Electronics"); 

var product = context.Product 
    .FromSql("EXECUTE dbo.GetProductByName @productCategory", productCategory) 
    .ToList(); 

Es gibt bestimmte Einschränkungen zu verwenden, um procedures.You RAW SQL-Abfragen oder gespeicherte ausführen nicht für INSERT/UPDATE verwenden können/LÖSCHEN. Wenn Sie INSERT-, UPDATE-, DELETE-Abfragen ausführen möchten, verwenden Sie das ExecuteSqlCommand-Objekt