CommunicationException sollevata quando la restituzione di un EF4 POCO dal funzionamento servizio WCF

StackOverflow https://stackoverflow.com/questions/4605910

Domanda

Il codice seguente solleva un System.ServiceModel.CommunicationException . Si chiama un'operazione di servizio WCF chiamato Accedi , che restituisce un EF4 POCO:

        var client = new AuthServiceReference.AuthServiceClient();

        try
        {
            Console.Write("Trying to logon...");
            var session = client.Login("user", "password"); // throws CommunicationException
            Console.WriteLine("done!");
            Console.WriteLine("Session ID: {0}. Expires {1}", 
                session.Id, session.UtcExpires.ToLocalTime());
        }
        finally
        {
            client.Close();
        }

mi è stato il debug e la ricerca di ore cercando di scoprire perché questo accade e come risolvere il problema. Quello che ho trovato finora:

  1. Questo è probabilmente un problema di serializzazione
  2. Quando rimuovo il DataMemberAttribute dalla Sessione classe di proprietario membro, le scompare eccezione, ma questo significa che non sarà serializzato.

sarei grato se qualcuno potesse fare luce su questo tema.

Di seguito è riportato il codice per il contratto di servizio e le classi POCO:

[ServiceContract]
public interface IAuthService
{
    [OperationContract]
    Session Login(string username, string passwordHash);

    [OperationContract]
    void Logout(Guid sessionId);
}

[DataContract]
public class Session
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public DateTime UtcCreated { get; set; }

    [DataMember]
    public DateTime UtcExpires { get; set; }

    [DataMember] // serializes correctly if commented out
    public virtual User Owner { get; set; }

    public static Session Create(User owner)
    {
        return new Session
        {
            Owner = owner,
            Id = Guid.NewGuid(),
            UtcCreated = DateTime.UtcNow,
            UtcExpires = DateTime.UtcNow.AddDays(1)
        };
    }
}

[DataContract]
public class User
{
    [DataMember]
    public int Id { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string PasswordHash { get; set; }

    [DataMember]
    public string PasswordSalt { get; set; }

    [DataMember]
    public bool IsContributor { get; set; }

    [DataMember]
    public bool IsConfirmed { get; set; }

    [DataMember]
    public bool IsAdmin { get; set; }

    [DataMember]
    public string Email { get; set; }

    [DataMember]
    public virtual ICollection<Post> Posts { get; set; }

    [DataMember]
    public virtual ICollection<Comment> Comments { get; set; }
}
È stato utile?

Soluzione

Si scopre che si tratta di un problema noto durante la serializzazione proxy POCO con WCF. C'è un MSDN WALKTHROUGH che spiega come il lavoro intorno ad esso utilizzando System.Data.Objects.ProxyDataContractResolver .

In sostanza, si crea una nuova classe chiamata ApplyDataContractResolverAttribute e applicarlo ai metodi del servizio di ritorno POCOS:

[ServiceContract]
public interface IAuthService
{
    [OperationContract]
    [ApplyDataContractResolver]
    Session Login(string username, string passwordHash);
}

using System;
using System.Data.Objects;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace WcfExampleBlog.Services
{
    public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
    {
        #region IOperationBehavior Members

        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)
        {
            // Do validation.
        }

        #endregion
    }
}

Altri suggerimenti

sto presumendo che la classe 'User' è una classe personalizzata? Se è così, è necessario aggiungere questo appena sotto l'attributo ServiceContract:

[KnownType(typeof(User))]

Si sarebbe anche bisogno di configurare il [DataMember] e [ServiceContract] attributi della classe utente pure.

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