| | 1 | | using Fluorite.Extensions; |
| | 2 | | using Fluorite.Strainer.Models.Metadata; |
| | 3 | | using Fluorite.Strainer.Services.Configuration; |
| | 4 | |
|
| | 5 | | namespace Fluorite.Strainer.Services.Metadata.FluentApi; |
| | 6 | |
|
| | 7 | | public class FluentApiMetadataProvider : IMetadataProvider |
| | 8 | | { |
| | 9 | | private readonly IStrainerOptionsProvider _strainerOptionsProvider; |
| | 10 | | private readonly IConfigurationMetadataProvider _metadataProvider; |
| | 11 | | private readonly IFluentApiPropertyMetadataBuilder _propertyMetadataBuilder; |
| | 12 | | private readonly IPropertyInfoProvider _propertyInfoProvider; |
| | 13 | |
|
| 60 | 14 | | public FluentApiMetadataProvider( |
| 60 | 15 | | IStrainerOptionsProvider strainerOptionsProvider, |
| 60 | 16 | | IConfigurationMetadataProvider metadataProvider, |
| 60 | 17 | | IPropertyInfoProvider propertyInfoProvider, |
| 60 | 18 | | IFluentApiPropertyMetadataBuilder propertyMetadataBuilder) |
| | 19 | | { |
| 60 | 20 | | _strainerOptionsProvider = Guard.Against.Null(strainerOptionsProvider); |
| 60 | 21 | | _metadataProvider = Guard.Against.Null(metadataProvider); |
| 60 | 22 | | _propertyInfoProvider = Guard.Against.Null(propertyInfoProvider); |
| 60 | 23 | | _propertyMetadataBuilder = Guard.Against.Null(propertyMetadataBuilder); |
| 60 | 24 | | } |
| | 25 | |
|
| | 26 | | public IReadOnlyDictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>>? GetAllPropertyMetadata() |
| | 27 | | { |
| 4 | 28 | | if (!IsFluentApiEnabled()) |
| | 29 | | { |
| 1 | 30 | | return null; |
| | 31 | | } |
| | 32 | |
|
| 3 | 33 | | var objectMetadata = _metadataProvider.GetObjectMetadata(); |
| 3 | 34 | | var propertyMetadata = _metadataProvider.GetPropertyMetadata(); |
| | 35 | |
|
| 3 | 36 | | return propertyMetadata |
| 3 | 37 | | .Keys |
| 3 | 38 | | .Union(objectMetadata.Keys) |
| 2 | 39 | | .Select(type => (type, BuildMetadataKeyValuePair(type, objectMetadata, propertyMetadata))) |
| 4 | 40 | | .ToDictionary(tuple => tuple.type, tuple => tuple.Item2) |
| 3 | 41 | | .ToReadOnly(); |
| | 42 | | } |
| | 43 | |
|
| | 44 | | public IPropertyMetadata? GetDefaultMetadata(Type modelType) |
| | 45 | | { |
| 10 | 46 | | Guard.Against.Null(modelType); |
| | 47 | |
|
| 10 | 48 | | if (!IsFluentApiEnabled()) |
| | 49 | | { |
| 1 | 50 | | return null; |
| | 51 | | } |
| | 52 | |
|
| 9 | 53 | | _metadataProvider.GetDefaultMetadata().TryGetValue(modelType, out var propertyMetadata); |
| | 54 | |
|
| 9 | 55 | | if (propertyMetadata == null) |
| | 56 | | { |
| 8 | 57 | | if (_metadataProvider.GetObjectMetadata().TryGetValue(modelType, out var objectMetadata)) |
| | 58 | | { |
| 7 | 59 | | propertyMetadata = _propertyMetadataBuilder.BuildDefaultMetadata(objectMetadata); |
| | 60 | | } |
| | 61 | | } |
| | 62 | |
|
| 9 | 63 | | return propertyMetadata; |
| | 64 | | } |
| | 65 | |
|
| | 66 | | public IPropertyMetadata? GetPropertyMetadata( |
| | 67 | | Type modelType, |
| | 68 | | bool isSortableRequired, |
| | 69 | | bool isFilterableRequired, |
| | 70 | | string name) |
| | 71 | | { |
| 40 | 72 | | Guard.Against.Null(modelType); |
| 40 | 73 | | Guard.Against.NullOrWhiteSpace(name); |
| | 74 | |
|
| 40 | 75 | | if (!IsFluentApiEnabled()) |
| | 76 | | { |
| 1 | 77 | | return null; |
| | 78 | | } |
| | 79 | |
|
| 39 | 80 | | if (_metadataProvider.GetPropertyMetadata().TryGetValue(modelType, out var propertyMetadatas)) |
| | 81 | | { |
| 17 | 82 | | var propertyMetadata = propertyMetadatas |
| 17 | 83 | | .Where(pair => |
| 17 | 84 | | { |
| 17 | 85 | | return pair.Key.Equals(name) |
| 17 | 86 | | && (!isSortableRequired || pair.Value.IsSortable) |
| 17 | 87 | | && (!isFilterableRequired || pair.Value.IsFilterable); |
| 17 | 88 | | }) |
| 17 | 89 | | .FirstOrDefault().Value; |
| | 90 | |
|
| 17 | 91 | | return propertyMetadata; |
| | 92 | | } |
| | 93 | |
|
| 22 | 94 | | if (_metadataProvider.GetObjectMetadata().TryGetValue(modelType, out var objectMetadata)) |
| | 95 | | { |
| 21 | 96 | | if (((isFilterableRequired && objectMetadata.IsFilterable) || (!isFilterableRequired)) |
| 21 | 97 | | && ((isSortableRequired && objectMetadata.IsSortable) || (!isSortableRequired))) |
| | 98 | | { |
| 9 | 99 | | var propertyInfo = _propertyInfoProvider.GetPropertyInfo(modelType, name); |
| 9 | 100 | | if (propertyInfo is not null) |
| | 101 | | { |
| 9 | 102 | | return _propertyMetadataBuilder.BuildMetadataForProperty(objectMetadata, propertyInfo); |
| | 103 | | } |
| | 104 | | } |
| | 105 | | } |
| | 106 | |
|
| 13 | 107 | | return null; |
| | 108 | | } |
| | 109 | |
|
| | 110 | | public IReadOnlyList<IPropertyMetadata>? GetPropertyMetadatas(Type modelType) |
| | 111 | | { |
| 4 | 112 | | Guard.Against.Null(modelType); |
| | 113 | |
|
| 4 | 114 | | if (!IsFluentApiEnabled()) |
| | 115 | | { |
| 1 | 116 | | return null; |
| | 117 | | } |
| | 118 | |
|
| 3 | 119 | | if (_metadataProvider.GetPropertyMetadata().TryGetValue(modelType, out var metadatas)) |
| | 120 | | { |
| 1 | 121 | | return metadatas.Values.ToList().AsReadOnly(); |
| | 122 | | } |
| | 123 | |
|
| 2 | 124 | | if (_metadataProvider.GetObjectMetadata().TryGetValue(modelType, out var objectMetadata)) |
| | 125 | | { |
| 1 | 126 | | return _propertyInfoProvider.GetPropertyInfos(modelType) |
| 1 | 127 | | .Select(p => _propertyMetadataBuilder.BuildMetadataForProperty(objectMetadata, p)) |
| 1 | 128 | | .ToList(); |
| | 129 | | } |
| | 130 | |
|
| 1 | 131 | | return null; |
| | 132 | | } |
| | 133 | |
|
| | 134 | | private IReadOnlyDictionary<string, IPropertyMetadata> BuildMetadataKeyValuePair( |
| | 135 | | Type type, |
| | 136 | | IReadOnlyDictionary<Type, IObjectMetadata> objectMetadata, |
| | 137 | | IReadOnlyDictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>> propertyMetadata) |
| | 138 | | { |
| | 139 | | // TODO: Shouldn't property metadata override object metadata, but still be returned? |
| | 140 | | // So type-wide config is set with object call, but property call overrides that for some special case? |
| 2 | 141 | | if (propertyMetadata.TryGetValue(type, out var metadatas)) |
| | 142 | | { |
| 1 | 143 | | return metadatas; |
| | 144 | | } |
| | 145 | |
|
| 1 | 146 | | var objectMetadataForType = objectMetadata[type]; |
| | 147 | |
|
| 1 | 148 | | return GetPropertyMetadatasFromObjectMetadata(type, objectMetadataForType); |
| | 149 | | } |
| | 150 | |
|
| | 151 | | private IReadOnlyDictionary<string, IPropertyMetadata> GetPropertyMetadatasFromObjectMetadata(Type type, IObjectMeta |
| | 152 | | { |
| 1 | 153 | | return _propertyInfoProvider |
| 1 | 154 | | .GetPropertyInfos(type) |
| 1 | 155 | | .Select(propertyInfo => _propertyMetadataBuilder.BuildMetadataForProperty(objectMetadata, propertyInfo)) |
| 2 | 156 | | .ToDictionary(p => p.Name, p => p) |
| 1 | 157 | | .ToReadOnlyDictionary(); |
| | 158 | | } |
| | 159 | |
|
| 58 | 160 | | private bool IsFluentApiEnabled() => IsMetadataSourceEnabled(MetadataSourceType.FluentApi); |
| | 161 | |
|
| | 162 | | private bool IsMetadataSourceEnabled(MetadataSourceType metadataSourceType) |
| | 163 | | { |
| 58 | 164 | | return _strainerOptionsProvider |
| 58 | 165 | | .GetStrainerOptions() |
| 58 | 166 | | .MetadataSourceType |
| 58 | 167 | | .HasFlag(metadataSourceType); |
| | 168 | | } |
| | 169 | | } |