首页 > 解决方案 > 如何在控制器操作上使用可选的通用参数?

问题描述

我正在使用用于各种实体的 BaseController。它们可能具有主键intstring主键,由 . 表示<TPk>

例如:

[HttpGet]
public ActionResult Create(TPk id)
{
    return View();
}

一切都很好,直到我尝试将TPk其用作可选参数。

[HttpGet]
public ActionResult Create(TPk id = default(TPk))
{
    return View();
}

似乎“可选”部分不起作用。

很好/controller/create/2,但 /controller/create给了我以下错误:

参数字典包含方法“System.Web.Mvc.ActionResult Create(Int32)”的不可空类型“System.Int32”的参数“id”的空条目

可选的适用于intstringid。我可以打电话/controller/create/2AND /controller/create

但是使用泛型类型参数TPk,无参数路由不再有效。


我试过的

我试过使TPk参数可以为空,但它不会编译:

类型“TPk”必须是不可为空的值类型,才能将其用作泛型类型或方法“Nullable”中的参数“T”


我已经尝试根据这个问题id将参数名称从更改为- 不高兴altId


我尝试以完全相同的方式调用相同的方法,但使用非泛型参数。例如:

public virtual async Task<ActionResult> Create(int id = default(int))

这工作得很好。


我尝试创建一个简单的新项目来隔离此代码。(如下所示)。这仍然给无参数版本带来问题。


简单代码测试

控制器

public abstract class BaseController<TPk> : Controller
{
    public ActionResult Create(TPk id = default(TPk))
    {
        return View();
    }
}


public class NewsController : BaseController<int>
{

}

实体类

public class BaseDataModel<TPk>
{
    public TPk Id { get; set; }
    public string Title { get; set; }
}

public class PageDataModel : BaseDataModel<string>
{
    public string Content { get; set; }
}

public class NewsDataModel : BaseDataModel<int>
{
    public DateTime Date { get; set; }
}

标签: asp.net-mvcgenericsparametersasp.net-mvc-controller

解决方案


Asp.net 约定很大程度上基于反射。所以这可以解释这种行为。我还没有测试它是否真的不起作用,但我确信在这种状态下您已经尝试创建一个新项目 (POC) 以排除任何自定义代码。

也许可以通过更深入地研究路由(方法选择)和 ModelBinder 源代码来修复它......

我只会创建一个不同的DuplicateRecord动作。

如果您在没有此注释的情况下不理解您的方法,则说明您当前的代码可能有异味。(你在做同样的事情):

// duplicates existing record if id is passed in, otherwise from scratch

将共享的东西提取到另一个方法(甚至可能是一个服务类),并为每个差异提供一个单独的方法。


也就是说,通用 CrudController 的想法很可爱,几年前我自己尝试过。但在尝试这样做时,我引入了各种通用参数、策略模式、事件委托,以使所有可能性成为可能。

  • 如果您需要加入,会发生什么?
  • 如果您需要交易会发生什么?
  • 你如何处理错误?
  • 如果您的 crud 逻辑需要 1、2、3 ... 附加参数来决定要做什么,会发生什么?
  • 软删除/硬删除?
  • 级联删除/限制删除?
  • 如果你...

我写了这么多代码,很高兴能恢复到好的旧的非泛型代码。如果在服务中抽象出来,ActionMethods 真的不需要变大。

public async Task<IActionResult> CreateProduct(CancellationToken ct, ProductCreateModel model)
{
    var result = await _productService.CreateAsync(model, ct);    
    //create response with some helpers... probably some ActionFilters
}

泛型可以在一个简单的 crud 映射中正常工作,其中每个视图都有一个确切的实体,但它不能很好地扩展。所以要小心并三思而后行你真正想要什么;)


推荐阅读