使用 C# 9 的records作为强类型ID - 路由和查询参数
发布日期:2021-05-09 05:28:39 浏览次数:16 分类:博客文章

本文共 9681 字,大约阅读时间需要 32 分钟。

������������������������������������ C# 9 ���record���������������������id���������������

public record ProductId(int Value);

������������������id���������������������������������������������������������������ASP.NET Core������������������������������������������������������������������������������������������������������������������������������������������������

���������������������������������������������

���������������������������������������

public record ProductId(int Value);public class Product{    public ProductId Id { get; set; }    public string Name { get; set; }    public decimal UnitPrice { get; set; }}

������������API���������

[ApiController][Route("api/[controller]")]public class ProductController : ControllerBase{    ...    [HttpGet("{id}")]    public ActionResult
GetProduct(ProductId id) { return Ok(new Product { Id = id, Name = "Apple", UnitPrice = 0.8M }); }}

������������������������Get������������������������ /api/product/1���

{    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",    "title": "Unsupported Media Type",    "status": 415,    "traceId": "00-3600640f4e053b43b5ccefabe7eebd5a-159f5ca18d189142-00"}

���������������������������������415���.NET Core ������������������URL������������������ProductId������������������int������������������������������ID������������������������������������������

���������������������

������������������������������������������������������ProductId���������������

public class ProductIdConverter : TypeConverter{    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) =>        sourceType == typeof(string);    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) =>        destinationType == typeof(string);    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)    {        return value switch        {            string s => new ProductId(int.Parse(s)),            null => null,            _ => throw new ArgumentException($"Cannot convert from {value} to ProductId", nameof(value))        };    }    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)    {        if (destinationType == typeof(string))        {            return value switch            {                ProductId id => id.Value.ToString(),                null => null,                _ => throw new ArgumentException($"Cannot convert {value} to string", nameof(value))            };        }        throw new ArgumentException($"Cannot convert {value ?? "(null)"} to {destinationType}", nameof(destinationType));    }}

������������������������������������������������������string,������������������������������������������������������int���

���������ProductId������TypeConverter������������������������������������������

[TypeConverter(typeof(ProductIdConverter))]public record ProductId(int Value);

���������������������������������������������������

{    "id": {        "value": 1    },    "name": "Apple",    "unitPrice": 0.8}

���������������������������������������������id ���json������������������������������������json������������������������������������������������������������������������������������������������������ProductId������������������������������������������������������������������������������������������������������������������������������

���������������id���������

������������������������������Helper

  • ������������������������������ID������������������������
  • ������������������������������������������������
public static class StronglyTypedIdHelper{    private static readonly ConcurrentDictionary
StronglyTypedIdFactories = new(); public static Func
GetFactory
(Type stronglyTypedIdType) where TValue : notnull { return (Func
)StronglyTypedIdFactories.GetOrAdd( stronglyTypedIdType, CreateFactory
); } private static Func
CreateFactory
(Type stronglyTypedIdType) where TValue : notnull { if (!IsStronglyTypedId(stronglyTypedIdType)) throw new ArgumentException($"Type '{stronglyTypedIdType}' is not a strongly-typed id type", nameof(stronglyTypedIdType)); var ctor = stronglyTypedIdType.GetConstructor(new[] { typeof(TValue) }); if (ctor is null) throw new ArgumentException($"Type '{stronglyTypedIdType}' doesn't have a constructor with one parameter of type '{typeof(TValue)}'", nameof(stronglyTypedIdType)); var param = Expression.Parameter(typeof(TValue), "value"); var body = Expression.New(ctor, param); var lambda = Expression.Lambda
>(body, param); return lambda.Compile(); } public static bool IsStronglyTypedId(Type type) => IsStronglyTypedId(type, out _); public static bool IsStronglyTypedId(Type type, [NotNullWhen(true)] out Type idType) { if (type is null) throw new ArgumentNullException(nameof(type)); if (type.BaseType is Type baseType && baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(StronglyTypedId<>)) { idType = baseType.GetGenericArguments()[0]; return true; } idType = null; return false; }}

������ Helper ������������������������������������������������������������������������������������

public class StronglyTypedIdConverter
: TypeConverter where TValue : notnull{ private static readonly TypeConverter IdValueConverter = GetIdValueConverter(); private static TypeConverter GetIdValueConverter() { var converter = TypeDescriptor.GetConverter(typeof(TValue)); if (!converter.CanConvertFrom(typeof(string))) throw new InvalidOperationException( $"Type '{typeof(TValue)}' doesn't have a converter that can convert from string"); return converter; } private readonly Type _type; public StronglyTypedIdConverter(Type type) { _type = type; } public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(TValue) || base.CanConvertFrom(context, sourceType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(string) || destinationType == typeof(TValue) || base.CanConvertTo(context, destinationType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string s) { value = IdValueConverter.ConvertFrom(s); } if (value is TValue idValue) { var factory = StronglyTypedIdHelper.GetFactory
(_type); return factory(idValue); } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is null) throw new ArgumentNullException(nameof(value)); var stronglyTypedId = (StronglyTypedId
)value; TValue idValue = stronglyTypedId.Value; if (destinationType == typeof(string)) return idValue.ToString()!; if (destinationType == typeof(TValue)) return idValue; return base.ConvertTo(context, culture, value, destinationType); }}

��������������������������������� Converter

public class StronglyTypedIdConverter : TypeConverter{    private static readonly ConcurrentDictionary
ActualConverters = new(); private readonly TypeConverter _innerConverter; public StronglyTypedIdConverter(Type stronglyTypedIdType) { _innerConverter = ActualConverters.GetOrAdd(stronglyTypedIdType, CreateActualConverter); } public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => _innerConverter.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => _innerConverter.CanConvertTo(context, destinationType); public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) => _innerConverter.ConvertFrom(context, culture, value); public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) => _innerConverter.ConvertTo(context, culture, value, destinationType); private static TypeConverter CreateActualConverter(Type stronglyTypedIdType) { if (!StronglyTypedIdHelper.IsStronglyTypedId(stronglyTypedIdType, out var idType)) throw new InvalidOperationException($"The type '{stronglyTypedIdType}' is not a strongly typed id"); var actualConverterType = typeof(StronglyTypedIdConverter<>).MakeGenericType(idType); return (TypeConverter)Activator.CreateInstance(actualConverterType, stronglyTypedIdType)!; }}

��������������������������������������������� ProductIdConvert��� ���������������������������������������������.NET Core ���������������������������������������������������������������������������������������JSON���������������������

[TypeConverter(typeof(StronglyTypedIdConverter))]public abstract record StronglyTypedId
(TValue Value) where TValue : notnull{ public override string ToString() => Value.ToString();}

������������: thomas levesque

���������������

������

������������������������������������ ���������������������������������������������������������������������������������������������������QQ��� 897216102

上一篇:使用 C# 9 的records作为强类型ID - JSON序列化
下一篇:使用 C# 9 的records作为强类型ID - 初次使用

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年04月18日 06时05分12秒