Ho 3 progetti:
L'app WPF ha un riferimento al servizio del progetto WCF. Il problema che stavo avendo è quando stavo provando qualcosa del genere:
public List<DbTable> GetItems()
{
try
{
IQueryable<DbTable> items;
using (var dbContext = new MyEntities())
{
items = dbContext.Items.Select(a => a);
return items.ToList();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return null;
}
}
Il servizio WCF non ha restituito alcun risultato e stavo ottenendo il messaggio di errore HTTP non impostato correttamente.
Quindi dopo aver letto alcuni ho deciso di creare classi POCO nel servizio WCF e questo ha funzionato.
using (var dbContext = new MyEntities())
{
var items = dbContext.Items.Select(a =>
new MyPocoClass
{
ItemId = a.ItemId,
Name = a.Name
});
return items.ToList();
}
La mia domanda è: non posso semplicemente restituire l'oggetto framework entità senza creare classi POCO per traghettare i dati avanti e indietro? Se posso, mi manca un passaggio o qualcosa del genere?
Ecco il messaggio quando tento di restituire un oggetto EF
come return dbContext.Items.ToList();
L'unico modo per farlo funzionare è usare una classe POCO.
Aggiornare:
Ecco il mio file di configurazione del servizio:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service name="MyProject.WebService.MyWebService">
<endpoint address="" binding="basicHttpBinding" contract="MyProject.WebService.IMyWebService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/Design_Time_Addresses/MyProject.WebService/MyWebService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
E il mio cliente:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyWebService" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8733/Design_Time_Addresses/MyProject.WebService/MyWebService/" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyWebService" contract="ServiceReference.IMyWebService" name="BasicHttpBinding_IMyWebService" />
</client>
</system.serviceModel>
</configuration>
Re-Update-
Inner Exception:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
Questo problema si verifica perché Entity Framework restituisce oggetti proxy dinamici con tipi derivati dai tipi originali e creati in fase di runtime. Per risolvere questo, dovresti creare un attributo ProxyDataContractResolver :
public class ApplyProxyDataContractResolverAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(
OperationDescription description,
BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(
OperationDescription description,
ClientOperation proxy)
{
var dataContractSerializerOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
}
public void ApplyDispatchBehavior(
OperationDescription description,
DispatchOperation dispatch)
{
var dataContractSerializerOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver =
new ProxyDataContractResolver();
}
public void Validate(OperationDescription description)
{
}
}
Quindi applica questo attributo al tuo metodo di servizio, ad esempio:
[ApplyProxyDataContractResolver]
public List<DbTable> GetItems()
{
// query to database with EF here
}
Tuttavia, il mio consiglio è di usare la concezione DTO. Usando DTOs puoi dividere i tuoi modelli di grasso in alcuni piccoli e trasferire solo le informazioni che i tuoi clienti hanno bisogno. Ciò rende la tua architettura più gestibile e riduce la quantità di trasferimento di dati attraverso la rete.
Penso che dovresti mantenere il livello DTO (POCOs) perché quando il tuo modello diventa più complesso, il caricamento lento causa tutti i tipi di problemi se provi a restituire le entità direttamente (a seconda della struttura dei dati). Ad esempio, se hai una configurazione di relazioni abbastanza standard come:
eccetera.
public class Client
{
public int ClientId { get; set; }
public string ClientName { get; set; }
public IList<Contact> Contacts { get; set; }
}
public class Contacts
{
public int ContactId { get; set; }
public string ContactName { get; set; }
public IList<Order> Order { get; set; }
}
Quando si tenta e si passa un oggetto Client oltre il limite WCF, il serializzatore toccherà ciascuna proprietà nell'entità e caricherà le entità correlate (contatti, ordini, elementi pubblicitari, archivi magazzino, foto).
Ciò può causare un'enorme cascata di caricamento dei dati, oppure fallire a causa di riferimenti circolari.
Se disabiliti il Caricamento Lazy, il tuo oggetto è pieno di null, ma ci sono molti altri motivi per non passare le entità in giro, come non richiedere una ricostruzione delle applicazioni client se cambi la struttura del tuo database ecc.
Se trovi che la mappatura DTO -> Entità -> DTO richiede molto tempo, dai un'occhiata a AutoMapper, che può farlo.
Trovo utile scrivere un test unitario per questa mappatura con AutoFixture e DeepEqual.