19

一般的なリストを保持するクラスチームがあります。

[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配列であり、フィールドタイプはILi​​st <>(?!)ですが、ジェネリックリストではなくなりました。このオブジェクトをWCFサービス経由で返送しようとすると、ログ出力が表示されます。

OnSerializing of Team called
XXX.Person[]

この後、ユニットテストがSystem.ExecutionEngineExceptionでクラッシュします。これは、WCFサービスがアレイをシリアル化できないことを意味します。(おそらくIList <>を期待していたため)

だから、私の質問は次のとおりです。IList<>のタイプが逆シリアル化後に配列になる理由と、その後チームオブジェクトをシリアル化できなくなる理由を誰かが知っていますか?

ありがとう

4

4 に答える 4

21

落とし穴の 1 つに遭遇し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 のバグはこちら

この問題は、DataMember を使用してオブジェクトを逆シリアルIList<T>化し、同じインスタンスを再度シリアル化しようとすると発生します。

クールなものを見たい場合:

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 つです。

于 2010-04-05T08:48:17.553 に答える
2

LINQを介してデータベースから読み取ったIListを転送しているときに、このエラーが発生しました。WCFは、Windows Server2008x64上のIIS7でホストされていました。

アプリプールは警告なしでクラッシュしました。

[ServiceBehavior]
public class Webservice : IWebservice
{

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

それはまったく同じ問題ではありませんが、同じ原因がある可能性があります。

の解決策は、MSホットフィックスKB973110http://support.microsoft.com/kb/971030/en-usをインストールすることでした

于 2010-08-26T09:25:15.360 に答える
1

WCF サービス参照が、既存の型を使用するのではなく、プロキシ クラスを作成しているようです。プロキシ クラスは単純な配列のみを使用でき、一般的な List のような .NET 固有の型は使用できません。

このプロキシ クラスの変換を回避するには、[サービス参照の追加] 画面で [詳細設定] ボタンをクリックし、[参照されたアセンブリで型を再利用する] がオンになっていることを確認します。これにより、オブジェクトをシリアル化および逆シリアル化するときに、既存のクラス (ジェネリック List を持つ) が使用されることが保証されます。

于 2010-04-01T16:57:42.487 に答える
1

ブログからそのまま引用。私はそれが役立つことを願っています:

最近、ASP.net MVC アプリで WCF サービスを使用し、カスタム モデル バインダーを使用しているという問題に遭遇しました。IList をシリアル化していたときの抜粋は、すべて正常に機能しました。デフォルトでは、IList は配列にシリアライズされます。最終的に、リフレクションを使用して配列を 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;
}
于 2012-05-18T16:01:44.780 に答える