我有一个.net core 2.0
项目使用.netcore identity' .
I have a class
ApplicationUser'(又名所有者)和3
其他类媒体对象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
课程,为每个媒体课程添加了ICollection<Type>
。无论我做什么,但当我访问user.images
等时,我总是得到null。(尝试过虚拟导航道具)
而不是坐在这里解释我认为它是什么(我可能不会对吸气剂和制定者做什么?)这会令人困惑我想我只是让你们告诉我什么方向,我应该进去,因为我已经失去了许多兔子洞,现在试图解决这个问题。实体框架似乎是为了它只是我不做的事情...我甚至无法弄清楚(如果不是在EF的某个地方)如何最初定义setter意味着它如何知道什么图像属于用户而不查询数据库看哪个图像有很多用户的fk ...无论如何,我输入的越多,我就越混淆自己LOL。
用户类:
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();
}
然后我的媒体类(只有一个只是不同的名字)
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;
}
}
修改
好的,所以我试着尽可能地实现建议的代码,到目前为止,这就是我想出的:
媒体类:
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类现在看起来像这样:
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(图片,视频等)
时仍然获得null答案 0 :(得分:0)
在DbContext类中,您可以覆盖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)。
进一步调查的另一个关键词可能是ObjectEntityConfiguration
。
答案 1 :(得分:0)
嘿伙计们再次感谢你的帮助(@nilsK) - 所以答案最终成为了一个实体框架“Thing”。
实际框架核心基本上不支持延迟加载直到现在预览的v2.1(不确定我是否错过了确保指定它是实体框架核心但我猜想所有假设因为它是.net核心项目,你必须使用Ef Core进行.net核心项目。)无论如何,即使更新并安装了2.1.0-previewfinal-2的Microsoft.EntityFrameworkCore,它仍然无效。
为了让它“工作”换句话说 - 在尝试访问相关实体时不要回退null,你还必须安装包Microsoft.EntityFramworkCore.Proxies。
实际上,这是在ef核心中“启用”延迟加载的最简单方法(根据MS);使用代理。虽然有两种其他方法可用于实现
这里是文章的链接,最终一劳永逸地回答了这个问题:https://docs.microsoft.com/en-us/ef/core/querying/related-data
所以short和skinny是一旦你安装了你必须的软件包(应该是上面的选项)在你的DI容器中调用.UseLazyLoadingProxies()选项 - 转到startup.cs并修改DBContext选项添加。在.UseSqlServer(...)之前使用UseLazyLoadingProixes(),就是它....
现在,当您实例化一个具有相关实体的实体时,它们将在首次访问该属性时加载...所以基本上它将停止返回null !!!
否则你必须使用隐式包含语句等... eager loading isnt和option,而是每个查询声明...
要点:
步骤1:安装Microsoft.EntityFrameworkCore预览2.1.xx 第2步:安装Microsoft.EntityFrameworkCore.Proxies 步骤3:修改服务容器/ Startup.cs(配置服务)DBContext以包含选项.UseLasyLoadingProxies 所以您的旧代码可能如下所示: services.AddDbContext(options =&gt;
services.AddDbContext(options =&gt; options.UseSqlServer(Configuration.GetConnectionString( “方LocalConnection”))
现在看起来像这样:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LocalConnection"))
.UseLazyLoadingProxies());
步骤4 - 将您的DBContext类修改为lasy加载相关实体(是的,它们必须全部列出,这可能不是最佳但我确定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 - 在表“AspNetUsers”中没有ApplicationUserId列所以你必须有外键约束,如果你已经适当地修改了你的类我应该已经存在显示如下: 第5步确保你的课程看起来像这样 - &gt;
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 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();
}
}
其中一个媒体类:
图片:
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);
然后将用户传递给视图
返回视图(用户)
第7步
在您的视图中使用户模型是applicationuser
在您视图中的某个位置尝试通过用户对象访问媒体类,以便
foreach (Audio clip in Model.Clips)
{
<img src="@clip.path" />
}
中提琴!
**仅编辑有关主题和代理等的有用阅读 - ef core 2-1 whats new with lazy loading