День 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/