Entity Framework - ICollection 属性 Always Null - getter setter 烦恼?

Entity Framework - ICollection Property Always Null - getter setter troubles?

我有一个 .net core 2.0 项目使用 .netcore identity' . I have a classApplicationUser'(又名所有者)和 3 其他 class 媒体对象 Videos, Images, and Audio ("Clips")。到目前为止一切都很好。我碰壁的地方是当我尝试重构时,而不是不得不撞到数据库并说类似的话:

user=_usermanager.getuserasync(User)
images= _context.Images.Where(i=>i.i.Owner.username== user.username)

我希望能够通过简单地说

来访问相同的内容
user.images

希望我可以这样说:'foreach(User.Clips 中的音频剪辑){//做 hokeypokee//}

所以我修改了我的 ApplicationUser class 以便为我的每个媒体 class 包含一个 ICollection<Type>。无论我做什么,但是当我访问 user.images 等时,我总是得到 null。(也尝试了虚拟导航道具)

而不是坐在这里解释我的想法(我没有用 getter 和 setter 做什么?)这会令人困惑我想我只是让你们告诉我什么方向,我应该进去,因为我现在已经陷入了很多兔子洞,试图弄清楚这一点。 Entity framework 似乎只是我没有做的事情......我什至无法弄清楚(如果不在某个地方的 EF 中)setter 最初是如何定义的,这意味着它如何知道哪些图像属于用户无需查询数据库以查看哪些图像具有如此多的用户...无论如何,我输入的越多,我就越迷惑自己,哈哈。

用户Class:

public class ApplicationUser : IdentityUser, ITraits
{
    public int Age { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public Color EyeColor { get; set; }
    public Color HairColor { get; set; }
    public int Height { get; set; }
    public Race Race { get; set; }
    public CuckRole CuckRole { get; set; }
    public BiologicalSex BiologicalSex { get; set; }
    public Sexuality Sexuality { get; set; }
    public Color SkinColor { get; set; }
    public int Weight { get; set; }
    public DateTime BirthDay { get; set; }
    public ICollection<Image> Images { get; set; }
    public ICollection<Audio> Clips { get; set; }
    public ICollection<Video> Videos { get; set; }

    public bool Equals(Traits other) => throw new NotImplementedException();

}

然后是我的媒体 class(只有一个完全相同,只是名称不同)

public class Audio 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
    public ApplicationUser Owner { get; set; }



    //Constructors//
    public Audio()
    {

    }


    public Audio(string path,ApplicationUser owner)
    {
    Path = path;
    Owner = owner;
    }



}

编辑

好的,所以我尽力实现建议的代码,到目前为止,这就是我想出的:

媒体Class:

public class Audio 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
    public ApplicationUser Owner { get; set; }
    public string OwnerId { get; set; }




    //Constructors//
    public Audio()
    {

    }


    public Audio(string path,ApplicationUser owner)
    {
    Path = path;
    Owner = owner;
    }



}

我的 ApplicationUser Class 现在看起来像这样:

public class ApplicationUser : IdentityUser, ITraits
{
    public int Age { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public Color EyeColor { get; set; }
    public Color HairColor { get; set; }
    public int Height { get; set; }
    public Race Race { get; set; }
    public CuckRole CuckRole { get; set; }
    public BiologicalSex BiologicalSex { get; set; }
    public Sexuality Sexuality { get; set; }
    public Color SkinColor { get; set; }
    public int Weight { get; set; }
    public DateTime BirthDay { get; set; }
    public ICollection<Image> Images { get; set; }
    public ICollection<Audio> Clips { get; set; }
    public ICollection<Video> Videos { get; set; }





    public bool Equals(Traits other) => throw new NotImplementedException();

}

最后是我的 DBContext :

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    public DbSet<Cucklist.Models.Image> Images { get; set; }
    public DbSet<Cucklist.Models.Video> Videos { get; set; }
    public DbSet<Cucklist.Models.Audio> Clips { get; set; }
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<Image>().ToTable("Image")
                               .HasOne(Image => Image.Owner)
                               .WithMany(Owner => Owner.Images)
                               .HasForeignKey(Image => Image.OwnerId);
        builder.Entity<Video>().ToTable("Video")
                               .HasOne(Video => Video.Owner)
                               .WithMany(Owner => Owner.Videos)
                               .HasForeignKey(Video =>Video.OwnerId );
        builder.Entity<Audio>().ToTable("Clips")
                               .HasOne(Audio => Audio.Owner)
                               .WithMany(Owner => Owner.Clips)
                               .HasForeignKey(Audio =>Audio.OwnerId );
    }




}

遗憾的是我在访问user.media(图片、视频等)时仍然得到一个空值

在您的 DbContext-class 中,您可以覆盖 OnModelCreating 方法并在此处配置您的关系。 有一些更简洁的方法,但这可能是一个好的开始。

protected override void OnModelCreationg(DbModelBuilder modelBuilder)
{
    // on-to-many relation example
    modelBuilder.Entity<Audio>()
        .HasOptional(audio => audio.Owner)
        .WithMany(owner => owner.Clips)
        .HasForeignKey(audio => audio.OwnerId) // this one is missing in your example
        .WillCascadeOnDelete(false); // maybe true? depending on what should happen if the user gets deleted
}

您需要告诉 EF 如何管理您的关系 - 您将需要一个外键 (Audio.OwnerId)。

另一个需要进一步调查的 key-word 可能是 ObjectEntityConfiguration

大家好,再次感谢您的帮助 (@nilsK) - 所以最终的答案是 entity framework "Thing".

在 v2.1 之前,Entity Framework Core 基本上不支持延迟加载,现在处于预览状态(不确定我是否错过了确保指定它是 entity framework 核心但我猜所有假设都是因为它是一个 .net 核心项目,你必须将 Ef Core 用于 .net 核心项目。)无论如何,即使 Microsoft.EntityFrameworkCore 更新并安装了 2.1.0-previewfinal-2 它仍然没有'没用。

为了使其能够 "work" 换句话说,在尝试访问相关实体时不返回空值,您还必须安装包 Microsoft.EntityFramworkCore.Proxies。

实际上,这是 "enable" 在 ef 核心中延迟加载的最简单方法(根据 MS);使用代理。尽管还有其他两种方法可以实现,但实施起来要复杂得多

这是最终一劳永逸地回答了这个问题的文章的link:https://docs.microsoft.com/en-us/ef/core/querying/related-data

所以简而言之,一旦你安装了包,你必须(对于上述选项)在你的 DI 容器中调用 .UseLazyLoadingProxies() 选项——转到 startup.cs 并修改DBContext 选项在 .UseSqlServer(...) 之前添加 .UseLazyLoadingProixes() 就这样....

现在,当您用相关实体实例化一个实体时,它们将在第一次访问 属性 时加载...所以基本上它会停止 returning 一个 null!!!

否则你必须使用隐式包含语句等...预加载不是和选项而是每个查询声明...

总结:

第 1 步:安装 Microsoft.EntityFrameworkCore 预览版 2.1.xx 第 2 步:安装 Microsoft.EntityFrameworkCore.Proxies 第 3 步:修改服务 container/Startup.cs(配置服务)DBContext 以包含选项 .UseLasyLoadingProxies 所以你的旧代码可能看起来像: services.AddDbContext(选项=>

services.AddDbContext(选项=> options.UseSqlServer(Configuration.GetConnectionString("LocalConnection"))

现在它可能看起来像这样:

services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("LocalConnection"))
                       .UseLazyLoadingProxies());

第 4 步 - 修改您的 DBContext Class 以延迟加载相关实体(是的,它们必须全部列出,这可能不是最佳选择,但我相信 MS 会解决这个问题)

builder.Entity<Image>().HasOne(u => u.ApplicationUser)
                       .WithMany(i => i.Images)
                       .HasForeignKey(u => u.ApplicationUserId)
                       .HasConstraintName
                            (
                              "FK_Image_AspNetUsers_ApplicationUserId"
                            );
builder.Entity<Video>().HasOne(u => u.ApplicationUser)
                       .WithMany(p => p.Videos)
                       .HasForeignKey(u => u.ApplicationUserId)
                       .HasConstraintName
                                 (
                                  "FK_Video_AspNetUsers_ApplicationUserId"
                                 );
builder.Entity<Audio>().HasOne(u => u.ApplicationUser)
                       .WithMany(p => p.Audios)
                       .HasForeignKey(u => u.ApplicationUserId)                                    
                       .HasConstraintName
                             (
                               "FK_Audio_AspNetUsers_ApplicationUserId"
                             );

你会注意到我正在使用 Identity Core - 在 table "AspNetUsers" 中没有 ApplicationUserId 列,所以你必须有外键约束,如果你'我已经适当地修改了你的class,我将在下面显示: 第 5 步确保您 class 看起来像这样 -->

应用程序用户Class

public class ApplicationUser : IdentityUser, ITraits
{




    public int Age { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public Color EyeColor { get; set; }
    public Color HairColor { get; set; }
    public int Height { get; set; }
    public Race Race { get; set; }
    public CuckRole CuckRole { get; set; }
    public BiologicalSex BiologicalSex { get; set; }
    public Sexuality Sexuality { get; set; }
    public Color SkinColor { get; set; }
    public int Weight { get; set; }
    public DateTime BirthDay { get; set; }

    public virtual ICollection<Image> Images { get; set; }<--must be virtual
    public virtual ICollection<Video> Videos { get; set; }<--must be virtual
    public virtual ICollection<Audio> Audios { get; set; }<--must be virtual



    public bool Equals(Traits other)
    {
        throw new NotImplementedException();
    }
}

媒体之一 classes :

图片:

public class Image
{
    //Fields and Properties//
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
    public virtual ApplicationUser ApplicationUser { get; set; }<-be virtual
    public virtual string ApplicationUserId { get; set; }<--must be virtual


    public Image()
    {

    }

    public Image(string path) => Path = path;


}

第6步 在视图中,您需要做的就是拥有一个 ApplicationUser,您可以像这样使用 httpcontext 拉取当前用户:

最好在您的控制器中,尽管您可以在视图中执行此操作

ApplicationUser user = await _usermanager.GetUserAsync(HttpContext.User);

然后将用户作为

传递给视图

return 查看(用户)

第 7 步

在你看来使用户模型是applicationuser

在你看来的某个地方尝试通过用户对象访问媒体 classes 所以

foreach (Audio clip in Model.Clips)
{
       <img src="@clip.path" />
}

中提琴!

**编辑有关主题和代理等的有用读物 - ef core 2-1 whats new with lazy loading