2012-04-03 15 views
5

Utilizzando WCF Data Services (e l'ultimo Entity Framework), voglio restituire i dati da una stored procedure. I campi sProc restituiti non corrispondono 1: 1 qualsiasi entità nel mio db, quindi creare un nuovo tipo complesso per esso nel modello edmx (piuttosto che attaccare un'entità esistente):Come consumare un oggetto complesso da una sproc usando WCF Data Services/OData?

  1. Fare clic con il *. modello edmx/Aggiungi/funzione di importazione
  2. Selezionare lo sproc (restituisce tre campi) - GetData
  3. fare clic su Informazioni Colonna
  4. Aggiungere la funzione di importazione Nome: GetData
  5. Fare clic su Crea nuovo tipo complesso - GetData_Result
.515.053.691,36321 milioni

Nel servizio, definisco:

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

ho creato una console app rapido per testare, e ha aggiunto un riferimento a System.Data.Services e System.Data.Services.Client - questo dopo l'esecuzione Install-Package EntityFramework -Pre, ma le versioni su librerie sono 4.0 e non 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(); 
     } 
    } 
} 

non ho usato la UriKind.Relative o qualsiasi altra cosa a complicare questo.

Quando navigo nel browser verso l'URL, vedo i dati, ma quando li consumo nella mia app per console, non ottengo nulla.

Aggiunta di tracciamento al mix:

<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> 

... e l'apertura utilizzando il Servizio Trace Viewer Microsoft, vedo due avvertimenti idential:

configurazione contesto di valutazione non è stato trovato.

<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> 

Allora perché sono in grado di vedere i dati dal browser, ma non se consumato nella mia app?

- AGGIORNAMENTO -

ho scaricato il Microsoft WCF Data Services October 2011 CTP che ha svelato DataServiceProtocolVersion.V3, ha creato un nuovo host e client e si fa riferimento Microsoft.Data.Services.Client (v4.99.2.0). Ora ottenere il seguente errore sul client quando si cerca iterate nel ciclo foreach:

V'è un tipo non corrispondente tra il client e il servizio. Digitare 'ConsoleApplication1.WcfDataServiceOctCTP1.GetDataSproc_Result' è un tipo di entità , ma il tipo nel payload di risposta non rappresenta un tipo di entità. Assicurarsi che i tipi definiti sul client corrispondano a il modello di dati del servizio o aggiornare il riferimento del servizio sul client .

Ho provato la stessa cosa facendo riferimento all'entità reale - funziona bene, quindi lo stesso problema.

risposta

2

Riepilogo: Desidero creare un servizio DAL con prestazioni elevate DAL (livello di accesso ai dati) che restituisca stored procedure fortemente tipizzate. Inizialmente ho usato un progetto "WCF Data Services" per realizzare questo. Sembra che abbia i suoi limiti, e dopo aver esaminato performance metrics di diversi ORM, ho finito per usare Dapper per l'accesso ai dati all'interno di un servizio WCF di base.

Ho prima creato il modello * .edmx e creato il POCO per il mio sproc.

Successivamente, ho creato un BaseRepository base e 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; 
      } 
     } 
    } 
} 

La classe di servizio:

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(); 
     } 
    } 
} 

... e poi creato una semplice applicazione console per visualizzare i dati:

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(); 
     } 
    } 
} 

Ho realizzato anche questo utilizzando PetaPOCO, che ha richiesto molto s il tempo di messa a punto di Dapper - un paio di righe di codice:

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"); 
     } 
    } 
} 

mi piace come semplice e veloce è stato per l'installazione PetaPOCO, ma utilizzando il modello repository con Dapper scalerà molto meglio per un progetto dell'organizzazione.

Era anche abbastanza semplice creare oggetti complessi direttamente da EDMX - per qualsiasi stored procedure, quindi consumarli.

Ad esempio, ho creato il tipo di ritorno di tipo complesso chiamato ProfileDetailsByID_Result in base allo sproc di sq_mobile_profile_get_by_id.

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; 
     } 
    } 
} 

Quindi usare Dapper insieme ad alcune entità EDMX sembra essere un bel modo veloce per far funzionare le cose. Potrei sbagliarmi, ma non sono sicuro del motivo per cui Microsoft non ci abbia pensato fino in fondo - nessun supporto per i tipi complessi con OData.

--- UPDATE ---

Così ho finalmente ricevuto una risposta da Microsoft, quando ho sollevato la questione nel corso di un mese fa:

Abbiamo fatto delle ricerche su questo e noi hanno rilevato che la libreria del client Odata non supporta tipi complessi. Pertanto, mi dispiace informare lo che non c'è molto che possiamo fare per risolverlo.

* Facoltativo: per ottenere una soluzione per questo problema, è necessario utilizzare un tipo di approccio da Xml a Linq per ottenere i tipi complessi.

Grazie mille per la vostra comprensione in questa materia. Per favore, fai sapere allo se hai qualche domanda. Se possiamo essere di ulteriore aiuto , faccelo sapere.

Con i migliori saluti,

sembra strano.

Problemi correlati