实现使用之前添加的现有Tag的Tag系统

时间:2016-04-11 16:33:37

标签: c# asp.net asp.net-mvc entity-framework

我使用ASP.NET 5,MVC6和EF7创建收集语法语法的网站,一个语法可以有很多Tag(Tag用于表示类似水平,含义等)和一个标签可用于许多语法。

当我添加语法然后将Tag附加到它时,我希望从Grammar指向现有的标签(第一次添加),而不是每次添加语法时添加新标签(后来我计划实现自动完成时)用户输入标签的方式与SO)相同。

这些是我使用的模型和上下文,我通过从http://docs.efproject.net/en/latest/modeling/relationships.html#many-to-many读取实现了多对多。

public class Tag
{
    public int TagId { get; set; }
    [Required]
    public string Text { get; set; }
    public List<GrammarTag> GrammarTags { get; set; }
}

public class Grammar
{
    public int GrammarId { get; set; }
    public List<GrammarTag> GrammarTags { get; set; }
        ....
}

public class GrammarTag
{
    public int TagId { get; set; }
    public Tag Tag { get; set; }
    public int GrammarId { get; set; }
    public Grammar Grammar { get; set; }
}

和ApplicationDbContext中的OnModelCreating

    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);

    }

在控制器中的Create方法

    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);
    }

问题是当我添加使用之前添加的相同标记的语法时,我收到了数据库错误SqlException: Cannot insert explicit value for identity column in table 'Tag' when IDENTITY_INSERT is set to OFF.
我之所以这么想,是因为我使用了与数据库查询相同的标签,但这是我想要的方式,所以我无法弄清楚如何实现这个标签系统。

1 个答案:

答案 0 :(得分:0)

最后,我想通了,问题就像错误消息所说的那样,因为我插入了从数据库查询的标签,因此它有标识列(PK)。
所以,我只需检查之前是否添加了Tag,然后只添加新的Tags到数据库,对于Tag与join表的关系,我只需将它添加到GrammarTag然后将所有新的GrammarTags添加到数据库。

    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();