4

.NET クライアント アプリを使用して、asp .net Web API JSON を介してオブジェクト B のコレクションを含むオブジェクト A を投稿し、LocalDB データベースを作成してデータを保存しようとしています。次に、オブジェクト A を再度取得します。

アプリには 3 つのプロジェクトが含まれています。asp .net mvc 4 Web API プロジェクト、.Net コンソール アプリ、および .Net クラス ライブラリ。asp .net アプリとコンソール アプリの両方が、オブジェクト A と B のクラス定義を含むクラス ライブラリを参照します。

クラス ライブラリ:

public class Actor
{
    public Actor()
    {
        this.Movies = new HashSet<Movie>();
    }

    public int ActorID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Movie> Movies { get; set; }
}

public class Movie
{
    public Movie()
    {
        this.Actors = new HashSet<Actor>();
    }

    public int MovieID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Actor> Actors { get; set; }
}

コンソール アプリ:

Movie movie = new Movie()
{
    Name = "Dr. No"
};

Actor actor = new Actor()
{
    Name = "Sean Connery"
};

movie.Actors.Add(actor);

using (HttpClient client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:3654");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var response = client.PostAsJsonAsync<Movie>("api/movies", movie).Result;
    response.EnsureSuccessStatusCode();

    response = client.GetAsync("api/movies/1").Result;
    response.EnsureSuccessStatusCode();

    Movie newMovie = response.Content.ReadAsAsync<Movie>().Result;
}

asp .net mvc DbContext:

public class MediaContext : DbContext
{
    public MediaContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }

    public DbSet<Movie> Movies { get; set; }
    public DbSet<Actor> Actors { get; set; }
}

問題 #1: JSON は循環参照を好まないようですが、コレクションを両方のオブジェクトに追加しないと、EF5 は参照を保持するための MoviesActors テーブルを作成しません。

問題 #2:コントローラーに参照を追加しても、そのオブジェクトを返すときに、アクタと一緒に返されません。たとえば、次のようなものを期待していました

Movie
{
   MovieID = "1",
   Name = "???",
   Actors[] = { 1 }
}

しかし代わりに、Actors は単に null です。

更新:自己参照例外は次のとおりです。

ExceptionMessage = タイプ 'System.Data.Entity.DynamicProxies.Movie_157D88BDC89E46A7CE4875C2970C7BBFB893972095EFA0745C2261AACC007969' で自己参照ループが検出されました。パス '[0].Actors[0].Movies'.

Json シリアル化循環参照エラーをどのように解決しましたか?の方法を使用して、この例外を回避することができました。プロキシを無効にするだけです。これで問題 1 は解決します。ただし、ムービーを取得すると、Include("Actors") を使用しても、アクターなしで返されます。LocalDb の中間テーブルに参照が正しく作成されていることがわかります。

更新 2

最終的にこれを理解しました。以下に答えてください。

本当にありがとう!

4

2 に答える 2

8

多くの検索の後、私は最終的に私の問題を解決することができました. もともと、Movies セットに [ScriptIgnore] タグを追加しようとしましたが、デフォルトのシリアライザーを JSON .NET に変更する EF5 final を使用していました。最終的に [ScriptIgnore] が機能しないことがわかり、[JsonIgnore] を次のように設定する必要がありました。

public class Actor
{
    public int ActorID { get; set; }
    public string Name { get; set; }

    [JsonIgnore]
    public virtual ICollection<Movie> Movies { get; set; }
}

public class Movie
{
    public int MovieID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Actor> Actors { get; set; }
}

Get メソッドで .Include("Actors") 熱心な読み込みと組み合わせることで、最終的に問題が解決しました。

要約すると、次のことが必要でした。

  1. 両方のオブジェクトに仮想 ICollection 参照を設定して、中間テーブルを作成します。
  2. [JsonIgnore] を Movies コレクション参照に追加して、循環参照を解決します。
  3. 500 エラーを解決するには、プロキシをオフにします。
  4. .Include("Actors") を使用して、GetMovies/GetMovie メソッドでアクターをイーガー ロードします。

さらに、Actors コレクションが変更された Movie オブジェクトの後続の PUT で、関連するコレクションが更新されたことを確認するために、各子アクタの RelationShip 状態を手動で追加/削除のいずれかに変更する必要があることがわかりました。

于 2012-08-21T03:51:49.953 に答える
1

あなたのコメントについて:

また、public virtual ICollection<Movie> Movies { get; set; }Actor クラスから を完全に削除できれば幸いですが、そうすると、EF は LocalDb に中間テーブルを生成しませんでした。

コレクションの 1 つを削除すると、EF は慣例により、2 つのモデルとは対照的に、関係が 1 対多の関係 (中間テーブルはなく、テーブルの 1 つに外部キーのみ) であると想定します。 EF が多対多の関係 (中間テーブルを持つ) を作成するコレクション。

ただし、Fluent API を使用してこの規則をオーバーライドし、残りのコレクションが 1 対多ではなく多対多の関係に属していることを EF に明示的に伝えることができます。このモデルを持っている...

public class Actor
{
    //...
    public int ActorID { get; set; }
    public string Name { get; set; }
}

public class Movie
{
    //...
    public int MovieID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Actor> Actors { get; set; }
}

...多対多の関係を次のように定義できます。

public class MediaContext : DbContext
{
    //...
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Movie>()
            .HasMany(m => m.Actors)
            .WithMany()
            .Map(a =>
            {
                a.MapLeftKey("MovieID");
                a.MapRightKey("ActorID");
                a.ToTable("MovieActors"); // intermediate table
            });
    }
}
于 2012-08-19T12:00:46.583 に答える