从WCF服务操作返回EF4 POCO时引发的CommunicationException

时间:2011-01-05 15:39:17

标签: c# wcf entity-framework-4 ef-code-first

以下代码引发了 System.ServiceModel.CommunicationException 。它正在调用一个名为 Login 的WCF服务操作,它返回一个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();
        }

我一直在调试&寻找数小时试图找出为什么这种情况发生了如何解决它。到目前为止我发现了什么:

  1. 这可能是序列化问题
  2. 当我从 Session 类的 Owner 成员中删除 DataMemberAttribute 时,异常消失,但这意味着它不会被序列化。
  3. 如果有人能对这个问题有所了解,我将不胜感激。

    以下是服务合同的代码& 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; }
    }
    

2 个答案:

答案 0 :(得分:3)

当使用WCF序列化POCO代理时,这是一个已知问题。有一个MSDN walkthough解释了如何使用 System.Data.Objects.ProxyDataContractResolver 来解决它。

基本上,您创建一个名为 ApplyDataContractResolverAttribute 的新类,并将其应用于返回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
    }
}

答案 1 :(得分:0)

我假设'User'类是自定义类?如果是这样,您需要在ServiceContract属性下面添加它:

[KnownType(typeof(User))]

您还需要在User类上设置[DataMember]和[ServiceContract]属性。