7

ドメイン モデルをシリアル化しようとしていたときに、動的プロキシを POCO に変換する必要があるという問題に遭遇しました。私が遭遇した問題は、モデル内の仮想プロパティを介して循環参照が存在することでした。[ScriptIgnore]シリアライザーがこれらのプロパティを解析しないようにするために使用しようとしましたが、それでも解析されます。これは、オブジェクトが動的プロキシであり、パーサーが入る原因となるプロパティにまだいくつかの残りがあるためだと思います(これにより、再帰エラー「循環参照」が発生します-再帰を3ステップに制限しようとしましたが、取得しました「再帰ステップを超えました」のエラー)。

オブジェクトを動的プロキシから POCO に変換してシリアライズできるようにするにはどうすればよいですか?

編集:簡単な例

public class One : BaseViewModel
{
    public int OneId { get; set; }
    public virtual ICollection<Two> Two { get; set; }
}

public class Two
{
    public int TwoId { get; set; }
    public int OneId { get; set; }
    [ScriptIgnore]
    public virtual One One { get; set; }
}

public abstract class BaseViewModel
{
    public string AsJson()
    {
        var serializer = new JavaScriptSerializer();
        return serializer.Serialize(this);
    }
}
4

3 に答える 3

4

Jim の Automapper ソリューションの代替として、POCO プロキシを同じ POCO のインスタンスに「マッピング」(浅いコピー) することに成功しました。これを行う簡単な方法は、POCO を生成するテンプレート ファイルを変更して ToSerializable() メソッドを含めることです。そのため、POCO クラスは次のようになります。

public partial class cfgCountry
    {
        public cfgCountry()
        {
            this.People = new HashSet<Person>();
        }

        [Key]
        public int CountryID { get; set; }
        public string Name { get; set; }
        public int Ordinal { get; set; }

        public virtual ICollection<Person> People { get; set; }

        public cfgCountry ToSerializable()
        {
            return new cfgCountry()
            {
            CountryID = this.CountryID,
            Name = this.Name,
            Ordinal = this.Ordinal,
            };
        }
    }

ToSerializable 関数を作成するために POCO テンプレート (tt) ファイルに追加した関数を次に示します (このような醜い構文)。

<#+
void WriteToSerializableMethod (CodeGenerationTools code, IEnumerable<EdmProperty> primitiveProperties, EntityType entity)
{

#>
public <#=code.Escape(entity)#> ToSerializable()
{
    return new <#=code.Escape(entity)#>()
    {
<#+
    foreach(var edmProperty in primitiveProperties)
    {
#>
    <#=edmProperty.Name#> = this.<#=edmProperty.Name#>,
<#+
    }
#>
    };
}
<#+
}
#>

結果がシリアライズされると予想されるときはいつでも foo 自体ではなく foo.ToSerializable() を返すことを忘れないでください。

于 2012-12-20T16:24:55.583 に答える
3

トラビス、ここであなたが受け入れた答えがあることは知っていますが、これについて少し横方向の考え方を伝えたいと思いました. 最近、非常によく似た問題に直面し、何も機能せず、すべての [scriptignore] 属性などを試しました。

最終的にうまくいったのは、Automapper を使用して、プロキシ オブジェクトからスリム化された poco オブジェクトへのマップを作成することでした。これにより、2分以内にすべての問題が解決しました。これはすべて、プロキシにボールをプレーさせようとするときに36時間の包囲精神が優勢になった後のことです-shish :-)

暫定的に考えるべき別のアプローチ。

[編集] - Automapper の使用 (これは automapper を参照する小さなテスト アプリです)

参照: http://automapper.codeplex.com/

nuget: インストール パッケージ AutoMapper

クラス:

public sealed class One : BaseViewModel
{
    // init collection in ctor as not using EF in test
    // no requirement in real app
    public One()
    {
        Two = new Collection<Two>();
    }
    public int OneId { get; set; }
    public ICollection<Two> Two { get; set; }
}

public class Two
{
    public int TwoId { get; set; }
    public int OneId { get; set; }
    [ScriptIgnore]
    public virtual One One { get; set; }
}

public abstract class BaseViewModel
{
    public string AsJson()
    {
        var serializer = new JavaScriptSerializer();
        return serializer.Serialize(this);
    }
}

public class OnePoco  : BaseViewModel
{
    public int OneId { get; set; }
    public virtual ICollection<TwoPoco> Two { get; set; }
}

public class TwoPoco
{
    public int TwoId { get; set; }
    public int OneId { get; set; }
}

テスト コントローラー コード:

public ActionResult Index()
{
    // pretend this is your base proxy object
    One oneProxy = new One { OneId = 1 };
    // add a few collection items
    oneProxy.Two.Add(new Two() { OneId = 1, TwoId = 1, One = oneProxy});
    oneProxy.Two.Add(new Two() { OneId = 1, TwoId = 2, One = oneProxy});

    // create a mapping (this should go in either global.asax 
    // or in an app_start class)
    AutoMapper.Mapper.CreateMap<One, OnePoco>();
    AutoMapper.Mapper.CreateMap<Two, TwoPoco>();

    // do the mapping- bingo, check out the asjson now
    // i.e. oneMapped.AsJson
    var oneMapped = AutoMapper.Mapper.Map<One, OnePoco>(oneProxy);

    return View(oneMapped);
}

これを試してみて、どうやってうまくいくか見てください。確かに私にとってはうまくいきました。「地球」が動きました:)

于 2012-07-05T20:01:41.507 に答える
3

これは既知の問題です

派生クラスに伝達されていなかった ScriptIgnoreAttribute の問題を修正しました。POCO プロキシ タイプは、ユーザーが提供する POCO クラスから派生することによって作成されるため、JavaScriptSerializer は、再現にある [ScriptIgnore] 属性を認識できませんでした。

この修正は、.NET 4.5 の次のプレビュー リリースには含まれません。

(したがって、次のプレビュー リリースまたは最終リリースを待つ必要があると思われます)

http://connect.microsoft.com/VisualStudio/feedback/details/723060/ef-4-2-code-first-property-attributes-not-honoured

これは.NET 4.5で修正されています

その問題に関するコメントから、現在のバージョンの JSON.Net を使用している場合は、 ScriptIgnoreAttribute の代わりに NonSerializedAttribute を使用して回避できるようです。

于 2012-07-05T18:17:22.037 に答える