【问题标题】:Ensuring another record does not already contain the same value for a field确保另一条记录尚未包含字段的相同值
【发布时间】:2016-09-01 00:20:43
【问题描述】:

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

我有创建页面的功能(列出页面、删除页面等)

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

但是我的 ViewName 也必须是唯一的(数据库中不能有两个同名的视图)。我的 ViewPath 也必须是唯一的(你不能有两条相同的路线)。

在我的创建页面中,我进行了检查(所有工作)以确保使用唯一 ID 创建新视图,并在创建视图之前检查 ViewName 和 ViewPath 都是唯一的,否则返回相关错误。

所有这些都有效。

现在当我去编辑视图时。用户可以选择更改 ViewName 和 ViewPath。所以我们需要确保它不会被更改为已经存在的 ViewName 和 ViewPath。但是,当我们正在编辑视图时,视图本身已经存在,所以我们会收到这些错误。

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

问题是我打线时出现以下错误

db.Entry(view).State = EntityState.Modified;

错误是:

附加信息:附加类型为“xxx”的实体失败 因为另一个相同类型的实体已经有相同的主 核心价值。使用“附加”方法或设置时可能会发生这种情况 实体的状态为“未更改”或“已修改”,如果存在 该图具有冲突的键值。这可能是因为一些 实体是新的,尚未收到数据库生成的密钥 价值观。在这种情况下,使用“添加”方法或“添加”实体状态 跟踪图,然后将非新实体的状态设置为 “未更改”或“已修改”(视情况而定)。

问题: 我不知道我的解决方案是否过于复杂,如果是这样,有人可以通过示例提出更好的解决方案。

如果这是正确的“方法”,但我只是以某种方式错误地实现了它,有人可以告诉我如何解决它。

下面是我遇到问题的编辑控制器操作的 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);
}

下面是 Create 控制器动作的 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);
}

如果添加 cmets 有帮助,请告诉我,我会进行编辑。本质上它只是从资源文件中读取错误字符串。它也只是对 ViewName、ViewPath 进行几次读取以检查记录是否已存在。

在编辑控制器操作的情况下,有一个额外的检查来拉回当前记录 id,以验证如果我们更改名称,记录仍然是相同的(因此它不匹配基于现有记录在 ViewName 上)。

封装它对任何人都有帮助,我确实对数据库中的 ViewName 和 ViewPath 字段有独特的限制。在字段不唯一并且由于某些奇怪的原因未在代码中处理的情况下,这些将导致代码中的异常。

我不禁觉得这里太复杂了?

【问题讨论】:

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


【解决方案1】:

是的,你把它复杂化了,你可以通过检查它是否有效使用来简化你的代码并减少数据库调用的次数

bool isViewNameInvalid = db.View.Any(v => v.ViewName == view.ViewName && v.Id != view.Id)

ViewPath 也是如此。您还应该考虑在返回视图之前进行两项检查,因此如果用户同时遇到两个错误,他们不需要进行额外的提交来通知第二个错误。

另请注意,您的 View sameView1 = db.View.First(v =&gt; 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,以进行客户端验证(请参阅How to: Implement Remote Validation in ASP.NET MVC)。在您的情况下,您会将Id 值作为additionalFields 传递。

由于您正在编辑数据,我建议您使用视图模型(并删除 [Bind] 属性 - 请参阅 What is ViewModel in MVC?

【讨论】:

  • 非常感谢您对斯蒂芬的所有反馈。这比我预期的要多得多,我学到了很多。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-15
  • 1970-01-01
  • 1970-01-01
  • 2016-06-29
  • 1970-01-01
相关资源
最近更新 更多