4

我正在创建一个API端点,以便创建一个与应与谈话关联的标签的新Talk。我在我的域名中的标签和会话之间建立了多对多的关系,请参阅下面的关系。添加标签以在实体框架中讨论多对多关系

Tag.cs​​

using System; 
using System.Collections.Generic; 

namespace Conferency.Domain 
{ 
    public class Tag : IAuditable 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public List<TalkTag> TalkTags { get; set; } 
     public DateTime ModifiedAt { get; set; } 
     public DateTime CreatedAt { get; set; } 
    } 
} 

Talk.cs

using System; 
using System.Collections.Generic; 

namespace Conferency.Domain 
{ 
    public class Talk : IAuditable 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public string Url { get; set; } 
     public List<TalkTag> TalkTags { get; set; } 
     public DateTime Presented { get; set; } 
     public DateTime ModifiedAt { get; set; } 
     public DateTime CreatedAt { get; set; } 
    } 
} 

TalkTag.cs​​

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace Conferency.Domain 
{ 
    public class TalkTag 
    { 
     public int TalkId { get; set; } 
     public Talk Talk { get; set; } 
     public int TagId { get; set; } 
     public Tag Tag { get; set; } 
    } 
} 

ConferencyContext.cs(删除不相关的代码)

using Microsoft.EntityFrameworkCore; 
using Microsoft.EntityFrameworkCore.ChangeTracking; 
using System; 
using Conferency.Domain; 

namespace Conferency.Data 
{ 
    public class ConferencyContext: DbContext 
    { 
     public DbSet<Talk> Talks { get; set; } 
     public DbSet<Tag> Tags { get; set; } 
     public DbSet<TalkTag> TagTalks { get; set; } 

     protected override void OnModelCreating(ModelBuilder modelBuilder) 
     { 
      modelBuilder.Entity<TalkTag>() 
       .HasKey(s => new { s.TalkId, s.TagId }); 

      modelBuilder.Entity<TalkTag>() 
       .HasOne(pt => pt.Talk) 
       .WithMany(p => p.TalkTags) 
       .HasForeignKey(pt => pt.TalkId); 

      modelBuilder.Entity<TalkTag>() 
       .HasOne(pt => pt.Tag) 
       .WithMany(t => t.TalkTags) 
       .HasForeignKey(pt => pt.TagId); 

      base.OnModelCreating(modelBuilder); 
     } 
    } 
} 

TalkViewModel.cs

using System; 
using System.Collections.Generic; 

namespace Conferency.Application.Models 
{ 
    public class TalkViewModel 
    { 
     public string Name { get; set; } 
     public string Url { get; set; } 
     public List<String> Tags { get; set; } 
    } 
} 

问题是我无法弄清楚如何创建一个谈话,其标签(连接是否存在他们,如果他们不创造)。我不确定以什么顺序完成这一任务。我是否必须查询每个标签以检查它们是否存在,或者是否有可以使用的findOrCreate方法?如果Talk尚未创建,如何创建TalkTag记录?有没有一种优雅的方式来完成这一点,我不理解?

TalkRepository.cs

using System.Collections.Generic; 
using Conferency.Domain; 
using Microsoft.EntityFrameworkCore; 
using System; 
using System.Linq; 
using System.Threading.Tasks; 

namespace Conferency.Data 
{ 
    public class TalkRepository : ITalkRepository 
    { 
     private ConferencyContext _context; 

     public TalkRepository(ConferencyContext context) 
     { 
      _context = context; 
     } 

     public void Add(Talk entity) 
     { 
      _context.Add(entity); 
     } 

     public void AddWithTags(Talk entity, List<String> tags) 
     { 
      // Create Talk 
      // Query for each tag 
      // Create if they don't exist 
      // Attach to talk 
      // ?? 
     } 

     public IEnumerable<Talk> GetAllTalks() 
     { 
      return _context.Talks 
       .Include(c => c.TalkTags) 
       .OrderBy(c => c.Presented) 
       .ToList(); 
     } 

     public Talk GetTalk(int id) 
     { 
      return _context.Talks 
       .Include(c => c.TalkTags) 
       .Where(c => c.Id == id) 
       .FirstOrDefault(); 
     } 

     public async Task<bool> SaveAllAsync() 
     { 
      return (await _context.SaveChangesAsync()) > 0; 
     } 
    } 
} 

我新的C#和我想学习最佳实践,并与EF熟悉自己和ASP.NET的核心,所以希望有人能帮助指导我在正确的道路。该完整的解决方案是在这里,如果你想看看https://github.com/bliitzkrieg/Conferency

我试图解决它自己,但即时得到一个NullPointerException异常,这是我尝试在一个解决方案:

TalksController.cs

[HttpPost()] 
     public async Task<IActionResult> Post([FromBody]TalkViewModel model) 
     { 
      try 
      { 
       _logger.LogInformation("Creating a new Talk"); 

       List<Tag> tags = _tagRepo.FindOrCreateTags(model.Tags); 

       Talk talk = new Talk { Name = model.Name, Url = model.Url }; 

       List<TalkTag> talkTags = new List<TalkTag>(); 
       tags.ForEach(tag => 
       { 
        var talkTag = new TalkTag { TagId = tag.Id, Talk = talk }; 
        talkTags.Add(talkTag); 
       }); 

       talk.TalkTags.AddRange(talkTags); // Exception being thrown here 
       _repo.Add(talk); 

       if (await _repo.SaveAllAsync()) 
       { 
        string newUri = Url.Link("TalkGet", new { id = talk.Id }); 
        return Created(newUri, talk); 
       } 
       else 
       { 
        _logger.LogWarning("Could not save Talk"); 
       } 
      } 
      catch (Exception ex) 
      { 
       _logger.LogError($"Threw exception while saving Talk: {ex}"); 
      } 

      return BadRequest(); 
     } 
    } 

TagRepository.cs

using System; 
using System.Collections.Generic; 
using Conferency.Domain; 
using System.Threading.Tasks; 
using Microsoft.EntityFrameworkCore; 
using System.Linq; 

namespace Conferency.Data 
{ 
    public class TagRepository: ITagRepository 
    { 
     private ConferencyContext _context; 

     public TagRepository(ConferencyContext context) 
     { 
      _context = context; 
     } 

     public void Add(Tag entity) 
     { 
      _context.Add(entity); 
     } 

     public List<Tag> FindOrCreateTags(List<string> tags) 
     { 
      List<Tag> _tags = new List<Tag>(); 
      tags.ForEach(t => 
      { 
       try 
       { 
        var tag = _context.Tags 
         .Where(c => c.Name == t) 
         .FirstOrDefault(); 

        if (tag != null) 
        { 
         _tags.Add(tag); 
        } 
        else 
        { 
         Tag created = new Tag { Name = t }; 
         this.Add(created); 
         _tags.Add(created); 
        } 
       } 
       catch (Exception ex) 
       { 

       } 

      }); 

      return _tags; 
     } 

     public async Task<bool> SaveAllAsync() 
     { 
      return (await _context.SaveChangesAsync()) > 0; 
     } 
    } 
} 
+0

由于您的实体的属性都不是'virtual',因此您似乎没有正确设置导航属性。 – Eris

+0

我对虚拟实体了解不多,但是我遵循EF核心文档(https://docs.microsoft.com/en-us/ef/core/modeling/relationships)上的多对多指南。 – Luca

+0

你也应该提供你的TalkViewModel代码。从我能看到的代码中,我猜你需要在具有标签ID,标签文本和bool“Selected”属性的TalkViewModel上使用List 属性。然后,当您将TalkViewModel传递到您的回购站时,请筛选出所选的TagViewModel,并为每一个TagViewModel添加一个带有正确TagId的TalkTag给您Talk上的TalkTags属性。 EF应该照顾在SaveChanges()上添加正确的TalkId。 –

回答

2

在您TalkViewModel添加List<TagViewModel>属性具有以下属性:

public int TagId { get; set; } 
public string TagName { get; set; } 
public bool Selected { get; set; } 

当您通过TalkViewModel你的回购,筛选出所选TagViewModel S和为每一个,加用适当的TagId一个TalkTagTalkTags财产上的Talk。 EF应该照顾在_context.SaveChanges()上添加正确的TalkId

如果Tag不存在,创建一个新的Tag和新Talk其性能TalkTag,然后将其添加到您的_context。 EF应该照顾其余的人。

0

您尚未初始化导致空指针的TalkTags集合。初始化谈对象时,试试这个:

Talk talk = new Talk { Name = model.Name, Url = model.Url, TalkTags = new List<TalkTag>() }; 

你需要一个TalkTag对象的更多属性?否则,您只需在Talk类中有List<Talk>和Talk类中的List<Tag>,并且映射将由EF完成(TalkTag表将在DB中创建)。

Michael Tranchida已经描述了将对象添加到上下文的方法。