File: EFAssociationProvider.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (129 lines) | stat: -rw-r--r-- 7,307 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

namespace System.Web.DynamicData.ModelProviders {
    internal sealed class EFAssociationProvider : AssociationProvider {
        public EFAssociationProvider(EFColumnProvider column, NavigationProperty navigationProperty) {
            FromColumn = column;

            var entityMemberParentEntity = (EFTableProvider)column.Table;
            var parentEntityModel = (EFDataModelProvider)entityMemberParentEntity.DataModel;

            EFColumnProvider columnProvider;
            EntityType otherEntityType = navigationProperty.ToEndMember.GetEntityType();

            // If we can get to the entityType of the ToMember side of the relaionship then build a relationship key and try to lookup the column provider.
            if (otherEntityType != null) {
                long key = BuildRelationshipKey(otherEntityType, navigationProperty.ToEndMember);
                if (parentEntityModel.RelationshipEndLookup.TryGetValue(key, out columnProvider)) {
                    ToColumn = columnProvider;
                }
                else {
                    // Otherwise just lookup the entityType in the table lookup
                    ToTable = parentEntityModel.TableEndLookup[otherEntityType];
                }
            }
            else {
                EntityType value = (EntityType)navigationProperty.ToEndMember.TypeUsage.EdmType.MetadataProperties.Single(prop => prop.Name == "ElementType").Value;
                ToTable = parentEntityModel.TableEndLookup[value];
            }

            if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
                if (navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
                    Direction = AssociationDirection.ManyToMany;
                }
                else {
                    Direction = AssociationDirection.OneToMany;
                }
            }
            else {
                if (navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) {
                    Direction = AssociationDirection.ManyToOne;
                }
                else {
                    Direction = AssociationDirection.OneToOne;
                }
            }

            // If it's a foreign key reference (as opposed to a entity set), figure out the foreign keys
            if (IsForeignKeyReference) {
                var foreignKeyNames = new List<string>();
                var primaryKeyNames = FromColumn.Table.Columns.Where(c => c.IsPrimaryKey).Select(c => c.Name);

                // Add the foreign keys for this association.
                foreignKeyNames.AddRange(GetDependentPropertyNames(navigationProperty));

                if (IsZeroOrOne(navigationProperty)) {
                    // Assume this is true for 1 to 0..1 relationships on both sides
                    IsPrimaryKeyInThisTable = true;
                }
                else {
                    // If any of the foreign keys are also PKs, set the flag                    
                    IsPrimaryKeyInThisTable = foreignKeyNames.Any(fkName => primaryKeyNames.Contains(fkName, StringComparer.OrdinalIgnoreCase));
                }

                if (!foreignKeyNames.Any()) {
                    // If we couldn't find any dependent properties, we're dealing with a model that doesn't
                    // have FKs, and requires the use of flattened FK names (e.g. Category.CategoryId)
                    foreach (ColumnProvider toEntityColumn in ToTable.Columns.Where(c => c.IsPrimaryKey)) {
                        foreignKeyNames.Add(FromColumn.Name + "." + toEntityColumn.Name);
                    }
                }

                ForeignKeyNames = foreignKeyNames.AsReadOnly();
            }
        }

        private static bool IsZeroOrOne(NavigationProperty navigationProperty) {
            return (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne &&
                                navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) ||
                                (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One &&
                                navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);
        }

        private bool IsForeignKeyReference {
            get {
                return Direction == AssociationDirection.OneToOne || Direction == AssociationDirection.ManyToOne;
            }
        }

        internal static long BuildRelationshipKey(EntityType entityType, RelationshipEndMember member) {
            return Misc.CombineHashCodes(entityType.GetHashCode(), member.GetHashCode());
        }

        internal static IEnumerable<string> GetDependentPropertyNames(NavigationProperty navigationProperty) {
            return GetDependentPropertyNames(navigationProperty, true /*checkRelationshipType*/);
        }

        internal static IEnumerable<string> GetDependentPropertyNames(NavigationProperty navigationProperty, bool checkRelationshipType) {
            if (checkRelationshipType) {
                if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne &&
                    navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) {
                    // Get constraint when this association is on the "parent" (aka "from") side. This means
                    // that the navProperty represents a Children association in a 1-1 relationship. For example,
                    // this could be a Item-ItemDetail scenario where the ItemDetail table has a PK that is also an FK
                    // into the Item table (thus ensuring the 1-1 cardinality). We need to special case this situation because normally we would try
                    // to build a foreign key name of the form "Item.ItemID", but we want just "ItemID".
                    AssociationType relationshipType = (AssociationType)navigationProperty.RelationshipType;
                    ReferentialConstraint constraint = relationshipType.ReferentialConstraints.FirstOrDefault(c => c.ToRole == navigationProperty.ToEndMember);
                    if (constraint != null) {
                        return constraint.FromProperties.Select(p => p.Name);
                    }

                    // Fall back on the primary keys if no constraints were found but only if we are on the parent side. i.e the 1 side Item side in an Item-ItemDetail
                    // Get the primary keys on the "from" side of the relationship. i.e Product.Category -> ProductID
                    return navigationProperty.FromEndMember.GetEntityType().KeyMembers.Select(m => m.Name);
                }
            }
            return navigationProperty.GetDependentProperties().Select(m => m.Name);
        }

        public override string GetSortExpression(ColumnProvider sortColumn) {
            return GetSortExpression(sortColumn, "{0}.{1}");
        }
    }
}