C# 处理模型多个复选框的简明方法

C# 处理模型多个复选框的简明方法,c#,asp.net-mvc,entity-framework,C#,Asp.net Mvc,Entity Framework,我有两个类,歌曲和标签。这种关系是多对多的。我遇到的问题是,当用户添加一首新歌并检查他/她想要的标记时,我在控制器中调用db.SaveChanges,它会添加重复的标记,而不是只向联接表添加记录。我已经解决了这个问题,但它是一个黑客。一定有更好的解决办法 为了清楚起见,这里是我的代码删节 歌曲: 标签: 控制器: public ActionResult Create() { Song song = new Song(); song.Tags = utilities.TagName

我有两个类,歌曲和标签。这种关系是多对多的。我遇到的问题是,当用户添加一首新歌并检查他/她想要的标记时,我在控制器中调用db.SaveChanges,它会添加重复的标记,而不是只向联接表添加记录。我已经解决了这个问题,但它是一个黑客。一定有更好的解决办法

为了清楚起见,这里是我的代码删节

歌曲:

标签:

控制器:

public ActionResult Create()
{
    Song song = new Song();
    song.Tags = utilities.TagNames(_db, User.Identity.Name);
    return View(song);
}

[HttpPost]
public ActionResult Create(Song song)
{
    //cycle thru the tags and grab ids of checked tags
    int tagCount = song.Tags.Count;
    List<int> tagIds = new List<int>();
    for (int i = tagCount - 1; i > -1; i--)
    {
        Tag tag = song.Tags[i];
        if (tag.Selected)
            tagIds.Add(tag.Id);
    }
    song.Tags = null;

    if (ModelState.IsValid)
    {                
        _db.Songs.Add(song);
        _db.SaveChanges();
        int songId = song.Id;

        string sql = "INSERT INTO [dbo].[TagSongs] (Tag_Id, Song_Id) VALUES ";
        foreach(int tagid in tagIds)
        {
            sql += "(" + tagid + "," + songId + "),";
        }
        //remove the last comma
        sql = sql.Substring(0, sql.Length - 1);
        _db.Database.ExecuteSqlCommand(sql);

        return RedirectToAction("Index");
    }
    SetCategoriesAndTags(song);
    return View(song);
}
视图:


首先,不要对SQL查询使用字符串连接,否则这将使您面临SQL注入攻击。这段代码应该能满足您的需要。从控制器ActionMethod调用它

private void AddSongs(Song song, List<int> tagIds)
{
    _db.Songs.Add(song);
    foreach(var tagId in tagIds)
    {
        var tag = _db.Tags.SingleOrDefault(tagId);

        if(tag != null) song.Tags.Add(tag);
    }
    _db.SaveChanges();
}

Song和Tag类是否对第一个实体进行编码?另外,不要对SQL查询使用字符串连接,否则这将使您面临SQL注入攻击。@ken4z不确定您所说的第一个实体是什么意思。它们不是派生类。在EntityFramework中,代码优先和数据库优先。这就是我的意思我很确定这段代码会引起同样的问题,也就是说,它会创建重复的标记。假设我有两个标签,搞笑和模仿。用户同时检查它们并添加歌曲。下次用户添加歌曲时,他/她将看到4个标签作为选项,2个搞笑标签和2个模仿标签。至于SQL注入,除了用错误的Tag_Id污染连接表之外,我不确定我是如何容易受到攻击的,如果TagId不是整数,模型绑定将失败。您是正确的,在这种情况下,您不会成功受到攻击,但最好使用最佳实践,并且它们易于使用。如果开发人员稍后来重构此代码,并且需要为where子句或其他内容添加字符串值,那么他们更有可能更新现有的实现,从而打开攻击窗口。应该可以。我已经将该构造用于其他多对多插入。注意:我稍微更新了代码,以验证标记ID是否有效,即已知标记。可能您使用的tagid超出了数据库中已有的id范围,因此它正在插入一个新的。
public ActionResult Create()
{
    Song song = new Song();
    song.Tags = utilities.TagNames(_db, User.Identity.Name);
    return View(song);
}

[HttpPost]
public ActionResult Create(Song song)
{
    //cycle thru the tags and grab ids of checked tags
    int tagCount = song.Tags.Count;
    List<int> tagIds = new List<int>();
    for (int i = tagCount - 1; i > -1; i--)
    {
        Tag tag = song.Tags[i];
        if (tag.Selected)
            tagIds.Add(tag.Id);
    }
    song.Tags = null;

    if (ModelState.IsValid)
    {                
        _db.Songs.Add(song);
        _db.SaveChanges();
        int songId = song.Id;

        string sql = "INSERT INTO [dbo].[TagSongs] (Tag_Id, Song_Id) VALUES ";
        foreach(int tagid in tagIds)
        {
            sql += "(" + tagid + "," + songId + "),";
        }
        //remove the last comma
        sql = sql.Substring(0, sql.Length - 1);
        _db.Database.ExecuteSqlCommand(sql);

        return RedirectToAction("Index");
    }
    SetCategoriesAndTags(song);
    return View(song);
}
@for (int i = 0; i < Model.Tags.Count; i++)
{
    Tag t = Model.Tags[i];
    @Html.CheckBoxFor(m => m.Tags[i].Selected)
    <label for="Tags_@i.ToString()__Selected">@t.TagName</label>
    @Html.HiddenFor(m => m.Tags[i].Id)

    @Html.HiddenFor(m => m.Tags[i].TagName)
}
private void AddSongs(Song song, List<int> tagIds)
{
    _db.Songs.Add(song);
    foreach(var tagId in tagIds)
    {
        var tag = _db.Tags.SingleOrDefault(tagId);

        if(tag != null) song.Tags.Add(tag);
    }
    _db.SaveChanges();
}