Domanda

Come faccio a dire al servizio WCF cosa KnownTypes da utilizzare quando passa i dati al client?

so di poter utilizzare l'attributo [ServiceKnownType], il che rende la chiamata di servizio funzionano bene da un WCF Test Server, ma non riesce ancora dal client. Mi sto perdendo qualcosa qui?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

Il messaggio di errore dal client è:

  

{ "Element   'Http://schemas.datacontract.org/2004/07/BaseClassZ'   contiene i dati da un tipo che mappa   il nome   'Http://schemas.datacontract.org/2004/07/SubClassA'.   Il deserializzatore non è a conoscenza di   qualsiasi tipo che associa a questo nome.   Considerare l'utilizzo di un DataContractResolver   o aggiungere il tipo corrispondente alle   'SubClassA' alla lista dei tipi conosciuti   - per esempio, utilizzando l'attributo KnownTypeAttribute o   di aggiungerlo alla lista dei tipi conosciuti   passato a DataContractSerializer. "}

Serializzare / deserializzazione dell'oggetto sul server WCF utilizzando un DataContractSerializer e una lista di KnownTypes funziona bene.

UPDATE: Sembra che posso ottenere al cliente di leggere l'oggetto correttamente se aggiungo KnownType attribuisce alla classe di base, ma sono ancora alla ricerca di un modo per aggirare questo, se possibile, dal momento che la classe base viene utilizzato per un sacco di oggetti e non voglio avere a modificare gli attributi del KnownType sulla classe di base in qualsiasi momento posso aggiungere un nuovo elemento.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
È stato utile?

Soluzione

Per evitare di scoraggiare il codice del servizio messo i tipi conosciuti nel web.config del servizio:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

Se si vuole farlo con il codice è necessario utilizzare questo attributo sull'interfaccia servizio e non sul metodo di funzionamento, ma io preferirei l'approccio dichiarativo:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}

UPDATE:

Ho messo un href="http://jump.fm/KCEWW" rel="noreferrer"> progetto di esempio che illustra l'uso di web.config per i tipi di configurazione noto che è il mio approccio preferito. E un altro progetto di esempio che dimostra il secondo approccio.


UPDATE 2:

Dopo aver guardato il codice aggiornato con il cliente applicazione Silverlight notiamo la seguente definizione:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Si noti come il metodo BeginGetSingle contiene il tipo noto attributi mentre il metodo BeginGetMany non lo fa. In realtà quegli attributi dovrebbero essere immessi sul la definizione di servizio in modo che gli sguardi di classe come questo.

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Dato che questa è una classe generata automaticamente ci potrebbe essere un bug nel SLsvcUtil.exe e svcutil.exe quanto presenta lo stesso comportamento. Mettendo i noti attributi di tipo sui loro posizione corretta risolve il problema. Il problema è che questa classe è generata automaticamente da uno strumento e se si tenta di rigenerarlo dal WSDL lo farà rovinare di nuovo.

Così sembra che se avete la seguente definizione del servizio:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

E i contratti 3 di dati utilizzati qui sono condivisi tra il client e il server durante l'importazione della definizione del servizio, il metodo che restituisce un insieme non ottiene il tipo noto corretta attributi nel proxy client generato. Forse questo è in base alla progettazione.

Altri suggerimenti

ho passato ore di oggi su ciò che, come meglio posso dire, è esattamente lo stesso problema. La soluzione per me è stato quello di utilizzare il metodo AddGenericResolver dalla libreria ServiceModelEx di Idesign.

NOTA: NET 4.0 necessaria in quanto utilizza DataContractResolver

È possibile trovare su Idesign Download pagina .

Tutto quello che dovevo fare nel mio caso è stato aggiungere la seguente riga di codice:

Client.AddGenericResolver( typeof ( K2Source ) );

Spero che questo ci aiuta a qualcun altro fuori salvare un paio di ore!

E 'possibile trovare maggiori informazioni nel libro "Programmazione WCF Servizi: Mastering WCF e l'Azure AppFabric Service Bus" di Juval Lowy

C'è un altro modo per fare questo. Invece di utilizzare "di riferimento servizio aggiunto" si codifica le classi proxy. E 'un po' più di codifica inizialmente, ma ti dà una più stabile e molto più robusta soluzione. Abbiamo scoperto che questo ci consente di risparmiare tempo nel lungo periodo.

See: http://www.dnrtv.com/default.aspx?showNum= 122

. Nota: questo funziona solo se si ha il controllo sia del server e il client

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top