C# 实体框架核心不';t在命中api端点时检查实体是否存在
当点击API端点以插入实体时,EF会将任何填充的导航属性添加到相应的表中。这很好,除非该实体已经存在,然后抛出重复记录错误 我正在为另一种语言创建一个单词数据库,这些单词之间有关系。假设我有一个名词类、一个语义类、一个链接名词类和一个api控制器来添加名词C# 实体框架核心不';t在命中api端点时检查实体是否存在,c#,entity-framework-core,C#,Entity Framework Core,当点击API端点以插入实体时,EF会将任何填充的导航属性添加到相应的表中。这很好,除非该实体已经存在,然后抛出重复记录错误 我正在为另一种语言创建一个单词数据库,这些单词之间有关系。假设我有一个名词类、一个语义类、一个链接名词类和一个api控制器来添加名词 public class Noun { public int Id { get; set; } public string Value { get; set; } public
public class Noun
{
public int Id { get; set; }
public string Value { get; set; }
public List<NounMeaning> NounMeanings { get; set; }
}
如果我使用上面的JSON访问端点,那么在我第一次得到有效响应时。名词被添加到名词表中,连同含义表和链接表中的含义一起,名词含义被正确添加到
如果我再次点击端点,只更改JSON中的名词值,比如说,使用Word2值,我希望发生的是名词被添加到名词表中,含义表保持不变,因为含义已经存在,并且名词含义表插入另一行(2,1表示eg的列)
相反,返回一个错误,表示在尝试再次输入含义时,含义表中会出现重复(这是来自Postman的错误消息):
SqlException:无法在对象中插入重复的键行
';dbo.含义';具有唯一索引';九、意义、价值;。复制钥匙
价值是(男孩)
;声明已终止。
我希望EF足够聪明,在添加记录之前检查是否存在意义
相反,我必须添加以下逻辑以使其正确通过(请注意,下面的代码是一个测试,以查看它是否工作,因此需要硬编码索引)
public异步任务后缀([FromBody]名词)
{
如果(!ModelState.IsValid)
{
返回请求(ModelState);
}
语义=_context.means.SingleOrDefault(m=>m.Value==noun.nounmeans[0].means.Value);
if(意思是!=null)
{
noun.nounmeans=新列表();
名词.名词含义.Add(新名词含义{means=means});
}
_上下文。名词。添加(名词);
wait_context.SaveChangesAsync();
这并不好,因为它需要更多的时间进行这些检查(因为实际的数据库更复杂),并且需要额外的数据库调用
我不记得必须在EF框架中这样做,但自从我从头开始编写DB以来,已经有很长时间了,所以我可能遗漏了一些东西
谢谢你的帮助。这让我整个周末都很沮丧。实体框架从来没有你期望的行为-DbSet.Add方法只用于插入记录。如果你试图更新现有记录,你应该附加一个实体。这是针对EF6的,但逻辑适用: 这属于“意见”的范畴,而不是冷冰冰的事实,但使用数据模型(为数据库表建模的类)作为视图模型(用于在更高级别绑定数据的类,如UI)通常是一个坏主意—这很少见(以我的经验)您需要在UI中使用与数据源所需相同的对象图 例如,您已将其显示为您的数据库模型:
public class Noun
{
public int Id { get; set; }
public string Value { get; set; }
public List<NounMeaning> NounMeanings { get; set; }
}
public class Meaning
{
public int Id { get; set; }
public string Value { get; set; }
public List<NounMeaning> NounMeanings { get; set; }
}
public class NounMeaning
{
public int NounId { get; set; }
public int MeaningId { get; set; }
public Noun Noun { get; set; }
public Meaning Meaning { get; set; }
}
这使得具有逻辑性意义-一个名词有多个含义,一个含义可以用多个名词来描述。开发人员可能会发现该模型在应用程序级别更容易处理,因此您需要创建DTO(数据传输对象)然后,您还可以添加任何有意义的助手方法,而不会污染您的数据模型
实际上有一篇MSDN/MS Docs文章也讨论了这一点:请编辑您的问题,包括对数据库和EDMX文件或模型配置中的键和索引的描述。如果可能,请将完整的数据库表描述添加为
CREATE table
语句。还包括完整的complete您收到的错误消息。我添加了用于构建db的上下文和ModelBuilder,因为这是EF核心代码优先解决方案。我还包括了从发送请求的邮局收到的错误消息。当表具有主键时,您不能在具有相同键的项上放置多个。因此需要用新val替换当前值或者创建两个表。一个表具有唯一键,另一个表具有值。然后连接两个表。因此,在这种情况下,我必须自己进行逻辑处理。另外,您能否扩展您的第二段,以及我如何改进它?是的。EF似乎应该“更智能”,但关于构成“添加”与“插入”的逻辑这不是一个一刀切的命题。我在答案中添加了一点-让我知道这是否有帮助。这是有意义的。这些DTO对象就是我将使用的端点,然后使用任何帮助器方法将它们映射到数据模型,还使用帮助器/repo进行数据检查(例如检查含义是否已经存在等).我理解正确吗?正确。当然,在一个简单的应用程序中,只使用数据模型可能更有意义,但使用DTO可以让您更好地控制传递的数据。
public class NounMeaning
{
public int NounId { get; set; }
public int MeaningId { get; set; }
public Noun Noun { get; set; }
public Meaning Meaning { get; set; }
}
public class LanguageContext : DbContext
{
public LanguageContext(DbContextOptions<LanguageContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<NounMeaning>().HasKey(nm => new { mm.NounId, nm.MeaningId });
modelBuilder.Entity<Meaning>().HasIndex(m => m.Value).IsUnique();
}
public DbSet<Noun> Nouns { get; set; }
public DbSet<Meaning> Meanings { get; set; }
}
public async Task<IActionResult> PostNoun([FromBody] Noun noun)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Nouns.Add(noun);
await _context.SaveChangesAsync();
{
"Value": "Word",
"NounMeanings": [
{
"Meaning": {
"Value": "boy"
}
}
]
}
<h2 class="stackerror">SqlException: Cannot insert duplicate key row in object
'dbo.Meanings' with unique index 'IX_Meanings_Value'. The duplicate key
value is (boy).
The statement has been terminated.</h2>
<ul>
public async Task<IActionResult> PostNoun([FromBody] Noun noun)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Meaning meaning = _context.Meanings.SingleOrDefault(m => m.Value == noun.NounMeanings[0].Meaning.Value);
if(meaning != null)
{
noun.NounMeanings = new List<NounMeaning>();
noun.NounMeanings.Add(new NounMeaning { Meaning = meaning });
}
_context.Nouns.Add(noun);
await _context.SaveChangesAsync();
public class Noun
{
public int Id { get; set; }
public string Value { get; set; }
public List<NounMeaning> NounMeanings { get; set; }
}
public class Meaning
{
public int Id { get; set; }
public string Value { get; set; }
public List<NounMeaning> NounMeanings { get; set; }
}
public class NounMeaning
{
public int NounId { get; set; }
public int MeaningId { get; set; }
public Noun Noun { get; set; }
public Meaning Meaning { get; set; }
}
public class Noun
{
public int Id { get; set; }
public string Value { get; set; }
public List<Meaning> Meanings { get; set; }
}
public class Meaning
{
public int Id { get; set; }
public string Value { get; set; }
public List<Noun> Nouns { get; set; }
}