首页 文章

WCF:序列化和反序列化通用集合

提问于
浏览
20

我有一个拥有通用列表的类Team:

[DataContract(Name = "TeamDTO", IsReference = true)]
public class Team
{
    [DataMember]
    private IList<Person> members = new List<Person>();

    public Team()
    {
        Init();
    }

    private void Init()
    {
        members = new List<Person>();
    }

    [System.Runtime.Serialization.OnDeserializing]
    protected void OnDeserializing(StreamingContext ctx)
    {
        Log("OnDeserializing of Team called");
        Init();
        if (members != null) Log(members.ToString());
    }

    [System.Runtime.Serialization.OnSerializing]
    private void OnSerializing(StreamingContext ctx)
    {
        Log("OnSerializing of Team called");
        if (members != null) Log(members.ToString());
    }

    [System.Runtime.Serialization.OnDeserialized]
    protected void OnDeserialized(StreamingContext ctx)
    {
        Log("OnDeserialized of Team called");
        if (members != null) Log(members.ToString());
    }

    [System.Runtime.Serialization.OnSerialized]
    private void OnSerialized(StreamingContext ctx)
    {
        Log("OnSerialized of Team called");
        Log(members.ToString());
    }

当我在WCF服务中使用此类时,我得到以下日志输出

OnSerializing of Team called    
System.Collections.Generic.List 1[XXX.Person]

OnSerialized of Team called    
System.Collections.Generic.List 1[XXX.Person]

OnDeserializing of Team called    
System.Collections.Generic.List 1[XXX.Person]

OnDeserialized of Team called    
XXX.Person[]

反序列化之后 members 是一个数组,不再是通用列表,虽然字段类型是IList <>(?!)当我尝试通过WCF服务发回这个对象时,我得到了日志输出

OnSerializing of Team called
XXX.Person[]

在此之后,我的单元测试与System.ExecutionEngineException崩溃,这意味着WCF服务无法序列化该数组 . (也许是因为它期待一个IList <>)

所以,我的问题是:有人知道为什么反序列化后我的IList <>的类型是一个数组,以及为什么我不能在那之后再序列化我的Team对象?

谢谢

4 回答

  • 22

    你遇到了一个 DataContractSerializer 陷阱 .

    Fix: 将您的私人会员声明更改为:

    [DataMember]
    private List<Person> members = new List<Person>();
    

    或者将属性更改为:

    [DataMember()]
    public IList<Person> Feedback {
        get { return m_Feedback; }
        set {
            if ((value != null)) {
                m_Feedback = new List<Person>(value);
    
            } else {
                m_Feedback = new List<Person>();
            }
        }
    }
    

    它会工作 . Microsoft Connect错误是here

    使用 IList<T> DataMember反序列化对象,然后尝试再次序列化同一实例时,会发生此问题 .

    如果你想看到一些很酷的东西:

    using System;
    using System.Collections.Generic;
    
    class TestArrayAncestry
    {
        static void Main(string[] args)
        {
            int[] values = new[] { 1, 2, 3 };        
            Console.WriteLine("int[] is IList<int>: {0}", values is IList<int>);
        }
    }
    

    它将打印 int[] is IList<int>: True .

    我怀疑这可能是你看到它在反序列化后作为数组返回的原因,但它非常不直观 .

    如果你在数组的 IList<int> 上调用Add()方法,它会抛出 NotSupportedException .

    其中一个.NET怪癖 .

  • 1

    我通过LINQ从数据库传输IList读取时出现此错误 . WCF在Windows Server 2008 x64上的IIS 7中托管 .

    应用程序池崩溃,没有任何警告 .

    [ServiceBehavior]
    public class Webservice : IWebservice
    {
    
        public IList<object> GetObjects()
        {
            return Database.Instance.GetObjects();
        }
    }
    

    它不是完全相同的问题,但可能有相同的原因 .

    分辨率是安装MS修补程序KB973110 http://support.microsoft.com/kb/971030/en-us

  • 1

    听起来您的WCF服务引用正在创建代理类而不是使用现有类型 . 代理类只能使用简单数组,而不能使用任何.NET特定类型,如通用List .

    要避免此代理类转换,请在“添加服务引用”屏幕中单击“高级”按钮,然后确保选中“在引用的程序集中重用类型” . 这将确保在序列化和反序列化对象时使用现有类(使用通用List) .

  • 2

    直接从我的博客 . 我希望它会有所帮助:

    我最近遇到了一个问题,我们在ASP.net MVC应用程序中使用WCF服务并使用自定义模型 Binders . 当我们序列化我们的IList时,一切都工作得很好 . IList默认情况下会序列化为数组 . 我最终使用Reflection将数组转换回ILists并在自定义模型 Binders 中调用以下方法 . 方法如下:

    public object ConvertArraysToIList(object returnObj)    
    {
    
    if (returnObj != null)
    {
        var allProps = returnObj.GetType().GetProperties().Where(p => p.PropertyType.IsPublic 
            && p.PropertyType.IsGenericType 
            && p.PropertyType.Name==typeof(IList<>).Name).ToList();
    
        foreach (var prop in allProps)
        {
            var value = prop.GetValue(returnObj,null);
            //set the current property to a new instance of the IList<>
            var arr=(System.Array)value; 
            Type listType=null;
    
            if(arr!=null)
            {
                listType= arr.GetType().GetElementType();
            }
    
            //create an empty list of the specific type
            var listInstance = typeof(List<>)
              .MakeGenericType(listType)
              .GetConstructor(Type.EmptyTypes)
              .Invoke(null);
    
            foreach (var currentValue in arr)
            {
                listInstance.GetType().GetMethod("Add").Invoke(listInstance, new[] { currentValue });
            }
    
            prop.SetValue(returnObj, listInstance, null);
        }
    }
    
    return returnObj;
    }
    

相关问题