Как использовать необязательный, универсальный параметр для действия контроллера? - PullRequest
0 голосов
/ 03 июля 2018

Я работаю с 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 выдает мне следующую ошибку:

Словарь параметров содержит пустую запись для параметра 'id' ненулевого типа 'System.Int32' для метода 'System.Web.Mvc.ActionResult Create (Int32)'

Опционально отлично работает с int или string id. Я могу позвонить /controller/create/2 И /controller/create.

Но с использованием аргумента универсального типа TPk маршрут без параметров больше не работает.


Что я пробовал

Я попытался сделать параметр TPk обнуляемым, но он не скомпилируется:

Тип 'TPk' должен быть типом значения, не допускающим значения NULL, чтобы использовать его в качестве параметра 'T' в универсальном типе или методе 'Nullable'


Я попытался изменить имя параметра с 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 голосов
/ 03 июля 2018

Соглашения Asp.net в значительной степени основаны на рефлексии. Так что это может объяснить поведение. Я не проверял, действительно ли он не работает, но я уверен, что в этом состоянии вы уже пытались создать новый проект (POC), чтобы исключить какой-либо пользовательский код.

Может быть, это можно исправить, заглянув глубже в маршрутизацию (выбор метода) и исходный код ModelBinder ...

Я бы просто создал другое действие DuplicateRecord.

Если вы не понимаете свой метод без этого комментария, это хороший признак того, что ваш текущий код, вероятно, все равно пахнет. (Вы делаете много для одного и того же):

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

Извлечение общих вещей в другой метод (возможно, даже в класс обслуживания) и наличие для каждого различия отдельного метода.


Тем не менее, идея универсального CrudController прекрасна, я попробовал это сам несколько лет назад. Но, пытаясь так, я ввел все виды общих параметров, шаблонов стратегий, делегатов событий, чтобы сделать возможными все возможности.

  • Что произойдет, если вам понадобится объединение?
  • Что произойдет, если вам понадобится транзакция?
  • Как вы справляетесь с ошибками?
  • Что произойдет, если вашей логике crud требуется 1, 2, 3 ... дополнительные параметры, чтобы решить, что делать?
  • Soft Delete / Hard Delete?
  • Каскадное удаление / ограничение удаления?
  • Что произойдет, если вы ...

Я написал так много кода, что было бы здорово вернуться к старому доброму не универсальному коду. И если абстрагироваться от службы, 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
}

Обобщения могут работать, как правило, в простом грубом отображении, в котором каждое представление имеет ровно одну сущность, но не очень хорошо масштабируется. Так что будьте осторожны и подумайте дважды о том, чего вы действительно хотите;)

...