Asked  7 Months ago    Answers:  5   Viewed   46 times

I cannot figure out why my view only passes back a NULL for a model to my controller. This is for an Edit Post method. I checked other controllers with Edit Post methods that are structured the same way as this one and they work fine. It seems to be just this view and controller.

Here is my view:

@model Non_P21_Quote_System_v1._0.Models.gl_code

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Edit</h2>

@if (TempData["Message"] != null)
{
    <div style="color:green">
        @TempData["Message"]
    </div><br />
}
@if (ViewBag.error != null)
{
    <div style="color:red">
        <h3>@ViewBag.error</h3>
    </div><br />
}

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>gl_code</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.GL_code, "GL Code", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.GL_code, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.GL_code, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.GL_description, "Gl Description", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.GL_description, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.GL_description, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.expense_type_ID, "Expense", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("expense_type_ID", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.expense_type_ID, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.eag, "Employee Account Group", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("eag", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.eag, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "gl_Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Here is my controller method:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "ID,GL_code,GL_description,expense_type_ID,eag")] gl_code gl_code)
{
    if (ModelState.IsValid)
    {           
        db.Entry(gl_code).State = EntityState.Modified;
        await db.SaveChangesAsync();
        return RedirectToAction("gl_Index");      
    }
    ViewBag.eag = new SelectList(db.employee_account_group, "ID", "eag_name");
    ViewBag.expense_type_ID = new SelectList(db.expense_type, "ID", "type", gl_code.expense_type_ID);
    return View(gl_code);
}

When I debug it, I see the model being passed in is of value NULL. I am seeing this on the controller side at the the parameters part of the Edit method.

 Answers

55

Its null because your model contains a property named gl_code and you have also named the parameter for your model gl_code in the POST method.

Change the name of one or the other and the model will bind correctly.

What is happening internally is that the form submits a name/value pair for each successful form control, in your case gl_code=someValue. The DefaultModelBinder first initializes a new instance of your model. It then reads the form values and finds a match for the property in your model and sets it to someValue. But it also finds a match in the method parameters and tries set the value of the parameter to someValue, which fails (because you cannot do gl_code gl_code = "someValue";) and the model becomes null.

Tuesday, June 1, 2021
 
Litty
answered 7 Months ago
49

Well I have "solved" it, but I do not really understand why the changes I made have helped.

I had to do three things:

  1. Remove the json filter (filters don't bind)

  2. Change the contentType to application/json

    $.ajaxSetup({ contentType: "application/json; charset=utf-8" });

  3. Use the MVC futures download Microsoft.Mvc.dll as described here: http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx. Where is says to add this to Application_Start() in Global.asax.cs:

    ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());

Now I dont know exactly why that has worked, but it has.

Unfortunately it has had a negative side effect: the contenttype is applied to all $.get() and $.post() methods, and broken all my jqgrids - they only seem to work if the content type is the default of application/x-www-form-urlencoded

So I've asked 2 follow on questions:

  1. Is it possible to set the content type in a $.post() call? Then I wouldn't need to set it globally Jquery - How to make $.post() use contentType=application/json?

  2. Is it possible to make jqrid work if the contenttype is application/json? Jquery - How to make $.post() use contentType=application/json?

Monday, August 16, 2021
 
Silfverstrom
answered 4 Months ago
92

This is what's going on:

In your connection string you have the following setting: integrated security=True What this means is that the SQL Server connection will be authenticated with the credentials of the process which initiates the connection. Since you are running under IIS and IIS uses application pools, the connection will be authenticated with the Windows user which runs the application pool. By default this is a user with almost no permissions called NetworkService. NetworkService (or maybe in IIS7.5 it's a different one) will never have access rights to your database. The nuances of your particular scenario might be a bit different because there is a bunch of different security inheritances in IIS and a bunch of different users your process may end up, however, the basic problem is that you have integrated security=True and the user the IIS process is running with is a standard user with almost no rights.

To fix you have a few options:

  1. Change integrated security=True to usernamepassword authentication. This will solve it 100%, but you may not want to store your password clear text in the web.config file.
  2. In your IIS virtual directory settings, configure the anonymous user to be a meaningful one which has access rights to your db. This will help eventually, but you will have to play with different settings to get it right.

If you need more help with #2, you have to provide the following information:

  1. The identity of the AppPool
  2. The identity of the Virtual Directory and all the authentication settings of the virtual directory.
Sunday, August 22, 2021
 
Success Man
answered 4 Months ago
18

Try the following in your Global.asax:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // Web Forms default
    routes.MapPageRoute(
        "WebFormDefault",
        "",
        "~/default.aspx"
    );

    // MVC default
    routes.MapRoute(
        "Default",                          // Route name
        "{controller}/{action}/{id}",       // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }  // parameter default
    );
}

Also I don't think you'll need the .mvc portion of the Default route.

Thursday, October 14, 2021
 
knagode
answered 2 Months ago
70

Here's how you could proceed:

public class Something
{
    public Guid ID { get; set; }
    public DateTime LastModified { get; set; }
    public DateTime CreatedAt { get; set; }
    public string Owner { get; set; }
    public string Status { get; set; }
    public string Title { get; set; }
}

public interface ISomeRepository
{
    Something GetDocumentByID(Guid id);
    void SaveChanges();
}

public class HomeController : Controller
{
    private readonly ISomeRepository _repository;
    public HomeController(ISomeRepository repository)
    {
        _repository = repository;
    }

    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Edit(Something something)
    {
        if (ModelState.IsValid)
        {
            var _something = _repository.GetDocumentByID(something.ID);
            TryUpdateModel(_something);
            _something.LastModified = DateTime.Now;
            _repository.SaveChanges();
            return RedirectToAction("Index", "Home");
        }
        return View(something);
    }
}

and the test:

// arrange
var something = new Something
{
    ID = Guid.NewGuid(),
    CreatedAt = DateTime.Now,
    LastModified = DateTime.Now,
    Owner = "Me",
    Status = "new",
    Title = "my Title",
};
var somethingElse = new Something();
var repo = MockRepository.GenerateStub<ISomeRepository>();
var controller = new HomeController(repo);
new TestControllerBuilder().InitializeController(controller);
repo.Stub(x => x.GetDocumentByID(something.ID)).Return(somethingElse);

var formValues = new FormCollection() 
{
    { "Owner", "some owner" },
};
controller.ValueProvider = formValues.ToValueProvider();

// act
var actual = controller.Edit(something);

// assert
repo.AssertWasCalled(x => x.SaveChanges());
actual
    .AssertActionRedirect()
    .ToAction<HomeController>(x => x.Index());
Friday, October 22, 2021
 
Jeff
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share