Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

4 года назад
Открыть в
День 1414. #ЧтоНовенького Новые Возможности MVC в .NET 7. Часть 1 В этой серии рассмотрим некоторые нововведения в ASP.NET Core в .NET 7. 1. IParseable/TryParse для привязки примитивных типов MVC использует подход «поставщика привязки». Можно привязать параметры методов действий к значениям формы, строки запроса, заголовкам и т.п. По умолчанию MVC связывает некоторые простые типы или любые типы, имеющие TypeConverter из строки. Всё остальное рассматривается как сложный тип, т.е. либо привязывается к телу запроса, либо привязывается не сам тип, а каждое из отдельных свойств типа. Допустим, у нас есть тип:
public class Range
{
  public int From { get; set; }
  public int To { get; set; }
}
и привязка его в методе действия контроллера:
public class MyController : ControllerBase
{
  public IEnumerable<MyClass> Get(Range days)
  { … }
}
В .NET 6 вам нужно либо написать TypeConverter для Range, либо привязать свойства From и To:
/MyController?from=1&to=3

В .NET 7 есть другой вариант. Можно реализовать интерфейс IParseable<T> и создать объект Range из любой строки. Например, мы хотим иметь такой URL:
/MyController/1-3

Формально реализовывать IParseable<T> не нужно, достаточно реализовать метод TryParse(). Пример реализации ниже. Детали не особенно важны, и здесь не учитывается множество пограничных случаев:
public readonly struct Range : IParsable<Range>
{
  public int From { get; }
  public int To { get; }

  public Range(int from, int to)
  {
    From = from;
    To = to;
  }

  public static bool TryParse(
    string? value,
    IFormatProvider? provider,
    out Range result)
  {
    if (value != null)
    {
      var sep = value.IndexOf('-');
      if (sep > 0 && sep < value.Length - 1)
      {
        var fromSpan = value.AsSpan().Slice(0, sep);
        var toSpan = value.AsSpan(sep + 1);

        if (int.TryParse(fromSpan, 
             out var from)
          && int.TryParse(toSpan,
              out var to))
        {
          result = new Range(from, to);
          return true;
        }
      }
    }

    result = default;
    return false;
  }

  public static Range Parse(
   string value,
   IFormatProvider? provider)
  {
    if (!TryParse(value, provider, out var result))
      throw new ArgumentException();

    return result;
  }
}

Затем можно просто добавить атрибут к методу контроллера:
[HttpGet("{days}")]
public IEnumerable<MyClass> Get(Range days)
{ … }

Проще ли реализовать IParseable<T>, чем написать свой связыватель модели? Вероятно. Проще, чем свой TypeConverter? Возможно. Однако при этом подходе вы можете использовать одни и те же типы в минимальных API и в контроллерах. Это должно упростить переход от минимальных API к контроллерам (и наоборот). Если же вы хотите отключить эту функцию, удалите TryParseModelBinderProvider из MvcOptions:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MvcOptions>(opts => {
  opts.ModelBinderProviders
   .RemoveType<TryParseModelBinderProvider>();
});

Источник: https://andrewlock.net/5-new-mvc-features-in-dotnet-7/