确保另一条记录没有包含相同的字段值

本文关键字:字段 包含相 一条 记录 确保 | 更新日期: 2023-09-27 18:04:56

我正在使用c# MVC为我自己的学术目的构建一个小型的内容管理系统。

我有创建页面的功能(列表页面,删除页面等)

在基本级别上,我使用ID来验证所有页面都是唯一的。但是当我使用MVC时,一个页面本质上是一个视图——它可以包含razor。这些都没问题。

然而,我的ViewName也必须是唯一的(你不能有两个视图在数据库中具有相同的名称)。此外,我的ViewPath必须是唯一的(你不能有两个路由是相同的)。

在我的创建页面,我有检查(所有工作),以确保一个新的视图创建一个唯一的ID,它检查之前创建的视图,既ViewName和ViewPath是唯一的返回相关的错误,如果不是。

这些都可以。

现在当我去编辑一个视图。用户可以选择修改ViewName和ViewPath。所以我们需要确保它没有被修改为已经存在的ViewName和ViewPath。然而,当我们编辑视图时,视图本身已经存在,所以我们得到这些错误提示。

所以我们需要检查我们是否保存到相同的页面,所以我们寻找记录的id在数据库的ViewName与当前的ViewName(因为ViewName是唯一的,所以我们可以得到id),并验证我们确实保存到相同的视图。然后我们可以使用这个检查来确保我们不会显示上面的错误,因为我们只是简单地改变了视图的名称,它不匹配任何现有的视图。

问题是,当我点击

行时,我得到以下错误
db.Entry(view).State = EntityState.Modified;

错误是:

附加信息:连接类型为"xxx"的实体失败因为相同类型的另一个实体已经具有相同的主节点键值。在使用"Attach"方法或设置时可能会发生这种情况一个实体的状态为"未改变"或"修改",如果有任何实体在图中的键值冲突。这可能是因为一些实体是新的,尚未收到数据库生成的密钥值。在这种情况下,使用'Add'方法或'Added'实体状态来跟踪图形,然后将非新实体的状态设置为"未修改"或"修改"视情况而定。

问题:我不知道我是否把我的解决方案复杂化了,如果是这样,谁能给我一个更好的解决方案和一个例子吗?

如果这是正确的"方式",但我刚刚实现了错误的方式,谁能告诉我如何修复它。

下面是我遇到问题的编辑控制器动作的POST方法:

public ActionResult Edit([Bind(Include = "Id,ViewName,ViewPath,ViewContent")] View view)
{
    View sameView1 = db.View.First(v => v.ViewName == view.ViewName);
    bool sameview2 = view.Id == sameView1.Id;
    bool ViewExists = db.View.Any(v => v.ViewName == view.ViewName);
    if (ViewExists && !sameview2)
    {
        ModelState.AddModelError("ViewName", UI_Prototype_MVC_Resources.ErrorViewAlreadyExists);
        return View(view);
    }
    bool PathExists = db.View.Any(v => v.ViewPath == view.ViewPath);
    if (PathExists && !sameview2)
    {
        ModelState.AddModelError("ViewPath", UI_Prototype_MVC_Resources.ErrorPathAlreadyExists);
        return View(view);
    }
    if (ViewExists && PathExists && sameview2)
    {
        if (ModelState.IsValid)
        {
            db.Entry(view).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    return View(view);
}

下面是创建控制器动作的POST方法。这是正常工作,这只是供参考,它可以帮助任何人:

public ActionResult Create(View view)
{
    bool ViewExists = db.View.Any(v => v.ViewName == view.ViewName);
    if (ViewExists)
    {
        ModelState.AddModelError("ViewName", UI_Prototype_MVC_Resources.ErrorViewAlreadyExists);
        return View(view);
    }
    bool PathExists = db.View.Any(v => v.ViewPath == view.ViewPath);
    if (PathExists)
    {
        ModelState.AddModelError("ViewPath", UI_Prototype_MVC_Resources.ErrorPathAlreadyExists);
        return View(view);
    }
    if (!ViewExists && !PathExists)
    {
        if (ModelState.IsValid)
        {
            view.ViewContent = "<p>Initial Content</p>";
            db.View.Add(view);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    return View(view);
}

如果添加评论会有帮助,请让我知道,我会编辑。本质上它只是从资源文件中读取错误字符串。它也只是做了几个读取ViewName, ViewPath来检查记录是否已经存在。

在Edit controller动作的情况下,有一个额外的检查来拉回当前记录id,以验证如果我们改变了名称,记录仍然是相同的(因此它不匹配基于ViewName的现有记录)。

如果它有帮助,我确实对数据库中的ViewName和ViewPath字段有唯一的约束。如果字段不是唯一的,并且由于某些奇怪的原因没有在代码中处理,这些将在代码中导致异常。

我忍不住觉得这里太复杂了。

确保另一条记录没有包含相同的字段值

是的,你把它复杂化了,你可以通过使用

检查它是否有效来简化你的代码并减少数据库调用的数量
bool isViewNameInvalid = db.View.Any(v => v.ViewName == view.ViewName && v.Id != view.Id)

ViewPath也是如此。你还应该考虑在返回视图之前做这两个检查,这样如果用户有两个错误,他们就不需要为了通知第二个错误而做额外的提交。

还请注意,您的View sameView1 = db.View.First(v => v.ViewName == view.ViewName);行代码可能不会返回您期望的View记录(它可能是您编辑的记录或先前创建的具有相同ViewName值的记录),这可能导致意想不到的结果。

你的代码可以简化为
public ActionResult Edit([Bind(Include = "Id,ViewName,ViewPath,ViewContent")] View view)
{
    bool isViewNameInvalid = db.View.Any(v => v.ViewName == view.ViewName && v.Id != view.Id)
    if (isViewNameInvalid)
    {
        ModelState.AddModelError("ViewName", UI_Prototype_MVC_Resources.ErrorViewAlreadyExists);
    }
    bool isViewPathInvalid = db.View.Any(v => v.ViewPath == view.ViewPath && v.Id != view.Id)
    if (isViewPathInvalid)
    {
        ModelState.AddModelError("ViewName", UI_Prototype_MVC_Resources.ErrorPathAlreadyExists);
    }
    if (!ModelState.IsValid)
    {
        return View(view);
    }
    // Save and redirect
    db.Entry(view).State = EntityState.Modified;
    db.SaveChanges();
    return RedirectToAction("Index")
}

作为旁注,您可以考虑在ViewNameViewPath属性上实现RemoteAttribute,以提供客户端验证(参见如何:在ASP中实现远程验证)。净MVC)。在您的情况下,您将传递Id值为additionalFields

并且由于您的编辑数据,我建议您使用视图模型(并删除[Bind]属性-参考MVC中的ViewModel是什么?