Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架核心不';t在命中api端点时检查实体是否存在_C#_Entity Framework Core - Fatal编程技术网

C# 实体框架核心不';t在命中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

当点击API端点以插入实体时,EF会将任何填充的导航属性添加到相应的表中。这很好,除非该实体已经存在,然后抛出重复记录错误

我正在为另一种语言创建一个单词数据库,这些单词之间有关系。假设我有一个名词类、一个语义类、一个链接名词类和一个api控制器来添加名词

    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
                        &#x27;dbo.Meanings&#x27; with unique index &#x27;IX_Meanings_Value&#x27;. The duplicate key
                        value is (boy).&#xD;&#xA;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; }
}