【问题标题】:Why is this crashing on Delete, and why are null values passed into it?为什么这会在删除时崩溃,为什么将空值传递给它?
【发布时间】:2020-07-21 23:10:42
【问题描述】:

我在使 Delete 方法 (MVC) 中的代码崩溃时遇到了一些困难。这是我的 ProductItem 控制器类中的两个方法。 “删除”工作正常,但我收到一条错误消息,当我单步执行时,我注意到它下面的 DeleteConfirmed 获得了 prodID 的空值(itemID 正常传递)。我很难解决这个问题,因为我不知道 DeleteConfirmed 方法是如何被“调用”的,也不知道值是如何传入(或应该传入)的,所以我不知道要跟踪或逐步执行什么.

编辑:我从 itemID 和 productID 的两个参数中删除了 nullable,现在它甚至没有到达 DeleteConfirmed,我明白了:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'prodID' of non-nullable type 'System.Int32' for method 'System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult] DeleteConfirmed(Int32, Int32)' in 'Memberships.Areas.Admin.Controllers.ProductItemController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters]
   System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo) +527
   System.Web.Mvc.Async.<>c__DisplayClass20_0.<BeginExecute>b__0(ParameterInfo parameterInfo) +19
   System.Linq.WhereSelectArrayIterator`2.MoveNext() +58
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +196
   System.Linq.Enumerable.ToArray(IEnumerable`1 source) +79
   System.Web.Mvc.Async.TaskAsyncActionDescriptor.BeginExecute(ControllerContext controllerContext, IDictionary`2 parameters, AsyncCallback callback, Object state) +152
   System.Web.Mvc.Async.<>c__DisplayClass8_0.<BeginInvokeAsynchronousActionMethod>b__0(AsyncCallback asyncCallback, Object asyncState) +28
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAsynchronousActionMethod(ControllerContext controllerContext, AsyncActionDescriptor actionDescriptor, IDictionary`2 parameters, AsyncCallback callback, Object state) +179
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters, AsyncCallback callback, Object state) +45
   System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +87
   System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +629
   System.Web.Mvc.Async.<>c__DisplayClass7_0.<BeginInvokeActionMethodWithFilters>b__0(AsyncCallback asyncCallback, Object asyncState) +58
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters, AsyncCallback callback, Object state) +197
   System.Web.Mvc.Async.<>c__DisplayClass3_1.<BeginInvokeAction>b__0(AsyncCallback asyncCallback, Object asyncState) +640
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +346
   System.Web.Mvc.<>c.<BeginExecuteCore>b__152_0(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +27
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +494
   System.Web.Mvc.<>c.<BeginExecute>b__151_1(AsyncCallback asyncCallback, Object callbackState, Controller controller) +16
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +403
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16
   System.Web.Mvc.<>c.<BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +54
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +427
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +105
   System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +50
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +163


// GET: Admin/ProductItem/Delete/5
public async Task<ActionResult> Delete(int? itemID, int? productID)
{
    if (itemID == null || productID == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Product_Item product_Item = await GetProductItem(itemID, productID);
    if (product_Item == null)
    {
        return HttpNotFound();
    }
    return View(await product_Item.Convert(db));
}

    // POST: Admin/ProductItem/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> DeleteConfirmed(int itemID, int prodID)
    {
        Product_Item product_Item = await GetProductItem(itemID, prodID);
        db.Products_Items.Remove(product_Item);
        await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }

根据要求:视图模型

using Memberships.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace Memberships.Areas.Admin.Models
{
    public class ProductItemModel
    {
        [DisplayName("Product ID")]
        public int ProductID { get; set; }
        [DisplayName("Item ID")]
        public int ItemID { get; set; }
        [DisplayName("Product")]
        public string ProductTitle { get; set; }
        [DisplayName("Item")]
        public string ItemTitle { get; set; }

        public ICollection<Product> Products { get; set; }
        public ICollection<Item> Items { get; set; }

    }
}

public static async Task<ProductItemModel> Convert
        (this Product_Item productItem, ApplicationDbContext db, bool addListData=true)
        //Only fill collections if the parameter passed in is true
        {
            var model = new ProductItemModel
            {
                ItemID = productItem.ItemID,
                ProductID = productItem.ProductID,
                Items = addListData ? await db.Items.ToListAsync() : null,
                Products = addListData ? await db.Products.ToListAsync() : null,

                ItemTitle = (await db.Items.FirstOrDefaultAsync(i => i.ID.Equals(productItem.ItemID))).Title,
                ProductTitle = (await db.Products.FirstOrDefaultAsync(p => p.ID.Equals(productItem.ProductID))).Title
            };
            return model;

        }


private async Task<Product_Item> GetProductItem(int? item, int? prod)
    {
        try
        {
            int itemID = 0, prodID = 0;
            int.TryParse(item.ToString(), out itemID);
            int.TryParse(prod.ToString(), out prodID);
            var productItem = await db.Products_Items.FirstOrDefaultAsync
            (
                pi => pi.ProductID.Equals(prodID) && pi.ItemID.Equals(itemID)
            );
            return productItem;
        }
        catch {return null;}
    }

【问题讨论】:

  • 没有“堆栈跟踪?”你需要知道谁在打电话。毫无疑问,这种错误是由调用例程中的问题引起的。 (这就是为什么这些方法特别注意 Null ...)
  • 好吧,如果 ItemID 和 prodID 不能为 NULL,那么您应该将它们的签名更改为不可为空的参数,即删除 ?分数。如果它们可以为 NULL,那么您只需要在调用 Remove() 之前进行检查,因为...它们可以根据您的设计为 NULL。
  • 很抱歉给您带来了困惑。 ConfirmDelete 的可为空参数是一个错误。我解决了这个问题,但现在我得到了一个完全不同的错误(我在上面粘贴了),我还将粘贴堆栈跟踪。不幸的是,我不明白我在看什么:
  • @DavidLiang 两个参数都应该是非空的,我更改了参数以反映这一点。我可以在 .Remove() 处检查它们,但无论如何它们都不应该为空。错误已更改为“参数字典包含参数'prodID'的空条目”所以问题是它仍然传入一个空值(问题是我不知道“它”是什么)我正在尝试弄清楚它为什么这样做以及从哪里“调用”这个方法。
  • @DavidBritz:如果您可以向我们展示视图模型(product_Item.Convert(db) 生成的内容)以及您如何在视图中构建表单,这可能会有所帮助。

标签: asp.net-mvc entity-framework model-view-controller


【解决方案1】:

我想通了,奇怪的是,当我不得不复制代码并将其粘贴以用于新功能时。问题出在删除按钮本身。用于为“编辑详细信息”和“删除”按钮构建参数字符串的模型(粘贴在下面的代码)使用“ProductID”作为参数名称,但控制器的删除方法使用参数 ProdID 而不是 ProductID,因此它为空!!

public string ActionParameters //Build a string containing any id paramaters which are passed in.
{
    get 
    {
        var param = new StringBuilder("?");
        if(ID != null && ID>0)
        {   //If the ID is not null then "id=<ID>" will be appended to the parameter string.
            param.Append(String.Format("{0}={1}&","id",ID));
        }
        if ((ItemID ?? 0) > 0)
        {   //If the ID is not null then "id=<ID>" will be appended to the parameter string.
            param.Append(String.Format("{0}={1}&", "ItemID", ItemID));
        }
        if ((ProductID ?? 0) > 0)
        {   //If the ID is not null then "id=<ID>" will be appended to the parameter string.
            **param.Append(String.Format("{0}={1}&", "ProductID", ProductID));**
        }
        if ((SubscriptionID ?? 0) > 0)
        {   //If the ID is not null then "id=<ID>" will be appended to the parameter string.
            param.Append(String.Format("{0}={1}&", "Subscription", SubscriptionID));
        }
        return param.ToString().Substring(0,param.Length-1); //Remove last "&"
    }

当我不得不复制和粘贴类似函数的代码并注意到参数不匹配时,我发现了这一点。 感谢所有贡献的人!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    相关资源
    最近更新 更多