C# 使用之前添加的现有标记实现标记系统

C# 使用之前添加的现有标记实现标记系统,c#,asp.net,asp.net-mvc,entity-framework,C#,Asp.net,Asp.net Mvc,Entity Framework,我正在使用ASP.NET5、MVC6和EF7创建一个收集语言语法的网站,一个语法可以有许多标记(标记用于表示级别、含义等),一个标记可以用于许多语法 当我添加语法然后将标记附加到它时,我希望现有的标记(第一次添加的)从语法中指向,而不是每次添加语法时都添加新的标记(稍后我计划在用户以同样的方式键入标记时实现自动完成) 这些是我使用的模型和上下文,我通过读取实现了多对多 公共类标记 { public int TagId{get;set;} [必需] 公共字符串文本{get;set;} 公共列表文法

我正在使用ASP.NET5、MVC6和EF7创建一个收集语言语法的网站,一个语法可以有许多标记(标记用于表示级别、含义等),一个标记可以用于许多语法

当我添加语法然后将标记附加到它时,我希望现有的标记(第一次添加的)从语法中指向,而不是每次添加语法时都添加新的标记(稍后我计划在用户以同样的方式键入标记时实现自动完成)

这些是我使用的模型和上下文,我通过读取实现了多对多

公共类标记
{
public int TagId{get;set;}
[必需]
公共字符串文本{get;set;}
公共列表文法标记{get;set;}
}
公共课语法
{
公共整数{get;set;}
公共列表文法标记{get;set;}
....
}
公共课语法课
{
public int TagId{get;set;}
公共标记{get;set;}
公共整数{get;set;}
公共语法{get;set;}
}
和在ApplicationDbContext中创建模型

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<GrammarTag>().HasKey(g => new { g.GrammarId, g.TagId });

        builder.Entity<Tag>()
            .HasMany(t => t.GrammarTags)
            .WithOne()
            .OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Cascade);

        builder.Entity<Grammar>()
            .HasMany(g => g.GrammarTags)
            .WithOne()
            .OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Cascade);

        builder.Entity<GrammarTag>()
            .HasOne(gt => gt.Grammar)
            .WithMany(g => g.GrammarTags)
            .HasForeignKey(gt => gt.GrammarId);

        builder.Entity<GrammarTag>()
            .HasOne(gt => gt.Tag)
            .WithMany(t => t.GrammarTags)
            .HasForeignKey(gt => gt.TagId);

    }
模型创建时受保护的覆盖无效(ModelBuilder)
{
基于模型创建(生成器);
builder.Entity().HasKey(g=>new{g.grammand,g.TagId});
builder.Entity()
.HasMany(t=>t.GrammarTags)
.WithOne()
.OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Cascade);
builder.Entity()
.HasMany(g=>g.GrammarTags)
.WithOne()
.OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Cascade);
builder.Entity()
.HasOne(gt=>gt.Grammar)
.WithMany(g=>g.GrammarTags)
.HasForeignKey(gt=>gt.grammand);
builder.Entity()
.HasOne(gt=>gt.Tag)
.WithMany(t=>t.GrammarTags)
.HasForeignKey(gt=>gt.TagId);
}
在控制器中创建方法

    public async Task<IActionResult> Create(AddGrammarViewModel viewmodel)
    {
        if (ModelState.IsValid)
        {
            if (!User.IsSignedIn())
            {
                return RedirectToAction("Index", "Account");
            }

            var grammar = new Grammar()
            {
                 ....
            };
            // in viewmodel, Tag is string and is delimited each Tag by comma
            var tags = viewmodel.Tags.Split(',');
            var tagList = new List<Tag>();

            foreach (var tag in tags)
            {
                if (_context.Tag.Any(t => t.Text == tag))
                {
                    tagList.Add(_context.Tag.Single(t => t.Text == tag));
                }
                else
                {
                    tagList.Add(new Tag { Text = tag });
                }
            }

            var grammaTagList = new List<GrammarTag>();

            foreach (var tag in tagList)
            {
                grammaTagList.Add(new GrammarTag { Tag = tag, Grammar = grammar });
            }

            _context.Grammar.Add(grammar);
            _context.Tag.AddRange(tagList);
            _context.GrammarTag.AddRange(grammaTagList);
            await _context.SaveChangesAsync();

            return RedirectToAction("Index");
        }
        return View(viewmodel);
    }
公共异步任务创建(AddGrammarViewModel viewmodel) { if(ModelState.IsValid) { 如果(!User.IsSignedIn()) { 返回重定向操作(“索引”、“帐户”); } var grammar=新语法() { .... }; //在viewmodel中,标记是字符串,每个标记用逗号分隔 var tags=viewmodel.tags.Split(','); var tagList=新列表(); foreach(标签中的var标签) { if(_context.Tag.Any(t=>t.Text==Tag)) { tagList.Add(_context.Tag.Single(t=>t.Text==Tag)); } 其他的 { 添加(新标记{Text=Tag}); } } var grammaTagList=新列表(); foreach(标记列表中的var标记) { Add(新的GrammarTag{Tag=Tag,Grammar=Grammar}); } _context.Grammar.Add(语法); _context.Tag.AddRange(标记列表); _context.GrammarTag.AddRange(grammaTagList); wait_context.SaveChangesAsync(); 返回操作(“索引”); } 返回视图(viewmodel); } 问题是,当我添加使用以前添加的相同标记的语法时,出现数据库错误
SqlException:当identity\u insert设置为OFF时,无法在表“Tag”中插入identity列的显式值。


我认为这是因为我使用的标签与从数据库查询的标签相同,但这是我想要的方式,所以我不知道如何实现这个标签系统。

最后,我发现了问题,正如错误消息所说,这是因为我插入了从数据库查询的标签,因此它具有标识列(PK)。
所以,我只需检查该标记之前是否已添加,然后只向数据库添加新标记,对于标记与联接表的关系,我只需将其添加到GrammarTag,然后将所有新的GrammarTag添加到数据库

    var tags = viewmodel.Tags.Split(',');
    // create old tag and new tag list
    var oldTags = new List<Tag>();
    var newTags = new List<Tag>();

    // check old and new tag and add it to list
    foreach (var tag in tags)
    {
        var testTag = await _context.Tag.FirstOrDefaultAsync(t => t.Text == tag);

        if (testTag != null)
        {
            oldTags.Add(testTag);
        }
        else
        {
            newTags.Add(new Tag { Text = tag });
        }
    }

    var grammaTagList = new List<GrammarTag>();
    oldTags.AddRange(newTags);

    // put every that in this Grammar to join table
    foreach (var tag in oldTags)
    {
        grammaTagList.Add(new GrammarTag { Tag = tag, Grammar = grammar });
    }

    _context.Grammar.Add(grammar);
    // add only the new tag
    _context.Tag.AddRange(newTags);
    _context.GrammarTag.AddRange(grammaTagList);
    await _context.SaveChangesAsync();
var-tags=viewmodel.tags.Split(',');
//创建旧标记和新标记列表
var oldTags=新列表();
var newTags=新列表();
//检查新旧标签并将其添加到列表中
foreach(标签中的var标签)
{
var testTag=await_context.Tag.FirstOrDefaultAsync(t=>t.Text==Tag);
if(testTag!=null)
{
添加(testTag);
}
其他的
{
Add(新标记{Text=Tag});
}
}
var grammaTagList=新列表();
oldTags.AddRange(newTags);
//把这个语法中的每一个都放到联接表中
foreach(oldTags中的var标记)
{
Add(新的GrammarTag{Tag=Tag,Grammar=Grammar});
}
_context.Grammar.Add(语法);
//仅添加新标记
_context.Tag.AddRange(newTags);
_context.GrammarTag.AddRange(grammaTagList);
wait_context.SaveChangesAsync();

听起来好像您正在尝试重新插入已创建到数据库中的标记。如果标签已经存在,您应该保留它(或者根据需要更新它)。如果您使用相同的ID再次尝试添加它,它将出错,因为ID必须是唯一的。@Jonathan那么我如何才能将其关系添加到语法中,我可以将现有的标记添加到新的GrammarTag中,并仅将新标记添加到_context.Tag中吗?如果您包含有效负载,则只需要为关系添加一个显式实体(即
GrammarTag
)(关于关系的附加数据),您不在此处执行此操作。如果只使用隐式联接表,您的生活和代码将大大简化。换句话说,
Tag
将具有属性
public virtual ICollection{get;set;}
Grammar
将有一个属性
公共虚拟ICollection标记{get;set;}
。EF可以以本机方式处理此问题,因此不需要fluen
    var tags = viewmodel.Tags.Split(',');
    // create old tag and new tag list
    var oldTags = new List<Tag>();
    var newTags = new List<Tag>();

    // check old and new tag and add it to list
    foreach (var tag in tags)
    {
        var testTag = await _context.Tag.FirstOrDefaultAsync(t => t.Text == tag);

        if (testTag != null)
        {
            oldTags.Add(testTag);
        }
        else
        {
            newTags.Add(new Tag { Text = tag });
        }
    }

    var grammaTagList = new List<GrammarTag>();
    oldTags.AddRange(newTags);

    // put every that in this Grammar to join table
    foreach (var tag in oldTags)
    {
        grammaTagList.Add(new GrammarTag { Tag = tag, Grammar = grammar });
    }

    _context.Grammar.Add(grammar);
    // add only the new tag
    _context.Tag.AddRange(newTags);
    _context.GrammarTag.AddRange(grammaTagList);
    await _context.SaveChangesAsync();