确保另一条记录尚未包含字段

时间:2016-09-01 00:20:44

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

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

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

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

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

在我的创建页面中,我检查(全部工作)以确保使用唯一ID创建新视图,并在创建视图之前检查ViewName和ViewPath是否唯一,如果不是,则返回相关错误。 / p>

这一切都有效。

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

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

问题是当我点击

时出现以下错误
db.Entry(view).State = EntityState.Modified;

错误是:

  

其他信息:附加类型' xxx'的实体失败   因为同一类型的另一个实体已经具有相同的主要实体   核心价值。使用'附加'方法或设置   一个实体的状态为“未变”'或者'修改'如果有任何实体   图表具有冲突的键值。这可能是因为一些   实体是新的,尚未收到数据库生成的密钥   值。在这种情况下,请使用'添加'方法或“添加”#39;实体国家   跟踪图形,然后将非新实体的状态设置为   '不变'或者'修改'酌情。

问题: 我不知道我是否过度使我的解决方案复杂化,如果是这样,有人可以通过一个例子来建议更好的解决方案。

如果这是正确的方式"但是我错误地以某种方式实现了它,有人可以告诉我如何修复它。

以下是我遇到问题的编辑控制器操作的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 controller操作的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进行了几次读取,以检查记录是否已经存在。

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

Encase it help any,我对DB中的ViewName和ViewPath字段有唯一的约束。在字段不唯一的情况下,这些将导致代码中的异常,并且由于某些奇怪的原因,它们不会在代码中处理。

我无法帮助认为这里有太多的复杂性?

1 个答案:

答案 0 :(得分:2)

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

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

作为旁注,您可以考虑在RemoteAttributeViewName属性上实施ViewPath,以便为您提供客户端验证(请参阅How to: Implement Remote Validation in ASP.NET MVC)。在您的情况下,您会将Id值作为additionalFields传递。

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