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:即使我在控制器中添加了引用,当我返回该对象时,它也不会与 Actors 一起返回它。例如,我期望像

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

但相反,Actors 只是空值。

更新:这是自引用异常:

ExceptionMessage=检测到类型为“System.Data.Entity.DynamicProxies.Movie_157D88BDC89E46A7CE4875C2970C7BBFB893972095EFA0745C2261AACC007969”的自引用循环。路径'[0].Actors[0].Movies'。

我设法使用如何解决 Json 序列化循环引用错误中的方法解决此异常?并且只是禁用代理。这解决了问题 #1。但是,当我得到电影时,即使使用 Include("Actors"),它们仍然没有任何 Actor。我可以看到引用已在 LocalDb 的中间表中正确创建。

更新 2

终于想通了,下面回答。

非常感谢!

4

2 回答 2

8

经过大量搜索,我终于能够解决我的问题。最初我尝试将 [ScriptIgnore] 标记添加到电影集,但是我使用的是 EF5 final,它将默认序列化程序更改为 JSON .NET。我终于发现 [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 方法中加载 Actor

此外,我发现对于 Movie 对象的后续 PUT,其中 Actors 集合已更改,我必须手动将每个子 Actor 上的 RelationShip 状态更改为已添加/已删除,以确保相关集合已更新。

于 2012-08-21T03:51:49.953 回答
1

关于您的评论:

此外,我很乐意public virtual ICollection<Movie> Movies { get; set; }从 Actor 类中删除 ,但是这样做,EF 并没有在 LocalDb 中生成中间表。

如果您删除其中一个集合 EF 按照惯例假定您的关系是一对多关系(它没有中间表,但其中一个表中只有一个外键)与具有两个的模型相反EF 创建多对多关系(具有中间表)的集合。

但是,您可以使用 Fluent API 覆盖此约定,并明确告诉 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 回答