Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User is null when Function not called from UI

In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.

My approach

To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.

When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.

EDIT Action method

    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();
            }
    }

The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.

Exception

Object reference not set to an instance of an object.

Question

Why does the edit work while called from the UI without any Exception , but not from HomeController?

Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?

EDIT - Added how the Edit function is called

    //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);

        }     
    }

Update - General Overview of the problem

The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.

Is it that the controller gets access to the Windows user property only when it is called from the UI?

Is it not possible to access the User Property in a function that is not called via UI?

And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.

like image 684
Vini Avatar asked Sep 26 '26 22:09

Vini


2 Answers

You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -

When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.

But when you are creating the controller variable like this -

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

You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.

ANSWER TO QUESTIONS:

  1. Is it that the controller gets access to the Windows user property only when it is called from the UI?

=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.

  1. Is it not possible to access the User Property in a function that is not called via UI?

=> Only if, you manually assign it otherwise NO.

MORE INSIGHT:

Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..

The best way to solve your situation is like this -

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();
    }
}

Then in both the controllers constructors -

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

You can take help from IoC Container for dependency injection, that is even better approach.

like image 98
brainless coder Avatar answered Sep 28 '26 13:09

brainless coder


Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.

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);

    }     
}

And the oter controller

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
}

Didn't check this code. but it should give you something to work with.

like image 33
Issac Avatar answered Sep 28 '26 13:09

Issac



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!