|
4 | 4 | using System.Diagnostics.CodeAnalysis; |
5 | 5 | using System.Globalization; |
6 | 6 | using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; |
| 7 | +using Microsoft.EntityFrameworkCore.Infrastructure; |
7 | 8 | using Microsoft.EntityFrameworkCore.Internal; |
8 | 9 | using Microsoft.EntityFrameworkCore.Storage.Json; |
9 | 10 |
|
@@ -767,53 +768,10 @@ private object? DefaultSentinel |
767 | 768 | public virtual ValueConverter? GetValueConverter() |
768 | 769 | { |
769 | 770 | var annotation = FindAnnotation(CoreAnnotationNames.ValueConverter); |
770 | | - if (annotation != null) |
771 | | - { |
772 | | - return (ValueConverter?)annotation.Value; |
773 | | - } |
774 | | - |
775 | | - var property = this; |
776 | | - var i = 0; |
777 | | - for (; i < ForeignKey.LongestFkChainAllowedLength; i++) |
778 | | - { |
779 | | - Property? nextProperty = null; |
780 | | - foreach (var foreignKey in property.GetContainingForeignKeys()) |
781 | | - { |
782 | | - for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++) |
783 | | - { |
784 | | - if (property == foreignKey.Properties[propertyIndex]) |
785 | | - { |
786 | | - var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex]; |
787 | | - if (principalProperty == this |
788 | | - || principalProperty == property) |
789 | | - { |
790 | | - break; |
791 | | - } |
792 | | - |
793 | | - annotation = principalProperty.FindAnnotation(CoreAnnotationNames.ValueConverter); |
794 | | - if (annotation != null) |
795 | | - { |
796 | | - return (ValueConverter?)annotation.Value; |
797 | | - } |
798 | | - |
799 | | - nextProperty = principalProperty; |
800 | | - } |
801 | | - } |
802 | | - } |
803 | | - |
804 | | - if (nextProperty == null) |
805 | | - { |
806 | | - break; |
807 | | - } |
808 | | - |
809 | | - property = nextProperty; |
810 | | - } |
811 | | - |
812 | | - return i == ForeignKey.LongestFkChainAllowedLength |
813 | | - ? throw new InvalidOperationException( |
814 | | - CoreStrings.RelationshipCycle( |
815 | | - DeclaringType.DisplayName(), Name, "ValueConverter")) |
816 | | - : null; |
| 771 | + return annotation != null |
| 772 | + ? (ValueConverter?)annotation.Value |
| 773 | + : GetConversion(throwOnProviderClrTypeConflict: FindAnnotation(CoreAnnotationNames.ProviderClrType) == null) |
| 774 | + .ValueConverter; |
817 | 775 | } |
818 | 776 |
|
819 | 777 | /// <summary> |
@@ -859,53 +817,118 @@ private object? DefaultSentinel |
859 | 817 | public virtual Type? GetProviderClrType() |
860 | 818 | { |
861 | 819 | var annotation = FindAnnotation(CoreAnnotationNames.ProviderClrType); |
862 | | - if (annotation != null) |
863 | | - { |
864 | | - return (Type?)annotation.Value; |
865 | | - } |
| 820 | + return annotation != null |
| 821 | + ? (Type?)annotation.Value |
| 822 | + : GetConversion(throwOnValueConverterConflict: FindAnnotation(CoreAnnotationNames.ValueConverter) == null) |
| 823 | + .ProviderClrType; |
| 824 | + } |
| 825 | + |
| 826 | + private (ValueConverter? ValueConverter, Type? ProviderClrType) GetConversion( |
| 827 | + bool throwOnValueConverterConflict = true, bool throwOnProviderClrTypeConflict = true) |
| 828 | + { |
| 829 | + var queue = new Queue<(Property CurrentProperty, Property CycleBreakingPropert, int CyclePosition, int MaxCycleLength)>(); |
| 830 | + queue.Enqueue((this, this, 0, 1)); |
866 | 831 |
|
867 | | - var property = this; |
868 | | - var i = 0; |
869 | | - for (; i < ForeignKey.LongestFkChainAllowedLength; i++) |
| 832 | + ValueConverter? valueConverter = null; |
| 833 | + Type? providerClrType = null; |
| 834 | + while (queue.Count > 0) |
870 | 835 | { |
871 | | - Property? nextProperty = null; |
| 836 | + var (property, cycleBreakingProperty, cyclePosition, maxCycleLength) = queue.Dequeue(); |
| 837 | + if (cyclePosition >= ForeignKey.LongestFkChainAllowedLength) |
| 838 | + { |
| 839 | + throw new InvalidOperationException( |
| 840 | + CoreStrings.RelationshipCycle(DeclaringType.DisplayName(), Name, "ValueConverter")); |
| 841 | + } |
| 842 | + |
872 | 843 | foreach (var foreignKey in property.GetContainingForeignKeys()) |
873 | 844 | { |
874 | 845 | for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++) |
875 | 846 | { |
876 | | - if (property == foreignKey.Properties[propertyIndex]) |
| 847 | + if (property != foreignKey.Properties[propertyIndex]) |
| 848 | + { |
| 849 | + continue; |
| 850 | + } |
| 851 | + |
| 852 | + var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex]; |
| 853 | + if (principalProperty == cycleBreakingProperty) |
| 854 | + { |
| 855 | + break; |
| 856 | + } |
| 857 | + |
| 858 | + var annotationFound = false; |
| 859 | + var valueConverterAnnotation = principalProperty.FindAnnotation(CoreAnnotationNames.ValueConverter); |
| 860 | + if (valueConverterAnnotation != null) |
877 | 861 | { |
878 | | - var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex]; |
879 | | - if (principalProperty == this |
880 | | - || principalProperty == property) |
| 862 | + var annotationValue = (ValueConverter?)valueConverterAnnotation.Value; |
| 863 | + if (annotationValue != null) |
881 | 864 | { |
882 | | - break; |
| 865 | + if (valueConverter != null) |
| 866 | + { |
| 867 | + throw new InvalidOperationException( |
| 868 | + CoreStrings.ConflictingRelationshipConversions( |
| 869 | + DeclaringType.DisplayName(), Name, |
| 870 | + valueConverter.GetType().ShortDisplayName(), annotationValue.GetType().ShortDisplayName())); |
| 871 | + } |
| 872 | + |
| 873 | + if (providerClrType != null |
| 874 | + && throwOnProviderClrTypeConflict) |
| 875 | + { |
| 876 | + throw new InvalidOperationException( |
| 877 | + CoreStrings.ConflictingRelationshipConversions( |
| 878 | + DeclaringType.DisplayName(), Name, |
| 879 | + providerClrType.ShortDisplayName(), annotationValue.GetType().ShortDisplayName())); |
| 880 | + } |
| 881 | + |
| 882 | + valueConverter = annotationValue; |
883 | 883 | } |
| 884 | + annotationFound = true; |
| 885 | + } |
884 | 886 |
|
885 | | - annotation = principalProperty.FindAnnotation(CoreAnnotationNames.ProviderClrType); |
886 | | - if (annotation != null) |
| 887 | + var providerClrTypeAnnotation = principalProperty.FindAnnotation(CoreAnnotationNames.ProviderClrType); |
| 888 | + if (providerClrTypeAnnotation != null) |
| 889 | + { |
| 890 | + var annotationValue = (Type?)providerClrTypeAnnotation.Value; |
| 891 | + if (annotationValue != null) |
887 | 892 | { |
888 | | - return (Type?)annotation.Value; |
| 893 | + if (providerClrType != null) |
| 894 | + { |
| 895 | + throw new InvalidOperationException( |
| 896 | + CoreStrings.ConflictingRelationshipConversions( |
| 897 | + DeclaringType.DisplayName(), Name, |
| 898 | + providerClrType.ShortDisplayName(), annotationValue.ShortDisplayName())); |
| 899 | + } |
| 900 | + |
| 901 | + if (valueConverter != null |
| 902 | + && throwOnValueConverterConflict) |
| 903 | + { |
| 904 | + throw new InvalidOperationException( |
| 905 | + CoreStrings.ConflictingRelationshipConversions( |
| 906 | + DeclaringType.DisplayName(), Name, |
| 907 | + valueConverter.GetType().ShortDisplayName(), annotationValue.ShortDisplayName())); |
| 908 | + } |
| 909 | + |
| 910 | + providerClrType = annotationValue; |
889 | 911 | } |
| 912 | + annotationFound = true; |
| 913 | + } |
890 | 914 |
|
891 | | - nextProperty = principalProperty; |
| 915 | + if (!annotationFound) |
| 916 | + { |
| 917 | + if (cyclePosition == maxCycleLength) |
| 918 | + { |
| 919 | + queue.Enqueue((principalProperty, property, 0, maxCycleLength << 1)); |
| 920 | + } |
| 921 | + else |
| 922 | + { |
| 923 | + queue.Enqueue((principalProperty, cycleBreakingProperty, cyclePosition + 1, maxCycleLength)); |
| 924 | + } |
892 | 925 | } |
| 926 | + break; |
893 | 927 | } |
894 | 928 | } |
895 | | - |
896 | | - if (nextProperty == null) |
897 | | - { |
898 | | - break; |
899 | | - } |
900 | | - |
901 | | - property = nextProperty; |
902 | 929 | } |
903 | 930 |
|
904 | | - return i == ForeignKey.LongestFkChainAllowedLength |
905 | | - ? throw new InvalidOperationException( |
906 | | - CoreStrings.RelationshipCycle( |
907 | | - DeclaringType.DisplayName(), Name, "ProviderClrType")) |
908 | | - : null; |
| 931 | + return (valueConverter, providerClrType); |
909 | 932 | } |
910 | 933 |
|
911 | 934 | /// <summary> |
@@ -1376,7 +1399,7 @@ public virtual bool IsForeignKey() |
1376 | 1399 | /// doing so can result in application failures when updating to a new Entity Framework Core release. |
1377 | 1400 | /// </summary> |
1378 | 1401 | public virtual IEnumerable<ForeignKey> GetContainingForeignKeys() |
1379 | | - => ForeignKeys?.OrderBy(fk => fk.Properties, PropertyListComparer.Instance) ?? Enumerable.Empty<ForeignKey>(); |
| 1402 | + => ForeignKeys?.OrderBy(fk => fk, ForeignKeyComparer.Instance) ?? Enumerable.Empty<ForeignKey>(); |
1380 | 1403 |
|
1381 | 1404 | /// <summary> |
1382 | 1405 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to |
|
0 commit comments