我正在创建一个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;
}
}
}
由于您的实体的属性都不是'virtual',因此您似乎没有正确设置导航属性。 – Eris
我对虚拟实体了解不多,但是我遵循EF核心文档(https://docs.microsoft.com/en-us/ef/core/modeling/relationships)上的多对多指南。 – Luca
你也应该提供你的TalkViewModel代码。从我能看到的代码中,我猜你需要在具有标签ID,标签文本和bool“Selected”属性的TalkViewModel上使用List属性。然后,当您将TalkViewModel传递到您的回购站时,请筛选出所选的TagViewModel,并为每一个TagViewModel添加一个带有正确TagId的TalkTag给您Talk上的TalkTags属性。 EF应该照顾在SaveChanges()上添加正确的TalkId。 –