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

发布于 2024-08-27 09:10:20 字数 1912 浏览 3 评论 0原文

我有一个包含通用列表的 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<> 类型是这样的?是反序列化后的数组,为什么此后我无法再序列化我的团队对象?

谢谢

I have a class Team that holds a generic list:

[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());
    }

When I use this class in a WCF service, I get following log output

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[]

After the deserialization members is an Array and no longer a generic list although the field type is IList<> (?!)
When I try to send this object back over the WCF service I get the log output

OnSerializing of Team called
XXX.Person[]

After this my unit test crashes with a System.ExecutionEngineException, which means the WCF service is not able to serialize the array. (maybe because it expected a IList<>)

So, my question is: Does anybody know why the type of my IList<> is an array after deserializing and why I can't serialize my Team object any longer after that?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心安伴我暖 2024-09-03 09:10:20

您遇到了 DataContractSerializer 陷阱之一。

修复:将您的私有成员声明更改为:

[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 错误为 此处

当您使用 IList 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: True

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

如果您对数组的 IList 调用 Add() 方法,则会抛出 NotSupportedException

.NET 的怪癖之一。

You've run into one of the DataContractSerializer gotchas.

Fix: Change your private member declaration to:

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

OR change the property to:

[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>();
        }
    }
}

And it will work. The Microsoft Connect bug is here

This problem occurs when you deserialize an object with an IList<T> DataMember and then try to serialize the same instance again.

If you want to see something cool:

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>);
    }
}

It will print int[] is IList<int>: True.

I suspect this is possibly the reason you see it come back as an array after deserialization, but it is quite non-intuitive.

If you call the Add() method on the IList<int> of the array though, it throws NotSupportedException.

One of those .NET quirks.

谢绝鈎搭 2024-09-03 09:10:20

我在通过 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/zh-cn

I got this error while transporting an IList read from a database via LINQ. The WCF was hosted in IIS 7 on a Windows Server 2008 x64.

The app pool crashed with no warnings.

[ServiceBehavior]
public class Webservice : IWebservice
{

    public IList<object> GetObjects()
    {
        return Database.Instance.GetObjects();
    }
}

Its not exactly the same problem but may have the same cause.

The resolution for was to install MS hotfix KB973110 http://support.microsoft.com/kb/971030/en-us

不必你懂 2024-09-03 09:10:20

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

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

It sounds like your WCF service reference is creating a proxy class rather than using the existing type. Proxy classes can only use simple arrays and not any .NET specific types like the generic List.

To avoid this proxy class conversion, in the Add Service Reference screen, click the Advanced button, and then make sure "Reuse types in referenced assemblies" is checked. This will ensure that the existing class (with the generic List) is used when serializing and deserializing the object.

苏大泽ㄣ 2024-09-03 09:10:20

直接取自我的博客。我希望这会有所帮助:

我最近遇到了一个问题,我们在 ASP.net MVC 应用程序中使用 WCF 服务并使用自定义模型绑定器。当我们序列化 IList 时,一切都运行良好摘录。默认情况下,IList 被序列化为数组。我最终使用 Reflection 将数组转换回 IList,并在自定义模型绑定器中调用以下方法。方法如下:

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;
}

Taken straight from my blog. i hope it will be helpful:

I recently ran into an issue where we were consuming a WCF service and using a custom model binder in our ASP.net MVC app. Everything worked fine excerpt when we were serializing our ILists. IList gets serialized to arrays by default. I ended up converting our the arrays back to ILists using Reflection and calling the following method in the custom model binder. Here is how method looks like:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文