0

I'm sorry if this ends up simple or stupid question, I'm very new to MVC and Razor, being dropped in deep end with no formal training on anything current...

I don't understand why the data in my model is available in my first view is not available in my second view.

Controller initiates an instance of model using code

Product _Product = new Product();

then the first view is called after a bunch of initial data queries have ran. The view is called using

return View("Attributes", _Product)

to call the view passing it the _Product instance of model.

My first view does many things but relevant to this question it shows a table using foreach row on dataset filled by SQL on controller stored on model.

@foreach(DataRow row in Model.attList.Tables[0].Rows)

The view has multiple buttons, such as add, delete, edit... Each have their own action assigned such as

<input type="submit" class="btn btn-primary" name="action:btnDelete" value="Delete" />

If the user presses one of the buttons on that view that triggers HTTPPOST. One of them calls a new view but if I pass my model to that view again, the data is now blank? I am passing the model to the view, and accepting it in HTTPPOST and passing it to the next view (or so I thought).

I'm using very generic beginning to form

@using (Html.BeginForm()) {

The code in controller for this button is very basic

[HttpPost] 
[MultipleButton(Name = "action", Argument = "btnEditValues")] 
public ActionResult btnEditValues(Product _Product) 
{ 
    return View("btnEditValues", _Product); 
}

But then in that 2nd view if I try to re-display the same table, it now says my Model.attList is empty. I added quick script of console.log(@Model.attList); and when I click the button it is showing the 2nd view but the console is logging a blank value (not NULL, just blank)...

I'm sure I'm just missing something simple - or perhaps I'm trying to do something I can't ???


The following was copied/pasted, without edit, into this question from an errantly posted "answer" which was intended to be an edit on the question. The answer has been flagged and should be removed.

Per David's Request.

Here is complete M, V, V, and C (didn't include other 2 views they don't use Model and both work)...

!!! NOTE !!!: This code is being used somewhere everyone has SQL manager and Admin rights to SQL server. As such SQL parameters were not used. This code would be vulnerable to SQL injection if used on open Website.

Model

namespace P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
{
    public class Product
    {
        // Variables used between views
        public int RowSelected { get; set; }

        // Declare datasets to use as list
        public DataSet attList { get; set; }
        public DataSet lowList { get; set; }
    }
}

Controller

namespace P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Controllers
{
    #region Multiple Buttons
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class MultipleButtonAttribute : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public string Argument { get; set; }

        public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
        {
            var isValidName = false;
            var keyValue = string.Format("{0}:{1}", Name, Argument);
            var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

            if (value != null)
            {
                controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
                isValidName = true;
            }

            return isValidName;
        }
    }
    #endregion


    public class KCDA_ItemMaint_AttributesController : BaseRuleController
    {
        #region public variables
        // public dataset for loading SQL values
        DataSet attload = new DataSet();
        DataSet lowload = new DataSet();
        #endregion

        #region Main Action
        // GET: KCDA_ItemMaint_Attributes/KCDA_ItemMaint_Attributes
        public ActionResult Attributes()
        {

            if (Data.Fields["item_id"].FieldValue == string.Empty)
            {
                // Report error and prevent form pop-up if no product group has been selected
                Rule.RuleResult.Success = false;
                Rule.RuleResult.Message = "You must first select an Item before listing Attributes";
                return RedirectToAction("Close", "Initialize", new { area = "" });
            }
            else
            {

                try
                {
                    // Create container and setup Group values
                    Product _Product = new Product();

                    //Get Attributes for selected item
                    LoadAttributes();
                    _Product.attList = attload.Copy();

                    //Get ECom Server Side Attribute for selected item
                    LoadLower();
                    _Product.lowList = lowload.Copy();

                    return View("Attributes", _Product);

                }
                catch (Exception ex)
                {
                    //catch the error and send it to the Error view with the HandleErrorInfo
                    return View("Error", new HandleErrorInfo(ex, "KCDA_ItemMaint_Attributes", "Attributes"));
                }

            }
        }
        #endregion

        #region Buttons
        [HttpPost]
        [MultipleButton(Name = "action", Argument = "btnDelete")]
        public ActionResult btnDelete(Product _Product)
        {
            // create SQL command to delete the 
            string sqlDelete = "UPDATE BL_ProductAttribute SET DeleteFlag = '1' WHERE [KEY] = '" + _Product.RowSelected + "'";

            // Run the sqlDELETE command
            SqlCommand cmdDelete = new SqlCommand(sqlDelete, P21SqlConnection);
            cmdDelete.ExecuteNonQuery();
            SqlDataAdapter daDelete = new SqlDataAdapter(cmdDelete);

            return View("btnDelete", _Product);
        }

        [HttpPost]
        [MultipleButton(Name = "action", Argument = "btnAdd")]
        public ActionResult btnAdd(Product _Product)
        {
            // Retrieve selected/loaded drop-down values
            string ddGroup = Request["ApplyGroup"];
            string ddName = Request["AttributeName"];
            string ddValue = Request["AttributeValue"];

            if (ddValue == "")
            {
                ViewBag.msg = "No Value Selected";
            }
            else
            {
                // default duplicate count to 0
                int duplicate = 0;

                // create SQL command to check for duplicate attribute 
                string sqlDuplicate = "SELECT COUNT(1) FROM BL_ProductAttribute " +
                                      "WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' " +
                                      "AND AttributeGroupName = '" + ddGroup + "'";

                // Run the sqlDuplicate command
                SqlCommand cmdDuplicate = new SqlCommand(sqlDuplicate, P21SqlConnection);
                cmdDuplicate.CommandType = CommandType.Text;
                SqlDataAdapter daDuplicate = new SqlDataAdapter(cmdDuplicate);

                // Create dataset from duplicate check
                DataTable dupcheck = new DataTable();
                daDuplicate.Fill(dupcheck);

                // Set count if exists
                duplicate = int.Parse(dupcheck.Rows[0][0].ToString());

                // if exists update/undelete otherwise insert
                if (duplicate > 0)
                {
                    // create SQL command to update the attribute 
                    string sqlAdd = "UPDATE BL_ProductAttribute " +
                                    "SET BL_ProductAttribute.Value = '" + ddValue.Replace("'", "''") + "', " +
                                    "BL_ProductAttribute.AttributeTitle = '" + ddName + "', " +
                                    "BL_ProductAttribute.DeleteFlag = 0, " +
                                    "BL_ProductAttribute.ProductID = '" + Data.Fields["product_group_id"].FieldValue + "' " +
                                    "FROM BL_ProductAttribute " +
                                    "WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' AND AttributeGroupName = '" + ddGroup + "' ";

                    // Run the sqlAdd command
                    SqlCommand cmdAdd = new SqlCommand(sqlAdd, P21SqlConnection);
                    cmdAdd.ExecuteNonQuery();
                    SqlDataAdapter daAdd = new SqlDataAdapter(cmdAdd);

                    ViewBag.msg = "Record Updated";
                }
                else
                {
                    // If adding determine next key value for unique ID
                    string newKey = string.Empty;

                    // create SQL command to get next KEY value for insert reset current maxkey
                    string sqlMax2 = "SELECT max([key])+1 FROM BL_AttributeEnumValue";

                    // Run the sqlMax command
                    SqlCommand cmdKey2 = new SqlCommand(sqlMax2, P21SqlConnection);
                    cmdKey2.CommandType = CommandType.Text;
                    SqlDataAdapter daKey2 = new SqlDataAdapter(cmdKey2);

                    // Create dataset from newKey check and assign to newKey 
                    DataTable KeyCheck2 = new DataTable();
                    daKey2.Fill(KeyCheck2);
                    newKey = KeyCheck2.Rows[0][0].ToString();

                    // create SQL command to update the attribute 
                    string sqlAdd = "INSERT INTO BL_ProductAttribute ([Key], ProductId, SKU, AttributeTitle, " +
                                    "isSKUlevel, isRequired, isDefault, Value, AttributeGroupName, DeleteFlag) " +
                                    "VALUES('" + newKey + "', '" + Data.Fields["product_group_id"].FieldValue + "', '" +
                                    Data.Fields["item_id"].FieldValue + "', '" + ddName + "', 1, 1, 1, '" +
                                    ddValue.Replace("'", "''") + "', '" + ddGroup + "', 0)";

                    // Run the sqlAdd command
                    SqlCommand cmdAdd = new SqlCommand(sqlAdd, P21SqlConnection);
                    cmdAdd.ExecuteNonQuery();
                    SqlDataAdapter daAdd = new SqlDataAdapter(cmdAdd);

                    ViewBag.msg = "Record Added";
                }

            }

            return View("btnAdd", _Product);
        }

        [HttpPost]
        [MultipleButton(Name = "action", Argument = "btnEditValues")]
        public ActionResult btnEditValues(Product _Product)
        {
            return View("btnEditValues", _Product);
        }
        #endregion

        #region SQL Loads
        private void LoadAttributes()
        {          
            // Define SQL select command
            string sqlAttributes = "SELECT * FROM BL_ProductAttribute " +
                                   "WHERE SKU = '" + Data.Fields["item_id"].FieldValue + "' AND DeleteFlag = '0' " +
                                   " AND AttributeGroupName in ('SKU_Color', 'SKU_SelectableAttribute_1', 'SKU_SelectableAttribute_2')";

            // Set SQL command type to text and run it
            SqlCommand cmdlist = new SqlCommand(sqlAttributes, P21SqlConnection);
            cmdlist.CommandType = CommandType.Text;
            SqlDataAdapter dalist = new SqlDataAdapter(cmdlist);

            // Load results from SQL into DataSet
            dalist.Fill(attload);
        }

        private void LoadLower()
        {           
            string DBconn = "vsldb1";

            // Define SQL select command
            string sqllist = "SELECT [Key], ProductID, SKU, AttributeTitle, isSKUlevel, isRequired, isDefault, " +
                                    "\"Value\" = Case " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '0') then '0 - Warehouse Regular Item' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '1') then '1 - Not on Website/Pending' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '2') then '2 - RFQ' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '3') then '3 - Limited Quote' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '4') then '4 - Discontinued/Obsolete' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '5') then '5 - Specials' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '6') then '6 - Direct Ship' " +
            "when (AttributeTitle = 'KCDASKUStatus' and ltrim(convert(varchar,Value)) = '7') then '7 - Offline' " +
            "else value end, AttributeGroupName, UpdateFlag  FROM OPENDATASOURCE('SQLOLEDB','Data Source=" + DBconn + ";user " +
                                   "id=sa;password=KCDAAdmin').KCDA.dbo.KCDA_ProductAttribute PA" +
                                   " WHERE PA.SKU = '" + Data.Fields["item_id"].FieldValue + "' AND PA.AttributeGroupName not in " +
                                   "('SKU_Color', 'SKU_SelectableAttribute_1', 'SKU_SelectableAttribute_2')";

            // Set SQL command type to text and run it
            SqlCommand cmdlist = new SqlCommand(sqllist, P21SqlConnection);
            cmdlist.CommandType = CommandType.Text;
            SqlDataAdapter dalist = new SqlDataAdapter(cmdlist);

            // Load results from SQL into DataSet
            dalist.Fill(lowload);
        }
        #endregion

        #region Drop Downs
        [HttpPost]
        public ActionResult GetAttributeNames(string selectedOption)
        {

            // Define variables for JSON query to use
            JsonResult result = new JsonResult();
            List<string> attNames = new List<string>();
            List<string> attValues = new List<string>();

            if (selectedOption != null)
            {
                // SQL to get attribute name for the selected attribute group for this product group 
                string sql = "SELECT Title FROM BL_Attribute (NOLOCK) WHERE BL_Attribute.DeleteFlag = '0' AND BL_Attribute.AttributeGroupName = '" + selectedOption + "'" +
                                 " AND BL_Attribute.Title in (select AD.AttributeTitle from BL_AttributeDept AD where AD.product_group_id = '" + Data.Fields["product_group_id"].FieldValue.Substring(0, 2) +"')";

                using (SqlCommand selectAttNames = new SqlCommand(sql, P21SqlConnection))
                {
                    using (SqlDataReader reader = selectAttNames.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            attNames.Add(reader["Title"].ToString());
                        }            
                    }
                }

                // SQL to get list of current available values for this attribute type
                string sql2 = "SELECT Value FROM BL_AttributeEnumValue (NOLOCK) WHERE DeleteFlag = '0' and AttributeTitle = '" + 
                              attNames[0] + "' ORDER BY Value";

                using (SqlCommand selectAttValues = new SqlCommand(sql2, P21SqlConnection))
                {
                    using (SqlDataReader reader2 = selectAttValues.ExecuteReader())
                    {
                        while (reader2.Read())
                        {
                            attValues.Add(reader2["Value"].ToString());
                        }
                    }
                }

                // define return object 
                var retObj = new
                {
                    retNames = attNames,
                    retValues = attValues
                };
                return Json(retObj, JsonRequestBehavior.AllowGet);

            }

            return Json(new { Success = "false" });
        }


        #endregion

        #region Edit Values



        #endregion

        #region Close Rule
        [HttpPost]
        public ActionResult Return()
        {
            Rule.RuleResult.Success = true;

            //IMPORTANT - This is what returns the Visual Rule control back to the server
            //DO NOT REMOVE
            return RedirectToAction("Close", "Initialize", new { area = "" });
        }
        #endregion
    }

}

Attributes View

@using P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
@using System.Data


@model Product


@{
    ViewBag.Title = "Attributes";
    Layout = "~/Views/Shared/_VisualRuleLayout.cshtml";

    var listAttGroups = new List<SelectListItem>
    {
        new SelectListItem { Text = "SKU_Color", Value = "SKU_Color"},
        new SelectListItem { Text = "SKU Select Att 1", Value = "SKU_SelectableAttribute_1"},
        new SelectListItem { Text = "SKU Select Att 2", Value = "SKU_SelectableAttribute_2"}
    };
}

@section scripts{
    <script>
        $(function () {
            $("#ApplyGroup").change(function () {
                var option = $(this).val();

                //Clear and activate 2nd and 3rd drop down
                $("#AttributeName").empty();
                $("#AttributeValue").empty();
                $("#AttributeName").prop('disabled', false);
                $("#AttributeValue").prop('disabled', false);

                var url = "GetAttributeNames?selectedOption=" + option;

                $.post(url, function (retObj) {
                    $.each(retObj.retNames, function (i, attName) {
                        $("#AttributeName").append($('<option></option>').val(attName).html(attName));
                    });
                    $.each(retObj.retValues, function (i, attValue) {
                        $("#AttributeValue").append($('<option></option>').val(attValue).html(attValue));
                    });
                });
            });
        });
    </script>
}

@using (Html.BeginForm())
{
        <div class="container">
        <div class="row">
            <label class="col-md-3 control-label" for="ApplyGroup" id="lblApplyGroup">Attribute Group</label>
            <label class="col-md-3 control-label" for="AttributeName" id="lblAttributeName">Attribute Name</label>
            <label class="col-md-2 control-label" for="AttributeValue" id="lblAttributeValue">Attribute Value</label>
            <div class="col-md-2">
                <input type="submit" class="btn btn-primary" name="action:btnEditValues" value="Edit Values" />
            </div>
        </div>
        <div class="row" style="padding-top:5px">
            <div class="col-md-3">
                @Html.DropDownList("ApplyGroup", listAttGroups, "Select Group", new { @id = "ApplyGroup", @class = "form-control" })
            </div>
            <div class="col-md-3">
                @Html.DropDownList("AttributeName", new List<SelectListItem>(), new { @id = "AttributeName", @class = "form-control", disabled = "disabled" })
            </div>
            <div class="col-md-3">
                @Html.DropDownList("AttributeValue", new List<SelectListItem>(), new { @id = "AttributeValue", @class = "form-control", disabled = "disabled" })
            </div>
            <div class="col-md-3">
                <span class="pull-right">
                    <input type="submit" class="btn btn-primary" name="action:btnAdd" value="Add\Update" />
                </span>
            </div>
        </div>
        <div class="row" style="padding-top:20px">
            <div class="col-md-10">

            </div>
            <div class="col-md-2">
                <span class="pull-right">
                    <input type="submit" class="btn btn-primary" name="action:btnDelete" value="Delete" />
                </span>
            </div>
        </div>

        <div class="row" style="padding-top:10px">
            <div class="col-md-12">
                <table id="attTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
                    <tbody>
                        <tr style="background-color: #F0F8FF;">
                            <th></th>
                            <th>Dept</th>
                            <th>Item #</th>
                            <th>Attribute Name</th>
                            <th>SKU Level</th>
                            <th>Required</th>
                            <th>Default</th>
                            <th>Attribute Value</th>
                            <th>Attribute Group</th>
                        </tr>
                        @foreach (DataRow row in Model.attList.Tables[0].Rows)
                        {
                            <tr class="selectable-row">
                                <td>@Html.RadioButtonFor(m => Model.RowSelected, row[0])</td>
                                <td>@row[1]</td>
                                <td>@row[2]</td>
                                <td>@row[3]</td>
                                <td>@row[4]</td>
                                <td>@row[5]</td>
                                <td>@row[6]</td>
                                <td>@row[7]</td>
                                <td>@row[8]</td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        </div>

        <div class="row" style="padding-top:50px">
            <div class="col-md-12">
                <table id="lowTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
                    <tbody>
                        <tr style="background-color: #F0F8FF;">
                            <th>Dept</th>
                            <th>Item #</th>
                            <th>Attribute Name</th>
                            <th>SKU Level</th>
                            <th>Required</th>
                            <th>Default</th>
                            <th>Attribute Value</th>
                            <th>Attribute Group</th>
                        </tr>
                        @foreach (DataRow row in Model.lowList.Tables[0].Rows)
                        {
                            <tr class="selectable-row">
                                <td>@row[1]</td>
                                <td>@row[2]</td>
                                <td>@row[3]</td>
                                <td>@row[4]</td>
                                <td>@row[5]</td>
                                <td>@row[6]</td>
                                <td>@row[7]</td>
                                <td>@row[8]</td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        </div>
    </div>
}

btnEditValues View

@using P21.Rules.Visual.Areas.KCDA_ItemMaint_Attributes.Models
@using System.Data


@model Product

@{
    ViewBag.Title = "btnEditValues";
    Layout = "~/Views/Shared/_VisualRuleLayout.cshtml";

    var listAttGroups = new List<SelectListItem>
    {
        new SelectListItem { Text = "SKU_Color", Value = "SKU_Color"},
        new SelectListItem { Text = "SKU Select Att 1", Value = "SKU_SelectableAttribute_1"},
        new SelectListItem { Text = "SKU Select Att 2", Value = "SKU_SelectableAttribute_2"}
    };
}

@section scripts{
    <script>
        console.log(@Model.attList);
    </script>
}

<p align="right"><button type="button" class="btn btn-primary" onclick="location.href='@Url.Action("Attributes", "KCDA_ItemMaint_Attributes")'">X</button></p>
<center><h2>Edit Item Attribute Availble Values</h2></center>

@using (Html.BeginForm())
{
    <div class="container">
        <div class="row">
            <label class="col-md-4 control-label" for="EditApplyGroup" id="lblEApplyGroup">Attribute Group</label>
            <label class="col-md-4 control-label" for="EditAttributeName" id="lblEAttributeName">Attribute Name</label>
        </div>
        <div class="row" style="padding-top:5px">
            <div class="col-md-4">
                @Html.DropDownList("EditApplyGroup", listAttGroups, "Select Group", new { @id = "EditApplyGroup", @class = "form-control" })
            </div>
            <div class="col-md-4">
                @Html.DropDownList("EditAttributeName", new List<SelectListItem>(), "Select Name", new { @id = "EditAttributeName", @class = "form-control", disabled = "disabled" })
            </div>
        </div>

        <div class="row" style="padding-top:10px">
            <div class="col-md-12">
                <table id="attEditTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
                    <tbody>
                        <tr style="background-color: #F0F8FF;">
                            <th></th>
                            <th>Attribute Value</th>
                            <th>Item Count</th>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Bert
  • 35
  • 1
  • 1
  • 9
  • Are you coming from a WebForms background? The ASP.NET MVC framework doesn't work the same way, even though it's built on top of ASP.NET. – Tieson T. Oct 24 '19 at 20:37
  • You're putting a lot of effort into trying to describe what you think the code should be doing, but that isn't going to get you very far. Instead, create a minimal and simple example which demonstrates the problem. A controller with a couple action methods, a model, a couple small views. We can help identify the problem if we can see code which demonstrates the problem. But we can't tell you what's wrong with your code if you're only telling us what *you think* the code *should be* doing. – David Oct 24 '19 at 20:43
  • @Tieson - no like I explained, nothing current... Last training I received was on COBOL. – Bert Oct 24 '19 at 20:44
  • @David - those were actual code samples, sorry if not enough... Everything is working just as described. – Bert Oct 24 '19 at 20:45
  • @Bert: *"I am passing the model to the view, and accepting it in HTTPPOST and passing it to the next view (or so I thought)"* - is not a code sample. – David Oct 24 '19 at 20:46
  • @David - can you try reading a little further, that exact code is shown, but here it is again: ``` [HTTPPOST] [MultipleButton(Name = "action", Argument = "btnEditValues")] public ActionResult btnEditValues(Product _Product) { return View("btnEditValues", _Product) } ``` – Bert Oct 24 '19 at 20:48
  • @Bert: Can *you try* providing a more complete example? That method, which I did indeed read the first time, simply returns a view with whatever model is being passed to it. Is anything being passed to that action in the POST? If the `Product` model is empty then that would imply that either nothing is being sent or something is being sent in the wrong format and the model binding can't create a `Product` object from it. – David Oct 24 '19 at 20:52
  • @Bert I cleaned up your question a bit - code fences are more of a Github thing than Stack Overflow, which uses a slightly different version of Markdown. Basically, indent "code" four spaces to create a code block, or use the `{}` button. For inline code samples, use a single backtick. – Tieson T. Oct 24 '19 at 20:59
  • @David - sorry you are having trouble reading my post... As I explained I called the first view using the statement `return View("Attributes", _Product);` I then used the Model _Product.attList in that view to successfully display a table full of information (like 5 or 6 rows and 9 columns per row). When on that view that is displaying that table I have multiple WORKING buttons that do multiple different things. When I specifically click the button `` why is Model.attList now blank? – Bert Oct 24 '19 at 21:01
  • @David - sorry I just followed on-screen editing instructions to use ''' ''' for code... – Bert Oct 24 '19 at 21:04
  • @Bert: Currently all you're showing here is just that button. Nothing more. So there isn't much I or anybody else here can really tell you about the form in your HTML that's posting to that action. The behavior described strongly implies that the form doesn't include the data you are expecting it to. Other buttons are working? That's certainly great, but essentially immaterial to the problem. Nobody here is having trouble reading your post. But you are certainly having trouble demonstrating the problem. As it stands, it seems that your form just doesn't have the data you assume it does. – David Oct 24 '19 at 21:06
  • @Bert: Perhaps you could do some debugging in your browser's debugging tools. On the Network tab, capture and observe the POST request being made. Does it contain all of the data for the `Product` model? If not, what data does it contain? What data do you expect it to contain? Why? – David Oct 24 '19 at 21:09
  • @David - all relevant information is indeed there, but to make you happy I'll post a fake answer showing more code - ALL of which is 100% irrelevant to the issue. Nobody is talking about forms even - so your newest response is just odd... Give me few minutes to copy and paste it all... – Bert Oct 24 '19 at 21:11
  • @David - I told you, console.log is showing it as having values in first view but blank in second view... Not sure what other debugging or observing you mean or suggest. – Bert Oct 24 '19 at 21:13
  • @Bert: *"I'll post a fake answer"* - Please don't. To add information to your question, use the "edit" functionality on the question. *"Nobody is talking about forms even"* - Except for the part in your question where you talk about a form: *"I'm using very generic beginning to form"* Please, if this has come across as hostility then that certainly wasn't my intent. I'm trying to uncover information about the problem. Arguing with someone who is trying to help you is counterproductive to that help. *"what other debugging or observing you mean"* - Chrome developer tools, specifically. – David Oct 24 '19 at 21:16
  • 1
    @Bert: To elaborate, a submit button in HTML generally posts a form from that HTML. That form contains the data being sent to the server. Currently, none of the view code shown in the question indicates that the form contains any data to send to the server. If the resulting `Product` model on the server is `null`, that supports this conclusion. You can use the Network tab in your browser's debugging tools to observe the HTTP POST request and what data it contains. You can also examine your view's code to see if your are indeed including the `Product` values you expect. – David Oct 24 '19 at 21:19

2 Answers2

2

If your ultimate question is, "how can I post this model with all of it's data?",

public class Product
{
    // Variables used between views
    public int RowSelected { get; set; }

    // Declare datasets to use as list
    public DataSet attList { get; set; }
    public DataSet lowList { get; set; }
}

then the only real answer is "with a LOT of work." The answer that follows is probably mostly opinionated, so not really a great SO answer, but reflects my experience.

Basically, this controller action:

[HttpPost] 
[MultipleButton(Name = "action", Argument = "btnEditValues")] 
public ActionResult btnEditValues(Product _Product) 
{ 
    // ... other code

    return View("btnEditValues", _Product); 
}

expects the model binder to be able to construct an instance of the Product class. To do so, it needs to have every public property be present as a form value in the HTTP request that was made to this controller action. Those form values come from what are called successful controls, which is basically any form element (<input />, <select>, or <textarea>, generally) which has a name (the name="propertynamehere" attribute). If the field's not present, it's value doesn't exist.

Given how complex the DataSet class is, I would not recommend attempting to build enough form fields to successfully get your data back from the model binder. The only thing you really should care about is RowSelected - that lets you fetch the data you need from the database. Given that an SQL connection and query are generally pretty fast, it's probably a better user experience, as well, since posting enough data to repopulate a DataSet requires A LOT of form values (you might even run into a issue with exceeding the maximum allowed number of fields, although that is configurable in ASP.NET). If that makes sense and sounds feasible, I can elaborate on how to refactor at least this action to match how MVC is intended to work.

As a note, if the goal is to take a user to an edit page, that makes more sense as a GET request; loading an edit screen is an idempotent action, which matches the semantics of a GET. POST is normally meant to be used to update values, which you're not doing there.

You might find this useful, as it describes the recommended pattern to follow (PRG, or POST-Redirect-GET): https://en.wikipedia.org/wiki/Post/Redirect/Get

Of course, if the material you're learning from is on the older side, ASP.NET (via WebForms) used POST to transfer something called ViewState, which was/is used to give the illusion of state within a web application - the idea was "Windows Forms for the Web", but it adds a lot of overhead that you really don't want in a modern aplication. Given your background, it (WebForms) might be a better fit, though, since it lets you focus on an event-driven development model.

Tieson T.
  • 20,774
  • 6
  • 77
  • 92
  • Thanks for round about way of answering my original post's questions both that the data isn't persistent and my direct question of "or perhaps I'm trying to do something I can't ???" - I thought the model data was persistent, or if not that I could simply assign the value of that data easily to something that would be posted back. Thanks for clarifying for this newb... – Bert Oct 25 '19 at 03:55
  • To clarify since you have asked/commented about my background, up until 3 months ago I had never even heard of MVC or Razor. I've only used SQL to do limited report queries via Excel. I've never used HTML or Java before. In September I was given a play environment and 5 .net customizations currently being done via Windows forms for our fat-client ERP to convert to web-based visual forms because our ERP will be switched to 100% browser based by December. – Bert Oct 25 '19 at 04:04
  • If you have the time and / or inclination, I would suggest creating a simple WebForms application. If working with that programming model makes more sense, you'll be a lot more productive. You don't have to go the MVC route just because the cool kids are doing it. That, and since MVC is built on top of ASP.NET, you can slowly convert the app to MVC as you become more familiar with the terminology and conventions of the pattern. – Tieson T. Oct 25 '19 at 04:22
  • Nice thought, but don't have time to learn something else. Likewise, only MVC is supported. Specifically, our ERP only supports customizations being done via adding a new "Area" to their already existing MVC "Visual_Rule" project. – Bert Oct 25 '19 at 12:11
1

The form doesn't have any data for constructing the model.

Take a step back and consider the HTTP requests being made. An HTML <form>, when submitted, requests the action URL (as a POST request in this case) and includes whatever form values are present in that form. This likely does not include disabled form elements. (I could be mistaken on that.) And it definitely does not include the HTML of the form.

(You've indicated that you didn't come from a WebForms background, but coincidentally enough this is a very common mistake for people who did.)

With the exception of the buttons, the only form elements I'm seeing are three drop down lists (ApplyGroup, AttributeName, AttributeValue) as well as the radio buttons being emitted in the loop, called RowSelected.

But the model needs more values to be created:

public int RowSelected { get; set; }

// Declare datasets to use as list
public DataSet attList { get; set; }
public DataSet lowList { get; set; }

The model has RowSelected, and you can debug to confirm if that value is being correctly populated. (Your question indicates only that you're trying to debug the presence of the DataSet properties, not the RowSelected property.)

But including all data from two DataSet properties would be much more complex. And, perhaps more to the point, would be entirely unnecessary. Take a step back and consider that you'd be filling the page with data that you don't expect the user to edit and then posting all of that data back to the server that it just came from.

In your action you can get that RowSelected property in a variety of ways. As a method argument, as a model property, or simply as you currently get form values in your other action methods:

string rowSelected = Request["RowSelected"];

You can use int.TryParse() to convert that to an integer value if needed. Though the SQL code you're using is concatenating strings so you may not need to anyway.

(Side note: It's good that you're already aware that SQL injection is a bad thing. Even though in your particular case it's not necessarily a security problem, please be aware that SQL injection is also a very common source of bugs. It's worth learning the correct way of doing things.)

If you can get the fully-populated model from the database using an identifier, that would be ideal. It's certainly easier to just pass around the identifier than it is to pass around an entire complex model, especially if you're not expecting the user to edit that model on that page.

All in all, it looks like you're simply expecting the entire model to be posted to the server when really all you're posting is the identifier for the model. Which, fortunately enough, is all you actually need. Just use that identifier to fetch the intended record.

David
  • 208,112
  • 36
  • 198
  • 279
  • @Bert: Can you elaborate? Your HTML contains more markup, but where are you seeing more "form elements"? (inputs, selects, textareas, etc.) Where are you observing the HTTP POST request containing additional data? – David Oct 24 '19 at 21:50
  • @Bert: I just saw the radio button in the loop, so you may have the identifier already. I'll update the answer to indicate that. But the rest of the answer would still apply, you'd need to get that identifier and use it to fetch the data. The rest of the model data, particularly the `DataSet` properties, is definitely not included in the form. (Nor should it be.) – David Oct 24 '19 at 21:53
  • As stated clearly, all other functions work currently. That includes btnAdd; which as shown in controller users Request[""] for 3 different values. Likewise you seem focused on RowSelected, which is ALSO working already. My btnDelete SQL statement uses _Product.RowSelected without problems. – Bert Oct 24 '19 at 21:53
  • @Bert: *"My btnDelete SQL statement uses _Product.RowSelected without problems."* - Then this is great. You can use `RowSelected` to fetch the model you want from the data. (Before you delete it, of course. Your `btnDelete` action appears to also want it.) All in all it just sounds like you've mistakenly assumed that the view is posting *the entire model* to the server, but it definitely isn't. (And shouldn't in this case.) If you're already successfully posting an identifier for the record then that's exactly what you want. Just use that identifier to fetch the record from the data. – David Oct 24 '19 at 22:00
  • Huh, that doesn't answer my question at all? – Bert Oct 24 '19 at 22:07
  • @Bert: My understanding of the question is that you're asking why the model is "cleared", because you're observing that the posted model is empty of most of its data. Is that correct? That's the point I'm trying to make, really. The form doesn't post all of that data, it only posts a few fields. The framework can't re-construct the entire model from just those fields. But, since one of those fields is an identifier for the record in the database, you can use that identifier to get your model from the database. Is that a misunderstanding of your question? – David Oct 24 '19 at 22:10
  • Yup... I asked why Model getting blanked out at POST, then went into detail (more than once) that I was "specifically asking" about my Model.attList value that was used to show a table in my first view but is now blank when trying to show that same table in "Edit" view. @Model.attList has values in it while in the view "Attributes", my question is why is it being blanked out and is there a way to prevent it? Or are you saying it can't be retained and I'd have to keep running that SQL currently in my main controller over and over to reload the DataSet each time a diff view needs it? – Bert Oct 24 '19 at 22:24
  • 1
    @Bert: It's being "blanked out" because that data isn't being posted to the server. Each request to the server is independent of previous requests. The server-side code *only* has the data being sent in the HTTP request, regardless of what data was sent to the view in the previous request. You *could* fill the view with form elements (hidden fields, text boxes, drop down lists, etc.) for all of your model's data. But why? If the user isn't meant to edit it in that view, there's no need. The ideal and correct approach for this operation is to just pass along the ID and re-fetch from the DB. – David Oct 24 '19 at 22:28
  • Could make sense to not post all data back by default, but I'm trying to ask it to post Model back... The data is there for user to see in both views. Its already in the form being used (as display info), why not allow it to be passed back to webserver to prevent webserver from having to contact SQL server again. OK anyway reasoning aside - so you mention 2 possible answers, could you elaborate on either... 1. Hidden fields ??? How would I use a hidden field to pass the @Model.attList value to the HTTPPOST btnEditValues ??? 2. How to pass ID of Model to re-fetch??? Huh again sorry new... – Bert Oct 24 '19 at 22:43
  • @Bert: Basically you'd fill your loop not with not just output (like `@row[1]`) but with form elements. Something like `@Html.TextBoxFor(p => row[1])`, though it would have to be more complex than that because the form needs to know the exact name to emit to the page for the model binder to use on the return to the server. And with `DataSet` objects that may get tricky. I don't recommend that route. "Having to contact SQL again" isn't a bad thing. Performance-wise, it's actually preferable to what you're suggesting. Consider the needs to each request individually. This one only needs the ID. – David Oct 24 '19 at 22:58
  • 1
    @Bert The MVC framework has something called "model binding" that attempts to map values from the request to the parameters in the action's definition, or to the model class that was specified. It's only then that you're dealing with a model again. – Tieson T. Oct 24 '19 at 23:29
  • So far the only "CLUE" either of you have given is that I should probably be using @Html.hiddenfor() in some way to do so... In the view I can access the data in @Model.attList and do so to display a table. How do I ALSO get that same data back into my Model to be used again without using SQL? Or can this simply not be done??? I tried '@Html.hiddenfor(m => Model.attList, @Model.attList);' and a bunch a variations of that haven't worked... – Bert Oct 24 '19 at 23:51
  • No - you're still missing the simple question and follow up... Start with the YES or NO part... I am passing data to my 1 view by passing a DataSet to my 1st view using my Model. Can I pass that DataSet back to the controller from my 1st view to be used by my 2nd view ??? Yes/No – Bert Oct 25 '19 at 00:05
  • If YES - then can I use my Model to pass that data back, if so how? – Bert Oct 25 '19 at 00:06
  • @Bert: The "how" part is much more complex and is a drastically increased scope for this question. The aforementioned duplicate question is a great starting point to begin learning. – David Oct 25 '19 at 00:07