2012-04-03 7 views
5

Mit WCF Data Services (und dem neuesten Entity Framework) möchte ich Daten aus einer gespeicherten Prozedur zurückgeben. Die zurück sproc Felder passen nicht 1: 1 jede Einheit in meinem db, so dass ich erstellen Sie einen neuen komplexen Typen für sie im edmx Modell (statt eine bestehende Einheit befestigen):Wie kann ein komplexes Objekt von einem Sproc mit WCF Data Services/OData konsumiert werden?

  1. Rechtsklick auf den *. edmx Modell/Add/Funktion Import
  2. die sproc Wählen Sie (gibt drei Felder) - GetData
  3. Klicken Sie auf Spalteninformationen Erhalten
  4. Fügen Sie die Funktion Import Name: GetData
  5. Klicken Sie auf erstellen neuer komplexer Typ - GetData_Result

Im Dienst definiere ich:

[WebGet] 
    public List<GetData_Result> GetDataSproc() 
    { 
     PrimaryDBContext context = new PrimaryDBContext(); 
     return context.GetData().ToList(); 
    } 

ich eine schnelle Konsolenanwendung erstellt, um zu testen, und fügte einen Verweis auf System.Data.Services und System.Data.Services.Client - dieser nach Install-Package EntityFramework -Pre, aber die Versionen auf den Bibliotheken sind 4.0 und nicht aktiv 5.x.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data.Services.Client; 
using ConsoleApplication1.PrimaryDBService; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      DataServiceContext context = new DataServiceContext(new Uri("http://localhost:50100/PrimaryDataService1.svc/")); 
      IEnumerable<GetData_Result> result = context.Execute<GetData_Result>(new Uri("http://localhost:50100/PrimaryDataService1.svc/GetDataSproc")); 
      foreach (GetData_Result w in result) 
      { 
       Console.WriteLine(w.ID + "\t" + w.WHO_TYPE_NAME + "\t" + w.CREATED_DATE); 
      } 

      Console.Read(); 
     } 
    } 
} 

Ich habe nicht verwenden, um die UriKind.Relative oder irgendetwas anderes, dies zu erschweren.

Wenn ich im Browser zur URL navigiere, sehe ich Daten, aber wenn ich es in meiner Konsolen-App konsumiere, bekomme ich gar nichts.

Hinzufügen der Mischung Tracing:

<system.diagnostics> 
    <sources> 
     <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> 
     <listeners> 
      <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\temp\WebWCFDataService.svclog" /> 
     </listeners> 
     </source> 
    </sources> 
    </system.diagnostics> 

... und die Öffnung mit dem Microsoft Service Trace Viewer, ich sehe zwei idential Warnungen:

Konfiguration Auswertung Kontext nicht gefunden.

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent"> 
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system"> 
<EventID>524312</EventID> 
<Type>3</Type> 
<SubType Name="Warning">0</SubType> 
<Level>4</Level> 
<TimeCreated SystemTime="2012-04-03T14:50:11.8355955Z" /> 
<Source Name="System.ServiceModel" /> 
<Correlation ActivityID="{66f1a241-2613-43dd-be0c-341149e37d30}" /> 
<Execution ProcessName="WebDev.WebServer40" ProcessID="5176" ThreadID="10" /> 
<Channel /> 
<Computer>MyComputer</Computer> 
</System> 
<ApplicationData> 
<TraceData> 
<DataItem> 
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning"> 
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.EvaluationContextNotFound.aspx</TraceIdentifier> 
<Description>Configuration evaluation context not found.</Description> 
<AppDomain>fd28c9cc-1-129779382115645955</AppDomain> 
</TraceRecord> 
</DataItem> 
</TraceData> 
</ApplicationData> 
</E2ETraceEvent> 

Warum bin ich in der Lage Daten aus dem Browser zu sehen, aber nicht, wenn sie in meiner app verbraucht?

- UPDATE -

Ich habe die Microsoft WCF Data Services October 2011 CTP die DataServiceProtocolVersion.V3 ausgesetzt, erstellt (v4.99.2.0) einen neuen Host und Client und verwiesen Microsoft.Data.Services.Client. Nun erhalte die folgenden Fehler auf dem Client, wenn Iterierte in der foreach Schleife versucht:

Es ist eine Art Nichtübereinstimmung zwischen dem Client und dem Dienst. Typ 'ConsoleApplication1.WcfDataServiceOctCTP1.GetDataSproc_Result' ist ein Entitätstyp, aber der Typ in der Antwortnutzlast repräsentiert nicht einen Entitätstyp. Stellen Sie sicher, dass die auf dem Client definierten Typen dem Datenmodell des Service entsprechen, oder aktualisieren Sie die Service-Referenz auf dem -Client.

Ich versuchte das gleiche durch Bezugnahme auf die tatsächliche Einheit - funktioniert gut, so das gleiche Problem.

Antwort

2

Zusammenfassung: Ich möchte eine leistungsstarke WCF-Dienst-DAL (Datenzugriffsschicht) erstellen, die stark typisierte gespeicherte Prozeduren zurückgibt. Ich habe anfangs ein "WCF Data Services" -Projekt verwendet, um dies zu erreichen. Es scheint, als ob es seine Grenzen hat, und nachdem ich performance metrics von verschiedenen ORMs überprüft habe, benutzte ich Dapper für den Datenzugriff in einem grundlegenden WCF-Dienst.

Ich erstellte zuerst das * .edmx-Modell und erstellte das POCO für meinen Sproc.

Als nächstes habe ich eine Basis BaseRepository und MiscDataRepository:

namespace WcfDataService.Repositories 
{ 
    public abstract class BaseRepository 
    { 
     protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId) 
     { 
      dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single(); 
      T newId = (T)identity.Id; 
      setId(newId); 
     } 

     protected static IDbConnection OpenConnection() 
     { 
      IDbConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["PrimaryDBConnectionString"].ConnectionString); 
      connection.Open(); 
      return connection; 
     } 
    } 
} 

namespace WcfDataService.Repositories 
{ 
    public class MiscDataRepository : BaseRepository 
    { 
     public IEnumerable<GetData_Result> SelectAllData() 
     { 
      using (IDbConnection connection = OpenConnection()) 
      { 
       var theData = connection.Query<GetData_Result>("sprocs_GetData", 
        commandType: CommandType.StoredProcedure); 

       return theData; 
      } 
     } 
    } 
} 

Die Serviceklasse:

namespace WcfDataService 
{ 
    public class Service1 : IService1 
    { 
     private MiscDataRepository miscDataRepository; 

     public Service1() 
      : this(new MiscDataRepository()) 
     { 
     } 

     public Service1(MiscDataRepository miscDataRepository) 
     { 
      this.miscDataRepository = miscDataRepository; 
     } 

     public IEnumerable<GetData_Result> GetData() 
     { 
      return miscDataRepository.SelectAllData(); 
     } 
    } 
} 

... und erstellt dann eine einfache Konsolenanwendung, die Daten anzuzeigen:

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Service1Client client = new Service1Client(); 
      IEnumerable<GetData_Result> result = client.GetData(); 
      foreach (GetData_Result d in result) 
      { 
       Console.WriteLine(d.ID + "\t" + d.WHO_TYPE_NAME + "\t" + d.CREATED_DATE); 
      } 
      Console.Read(); 
     } 
    } 
} 

Ich habe dies auch unter Verwendung PetaPOCO, die viel dauerte, erreicht s Zeit zum Einrichten als Dapper - ein paar Zeilen Code:

namespace PetaPocoWcfDataService 
{ 
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together. 
    public class Service1 : IService1 
    { 
     public IEnumerable<GetData_Result> GetData() 
     { 
      var databaseContext = new PetaPoco.Database("PrimaryDBContext"); // using PetaPOCO for data access 
      databaseContext.EnableAutoSelect = false;        // use the sproc to create the select statement 

      return databaseContext.Query<GetData_Result>("exec sproc_GetData"); 
     } 
    } 
} 

Ich mag, wie schnell und einfach es einrichten PetaPOCO war, aber unter Verwendung der Repository-Muster mit Dapper wird viel besser für ein Enterprise-Projekt skalieren.

Es war auch ziemlich einfach, komplexe Objekte direkt aus dem EDMX zu erstellen - für jede gespeicherte Prozedur und dann zu konsumieren.

Zum Beispiel habe ich komplexe Rückgabetyp ProfileDetailsByID_Result basierend auf der sq_mobile_profile_get_by_id Sproc erstellt.

public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID) 
{ 
    using (IDbConnection connection = OpenConnection("DatabaseConnectionString")) 
    { 
     try 
     { 
      var profile = connection.Query<ProfileDetailsByID_Result>("sq_mobile_profile_get_by_id", 
       new { profileid = profileID }, 
       commandType: CommandType.StoredProcedure).FirstOrDefault(); 

      return profile; 
     } 
     catch (Exception ex) 
     { 
      ErrorLogging.Instance.Fatal(ex);  // use singleton for logging 
      return null; 
     } 
    } 
} 

So Dapper mit zusammen mit einigen EDMX Einheiten scheint werden ein schöner schneller Weg, um Dinge zu sein. Ich mag mich vielleicht irren, aber ich bin mir nicht sicher, warum Microsoft das nicht ganz durchdacht hat - keine Unterstützung für komplexe Typen mit OData.

--- UPDATE ---

Also habe ich endlich eine Antwort von Microsoft, wenn ich das Problem mehr als einem Monat angehoben:

Wir Forschung auf diesem und wir getan haben, haben festgestellt, dass die Odata-Client Bibliothek keine komplexen Typen unterstützt. Deshalb bedauere ich Ihnen, Ihnen mitzuteilen, dass es nicht viel gibt, was wir tun können, um es zu lösen.

* Optional: Um eine Lösung für dieses Problem zu erhalten, müssen Sie einen Xml zu Linq Ansatz verwenden, um die komplexen Typen zu erhalten.

Vielen Dank für Ihr Verständnis in dieser Angelegenheit. Bitte lassen Sie mich wissen, wenn Sie irgendwelche Fragen haben. Wenn wir von weiteren Hilfe sein können, lassen Sie es uns bitte wissen.

Mit freundlichen Grüßen,

scheint seltsam.

Verwandte Themen