如何在控制器动作上使用可选的通用参数?

时间:2018-07-02 23:14:19

标签: asp.net-mvc generics parameters asp.net-mvc-controller

我正在使用用于各种实体的BaseController。它们可能具有int表示的string<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'的空条目

可选的intstring id可以正常工作。我可以叫/controller/create/2/controller/create

但是使用通用类型参数TPk,无参数路由将不再起作用。


我尝试过的东西

我尝试使TPk参数为可空,但无法编译:

  

类型“ TPk”必须是非空值类型,才能在通用类型或方法“空”中用作参数“ T”


我尝试按照this question将参数名称从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; }
}

1 个答案:

答案 0 :(得分:1)

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

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

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

如果没有此注释您将不理解您的方法,这是一个很好的指示,即您的当前代码可能仍然有气味。 (您在同一件事上做的很多):

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

将共享的东西提取到另一个方法(甚至是服务类)中,并针对每个差异使用单独的方法。


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

  • 如果需要加入会怎样?
  • 如果您需要交易怎么办?
  • 您如何处理错误?
  • 如果您的原始逻辑需要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映射中正常工作,其中每个View都有确切的一个实体,但是伸缩性不是很好。所以要当心,三思而后行,真正想要什么;)