从UI

时间:2016-02-04 08:41:24

标签: c# asp.net-mvc asp.net-mvc-5 nullreferenceexception

在我的数据库表中,我有一个列,在保存到数据库之前操作这些值。在将大量值插入表格之后,在开发的后期阶段添加了操作的逻辑。现在我想编辑表的内容来操纵现有内容的值。

我的方法

要在表格中的所有项目中调用编辑功能,因为操作逻辑也会添加到EDIT操作方法中。

当我循环遍历数据库中的内容时调用编辑函数时,我得到一个空引用异常,当我使用UI中的编辑功能时,它不存在。

编辑操作方法

    public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
    {
       //Many lines removed for simplicity. all the variables used in the code are assigned.
            if (ModelState.IsValid)
            {
                // Valuestring from another function

                setValue.Value = valuestring;
                var original = db.SetValue.Find(setValue.SetValueID);
                bool modified = original.Value != setValue.Value;
                if (modified)
                {
                    var rev = new RevisionHistory();
                    rev.CreatedOn = original.ModifiedOn;
                    rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
                    db.Entry(original).CurrentValues.SetValues(setValue);
                    db.RevisionHistory.Add(rev);
                }
                original.ModifiedOn = DateTime.Now;
                original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
                db.Entry(original).State = EntityState.Modified; 
                db.SaveChanges();
            }
    }

对此功能的调用是从HomeController进行的。我在从HomeController调用它时注释了EDIT方法中的所有return语句。

异常

  

对象引用未设置为对象的实例。

问题

为什么编辑在从UI调用时没有任何异常,但不是来自HomeController?

即使我从另一个控制器调用该函数,为什么用户为null?使用Windows身份验证。如果用户已经过身份验证,如何获取用户名?

编辑 - 添加了如何调用编辑功能

    //Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
    public ActionResult Index()
    {
        test();
        return View();
    }

    public void test()
    {
        foreach(var item in db.SetValue.ToList())
        {
            var setvalcon = new SetValuesController();
            setvalcon.Edit(item);

        }     
    }

更新 - 问题概述

可以推广该问题,以便找到使用Windows身份验证时如何定义User属性的答案。

只有从UI调用它时,控制器才能访问Windows用户属性吗?

是否无法在未通过UI调用的函数中访问用户属性?

就我所知,问题最好的是,用户在Controller中被定义,test方法被调用,但不在另一个控制器的Edit中。

2 个答案:

答案 0 :(得分:3)

当您说您只能访问仅从UI访问的User属性时,您是部分正确的。正确答案是 -

当通过UI访问Edit方法时,它实际上是通过ASP.Net控制器初始化程序管道,该环境负责当前的浏览器会话并分配User变量。目前可以使用HttpContext

但是当你创建像这样的控制器变量时 -

var setvalcon = new SetValuesController();
setvalcon.Edit(item);

您正在绕过所有这些初始化代码。没有HttpContext,这只是另一个普通对象,因此没有填充User属性。

回答问题:

  1. 只有从UI调用它时,控制器是否才能访问Windows用户属性?
  2. =>是的,绝对正确,因为您正在浏览ASP.Net管道。但它不仅适用于Windows用户,而是HttpContext中的所有内容。

    1. 是否无法在未通过UI调用的函数中访问用户属性?
    2. =>只有在您手动分配否则否。

      更多洞察力:

      基本上,你想要实现的是一个非常糟糕的设计。没有地方,记得无处,你应该从控制器内部调用控制器,除非它是基础方法调用的子类。从另一个控制器内部调用另一个控制器的唯一方法是使用“重定向”方法重定向执行。没有人会阻止你这样调用控制器,但这显示了糟糕的设计原则..

      解决问题的最佳方法就是这样 -

      public class ModelService {
          public void Edit(IPrincipal user, SetValue setValue){
              setValue.Value = valuestring;
                  var original = db.SetValue.Find(setValue.SetValueID);
                  bool modified = original.Value != setValue.Value;
                  if (modified)
                  {
                      var rev = new RevisionHistory();
                      rev.CreatedOn = original.ModifiedOn;
                      rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
                      db.Entry(original).CurrentValues.SetValues(setValue);
                      db.RevisionHistory.Add(rev);
                  }
                  original.ModifiedOn = DateTime.Now;
                  original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
                  db.Entry(original).State = EntityState.Modified; 
                  db.SaveChanges();
          }
      }
      

      然后在两个控制器构造函数中 -

      public class ControllerOne : Controller {
          private readonly ModelService _modelService
          //constructor
          public ControllerOne(){
               _modelService= new ModelService ();
          }
      
          public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
          {
              //Many lines removed for simplicity. all the variables used in the code are assigned.
              if (ModelState.IsValid)
              {
                  _modelService.Edit(User, Setvalue);
              }
          }
      
      //controller 2
      public class HomeController: Controller {
          private readonly ModelService _modelService
          //constructor
          public ControllerOne(){
               _modelService= new ModelService ();
          }
      
          public ActionResult Index()
          {        
              foreach(var item in db.SetValue.ToList())
              {
                  _modelService.Edit(User, item);
              }     
          }
      }
      

      您可以从IoC Container获取依赖注入的帮助,这是更好的方法。

答案 1 :(得分:1)

从另一个Controller中调用Controller可能会使某些数据(作为用户)丢失。我不会做太多努力来理解为什么(除非好奇),因为这样做可能被认为是糟糕的设计。 (我打赌其他一些信息也会丢失,也许是饼干等)。您可以做的更好的事情是将逻辑与控制器分离到某个服务层,并使用IPrincipal User作为参数调用方法。如果您被迫按照描述的方式进行操作,则将用户作为参数发送给另一个控制器。

public ActionResult Index()
{
    test(User);
    return View();
}
public void test(IPrincipal user)
{
    foreach(var item in db.SetValue.ToList())
    {
        var setvalcon = new SetValuesController();
        setvalcon.Edit(item, user);

    }     
}

和oter控制器

public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
   var currentUser = User == null? user : User;//or somthing like that
}

没有检查此代码。但它应该给你一些工作。